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.
1. Get your API token
Section titled “1. Get your API token”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.
2. Exchange it for an access token
Section titled “2. Exchange it for an access token”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.
3. Send the access token
Section titled “3. Send the access token”Every Ingestion and Query API request carries it in the authorization header (gRPC metadata key, or HTTP header):
authorization: Bearer eyJhbGciOi…4. Refresh before it expires
Section titled “4. Refresh before it expires”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:
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.
Authorization
Section titled “Authorization”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.
Organizations
Section titled “Organizations”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.