# Authentication

## Auth Keys

When interfacing with the Transloadit REST API, we **require** an `auth` param to be part of the multipart form request. An example of the JSON required for this field is shown below.

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```json
{
  "auth": {
    "key": "23c96d084c744219a2ce156772ec3211"
  }
}

```

The `key` field refers to the Auth Key associated with your Transloadit Workspace, found on the [Credentials](/c/credentials/) page. The above is therefore the minimum to authenticate yourself to use most Transloadit API endpoints, and is required for almost all requests.

## Signature Authentication

We recommend enabling Signature Authentication on your account, especially if you're integrating with Transloadit from an untrusted environment (such as from the browser with[Uppy](https://uppy.io/)). You can enable Signature Authentication from your[Workspace Settings](/c/settings/).

###### Warning

We *strongly recommend* enabling Signature Authentication when interfacing with our API, particularly in untrusted environments where users may be able to access yourAuth Key.

With Signature Authentication enabled, your Workspace's Auth Secret (found next to your Auth Key on the [Credentials](/c/credentials/) page) is then used to salt a hash generated from the request body, which contains both a `key` (which is yourAuth Key as mentioned earlier), and an `expires` param, which is a timestamp in the near future used as an expiry date for the request.

For creating Assemblies with Transloadit, your back-end could calculate aSignature that only covers certain parameters, authenticated users, and a timeframe that it deems legitimate usage. For instance, it would refuse to generate a signature for users that are not logged in. You could use any business logic on the server-side here to decide if you hand out a signature, or not, and Transloadit can be configured to reject any request for your Account that is not accompanied by a correct signature for the payload.

If you want to make Signature Authentication mandatory for all requests concerning your account:

1. Go to the [Workspace Settings](/c/settings/) in your account.
2. In the API Settings section, enable the **Require a correct Signature** option.
3. Hit the Save button.

###### Note

Most back-end [SDKs](/docs/sdks.md) automatically use Signature Authentication when you supply your Auth Secret. So perhaps, just this introduction is all you need to know. If you are integrating Transloadit into untrusted environments, however, such as browsers ([Uppy](/docs/sdks/uppy.md)!), you'll want to continue reading to see how your back-end can supply signatures to it.

## How to generate Signatures

So, how does this all look?

The typical `params` field when creating an Assembly **without** Signature Authentication is as follows:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```jsonc
{
  "auth": {
    "key": "23c96d084c744219a2ce156772ec3211"
  },
  "steps": {
    // …
  }
}

```

The `auth.key` in this example is the Auth Key from[API Credentials in your account](/c/template-credentials/).

To sign this request, the additional `auth.expires` field needs to be added. This adds it to our payload, which is protected by our signature. If someone would change it, Transloadit would reject the request as the signature no longer matches. You signed a different payload than the one we received. If the signature does match, then we will naturally compare and reject by date as instructed. This way, requests become very hard to indefinitely repeat by a third party that got a hold of this payload. Because even though our A+ grade HTTPS should already go a long way in preventing that, browser cache could be easier to snoop on.

The `expires` property must contain a timestamp in the (near) future. Use ISO 8601 format (`YYYY-MM-DDTHH:mm:ss.sssZ`) for the date, making sure that UTC is used for the timezone. For example:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```jsonc
{
  "auth": {
    "key": "23c96d084c744219a2ce156772ec3211",
    "expires": "2009-08-28T01:02:03.000Z"
  },
  "steps": {
    // …
  }
}

```

To calculate the signature for this request:

