<span aria-hidden="true" id="create-a-bearer-token"></span>



# Create a bearer token

Exchanges Auth Key credentials for a scoped bearer token.

`POST` `https://api2.transloadit.com/token`

This is the OAuth 2.0 token endpoint. Headless clients exchange their Auth Key and
Auth Secret for a short-lived Bearer token with the `client_credentials` grant, handled
directly by the Transloadit API. MCP clients that
[connected by URL](/docs/api/authentication.md#connect-an-mcp-client-by-url) redeem the
authorization code from the Console consent screen with the `authorization_code` grant
(PKCE `S256`, no HTTP Basic credentials) and rotate the resulting refresh token with the
`refresh_token` grant.

At `/token`, OAuth grant errors for `authorization_code` and `refresh_token` use the standard
`error` and `error_description` body. Rate limits return `RATE_LIMIT_REACHED` (429), and
unexpected internal failures return `SERVER_500` (500). Malformed requests rejected
before the grant is identified can receive an HTTP Basic challenge (401), or
`TOKEN_INVALID_REQUEST` if Basic credentials were supplied.

Client-credentials tokens are minted server-side using your Auth Key/Secret. If you expose token
creation via a UI, call `/token` from your backend (never directly from the browser).

Canceled Workspaces cannot create new bearer tokens. For endpoints whose documentation
explicitly allows reads after cancellation, use the [signed read recipe](/docs/api/authentication.md#read-access-after-cancellation)
with an existing active Auth Key instead. Billing has its own
[signed billing recipe](/docs/api/bill-date-get.md#billing-access-after-cancellation).

Requests must use `application/x-www-form-urlencoded`; the `client_credentials` grant also needs HTTP Basic Auth:

In a trusted server-side shell with `curl` and `jq`, set `TRANSLOADIT_KEY` and `TRANSLOADIT_SECRET` to your Auth Key and Auth Secret. Keep both credentials and the resulting token secret; never run this setup in browser code.

This example requests Assembly read/write access and saves the returned `access_token` as `TRANSLOADIT_TOKEN` for subsequent requests in the same shell. For another endpoint, use its listed scopes instead; the Auth Key must already grant them. Endpoints authenticated by an Auth Key or bearer token include the appropriate token setup in their request examples.

```bash
if ! TOKEN_RESPONSE="$(curl --fail-with-body -sS \
  --request POST \
  --url 'https://api2.transloadit.com/token' \
  --user "${TRANSLOADIT_KEY:?Set TRANSLOADIT_KEY}:${TRANSLOADIT_SECRET:?Set TRANSLOADIT_SECRET}" \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'aud=api2' \
  --data-urlencode 'scope=assemblies:read assemblies:write')"; then
  printf '%s\n' "$TOKEN_RESPONSE" >&2
  exit 1
fi
TRANSLOADIT_TOKEN="$(printf '%s' "$TOKEN_RESPONSE" |
  jq -er '.access_token | strings | select(length > 0)')" || exit 1
```

<span aria-hidden="true" id="authentication"></span>



## Authentication

HTTP Basic authentication with your Auth Key and secret is required for the following `grant_type` values: `client_credentials`. Other supported values do not need account credentials. Do not send an `Authorization` header for those requests; their grant-specific proof is still required.

<span aria-hidden="true" id="post-fields"></span>



## Form fields

Content type: `application/x-www-form-urlencoded`

The following fields may be sent at most once: `aud`, `client_assertion`, `client_assertion_type`, `client_id`, `code`, `code_verifier`, `grant_type`, `redirect_uri`, `refresh_token`, `resource`, `scope`.

<span aria-hidden="true" id="form-fields"></span>



[Complete JSON Schema](/api-reference/schemas/token-post/form-fields.json)

OAuth 2.0 token request form for the client-credentials, authorization-code and refresh-token grants.

Fields beyond those listed for this object are accepted.

<span aria-hidden="true" id="form-fields-aud"></span>



### `aud`

`string`

Optional audience value for `client_credentials`. If omitted, the deployment’s configured default is used (`api2` unless overridden). The other grants derive the audience from `resource`.

<span aria-hidden="true" id="form-fields-client_assertion"></span>



### `client_assertion`

`string`

A JWT signed with one of the client’s published keys (RFC 7523), from clients whose metadata allows `private_key_jwt`. Once a grant’s token request carried one, every later request for that grant must too. Its `iss` and `sub` are the client ID. Its `aud` is a configured token endpoint URL or its origin (without a trailing slash) for this deployment, and it expires within five minutes. Each `jti` is accepted once per client per region while the assertion remains valid. Use a fresh `jti` for each request.

<span aria-hidden="true" id="form-fields-client_assertion_type"></span>



### `client_assertion_type`

`string`

Always `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` when `client_assertion` is sent.

<span aria-hidden="true" id="form-fields-client_id"></span>



### `client_id`

`string`

The client identifier from dynamic registration or the HTTPS URL of the client’s metadata document. Required for `authorization_code` and `refresh_token`, unless a `client_assertion` names the client.

<span aria-hidden="true" id="form-fields-code"></span>



### `code`

`string`

The one-time authorization code delivered to the client’s redirect URI after consent. Required for `authorization_code`.

<span aria-hidden="true" id="form-fields-code_verifier"></span>



### `code_verifier`

`string`

The PKCE code verifier whose SHA-256 digest was sent as `code_challenge` when authorization started. Required for `authorization_code`.

<span aria-hidden="true" id="form-fields-grant_type"></span>



### `grant_type` (required)

`"authorization_code" | "client_credentials" | "refresh_token"`

Which OAuth 2.0 grant to run. `client_credentials` exchanges the Auth Key credentials supplied through HTTP Basic authentication, `authorization_code` redeems a code issued after consent in the Console together with its PKCE verifier, and `refresh_token` rotates a refresh token. The two public-client grants send no HTTP Basic credentials.

<span aria-hidden="true" id="form-fields-redirect_uri"></span>



### `redirect_uri`

`string`

The redirect URI used in the authorization request. Required for `authorization_code`.

<span aria-hidden="true" id="form-fields-refresh_token"></span>



### `refresh_token`

`string`

The refresh token to rotate. Required for `refresh_token`; the presented token stops working once a new pair is issued.

<span aria-hidden="true" id="form-fields-resource"></span>



### `resource`

`string`

The protected resource the token is for: the hosted MCP endpoint (`aud=mcp`) or the API origin itself (`aud=api2`). When omitted, the resource the authorization code or refresh token was issued for is used; it must match otherwise.

<span aria-hidden="true" id="form-fields-scope"></span>



### `scope`

`string`

Optional, space- or comma-separated list of scopes for `client_credentials`. If omitted, the token inherits all scopes granted to your Auth Key. The other grants keep the scopes granted at consent.

<span aria-hidden="true" id="using-the-token"></span>



## Using the token

Pass the token as `Authorization: Bearer <access_token>` on API requests. When a request is
authenticated with a valid Bearer token, API2 treats
*Signature Authentication* as satisfied and
skips signature validation. Signature Authentication is enforced only for key/secret requests.
Scope checks still apply. The default `api2` audience is accepted by regular API2 endpoints and is
valid for 21,600 seconds by default. The `mcp` audience is accepted by the MCP server, which relays it
to API2 with a service credential; it is rejected by regular API2 endpoints when presented directly, and valid for
604,800 seconds by default. Treat the response’s `expires_in` value as authoritative.

<span aria-hidden="true" id="response"></span>



## Response

Here’s an example response body:

```jsonc
{
  "access_token": "opaque-token",
  "expires_in": 21600,
  "scope": "assemblies:read assemblies:write",
  "token_type": "Bearer"
}
```

<span aria-hidden="true" id="response-1"></span>



### 2xx success

JSON response body. `application/json`

<span aria-hidden="true" id="response-1-body"></span>



#### Response body schema

[Complete JSON Schema](/api-reference/schemas/token-post/response-1-body.json)

The response contains only the fields listed for this object.

<span aria-hidden="true" id="response-1-body-access_token"></span>



##### `access_token` (required)

`string` (minimum length: 1)

The token to send in the Authorization header of subsequent API requests. Keep it secret.

<span aria-hidden="true" id="response-1-body-expires_in"></span>



##### `expires_in` (required)

`integer` (exclusive minimum: 0)

Token lifetime in seconds from issuance. Request a new token after it expires.

<span aria-hidden="true" id="response-1-body-refresh_token"></span>



##### `refresh_token`

`string` (minimum length: 1)

Returned by the `authorization_code` and `refresh_token` grants. Present it to `POST /token` with `grant_type=refresh_token` to obtain a new pair; every use rotates it and reuse of a rotated token revokes the whole lineage.

<span aria-hidden="true" id="response-1-body-scope"></span>



##### `scope` (required)

`string` (minimum length: 1, maximum length: 512)

Space-separated scopes granted to this token.

[Validation pattern (regular expression)](/api-reference/schemas/token-post/response-1-body.json)

<span aria-hidden="true" id="response-1-body-token_type"></span>



##### `token_type` (required)

`string` (always: "Bearer")

<span aria-hidden="true" id="response-2"></span>



### HTTP 400

JSON response body. `application/json`

<span aria-hidden="true" id="response-2-body"></span>



#### Response body schema

[Complete JSON Schema](/api-reference/schemas/token-post/response-2-body.json)

**Any of the following schemas may apply:**

* **error: "GET\_ACCOUNT\_UNKNOWN\_AUTH\_KEY"**

  Could not get workspace, this is an unknown Auth Key.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-2-body-anyOf-1-error"></span>



    **`error` (required)**

    `string` (always: "GET\_ACCOUNT\_UNKNOWN\_AUTH\_KEY")
  * <span aria-hidden="true" id="response-2-body-anyOf-1-http_code"></span>



    **`http_code`**

    `number` (always: 400)
  * <span aria-hidden="true" id="response-2-body-anyOf-1-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
* **error: "TOKEN\_INVALID\_GRANT\_TYPE"**

  Invalid grant type.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-2-body-anyOf-2-error"></span>



    **`error` (required)**

    `string` (always: "TOKEN\_INVALID\_GRANT\_TYPE")
  * <span aria-hidden="true" id="response-2-body-anyOf-2-http_code"></span>



    **`http_code`**

    `number` (always: 400)
  * <span aria-hidden="true" id="response-2-body-anyOf-2-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
* **error: "TOKEN\_INVALID\_REQUEST"**

  Invalid token request.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-2-body-anyOf-3-error"></span>



    **`error` (required)**

    `string` (always: "TOKEN\_INVALID\_REQUEST")
  * <span aria-hidden="true" id="response-2-body-anyOf-3-http_code"></span>



    **`http_code`**

    `number` (always: 400)
  * <span aria-hidden="true" id="response-2-body-anyOf-3-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
* **required properties: error**

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-2-body-anyOf-4-error"></span>



    **`error` (required)**

    `string`

    An RFC 6749, 7591 or 8707 error code such as `invalid_grant`.

    [Validation pattern (regular expression)](/api-reference/schemas/token-post/response-2-body.json)
  * <span aria-hidden="true" id="response-2-body-anyOf-4-error_description"></span>



    **`error_description`**

    `string`

    A human-readable explanation that never names an account.

<span aria-hidden="true" id="response-3"></span>



### HTTP 401

JSON response body. `application/json`

<span aria-hidden="true" id="response-3-body"></span>



#### Response body schema

[Complete JSON Schema](/api-reference/schemas/token-post/response-3-body.json)

**Any of the following schemas may apply:**

* **error: "SERVER\_401"**

  Authorization required.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-3-body-anyOf-1-error"></span>



    **`error` (required)**

    `string` (always: "SERVER\_401")
  * <span aria-hidden="true" id="response-3-body-anyOf-1-http_code"></span>



    **`http_code`**

    `number` (always: 401)
  * <span aria-hidden="true" id="response-3-body-anyOf-1-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
* **error: "TOKEN\_INVALID\_CREDENTIALS"**

  Invalid client credentials.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-3-body-anyOf-2-error"></span>



    **`error` (required)**

    `string` (always: "TOKEN\_INVALID\_CREDENTIALS")
  * <span aria-hidden="true" id="response-3-body-anyOf-2-http_code"></span>



    **`http_code`**

    `number` (always: 401)
  * <span aria-hidden="true" id="response-3-body-anyOf-2-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
* **required properties: error**

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-3-body-anyOf-3-error"></span>



    **`error` (required)**

    `string`

    An RFC 6749, 7591 or 8707 error code such as `invalid_grant`.

    [Validation pattern (regular expression)](/api-reference/schemas/token-post/response-3-body.json)
  * <span aria-hidden="true" id="response-3-body-anyOf-3-error_description"></span>



    **`error_description`**

    `string`

    A human-readable explanation that never names an account.

<span aria-hidden="true" id="response-4"></span>



### HTTP 403

JSON response body. `application/json`

<span aria-hidden="true" id="response-4-body"></span>



#### Response body schema

[Complete JSON Schema](/api-reference/schemas/token-post/response-4-body.json)

**Any of the following schemas may apply:**

* **error: "TOKEN\_INVALID\_AUDIENCE"**

  Invalid audience.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-4-body-anyOf-1-error"></span>



    **`error` (required)**

    `string` (always: "TOKEN\_INVALID\_AUDIENCE")
  * <span aria-hidden="true" id="response-4-body-anyOf-1-http_code"></span>



    **`http_code`**

    `number` (always: 403)
  * <span aria-hidden="true" id="response-4-body-anyOf-1-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
* **error: "TOKEN\_INVALID\_SCOPE"**

  Invalid or unauthorized scope.

  The response may contain additional fields.

  * <span aria-hidden="true" id="response-4-body-anyOf-2-error"></span>



    **`error` (required)**

    `string` (always: "TOKEN\_INVALID\_SCOPE")
  * <span aria-hidden="true" id="response-4-body-anyOf-2-http_code"></span>



    **`http_code`**

    `number` (always: 403)
  * <span aria-hidden="true" id="response-4-body-anyOf-2-message"></span>



    **`message`**

    `string`

    Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.

<span aria-hidden="true" id="response-5"></span>



### HTTP 429

JSON response body. `application/json`

<span aria-hidden="true" id="response-5-body"></span>



#### Response body schema

[Complete JSON Schema](/api-reference/schemas/token-post/response-5-body.json)

Request limit reached.

The response may contain additional fields.

<span aria-hidden="true" id="response-5-body-error"></span>



##### `error` (required)

`string` (always: "RATE\_LIMIT\_REACHED")

<span aria-hidden="true" id="response-5-body-http_code"></span>



##### `http_code`

`number` (always: 429)

<span aria-hidden="true" id="response-5-body-message"></span>



##### `message`

`string`

Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.

<span aria-hidden="true" id="response-6"></span>



### HTTP 500

JSON response body. `application/json`

<span aria-hidden="true" id="response-6-body"></span>



#### Response body schema

[Complete JSON Schema](/api-reference/schemas/token-post/response-6-body.json)

Unexpected error.

The response may contain additional fields.

<span aria-hidden="true" id="response-6-body-error"></span>



##### `error` (required)

`string` (always: "SERVER\_500")

<span aria-hidden="true" id="response-6-body-http_code"></span>



##### `http_code`

`number` (always: 500)

<span aria-hidden="true" id="response-6-body-message"></span>



##### `message`

`string`

Human-readable explanation of the error. Its wording can vary; use the `error` code when handling a specific failure.
