Authentication
Auth Keys
For multipart Assembly-creation requests authenticated with an Auth Key, include an auth object
inside the JSON-encoded params form field. The smallest such params value is shown below.
{
"auth": {
"key": "23c96d084c744219a2ce156772ec3211"
}
}
The key field refers to the Auth Key associated with your Transloadit Workspace, found
on the Credentials page. The above is the minimum authentication object for
an Assembly request that uses an Auth Key. Other endpoints and authentication methods can use
different request formats, as described by their endpoint documentation and below.
Assemblies that use /transloadit/import, directly or through a Template, require
either a bearer token or signed params with a future params.auth.expires timestamp.
This applies even when Workspace Signature Authentication is disabled. An Auth Key alone
is not sufficient; bearer-authenticated requests do not need a separate signature or expiry.
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). You can enable Signature Authentication from your Workspace Settings.
We strongly recommend enabling Signature Authentication when interfacing with our API, particularly in untrusted environments where users may be able to access your Auth Key.
With Signature Authentication enabled, your Workspace's Auth Secret (found
next to your Auth Key on the Credentials page) is
then used as the key for an HMAC generated from the exact serialized params value. That value contains both a key (which is your
Auth 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 a Signature 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. Transloadit can require a correct signature for the payload when an API request authenticates with your Auth Key.
To require Signature Authentication for API requests authenticated with your Auth Key:
- Go to the Workspace Settings in your account.
- In the API Settings section, enable the Require a correct Signature option.
- Hit the Save button.
Valid bearer tokens bypass signature requirements, including Workspace and Template settings; token scopes and audience restrictions still apply. These settings do not add authentication to capability-based access such as Assembly status, cancellation or resumable upload URLs. Keep Assembly IDs and capability URLs private, and follow each endpoint’s authentication guidance.
Most back-end SDKs 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!), 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:
{
"auth": {
"key": "23c96d084c744219a2ce156772ec3211"
},
"steps": {
// …
}
}
The auth.key in this example is the Auth Key from
API Credentials in your account.
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:
{
"auth": {
"key": "23c96d084c744219a2ce156772ec3211",
"expires": "YOUR_FUTURE_ISO_8601_TIMESTAMP"
},
"steps": {
// …
}
}
To calculate the signature for this request:
- From your front-end, stringify the above JavaScript object into JSON and send it to your back-end.
- From your back-end, calculate an RFC 6234-compliant HMAC
hex signature on the string, with your Auth Secret as the key and the algorithm
configured in your Auth Key’s
signature_algo. New Auth Keys default tosha384. Legacy Auth Keys without a configured algorithm acceptsha384,sha256, orsha1. Prefix thesignaturestring with the algorithm name in lowercase. For example, the default algorithm usessha384:<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). - From your front-end, add a
signaturemultipart POST field containing this value to your request (e.g., with a hidden field in an HTML form).
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.
We strongly recommend including a randomly generated params.nonce value for each request
at the top level of params. This makes independently generated signatures distinct and avoids accidental
signature reuse. A nonce does not make retries idempotent: retrying an Assembly-creation request
can create another Assembly. Handle retry deduplication in your application when needed.
Do not reuse signed params across endpoints. Template reads, Auth Key listing, Template Credential
listing, and billing reads reject signatures already recorded for a different endpoint or used
to create an Assembly, with SIGNATURE_REUSE_DETECTED. Generate fresh signed params for each request.
The full request should look similar to the below:
{
"params": {
"auth": {
"key": "23c96d084c744219a2ce156772ec3211",
"expires": "YOUR_FUTURE_ISO_8601_TIMESTAMP",
},
"nonce": "04ac6cb6-df43-41fb-a7fd-e5dd711a64e1",
"steps": {
// …
},
},
"signature": "sha384:YOUR_SIGNATURE",
}
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:
- Generate a JSON payload to send to Transloadit as the
paramsfield. - Calculate a signature based off the contents of the payload, using your Auth Secret as a key.
- Send the request to Transloadit, with the signature passed in the
signaturefield - Transloadit will calculate the same signature using your account's Auth Secret, and the contents of the payload.
- 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 lets Transloadit authenticate the caller and verify the integrity of params, since a third
party could not calculate a matching signature without access to your Auth Secret. TLS authenticates Transloadit to your client
and protects the connection. Webhook signing is a separate flow for requests Transloadit sends to
your servers.
Below are a few examples of how to perform a POST request to create an Assembly. We strongly recommend using one of our SDKs, which handle signature generation automatically and are well-tested.
Webhooks are signed differently from API requests.
Transloadit signs the exact JSON string in the transloadit form field using
HMAC-SHA1 and the applicable Auth Secret. The signature field contains
the hexadecimal digest without an algorithm prefix. Follow the
webhook verification instructions, including how to select the Auth Secret,
instead of using the API-request signing examples below.
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.
// yarn add @transloadit/node
// or
// npm install --save @transloadit/node
import { Transloadit } from '@transloadit/node'
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:
const { signature, params } = transloadit.calcSignature({
template_id: 'YOUR_TRANSLOADIT_TEMPLATE_ID',
})
console.log(signature, params)
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 HMAC hex digest calculated on the
JSON-encoded params string, using your Auth Secret as
the key and the algorithm configured in your Auth Key’s signature_algo. New Auth Keys default
to sha384. Prefix the signature with its lowercase algorithm name
(e.g., sha384:...).
curl --fail-with-body -sS --location 'https://api2.transloadit.com/assemblies' \
--form 'params={"auth":{"key":"23c96d084c744219a2ce156772ec3211","expires":"YOUR_FUTURE_ISO_8601_TIMESTAMP"},"template_id":"9cf67cbba601e37ee10c442b037e0"}' \
--form 'signature=sha384:YOUR_SIGNATURE' \
--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.
To generate a signed Smart CDN URL, use the Auth Key designated for Smart CDN usage from your
Credentials page. Smart CDN URLs require sha256. Regular API request
signatures use the algorithm configured in the Auth Key’s signature_algo; new Auth Keys default
to sha384.
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 the Auth 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:
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:
- Add the
expquery 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 usingexp=1722517200000is valid until Thu, 01 Aug 2024 13:00:00 GMT. - Add the
auth_keyquery parameter to define the Auth Key corresponding to the Auth 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 theauth_keyparameters allows you to rotate your Auth Key without interrupting your users, and we thus highly recommend setting it. For example:auth_key=23c96d084c744219a2ce156772ec3211 - Sort the query parameters by key in ascending order using UTF-16 code units, matching
URLSearchParams.sort(). 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=123is sorted intoauth_key=hello&exp=123&f=png&f=jpg&h=100. - Construct the string to sign by concatenating the values:
The values for
[your-workspace]/[template-name]/[file-path]?[sorted-parameters][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. - Calculate an RFC 6234-compliant 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, usesha256:[hmac-signature]. - Append the prefixed hex signature under the
sigquery parameter to the URL, giving the signed Smart CDN URL:The values forhttps://[your-workspace].tlcdn.com/[template-name]/[file-path]?[parameters]&sig=sha256:[hmac-signature][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
expvalues reduce the replay window and tighten access control. - Longer
expvalues 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.
// yarn add @transloadit/node
// or
// npm install --save @transloadit/node
import { Transloadit } from '@transloadit/node'
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)
Read access after cancellation
Canceled Workspaces cannot create new bearer tokens. Endpoints that explicitly permit access after cancellation link to this recipe. Use an existing active Auth Key and its Auth Secret; the key must still grant the scopes listed for the endpoint. This does not restore write access or make other endpoints available after cancellation.
In a trusted server-side Node.js project, install the SDK with yarn add @transloadit/node.
Set TRANSLOADIT_KEY and TRANSLOADIT_SECRET, then set TRANSLOADIT_URL to the complete HTTPS
URL shown on the endpoint page, replacing any path parameters with your resource values.
Keep both credentials, the signed URL and the response private. Never run this setup in browser code.
The SDK signs params with sha384, the default for new Auth Keys, and supplies the expiry.
The example adds a fresh nonce to avoid signature reuse. If your key uses another signature
algorithm, pass it as the second argument to calcSignature. Put any endpoint filters inside
the object passed as its first argument, alongside the nonce.
The SDK does not call /token for this recipe.
Save this as read-api.mjs and run node read-api.mjs:
import { randomUUID } from 'node:crypto'
import { Transloadit } from '@transloadit/node'
const { TRANSLOADIT_KEY, TRANSLOADIT_SECRET, TRANSLOADIT_URL } = process.env
if (!TRANSLOADIT_KEY || !TRANSLOADIT_SECRET || !TRANSLOADIT_URL) {
throw new Error('Set TRANSLOADIT_KEY, TRANSLOADIT_SECRET, and TRANSLOADIT_URL')
}
const transloadit = new Transloadit({
authKey: TRANSLOADIT_KEY,
authSecret: TRANSLOADIT_SECRET,
})
const { params, signature } = transloadit.calcSignature({ nonce: randomUUID() })
const url = new URL(TRANSLOADIT_URL)
url.searchParams.set('params', params)
url.searchParams.set('signature', signature)
const response = await fetch(url, { redirect: 'error' })
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`)
const result = await response.json()
if (result.error) throw new Error(result.error)
console.log(JSON.stringify(result, null, 2))
Connect an MCP client by URL
Agent clients such as ChatGPT, Codex, Claude.ai, Claude Desktop, Claude Code and Cursor connect
to the hosted MCP server at https://api2.transloadit.com/mcp with nothing but that URL. The Transloadit API
is an OAuth 2.1 authorization server for it: the client discovers the endpoints, identifies
itself, sends you to the Console to log in and consent, and ends up with a short-lived Bearer
token for the mcp audience plus a refresh token. No Auth Secret ever leaves your account.
- Discovery. The MCP server answers unauthenticated requests with
401and aWWW-Authenticate: Bearer resource_metadata="https://api2.transloadit.com/.well-known/oauth-protected-resource/mcp"header. That RFC 9728 document nameshttps://api2.transloadit.comas the authorization server, whose RFC 8414 metadata athttps://api2.transloadit.com/.well-known/oauth-authorization-serverlists the endpoints below. - Client identification. Either present a
Client ID Metadata Document:
an HTTPS URL that is the
client_idand serves JSON with the sameclient_id, aclient_nameand theredirect_uris. Or register once through RFC 7591 Dynamic Client Registration athttps://api2.transloadit.com/oauth/register; clients either send only theirclient_id(token_endpoint_auth_method: "none") or authenticate withprivate_key_jwtand ajwks_uri, and the answer carries an opaqueclient_id. Redirect URIs must be HTTPS URLs, matched exactly, or loopbackhttp://localhost/http://127.0.0.1URLs, matched on any port. Token requests authenticate the client withnoneorprivate_key_jwt, whichever its document lists (intoken_endpoint_auth_methods_supported, or else astoken_endpoint_auth_method) or its registration declares. The first token request binds the grant to the method it used: once a request for a grant carries an RFC 7523client_assertion, every later token and revocation request for it must carry one too. That assertion is a JWT signed (RS256 or ES256) with a key from the client’sjwks_uri, with the client ID asissandsub. Itsaudis a configured token endpoint URL or its origin (without a trailing slash) for this deployment. It has anexpwithin five minutes and ajtithat is accepted once per client per region while the assertion remains valid. Use a freshjtifor each request. - Consent. The client opens
https://transloadit.com/c/oauth/authorizewithresponse_type=code, itsclient_id,redirect_uri, a PKCEcode_challenge(S256only), optionallyscopeandstate, andresource=https://api2.transloadit.com/mcp. You log in, pick a Workspace and approve; the Console redirects the browser back withcode, yourstateandiss=https://api2.transloadit.com. The code is bound to the client, the redirect URI, the challenge and the Workspace, and expires after 120 seconds. - Tokens. The client posts
grant_type=authorization_codewithcode,code_verifier,client_idandredirect_uritohttps://api2.transloadit.com/tokenand receives anaccess_token(valid for 604,800 seconds, 7 days, by default), itsscopeand arefresh_token. Postinggrant_type=refresh_tokenwithrefresh_tokenandclient_idreturns a new pair and retires the presented refresh token; refresh tokens live 30 days and reusing a retired one revokes the whole lineage. - Revocation. Post
token(an access or refresh token) tohttps://api2.transloadit.com/oauth/revokeper RFC 7009, or revoke the connection in the Console.
The resource indicator (RFC 8707) decides what the
token is for. resource=https://api2.transloadit.com/mcp (the default when omitted) mints an mcp token that
carries at most the safe MCP scopes (assemblies:write, assemblies:read, templates:read), narrowed to what the Workspace’s
Auth Key grants, and that only the MCP server accepts. resource=https://api2.transloadit.com (with or
without a trailing slash) mints an api2 token for applications that call the REST API directly,
such as Vercel Connect integrations: it carries the
requested scopes narrowed to the Auth Key’s scopes, or everything the key grants when scope is
omitted, except that a grant never carries Auth Key or Template Credential management
(auth_keys:*, template_credentials:*, or the global read/write that imply them), so it cannot
create or reveal lasting credentials. Any other resource answers invalid_target.
OAuth protocol errors from client registration, authorization, and revocation use
the standard error and error_description body; rate limits can return RATE_LIMIT_REACHED.
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.
Bearer tokens (client credentials)
CI jobs, servers and other headless clients that hold an
Auth Key and Auth Secret exchange them for a short-lived Bearer token
with the OAuth 2.0 client_credentials grant, handled directly by the Transloadit API. Use this when there is no browser
to consent in; interactive MCP clients should connect by URL as described above. For the full endpoint
reference, see the /token API docs.
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 and audience checks still apply. The mcp audience is accepted by the MCP server, which
relays it to API2 with a service credential; it is rejected when presented directly to regular
API2 endpoints. You can omit auth.key in params, but the params envelope is still required
for endpoints that expect it.
curl --fail-with-body -sS --request POST \
--url 'https://api2.transloadit.com/assemblies' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--form 'params={"template_id":"YOUR_TEMPLATE_ID"}'
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:
{
"mcp_servers": [
{
"type": "http",
"url": "https://api2.transloadit.com/mcp",
"auth": "transloadit"
}
]
}
Behavior:
- If
auth: "transloadit"is set and noAuthorizationheader is present, API2 mints a token and injectsAuthorization: Bearer <token>. - If
Authorizationis already provided inmcp_servers[].headers, it is left untouched. - Auto-auth only works over HTTPS for Transloadit-managed apex hosts and subdomains:
transloadit.com,*.transloadit.com,transloadit.dev,*.transloadit.dev,transloadit.website,*.transloadit.website,transloadit.work,*.transloadit.work. - The URL must use port
443and the exact/mcppath or a subpath below/mcp/. - The URL must not contain credentials, a query string, or a fragment.
- The Auth Key must grant at least one of these safe MCP scopes:
assemblies:write,assemblies:read,templates:read. - The minted token is narrowed to the intersection of those safe MCP scopes and the Auth Key’s scopes; it never gains a scope that the Auth Key does not grant.
FAQs
Does Transloadit include signatures with its requests?
Transloadit signs Webhook requests so your server can verify their authenticity. Webhook signing is separate from the HMAC that authenticates API requests sent to Transloadit.
Why can’t I use my Auth Secret as the Bearer token?
Except for the server-to-server token exchange described above, your Auth Secret must
never be transmitted to a client or included in API request parameters. POST /token sends it as
the HTTP Basic password over HTTPS and must be called only from your back-end. For signed requests,
the secret remains on your back-end and is used as the HMAC key. This prevents a malicious actor
from intercepting a signed request and spoofing requests to your account. Keep your Auth Secret safe by using
whichever secret management system you prefer. Examples are: Vault,
AWS Secrets Manager,
GCP Secret Manager, and
Kubernetes Secrets, but there are many
more that could be suitable depending on your back-end platform of choice.
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.