1. From your front-end, stringify the above JavaScript object into JSON and send it to your back-end.
2. From your back-end, calculate an [RFC 6234-compliant](https://www.ietf.org/rfc/rfc6234.txt) HMAC hex signature on the string, with your Auth Secret as the key, and SHA384 as the hash algorithm. Prefix the `signature` string with the algorithm name in lowercase. For example, for SHA384, use `sha384:<HMAC-signature>`. You can send that string to your front-end (as long as you made the appropriate checks to guarantee it was a genuine request from your front-end).
3. From your front-end, add a `signature` multipart POST field containing this value to your request (e.g., with a hidden field in an HTML form).

###### Note

If your implementation uses a `template_id` instead of `steps`, there's no need to generate a signature for the Instructions that your Template contains. We should only sign communication payloads.

###### Note

We strongly recommend including a randomly generated `nonce` — a unique value per request that prevents duplicate processing upon retries, can aid in debugging, and avoids attack vectors such as signature key reuse. It's important that the `nonce` needs to be unique for each request, otherwise it's ineffective.

The full request should look similar to the below:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```jsonc
{
  "params": {
    "auth": {
      "key": "23c96d084c744219a2ce156772ec3211",
      "expires": "2009-08-28T01:02:03.000Z",
      "nonce": "04ac6cb6-df43-41fb-a7fd-e5dd711a64e1",
    },
    "steps": {
      // …
    },
  },
  "signature": "9cf67cbba601e37ee10c442b037e0",
}

```

Once the request was received by Transloadit, we also generate a signature following the same process, and compare the two signatures. If the signatures are different, then our servers will respond with `INVALID_SIGNATURE`.

In summary, the process is as follows:

1. Generate a JSON payload to send to Transloadit as the `params` field.
2. Calculate a signature based off the contents of the payload, using your Auth Secret as a key.
3. Send the request to Transloadit, with the signature passed in the `signature` field
4. Transloadit will calculate the same signature using your account's Auth Secret, and the contents of the payload.
5. If the signatures match, the request is permitted, and an appropriate response is sent. Otherwise, the request is denied, and an error will be returned with the code`INVALID_SIGNATURE`.

This allows both parties to verify the other is authenticated (since a third party couldn't calculate a matching signature without access to your Auth Secret), without ever directly transmitting the Auth Secret.

Below are a few examples of how to perform a POST request to create an Assembly. We strongly recommend using one of our [SDKs](/docs/sdks.md), which handle signature generation automatically and are well-tested.

###### Important

To verify the signature we send with an Assembly Webhook, use the Auth Secret that belongs to the Auth Key used for that particular Assembly. To generate a signature for verification purposes, you must use the `sha1` algorithm due to backwards compatibility issues. Many longterm customers rely on these signatures to be created with sha1 from years ago, so we cannot just change that easily.

## Example code for different languages

The examples below demonstrate creating Assemblies using our official SDKs. The SDKs handle all signature generation internally, making integration simpler and more secure.

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```js
// yarn add transloadit
// or
// npm install --save transloadit

import { Transloadit } from 'transloadit'

const transloadit = new Transloadit({
  authKey: 'YOUR_TRANSLOADIT_KEY',
  authSecret: 'YOUR_TRANSLOADIT_SECRET',
})

const response = await transloadit.createAssembly({
  params: {
    template_id: 'YOUR_TRANSLOADIT_TEMPLATE_ID',
    // your other params like notify_url, fields, etc.
  },
  waitForCompletion: true,
})

console.log(response)

```

If you need to calculate a signature separately (e.g., for front-end use), you can use`calcSignature`:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```js
const { signature, params } = transloadit.calcSignature({
  template_id: 'YOUR_TRANSLOADIT_TEMPLATE_ID',
})

console.log(signature, params)

```

[View signature implementation source code](https://github.com/transloadit/node-sdk/blob/main/src/Transloadit.ts)

###### Note

If you prefer to see the raw signature implementation details (e.g., to implement signing in a language we don't have an SDK for), check the source code links above. The signature is an[RFC 6234-compliant](https://www.ietf.org/rfc/rfc6234.txt) HMAC hex digest calculated on the JSON-encoded params string, using your Auth Secret as the key and SHA384 as the hash algorithm. The signature must be prefixed with the algorithm name (e.g., `sha384:...`).

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```sh
curl --location 'https://api2.transloadit.com/assemblies' \
  --form 'params="{\"auth\":{\"key\":\"\23c96d084c744219a2ce156772ec3211\",\"expires\":\"2024/02/28 15:09:32.941Z\"},\"template_id\":\"\9cf67cbba601e37ee10c442b037e0\"}"' \
  --form 'signature="sha1:46253af0d7b2f0603375bbc6bfd5393363a8138c"' \
  --form 'files=@/path/to/your/file.jpg'

```

## Smart CDN signed URLs

To sign a Smart CDN URL, a similar process as for regular API signatures is used. A HMAC digest is calculated on a string, that is derived from the Smart CDN URL, with the Auth Secret as the key. For the signature to be valid, the used Auth Key must be enabled for Smart CDN use.

###### Important

To generate a signed Smart CDN URL, use the Auth Key designated for Smart CDN usage from your Credentials page. Smart CDN URLs use `sha256`. Regular API request signatures continue to use`sha384`.

###### Note

Legacy Smart CDN signatures based on `s=` and `expires=` are deprecated. New integrations should always use `sig=` with `exp=`.

The generation of a Smart CDN signature must be performed on the back-end. The process uses theAuth Secret, which is confidential and must not be exposed to your users on the front-end.

A typical Smart CDN URL has the following structure:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```
https://[your-workspace].tlcdn.com/[template-name]/[file-path]?[parameters]

```

* `[your-workspace]` is your Transloadit Workspace name
* `[template-name]` is the name of your Template
* `[file-path]` is the path to the file you want to transform
* `[parameters]` are desired transformation parameters (e.g. `h=100`)

A signed Smart CDN URL is generated by following these steps:

1. Add the `exp` query parameter for defining a time in the future after which the signature is not accepted by the Smart CDN anymore. This is useful to limit temporal access to a file. The expiration moment is represented by the number of milliseconds since the UNIX epoch (the midnight at the beginning of January 1, 1970, UTC). While this parameter is optional, we highly recommend always setting an expiration time. For example, a signature using `exp=1722517200000` is valid until Thu, 01 Aug 2024 13:00:00 GMT.
2. Add the `auth_key` query parameter to define the Auth Key corresponding to theAuth Secret that is used to create the signature. If this parameter is not set, Transloadit's API assumes that the oldest, Smart-CDN-enabled Auth Key pair has been used for the signature. Setting the `auth_key` parameters allows you to rotate your Auth Key without interrupting your users, and we thus highly recommend setting it. For example:`auth_key=23c96d084c744219a2ce156772ec3211`
3. Sort the query parameters according to unicode code points of the keys in descending order. The sorting should be stable, i.e. if a key appears multiple times in the query string, the corresponding values should retain their relative ordering. For example,`h=100&f=png&f=jpg&auth_key=hello&exp=123` is sorted into`auth_key=hello&exp=123&h=100&f=png&f=jpg`.
4. Construct the string to sign by concatenating the values:\
   ![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```
[your-workspace]/[template-name]/[file-path]?[sorted-parameters]  
```

The values for `[your-workspace]`, `[template-name]`, and `[file-path]` must be URL-encoded to ensure they only contain URL-safe characters. Note that the string does not start with a leading slash. The `?` character must be omitted if `[sorted-parameters]` is empty.
5\. Calculate an [RFC 6234-compliant](https://www.ietf.org/rfc/rfc6234.txt) HMAC hex signature on the string to sign, with your Auth Secret as the key, and SHA256 as the hash algorithm. Prefix the hex signature with the algorithm name in lowercase and a colon, i.e. `sha256`. For example, for SHA256, use `sha256:[hmac-signature]`.
6\. Append the prefixed hex signature under the `sig` query parameter to the URL, giving the signed Smart CDN URL:\
![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```
https://[your-workspace].tlcdn.com/[template-name]/[file-path]?[parameters]&sig=sha256:[hmac-signature]  
```

The values for `[your-workspace]`, `[template-name]`, and `[file-path]` must be URL-encoded to ensure they only contain URL-safe characters. This signed URL can then be sent to or used in your front-end until the expiration date is reached.

### Security and cache lifetime

Signed Smart CDN URLs are not just an access-control mechanism. Their expiration also determines how long a freshly generated result may remain cacheable.

* Shorter `exp` values reduce the replay window and tighten access control.
* Longer `exp` values increase cache reuse, reduce origin work, and generally lower latency and encoding volume.
* In practice, the effective cache lifetime of a signed Smart CDN response is bounded by the remaining signature lifetime.

That means the trade-off is straightforward:

* More security sensitivity: use a shorter `exp`, which also means a shorter effective cache TTL.
* More cache reuse and lower cost: use a longer `exp`, which also means the URL remains usable for longer.

Choose the expiration window according to the sensitivity of the content and how much cache reuse you want. For many image and preview use cases, a moderate expiration window gives a good balance. For highly sensitive content, use a much shorter one.

### Example code

Below you can find examples in different languages for generating signed Smart CDN URLs using our SDKs.

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```js
// yarn add transloadit
// or
// npm install --save transloadit

import { Transloadit } from 'transloadit'

const transloadit = new Transloadit({
  authKey: 'YOUR_TRANSLOADIT_KEY',
  authSecret: 'YOUR_TRANSLOADIT_SECRET',
})

const url = transloadit.getSignedSmartCDNUrl({
  workspace: 'YOUR_WORKSPACE',
  template: 'YOUR_TEMPLATE',
  input: 'image.png',
  urlParams: { height: 100, width: 100 },
})

console.log(url)

```

## Bearer tokens (client credentials)

If you need a short-lived token for server-to-server or headless clients, you can exchange yourAuth Key and Auth Secret for a Bearer token. This mirrors an OAuth 2.0 `client_credentials` flow but is handled directly by the Transloadit API. For the full endpoint reference, see the [/token API docs](/docs/api/token-post.md).

### `POST /token`

**Request**

* **Auth:** Basic Auth with your Auth Key and Auth Secret
* **Content-Type:** `application/x-www-form-urlencoded`
* **Body:**
  * `grant_type=client_credentials` (required)
  * `scope=assemblies:read assemblies:write` (optional; space or comma-separated)
  * `aud=api2` (optional)

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```bash
curl --request POST \
  --url 'https://api2.transloadit.com/token' \
  --user 'auth_key:auth_secret' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'scope=assemblies:read assemblies:write'

```

**Response**

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

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

```

Tokens are valid for six hours (`expires_in: 21600`).

###### Note

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

### 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 treatsSignature Authentication as satisfied and skips signature validation. Signature Authentication is enforced only for key/secret requests. Scope checks still apply. You can omit `auth.key` in `params`, but the `params` envelope is still required for endpoints that expect it. The `aud` value is stored for future audience enforcement.

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```bash
curl --request POST \
  --url 'https://api2.transloadit.com/assemblies' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --form 'params={"steps":{}}'

```

### MCP auto-auth for `/ai/chat`

If your `/ai/chat` steps call a Transloadit-hosted MCP server, API2 can mint and inject a short-lived Bearer token automatically (auto-auth). This is opt-in per MCP server entry:

![](/_next/static/media/copy.04p1cju9qekk_.svg?dpl=dpl_CtwzFbHWtqiCy9uvWb9fE7WvfP9N)

```json
{
  "mcp_servers": [
    {
      "type": "http",
      "url": "https://api2.transloadit.com/mcp",
      "auth": "transloadit"
    }
  ]
}

```

Behavior:

* If `auth: "transloadit"` is set and no `Authorization` header is present, API2 mints a token and injects `Authorization: Bearer <token>`.
* If `Authorization` is already provided in `mcp_servers[].headers`, it is left untouched.
* Auto-auth only works for Transloadit hosts (`*.transloadit.com`, `*.transloadit.dev`,`*.transloadit.work`) over HTTPS.

## FAQs

### Does Transloadit include signatures with its requests?

Yes, Signature Authentication is two-way, meaning that we will also provide a signature for any requests for [Webhooks](/docs/topics/webhooks.md) we send to you, so that you can verify the authenticity of any request received by your servers.

### Why can't I use my Auth Secret as the Bearer token?

As part of cryptographic standards, your Auth Secret should never be transmitted as part of the request, and instead is only used as a salt for the signature hash. This ensures a malicious actor can't intercept the request and spoof requests to your account using your secret. Therefore, we recommend keeping your Auth Secret safe, by only storing it on your back-end - using whichever secret management system you prefer. Examples are: [Vault](https://www.vaultproject.io/),[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/),[GCP Secret Manager](https://cloud.google.com/security/products/secret-manager), and[Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), but there are many more that could be suitable depending on your back-end platform of choice.

###### Note

You should ensure that Auth Secrets are *never* included as part of the front-end of your application, or exposed to users.

### What order do the keys in the body need to be in?

The order you choose for the keys in the body can be arbitrary, however it's important to note that whichever order you choose needs to be consistent with your signature generation. The hash generated depends on the contents of the JSON, and a different ordering will generate a different hash, meaning your request will be denied.
