Direct Upload

Upload a brand image, font, video or audio file in three steps, without sending the bytes through a JSON body

Published

POST
/v1/brand-assets/upload-url
curl -X POST "https://api.orshot.com/v1/brand-assets/upload-url" \
  -H "Authorization: Bearer <ORSHOT_API_KEY>"

Overview#

Use direct upload when the file is large or when your client cannot build a base64 string. The flow has three steps:

  1. Ask for an upload URL. It is valid for 5 minutes.
  2. PUT the raw file bytes to that URL. This request takes no API key; the URL itself is the credential.
  3. Confirm the upload. The file is checked and stored in your brand library, the same way the /add endpoints store it.

The same three endpoints work for every asset kind. The image-only paths /images/upload-url, /images/upload and /images/confirm still work for existing clients and behave the same with kind fixed to image.

Endpoints
POST https://api.orshot.com/v1/brand-assets/upload-url
PUT  <uploadUrl from step 1>
POST https://api.orshot.com/v1/brand-assets/confirm

Request#

JavaScript
const { data } = await fetch("https://api.orshot.com/v1/brand-assets/upload-url", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    kind: "video",
    contentType: "video/mp4",
    fileName: "launch-teaser.mp4",
  }),
}).then((r) => r.json());

// data.uploadUrl, data.key, data.maxBytes, data.expiresAt
Shell
curl -X PUT "<data.uploadUrl>" \
  -H "Content-Type: video/mp4" \
  --data-binary @launch-teaser.mp4

The response is { "data": { "key", "kind", "bytes" } }. No API key is sent on this step.

JavaScript
await fetch("https://api.orshot.com/v1/brand-assets/confirm", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    key: data.key,
    name: "Launch teaser",
    tags: ["launch", "video"],
  }),
});
JSON
{
  "data": {
    "kind": "video",
    "asset": {
      "id": 418,
      "name": "Launch teaser",
      "direct_url": "https://storage.orshot.com/cloud/w-50/renders/brand_assets/x9Kq2mVt7Lp.mp4",
      "file_size": 18329044,
      "tags": ["launch", "video"],
      "workspace_id": 50
    },
    "url": "https://storage.orshot.com/cloud/w-50/renders/brand_assets/x9Kq2mVt7Lp.mp4"
  }
}

The asset object has the same fields as the matching /add endpoint for that kind.

Step 1: Upload URL parameters#

ParameterTypeRequiredDescription
kindStringYesimage, font, video or audio. Not needed on the /images/upload-url alias
contentTypeStringYesMIME type of the file, for example image/png. Must be one of the kind's supported formats
fileNameStringNoUsed to work out the file extension when contentType is ambiguous

Response fields:

FieldDescription
uploadUrlPUT the file here. Valid for 5 minutes
directUploadUrlA second URL for the same upload that goes straight to storage. Use it only if your client cannot PUT to uploadUrl. For SVG, send the headers in directUploadHeaders with it
keyPass this to /confirm
kind, contentTypeEchoed back
maxBytesThe size limit for this kind
expiresAtWhen the upload URL stops working (ISO 8601)

Step 3: Confirm parameters#

ParameterTypeRequiredDescription
keyStringYesThe key from step 1
nameStringNoDisplay name in the library. Falls back to fileName
fileNameStringNoOriginal file name
tagsString[]NoTags, stored lowercase
metadataObjectNoArbitrary metadata. For video and audio, pass width, height or duration here if you want them stored; the API does not read them from the file. meta is accepted as an alias for images

Size limits and formats#

KindLimitFormats
image10MBPNG, JPEG, WebP, SVG, GIF
font10MBTTF, OTF, WOFF, WOFF2
video60MBMP4, WebM, MOV, AVI, MKV, FLV, WMV, M4V, GIF
audio50MBMP3, WAV, M4A, AAC, OGG, Opus, FLAC, WebM, AIFF, WMA

Processing after confirm is the same as for the /add endpoint of each kind: images are optimised and SVGs cleaned, videos may be converted, fonts are parsed for their family name. See Upload Brand Image, Upload Font, Upload Video and Upload Audio for the rules of each kind.

Notes#

  • The file type is read from the bytes, not from contentType or the file extension. A file that is not a real image, font, video or audio file is refused at confirm.
  • Unconfirmed uploads are deleted automatically. Confirm within a few minutes of the PUT.
  • A key can only be confirmed by the workspace that requested it.

Error responses#

StepStatusWhen
1400kind missing or unknown, or contentType missing or not supported for that kind
2400Empty body. Send the raw bytes, for example curl -X PUT --data-binary @file
2403The upload URL was changed or is not valid
2410The upload URL has expired. Request a new one
2413The file is larger than the kind's limit
2429More than 60 PUTs in a minute for one workspace
3400key missing, the file is over the limit, or the bytes are not a valid file of that kind. Image problems carry the same code values as Upload Brand Image
3403The key belongs to another workspace or was not issued by this API
3404Nothing was uploaded for this key yet, or the unconfirmed file was already cleaned up

Query Parameters#

ParameterTypeRequiredDescription
embedIdStringNoEmbed instance ID. Pair with embedUserId to scope the asset to one embed user
embedUserIdStringNoThe same userId your app passes to the embed URL. Requires embedId

Pass them on the /confirm call, which is where the asset is created. See Per-User Brand Assets.

Rate Limits#

  • Steps 1 and 3: 30 requests per minute per endpoint
  • Step 2: 60 PUTs per minute per workspace
Was this page helpful?

Ready to automate?

Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.

Get your API key
  • Image, PDF and video generation via API
  • Visual editor with AI and smart layouts
  • Zapier, Make, MCP and 40+ integrations
  • White-label embed for your own app
  • 100 free credits a month, no credit card required