Skip to content

Authentication

Both the Ingestion API and the Query API authenticate with an OAuth access token sent as a bearer token. You do not get an access token from the portal directly: you get a long-lived API token, and exchange it for access tokens as you need them. This page covers both APIs.

In the portal, open the user menu and choose Get API token — see API keys. The token acts as you: it can reach every workspace you are a member of, with your role in each.

The API token is a base64url-encoded JSON object naming the deployment it belongs to:

{"url": "https://app.metrana.ai", "token": "…"}

url is the base URL for everything below — the token exchange, the token refresh, and both APIs. Decode it rather than hard-coding app.metrana.ai, and the same code works against a self-hosted deployment.

Terminal window
curl -s -X POST "$METRANA_URL/api/management/v1/tokens/exchange" \
-H "Content-Type: application/json" \
-d "{\"api_token\": \"$METRANA_API_KEY\"}"
{"access_token": "eyJhbGciOi…", "refresh_token": "eyJhbGciOi…"}

The exchange endpoint takes no Authorization header — the API token is the credential. A token the deployment does not know returns 404.

Every Ingestion and Query API request carries it in the authorization header (gRPC metadata key, or HTTP header):

authorization: Bearer eyJhbGciOi…

Access tokens are short-lived JWTs; read the exp claim (seconds since the epoch) from the token’s payload to know when. Before it expires, trade the refresh token for a new pair at the deployment’s OpenID Connect token endpoint:

Terminal window
curl -s -X POST "$METRANA_URL/auth/realms/metrana/protocol/openid-connect/token" \
-d grant_type=refresh_token \
-d client_id=metrana-sdk \
-d "refresh_token=$REFRESH_TOKEN"

The response carries a new access_token and refresh_token. If the refresh is rejected (400/401 — the session was revoked or the refresh token expired), run the exchange from step 2 again. A 429 or 5xx from either endpoint is transient: back off and retry.

The SDK refreshes once less than 30 seconds of validity remain; doing the same keeps long-running jobs and streams from failing mid-request.

Authentication tells Metrana who you are; each request is then checked against the workspace it names:

API Requirement
Ingestion You must be a member (or higher) of the workspace. Viewers cannot write.
Query You must be able to read the workspace: any role, or none for a public workspace.

The Query API also accepts requests without an authorization header, as an anonymous caller that can read public workspaces only. A header that is present but malformed or invalid is rejected rather than downgraded to anonymous.

Failures look like this:

Failure gRPC status HTTP status
Missing, malformed or expired token UNAUTHENTICATED 401
Valid token, no access to the workspace (or the workspace does not exist) PERMISSION_DENIED 403

A workspace that does not exist is reported exactly like one you cannot access, so a caller cannot probe for workspace names.

Workspace names are unique per organization. Requests address a workspace by name, plus an optional organization slug: you can omit it while no other organization has a workspace of the same name. Once the name is ambiguous, requests without organization fail with INVALID_ARGUMENT (400). Setting it always is the safe choice for long-lived integrations.