Skip to main content

Authenticate

Every Foundation4 API request carries an API key identifier and secret. This tutorial confirms the master key, creates a key for the remaining tutorials and sets the shell variables that every example in the documentation uses. Integrators and administrators complete this tutorial before calling the API; Install for evaluation provides a deployment to call.

The tutorial follows four steps, which every new deployment repeats once:

  1. API access. A shell with kubectl access to the cluster opens a connection to the API server.
  2. Master key. The shell variables receive the master key from the Kubernetes secret.
  3. Key check. POST /login confirms that the master key authenticates.
  4. Tutorial key. The master key creates a key with fewer permissions, and the shell variables switch to the new key.

Shell variables​

The examples in the documentation read the base URL and the credentials from three shell variables:

VariableValue
FOUNDATION4_URLThe base URL of the API server, with no path or version prefix, such as http://localhost:8080 for the port forward in API access
FOUNDATION4_API_KEYThe identifier of an API key, a universally unique identifier (UUID)
FOUNDATION4_API_SECRETThe secret of the same API key

The variable names differ from the FOUNDATION4AI_ prefix of server configuration, so client settings and server settings are never confused.

API access​

The commands in this tutorial run in a shell on a machine with kubectl access to the cluster, such as the operator's workstation or the node of an evaluation installation. In a separate terminal on the same machine, forward a local port to the API server service:

kubectl port-forward -n foundation4ai svc/foundation4ai-api-server 8080:80

Expected result: kubectl reports that the port forward listens on 127.0.0.1:8080 and keeps running until the terminal closes. The API then answers at http://localhost:8080. A port forward that still runs from Install for evaluation serves the same purpose. An installation with an ingress or a Gateway answers at the installation's host name instead, as described in API and dashboard access, and FOUNDATION4_URL holds that URL.

Master key​

The installation creates the master key from the identifier and secret in the foundation4ai-secrets Kubernetes secret. The master key holds every permission and serves only for administration, such as creating the keys that client applications use. Access control describes the master key and the permission model.

Set the shell variables with the base URL and the master key, read from the secret. The commands copy the master key into the shell variables without printing the secret. base64 -d is base64 --decode or base64 -D on some systems.

export FOUNDATION4_URL=http://localhost:8080
export FOUNDATION4_API_KEY="$(kubectl get secret foundation4ai-secrets -n foundation4ai \
-o jsonpath='{.data.FOUNDATION4AI_APP_MASTER_KEY}' | base64 -d)"
export FOUNDATION4_API_SECRET="$(kubectl get secret foundation4ai-secrets -n foundation4ai \
-o jsonpath='{.data.FOUNDATION4AI_APP_MASTER_SECRET}' | base64 -d)"
echo "$FOUNDATION4_API_KEY"

Expected result: the master key identifier, a universally unique identifier (UUID). An operator who keeps the master key in the organization's secret store, as Secrets and keys describes, can set the two variables from the store instead.

Request headers​

Every request other than the root route, the health check, the metrics routes and the API documentation routes carries two headers:

  • x-api-key. The key identifier.
  • x-api-key-secret. The key secret.

A request that fails authentication returns HTTP 401 with one of the following messages in the error.message field of the response body:

MessageCause
Unauthorized: No API Key specified.The x-api-key header is missing.
Unauthorized: No API Secret Key specified.The x-api-key-secret header is missing.
Invalid key IDThe x-api-key value is not a UUID.
Invalid API Key or SecretThe key does not exist, the secret does not match, or the key is inactive or expired. The API server also returns this message when the database is unavailable during the check.

Key check​

POST /login confirms that a key authenticates and returns the key's name and permissions on object types. The request has no body.

curl -X POST "$FOUNDATION4_URL/login" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET"

Expected result: status 201. The following response is shortened to three of the object types:

{
"message": "OK",
"key": "Master Admin Key",
"description": null,
"permissions": {
"pipelines": {"access_level": 7, "classifications": ["*"]},
"agents": {"access_level": 7, "classifications": []},
"llms": {"access_level": 7, "classifications": []}
}
}

The key field holds the key name, not the identifier. Each access_level is the sum of the permission bits: read 4, write 2 and execute 1. The classifications list is the classification allow-list, which applies to pipelines only. The order of the entries in permissions can differ from one response to the next.

The complete response for the master key lists 13 object types: the nine object types that Access control describes, plus contexts, context-messages, documents and permissions. No API operation checks these four object types in the current release, so permissions on them have no effect.

Tutorial key​

The remaining tutorials use a key named getting-started with the following permissions on object types:

Object typePermissionPurpose in the tutorials
embedding-providers4 (read)List embedding providers
text-splitter-providers4 (read)List text splitter providers
embedding-models6 (read and write)Find the seeded embedding model and name the model in a new pipeline
text-splitters7 (all)Find the seeded text splitter, name the splitter in a new pipeline and split documents
pipelines7 (all), allow-list *Create a pipeline, add documents and search
llms7 (all)Register and query an LLM
agents7 (all)Create and run an agent

Creating a pipeline requires write permission on the text splitter and the embedding model that the pipeline names, and adding a document requires execute permission on the text splitter. The tutorial key therefore holds write permission on both object types. Keys for client applications hold narrower permissions on individual objects, as described in Scoped keys.

Create the key with the master key:

curl -X POST "$FOUNDATION4_URL/api-keys" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "getting-started",
"description": "Key for the getting-started tutorials",
"permissions": {
"embedding-providers": 4,
"text-splitter-providers": 4,
"embedding-models": 6,
"text-splitters": 7,
"pipelines": 7,
"llms": 7,
"agents": 7
},
"classifications": ["*"]
}'

Expected result: status 201 and the new key. The secret field holds a generated 48-character string of letters, digits, + and /, shown here as a placeholder:

{
"id": "9f1c2d3e-4b5a-4c6d-8e7f-0a1b2c3d4e5f",
"created_at": "2026-09-29T12:00:00.418273Z",
"updated_at": "2026-09-29T12:00:00.418273Z",
"name": "getting-started",
"description": "Key for the getting-started tutorials",
"secret": "change-me",
"active": true,
"expiration": null
}

Record the id and secret values. Foundation4 returns the secret only in this response and stores only a hash of the secret, so a lost secret cannot be recovered.

Tutorial key check​

Switch the shell variables to the tutorial key and repeat the key check:

export FOUNDATION4_API_KEY=<id from the response>
export FOUNDATION4_API_SECRET=<secret from the response>
curl -X POST "$FOUNDATION4_URL/login" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET"

Expected result: status 201, with key set to getting-started and the seven permission entries of the tutorial key:

{
"message": "OK",
"key": "getting-started",
"description": "Key for the getting-started tutorials",
"permissions": {
"embedding-providers": {"access_level": 4, "classifications": []},
"text-splitter-providers": {"access_level": 4, "classifications": []},
"embedding-models": {"access_level": 6, "classifications": []},
"text-splitters": {"access_level": 7, "classifications": []},
"pipelines": {"access_level": 7, "classifications": ["*"]},
"llms": {"access_level": 7, "classifications": []},
"agents": {"access_level": 7, "classifications": []}
}
}

First search continues with the tutorial key.

Later sessions​

The shell variables and the port forward last only as long as their terminals. A later session starts the port forward again, as in API access, and sets FOUNDATION4_URL, FOUNDATION4_API_KEY and FOUNDATION4_API_SECRET to the base URL and the recorded tutorial key.