Authentication
This article describes how to authenticate API requests to Tealium APIs using OAuth2 client credentials and bearer tokens.
All Tealium API access requires a bearer token. The Developer Portal uses the OAuth2 Client Credentials grant, which is designed for server-to-server communication where no end-user interaction is required.
The authentication flow works as follows:
- Your application sends its client ID and client secret to the token endpoint.
- The token endpoint validates the credentials and returns a JWT access token.
- Your application includes the token in the
Authorizationheader of every API request. - The API gateway validates the token and checks that the requested operation matches the token’s scopes.
Client credentials are for backend services only. Never use them in browser or mobile clients.
Before you begin
You need a client ID and client secret from an application registered in the Developer Portal. For instructions on creating an application, see Applications.
The Developer Portal displays the client secret only once when you create the application. If you did not save it, rotate the secret in the portal to generate a new one before authenticating.
Request an access token
You can generate a bearer token without writing any code by clicking Generate Token on the application detail page or from the API reference screen. For more information, see Generate a bearer token from the API reference.
Tealium APIs use OAuth 2.0 Client Credentials with compound scopes. The scope format is:
{account}:{profile}:{resource}:{action}
| Segment | Description |
|---|---|
account |
Your Tealium account name, for example, tealium-corp. |
profile |
A specific profile, for example, main, or * for all profiles in the account. |
resource |
The API resource, for example, labels or audiences. |
action |
read for GET requests, write for POST, PUT, and DELETE requests, manage for administrative operations. |
Request the scope exactly as it was granted. If your application was granted access to all profiles in an account (using *), you must request the wildcard form. A wildcard token authorizes requests against any profile in that account.
If your subscription grants access to all profiles in an account, request using the * wildcard:
curl -X POST \
https://gateway-am.tealium.com/{domain}/oauth/token \
-u '{client_id}:{client_secret}' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'scope=tealium-corp:*:labels:read'
If your subscription grants access to a specific profile, request that profile:
curl -X POST \
https://gateway-am.tealium.com/{domain}/oauth/token \
-u '{client_id}:{client_secret}' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'scope=tealium-corp:main:labels:read'
To request multiple scopes, separate them with a space:
-d 'scope=tealium-corp:*:labels:read tealium-corp:*:audiences:write'
Your token contains only the scopes you explicitly request. Even if your application has multiple scopes granted, specify only what you need for each operation. Cache the token and reuse it until it expires. Requesting a new token on every API call triggers throttling.
A successful response returns a JSON object with the following parameters:
| Field | Description |
|---|---|
access_token |
The JWT to include in API requests. |
token_type |
Always Bearer. |
expires_in |
Token lifetime in seconds. |
scope |
The scopes granted to this token. |
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "my-account:main:labels:read my-account:main:audiences:write"
}
For engine-enforced APIs, such as the Moments API, the scope field contains engine-level scopes instead of resource-level scopes:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "my-account:main:e01ce645-3964-4343-9a0c-7d165e653b35"
}
For details on scope format, see Scopes.
Make an authenticated request
Include the access token in the Authorization header of each API request:
curl -X GET \
https://api.tealium.com/v{YYYY-MM}/{api}/accounts/tealium-corp/profiles/main/labels \
-H 'Authorization: Bearer {access_token}'
Use an asterisk (*) as a wildcard to request access to any profile in the URL path. Both of the following requests work with a tealium-corp:*:labels:read token:
curl -X GET \
https://api.tealium.com/v{YYYY-MM}/{api}/accounts/tealium-corp/profiles/main/labels \
-H 'Authorization: Bearer {access_token}'
curl -X GET \
https://api.tealium.com/v{YYYY-MM}/{api}/accounts/tealium-corp/profiles/staging/labels \
-H 'Authorization: Bearer {access_token}'
Token expiration and renewal
| Aspect | Details |
|---|---|
| Format | JWT signed with RS256. |
| Expiration | Set by the expires_in value in the token response. Typically 3600 seconds (1 hour). |
| Renewal | Request a new token before expiration. There is no refresh token in the client credentials flow. |
| Scope claim | The JWT contains a scope claim listing the compound scopes granted to the token. |
Scope enforcement
The API gateway validates scopes on every request:
- The gateway extracts the
scopeclaim from the JWT. - It matches the request context (account, profile, resource, and action) against the token’s scopes.
- If a matching scope is found, the request proceeds.
- If no matching scope is found, the gateway returns
403 Forbidden.
When requesting a token, you can request a subset of your subscribed scopes. Use this scope subsetting to generate a token with only the permissions a specific task requires.
Moments API engine enforcement
For APIs with engine-level authorization, such as the Moments API, the gateway performs an additional check after validating account and profile scopes:
- The gateway extracts the engine ID from the request URL path (for example,
/engines/{engineId}/...). - It checks whether the token’s
scopeclaim contains a matching engine scope:{account}:{profile}:{engineId}. - A wildcard engine scope (
{account}:{profile}:*) matches any engine in that account and profile. - If no matching engine scope is found, the gateway returns
403 Forbidden.
Account and profile access alone is not sufficient for engine-enforced APIs. The resource owner must also grant engine-level access before your application can call engine-enforced endpoints.
| Scope format | API type | Example |
|---|---|---|
{account}:{profile}:{resource}:{action} |
Resource-scoped APIs | acme:main:labels:read |
{account}:{profile}:{engineId} |
Engine-enforced APIs | acme:main:e01ce645-3964-... |
{account}:{profile}:* |
Engine-enforced APIs (wildcard) | acme:main:* |
Security best practices
- Never expose client secrets. Store them in environment variables or a secrets manager. Never commit them to source control.
- Request minimal scopes. Only request the scopes your application needs for each operation.
- Cache tokens until expiry. Request a new token only when the current one is about to expire, not on every API call.
- Rotate secrets immediately if compromised. If you believe your credentials have been exposed, rotate them immediately via the Developer Portal application settings. Rotate regularly as a general practice.
- Use server-side requests only. Client credentials are for backend services only. Never use them in browser or mobile clients.
- Select specific engines. For engine-enforced APIs, prefer selecting specific engines rather than All engines to follow least-privilege principles.
Error responses
| HTTP status | Meaning | Resolution |
|---|---|---|
| 401 Unauthorized | Invalid or expired token | Request a new access token. |
| 403 Forbidden | Token is valid but lacks the required scope. | Check your subscription scopes and account/profile assignments. |
| 403 Forbidden | Token lacks engine-level authorization. | Ensure the resource owner has granted access to the specific engine. |
| 400 Bad Request | Invalid token request: wrong grant_type or missing fields. |
Verify your token request parameters. |
This page was last updated: July 27, 2026