Orshot API error reference

Every Orshot API error code, what triggers it and the fix. Covers auth, quota, template and render failures.

Updated

A quick reference for understanding and resolving errors from the Orshot API. Each error includes the HTTP code, message, why it happens, and how to fix it.


Authentication Errors#

Authorization Header Missing#

Code403
MessageAccess Forbidden: Authorization header is missing in the request

Why it happens: Your API request doesn't include the required Authorization header.

Fix: Add the header to your request:

JavaScript
headers: {
  "Authorization": "Bearer YOUR_API_KEY"
}

Get your API key from Workspace Settings → API Keys. See Get API Key for setup instructions.


API Key Missing#

Code403
MessageAccess Forbidden: API Key is missing in the request
Error codeapi_key_missing

Why it happens: The Authorization header exists but doesn't contain a valid API key after "Bearer ".

Fix: Ensure the format is Bearer YOUR_API_KEY (with a space after "Bearer").


Invalid API Key#

Code400
MessageAccess Forbidden: Invalid API Key

Why it happens: The API key doesn't exist or has been revoked.

Fix: Generate a new API key from Workspace Settings → API Keys.


Invalid API Key or User ID#

Code400
MessageAccess Forbidden: The API Key or User ID do not exist
Error codeinvalid_api_key_or_user_id

Why it happens: Your API key isn't linked to a valid user account.

Fix: Double-check your API key or generate a new one from your dashboard.


OAuth Token Errors#

OAuth Token Expired#

Code401
MessageAccess Forbidden: OAuth token expired

Why it happens: Your OAuth access token has expired (tokens are valid for 15 minutes).

Fix: Use the refresh token to obtain a new access token, or re-authenticate via the OAuth device flow.


Invalid OAuth Token#

Code401
MessageAccess Forbidden: Invalid OAuth token

Why it happens: The OAuth token doesn't exist, has been revoked, or is malformed.

Fix: Re-authenticate via the OAuth device flow to obtain a new token.


OAuth Insufficient Scope#

Code403
MessageInsufficient scope. This token is missing: <scope>

Why it happens: The OAuth token is valid but was granted without the scope this endpoint needs. Rendering requires render:generate; other endpoints require their own scopes.

Fix: Re-authorize the app requesting the missing scope, then retry with the new token. Scopes are granted at authorization time, so an existing token cannot gain one.


Insufficient Role#

Code403
Error codeinsufficient_role
MessageThis endpoint isn't available to your role in this workspace. Ask a workspace owner or admin.

Why it happens: The OAuth token acts for a reviewer, and this endpoint isn't part of what reviewers do. Reviewers comment, approve and request changes, so endpoints like stock media, connected integrations, brand assets, dynamic URLs, workflows and the workspace itself (/v1/workspace) answer this, even through the Orshot MCP server. Third-party apps get oauth_not_allowed from some of them, like dynamic URLs and workflows.

Fix: Ask a workspace owner or admin to do it, or to change your role to Member or Admin.


Workspace Not Accessible#

Code403
Error codeworkspace_not_accessible
MessageYou are no longer a member of this workspace, so this connection can't reach it. Ask an owner or admin to add you again.

Why it happens: The OAuth token acts for someone who has left the workspace, or was removed from it. The same code answers when the x-workspace-id header names a workspace the token wasn't granted: Workspace '<id>' is not accessible on this token.

Fix: Call GET /v1/me. When you still belong to another workspace on the token, it answers for that one and lists the workspaces you can reach. Send one of their IDs in the x-workspace-id header. If you belong to none of them, ask an owner or admin to add you again.


App in Development Mode#

Code403
MessageThis app is in development mode. Only the developer or approved test users can make API calls.

Why it happens: The developer application behind this token has not been approved for public use yet, so it only serves its owner and the test users listed on it.

Fix: Use the developer's own account or a listed test user while building. To serve everyone, submit the app for review from the developer application settings.


Subscription & Usage Errors#

Subscription Inactive#

Code403
MessageSubscription inactive or you've consumed all requests

Why it happens: Your subscription has expired, or you've used all your credits for the billing period.

Fix:

  • Check your usage at Billing & Usage (see Credit Usage to understand consumption)
  • Upgrade your plan or wait for the billing cycle to reset
  • Enable Overage Credits to keep rendering beyond limits

Free Monthly Credits Used Up#

Code403
Error codefree_monthly_credits_exhausted
Message100 free credits for this month reached, it resets on <date>. Upgrade to a bigger plan to get more monthly credits

Why it happens: The Free plan includes 100 render credits every month. The month runs from the anniversary of the day the account signed up to the next one (UTC), so each account has its own reset date. One credit is one image, one PDF page or one video second. The allowance belongs to the workspace owner, so every workspace they own and every member of those workspaces draws on the same pool, and every render route draws on it: the REST API, Studio, Dynamic URLs, embeds, workflows and agents.

Fix:

  • Wait for the date in the message. A fresh 100 credits arrive automatically at 00:00 UTC on your reset date, and the date shown is your own account's. There is nothing to claim and nothing to reset, and the same API key keeps working. Unused credits do not roll over.
  • Or move to a paid plan to render now. A plan change takes effect immediately and the refusal stops on the next request.
  • Check where you stand with GET /v1/me. plan.render_credits reports used, limit and remaining for the current period. Billing & Usage shows the same figures.

If the count is higher than you expected, see Credit Usage: a 3-page carousel costs 3 credits and a 10-second video costs 10.

This is not the AI credit limit. AI images, AI video, voiceovers and background removal draw on a separate pool that does not refill monthly. See AI Credits Exhausted.


Billing Account Unresolved#

Code403
Error codebilling_account_unresolved
MessageWe could not work out which account pays for this workspace, so nothing was rendered and nothing was charged.

Why it happens: A fault on our side. Renders bill to the workspace owner, and this request could not be matched to that account. It is not something you configured.

Fix: Nothing was charged, so retry the request once. If it keeps failing, contact hi@orshot.com with your workspace slug and the time of the request.


Video on Free Plan#

Code403
MessageVideo generation is available only on paid plans

Why it happens: You're trying to generate video output (MP4, WebM, MOV, MKV, GIF) on a Free plan.

Fix: Upgrade to a paid plan to unlock video generation.


Voice Not on Plan#

HTTP status403
Error codevoice_not_on_plan or voice_needs_subscription

Why it happens: AI voice generation is not included in the current plan, or the account does not have an active paid subscription.

Fix: Choose a plan with AI voice generation. Trial accounts can preview voices but cannot generate new voiceovers.


Voice Model Not on Plan#

HTTP status403
Error codevoice_model_not_on_plan

Why it happens: The template uses an expressive or multilingual voice model that is not included in the current plan.

Fix: Use the fast voice model or upgrade to a plan with premium voice models.


Voice Limit Reached#

HTTP status403 or 429
Error codevoice_limit_reached or voice_daily_burst_reached

Why it happens: The account has used its AI credits for the billing period, or generated too many new voice clips in one day. Existing generated narration continues to render.

Fix: Wait for the limit to reset or upgrade your plan. You can check current voice usage with Get Profile and Workspaces.


Voice Not Available#

HTTP status400
Error codevoice_not_available

Why it happens: The selected voice is not available in Orshot's public voice catalog.

Fix: Choose one of the voices returned by the Orshot MCP audio authoring options tool, or select another voice in Studio.


Partner Limit Reached#

Code429
MessagePartner monthly render limit reached (X/Y). Limit resets next month.

Why it happens: Your partner sandbox workspace has exceeded its monthly credit allocation. The API message reports the limit in renders (2 renders = 1 credit), so the X/Y values shown are render counts.

Fix: Wait for the monthly reset or contact your partner account manager.


Invalid Partner Account#

Code403
MessageThis sandbox workspace is not associated with a valid partner account

Why it happens: The sandbox workspace isn't properly linked to a partner.

Fix: Contact Orshot support to verify your partner account setup.


Version History Errors#

Version History Locked#

Code403
MessageRestoring versions needs a paid plan
Code idPLAN_LOCKED

Why it happens: Versions are kept on every plan, but restoring, copying and naming them need a paid workspace plan.

Fix: Upgrade the workspace, then retry. The versions are already there.


Version Out of Reach#

Code403
MessageThis version is older than your plan can restore
Code idPLAN_REACH

Why it happens: Each plan can restore the newest N automatic versions (Launch 3, Grow 8, Scale 12). Named versions are always within reach.

Fix: Name versions you will need later, or upgrade the plan.


Named Version Slots Used Up#

Code409
MessageYour plan keeps up to N named versions per template. Delete one to name another.
Code idNAMED_CAP

Fix: Delete a named version on the template (DELETE …/versions/:versionId) or upgrade the plan.


Template Changed While Restoring#

Code409
MessageThe template changed while restoring. Reload and try again.
Code idTEMPLATE_CHANGED

Why it happens: A save landed on the template between reading it and writing the restored design. Nothing was written.

Fix: Retry the restore; the version is still on the timeline.


Invalid Version Request#

Code400
Code idinvalid_template_id, invalid_version_id, invalid_body, invalid_query, label_required, invalid_label, label_too_long, invalid_name

Fix: Ids are integers, label is a non-empty string of at most 80 characters, name at most 255, and snapshot is true or false.


Template Errors#

Template ID Missing#

Code400
Message'templateId' is missing in the request body

Why it happens: Your request doesn't include a templateId.

Fix: Add the template ID to your request body:

JavaScript
body: JSON.stringify({
  templateId: "your-template-id",
  modifications: { ... }
})

Find your template ID in the Template Playground or when viewing a template. Learn more about Template IDs.


Template Not Found#

Code403
MessageStudio template with ID 'X' not found

Why it happens: The template ID doesn't exist in your workspace.

Fix:

  • Verify the template ID is correct
  • Make sure the template belongs to your workspace
  • Check your templates

Library Template Not Found#

Code400
MessagetemplateId not found. Please double check the 'templateId' value

Why it happens: The library template ID doesn't match any available template.

Fix: Check the Templates Library for valid template IDs.


Modifications Missing#

Code400
MessageModifications for 'X' is missing in the request

Why it happens: The modifications object is missing from your request.

Fix: Include modifications in your request body:

JavaScript
body: JSON.stringify({
  templateId: "your-template-id",
  modifications: {
    title: "My Title",
    // ... other modifications
  },
});

Learn more about Modifications.


Approval Errors#

When an approval flow needs approval for automation, its templates render through the API only once they're in a stage that allows the output (image, PDF or video). Every render path checks this before any cache: the REST API, async jobs, Dynamic URLs, webhooks, Automation workflows, n8n, Make, Zapier, the SDKs and the MCP server. A blocked render renders nothing and costs no credits. Nothing is queued, so send the render again once the template is approved. Templates in no approval flow are never affected, and neither are downloads from the studio or the embed editor.

Render refusals carry the readable text in error and message, the error family in code, one entry per approval flow holding the template in blockers, and a helpUrl pointing here:

JSON
{
  "error": "This template is waiting for approval in 'Legal' and can't be rendered through the API yet.",
  "code": "template_not_approved",
  "message": "This template is waiting for approval in 'Legal' and can't be rendered through the API yet.",
  "blockers": [
    {
      "flow": { "id": 3, "slug": "legal", "name": "Legal" },
      "stage": { "id": 9, "slug": "legal-review", "name": "Legal review", "category": "review" },
      "reason": "not_in_render_stage",
      "waitingOn": ["Priya Shah", "Legal group"],
      "renderStage": { "slug": "approved-for-automation", "name": "Automation approved" }
    }
  ],
  "context": { "output": "image" },
  "helpUrl": "https://orshot.com/docs/error-reference#template-not-approved"
}

Where they appear:

  • REST API, SDKs and integrations: the JSON body above.
  • Async jobs (response.mode: "async"): refused when you create the job, so no job is created.
  • Dynamic URLs: a placeholder PNG at the template's size, with the same status, Cache-Control: no-store and the code in the X-Orshot-Error header, so an <img> shows a placeholder instead of a broken image.
  • Automation workflows: the run is skipped before anything renders, no credits are used, and the workspace owner gets one email.
  • Render logs: the row records the status and the code.

A blocked render is never reported as a broken automation.

Template Not Approved#

Code403
Error codetemplate_not_approved
MessageThis template is waiting for approval in '<approval flow>' and can't be rendered through the API yet.

Why it happens: The template is in an approval flow that needs approval for automation, and it isn't approved yet. It sits in a stage that doesn't render, like a draft or review stage, or in an approved stage whose approval was withdrawn. blockers[].reason says which case applies:

ReasonMeaning
not_in_render_stageWaiting for approval in a stage that doesn't render.
design_onlyThe design is approved, but not for automation yet. See Design Approved, Not for Automation.
approval_missingIn an approved stage with no approval that counts, for example after the approval was withdrawn.
approved_version_missingThe stage keeps rendering the approved version while edits wait for approval, but that version isn't available. See Approved Version Missing.

Fix: Open Approvals in your workspace and approve the template, or move it to a stage that allows automation. blockers[].waitingOn lists who can approve it, and blockers[].renderStage names a stage that renders and that the template can move to from where it is (for example "Automation approved"), or its own stage when it only needs approving again. Then send the render again.


Design Approved, Not for Automation#

Code403
Error codetemplate_not_approved
MessageThis template's design is approved, but it isn't approved for automation yet. It needs approval in '<stage>' in <approval flow> next.

When the approval flow lets the template move straight to a stage that renders, the message ends with Move it to '<stage>' in <approval flow> to render it through the API. instead. blockers[].reason is design_only.

Why it happens: The template is in a "Design approved" stage. Someone signed off on how it looks, but that stage doesn't let automation render it. Approving a design and approving it for automation are two separate steps.

Fix: Open Approvals in your workspace and send the template on to the stage named in the message. blockers[].nextStage names the review it needs next (for example "Legal review"), and blockers[].renderStage names a stage that renders and that it can move to from where it is. Once it's approved there, send the render again.


Approved Version Missing#

Code403
Error codetemplate_not_approved
MessageThis template was edited after it was approved, and the approved version is no longer available. It needs to be approved again before it can render.

blockers[].reason or context.reason is approved_version_missing.

Why it happens: The template's stage is set to keep rendering the approved version while edits wait for approval. The template was edited since, so automation would render the approved version, but that version can't be found. The render is refused rather than render a design nobody approved.

Fix: Open Approvals in your workspace and approve the template again, so its current design becomes the approved version. Then send the render again.


Output Not Approved#

Code403
Error codeoutput_not_approved
MessageThis template is approved for image renders, not video. Get it approved for video to render it.

Why it happens: The template's stage, or the approval it received, covers other outputs. Approvals cover outputs, not file formats: image covers png, jpg, jpeg, webp and avif, PDF covers pdf, and video covers mp4, webm, mov and mkv. A gif of an animated template counts as video, and of a static one as image. When no output is approved yet, the message says "approved for nothing yet".

Fix: Render one of the approved outputs, or open Approvals in your workspace and get the template approved for this one. When the stage itself doesn't render this output, turn it on under the stage's "Automation can render" rule in the flow's settings, or move the template to a stage that renders it.


Edited Since Approval#

Code403
Error codeedited_since_approval
MessageThis template was edited after it was approved in '<stage>'. It needs to be approved again before it can render.

Why it happens: The design changed after it was approved, so the approval no longer covers it. Renaming, tags, folders and thumbnails don't count as edits. Approved stages don't allow edits unless a workspace turns that on, so edits in the studio cause this only where one did. A design changed outside the Orshot editor after it was approved causes it even in a locked stage.

Fix: Open Approvals in your workspace and approve the template again, or restore the version that was approved. Stages set to keep rendering the approved version during edits render that version instead, with the Approved Version Rendered warning.


Condition Violated#

Code403
Error codecondition_violated
MessageThis render goes past the field limits set when the template was approved: 'headline' must be at most 40 characters (got 41).

Why it happens: The approver set field limits on the values you send: a maximum or minimum length, fields that can't be changed, allowed values, or allowed image hosts. violations lists each field and the limit it goes past. In the approvals API these limits are the conditions of an approval.

Fix: Change the values named in violations, or ask an approver to approve the template again with different field limits.


Approvals Unavailable#

Code503
Error codeapprovals_unavailable
MessageApprovals aren't available right now. Try again in a moment.

Why it happens: A fault on our side: the approval state of this template couldn't be read. The render is refused rather than risk rendering something that isn't approved. Nothing was charged. The approvals API returns the same error, with code set to approval.storage_error, approval.internal_error or approval.transport_unavailable.

Fix: Retry in a moment. If it keeps happening, contact hi@orshot.com.


Approvals Action Errors#

Errors from Approvals itself: the dashboard, the approvals API and agents. The body has the error family in error, a specific code (for example approval.item.decide_denied), a readable message, and where it helps, required (the missing access, scope or level), role, context, issues and current. Branch on error; code is more specific.

Permission Denied#

Code403
Error codepermission_denied
MessageYou don't have access to do this. Ask an owner or admin.

Most refusals say exactly what's missing instead, for example You're not an approver in the 'Legal review' stage. Maya or someone in Legal can approve it.

Why it happens: Your role, or your access in that approval flow or stage, doesn't include the action. required names what was missing, and code tells the cases apart:

CodeMeaning
approval.item.decide_denied and other _denied codesYour access doesn't cover this action. The code names it.
approval.stage.enter_deniedTemplates in that stage can't move straight to this one.
approval.cross_site_requestA write to the Orshot app's approvals routes came from a page on another site.
insufficient_scopeThe OAuth token lacks the scope in required. Reconnect the app and allow approvals.
approval.person_requiredThis acts for one person, so it takes their OAuth token, or an API key with their user id in X-Orshot-User-Id.
approval.member_header_not_memberX-Orshot-User-Id names someone who isn't an owner, admin or member of the key's workspace now. Reviewers use their own OAuth token.
approval.member_header_not_allowedAn OAuth token sent X-Orshot-User-Id. It already acts for the person who connected it.
approval.decision_person_requiredAn API key sent X-Orshot-User-Id to approve, request changes or withdraw an approval. Approvals come from the person, through their OAuth token or the Orshot app.
approval.workspace_requiredThe key or token has no workspace. Pick one with the x-workspace-id header.
approval.workspace_not_accessibleThe request names another workspace that this key or token can't open.
approval.sandbox_workspaceSandbox workspaces can't use approval flows.

Reviewers comment, approve and request changes, whatever access a flow gives them. Everything else is refused with role: "reviewer" and these messages:

CodeMessage
approval.item.move_deniedReviewers can't move templates between stages. They approve or request changes.
approval.item.remove_deniedReviewers can't take templates out of a flow. They approve or request changes.
approval.item.add_deniedReviewers can't add templates to a flow. They approve or request changes.
approval.flow.manage_deniedReviewers can't change approval flows. They approve or request changes.
approval.flow.duplicate_deniedReviewers can't copy approval flows. They approve or request changes.

approval.flow.manage_denied covers stages, access and archiving too.

Through the API, an app or AI agent a reviewer connects is refused one step earlier for changing flows, stages or access, copying a flow and taking a template out: insufficient_scope, with required: "workspace:approvals:admin". Reconnecting doesn't help, because a reviewer's connection never gets that permission.

Fix: Ask a workspace owner or admin to give you access in the approval flow, or use a key or token that has it. No access lets a reviewer do the actions above, so ask someone on the team to do them.


Not Found#

Code404
Error codenot_found
MessageThis doesn't exist or isn't available to you.

Why it happens: The approval flow, stage, template or comment doesn't exist in this workspace. Reviewers limited to some approval flows also get this, never a 403, for templates outside them. Copying an approval flow to a workspace you're not in answers the same way as a workspace that doesn't exist: code approval.workspace_not_found, message Workspace not found.

Fix: Check the id, and that you're in the right workspace.


Stage Locked#

Code423
Error codestage_locked
MessageThis template can't be edited in the '<stage>' stage. Move it to a stage that allows edits first.

Why it happens: The template's stage doesn't allow edits. Review, approved and done stages don't by default, so a design in review or approved can't change by mistake from the studio, the API or an agent.

Fix: Someone who can move the template takes it to a stage that allows edits. Then it can be edited.

Ready for automation: a template in a locked stage that automation renders from answers with code approval.edit.choice_required instead, and context.choices lists what can happen after the edit. Send approvedStageEdit (keep, review or draft) with the design write, or pick one in the studio. keep needs an approver of the stage, an owner or an admin (code approval.edit.keep_denied otherwise). See how to edit a template after it's approved.


Stale Review#

Code409
Error codestale_review
MessageThe design changed while you were reviewing it. Review the latest version and try again.

Fix: Open the template again and review the latest version before you approve or request changes. Over the API, current.currentVersion is the version to send as reviewedVersion.


State Conflict#

Code409
Error codestate_conflict
MessageSomeone else moved this template first. Refresh to see where it is now.

Fix: current shows where the template is now and its revision. Refresh, then retry with the current revision if the move still makes sense.

Over the API, a retry with the same Idempotency-Key while the first request is still running gets this error too, with code approval.request_in_progress. Try again in a moment.


Plan Required#

Code403
Error codeplan_required
MessageApprovals are available on the Scale plan and above.

Parts of Approvals that bigger plans include say so, with their own code: Groups are not included in your plan. See the pricing page to upgrade. (approval.plan.groups), and the same for copying approval flows to another workspace (approval.plan.duplicateAcrossWorkspaces), requiring more than one approver (approval.plan.multiApprover) and keeping the approved version rendering during edits (approval.plan.keepApprovedRendering). After a downgrade, stages already set up that way keep working. Only switching to one of them is refused.

Fix: See pricing. Approval flows you already have are kept after a downgrade to a plan without them, and they no longer block automation. See what a downgrade changes.


Enterprise API Required#

Code403
Error codeenterprise_api_required
MessageUsing approvals with an API key needs an Enterprise plan. Contact hi@orshot.com to turn it on.

Third-party OAuth apps get Approvals through third-party apps need an Enterprise plan. Contact hi@orshot.com to turn it on.

Why it happens: The Approvals API is available on Enterprise plans, or through first-party apps (the Orshot app and the Orshot MCP server). For an API key or your own OAuth app, read, move, decide and manage access are turned on separately (see access levels), and required names the level this call needs. Approvals in the Orshot dashboard don't need it.

Fix: Contact hi@orshot.com to turn on the level you need, or see Enterprise pricing.


Flow Limit Reached#

Code403
Error codeflow_limit_reached
MessageYour plan allows 3 approval flows. Archive one or upgrade to add more.

The same error covers stages: Your plan allows 5 stages in one approval flow. Archive one or upgrade to add more. The codes are approval.limit.maxFlows and approval.limit.maxStagesPerFlow.

After a downgrade to a plan with fewer approval flows, the flows past the new limit are read-only. Adding, moving, approving, requesting changes, withdrawing an approval, taking a template out, or changing the flow, its stages or its access there is refused with code approval.flow.over_plan_limit, and context.limit holds the plan's limit:

This approval flow is over your plan's limit of 3. Archive a flow or upgrade to use it.

Reading, comments and archiving still work there, and such a flow blocks no automation. See what a downgrade changes.

Fix: Archive an approval flow (or a stage), or see pricing for higher limits. Archiving an active flow makes the next one in the list active.


No Approver#

Code422
Error codeno_approver
MessageNobody would be able to approve in the '<stage>' stage. Keep at least one approver there.

Why it happens: A review stage that has approvers must keep at least one, and this change to access or groups would remove the last.

Fix: Add another approver to the stage first, then make the change.

When a template goes into a review stage that nobody can approve in yet, the move still goes through, and the response carries a no_approver warning instead: Nobody can approve in the '<stage>' stage yet. An owner or admin can add approvers.


Comment Required#

Code422
Error codecomment_required
MessageAdd a comment explaining what needs to change.

Why it happens: Requesting changes needs a comment, and some stages ask for one when a template moves in.

Fix: Send the request again with a comment that says what to change.


Validation Failed#

Code422
Error codevalidation_failed
MessageSome fields aren't valid: <fields>.

Fix: issues lists each invalid field and why. Correct them and retry.

Over the API, two codes cover the Idempotency-Key header: approval.idempotency_key_invalid (it isn't 1 to 255 visible characters) and approval.idempotency_key_reused (the key was already used with a different request). approval.member_header_invalid means X-Orshot-User-Id isn't a user id.

Some limits show up here as an issue code: a comment can @mention up to 20 people (too_many_mentions), and an approval's field limits take field names of at most 200 characters and at most 32,000 characters as JSON (too_big).

The Orshot app's own approvals routes also refuse a write whose body isn't JSON with a 415 (code approval.json_required), and a body over 1 MB with a 413 (code approval.body_too_large). Both use this error family.


Request Body Errors#


Invalid JSON Request Body#

Code400
MessageRequest body is not valid JSON: ...
Error codeinvalid_json

Why it happens: The request body could not be parsed as JSON. Common causes: trailing commas, single quotes instead of double quotes, or a raw template expression pasted into the body.

Fix: Send a valid JSON body with Content-Type: application/json. If you build the body by hand in an automation tool, prefer the tool's JSON or key-value fields over a free-text body.


Request Body Too Large#

Code413
MessageRequest body too large (max 10mb)
Error codepayload_too_large

Why it happens: The request body exceeds the 10mb limit, usually because of large base64-encoded images inline in modifications.

Fix: Host large images and pass their URL instead of inlining base64 data.


Invalid PDF Options#

Code400
MessageInvalid imageFormat. Must be 'auto' or 'jpeg'. and similar

Why it happens: A value in pdfOptions is outside the accepted range. The checked fields are margin (pixel values only), rangeFrom and rangeTo (positive integers), colorMode ("rgb" or "cmyk"), imageFormat ("auto" or "jpeg"), imageQuality (1 to 100), and maxImageDpi (0 to 2400).

Fix: Correct the field named in the message. See PDF Options for every accepted value.


Provider Errors#

Some requests call another service on your behalf: stock media (Pixabay, Unsplash), voice generation, or a connected app like Google Sheets, Airtable or Notion. When that service fails, the message names it and the status it returned.

Provider Refused the Request#

Code424
MessageSheets read failed (HTTP 403), Notion query failed (HTTP 404) and similar

Why it happens: A connected app rejected this specific request: the sheet, base, database or file doesn't exist, or isn't shared with the connected account.

Fix: Check the id or link in your request and that the connected account can open it. Retrying the same request returns the same error.


Provider Unavailable#

Code503
MessageVoice provider did not respond within 60s. Try again. and similar

Why it happens: The other service is down, timed out, or returned a server error.

Fix: Retry after a short delay. A 429 from the same services means their rate limit was hit; wait a minute before retrying.


Render Warnings#

Studio render requests are inspected against the template before rendering. Problems with modification content never block the render: the render succeeds exactly as before, and a warnings array rides along so you can catch silently broken automations (a typo'd key, an unevaluated n8n expression) without losing output.

Where warnings appear:

  • JSON responses (response.type of url or base64) include a top-level warnings array when there is at least one warning:
JSON
{
  "data": { "content": "https://storage.orshot.com/...", "type": "url", "format": "png" },
  "warnings": [
    {
      "title": "Unknown modification 'titel' was ignored. No parameter named 'titel' exists on this template. Did you mean 'title'? Valid keys: title, description, photo.",
      "details": ""
    }
  ]
}
  • Binary responses carry an X-Orshot-Warnings header (a JSON-encoded array, capped at 5 entries) since there is no JSON body to extend.
  • Render logs always record the warnings, so you can debug from the dashboard even for dynamic URL image tags where headers and bodies are not inspectable.

The warning types:

Unknown Modification Key#

Unknown modification 'X' was ignored. Did you mean 'Y'?...

A key in modifications doesn't match any parameter on the template, so it did nothing. Common causes: a typo, a parameter renamed or deleted after you built the integration, or a payload meant for a different template. The warning suggests the closest matching key and lists valid keys (the 5 closest on large templates). Fetch the current parameter list from the template's modifications endpoint or the Template Playground and update your mapping.

Page Prefix Problems#

'page7@heading' was ignored: it targets page 7, but this template has 2 pages...

A pageN@key modification points at a page that doesn't exist, the prefix is malformed (pageX@heading), the parameter lives on a different page (the warning names the right one), or a multi-page template received a bare key that needs a page prefix to apply. Use pageN@key with a page number the template has, e.g. page1@heading. See Multi-Page Templates.

Unevaluated Template Expression#

The value for 'X' looks like an unevaluated n8n/Make expression ({{ ... }})...

A value like {{ $('Previous Node').item.json }} reached the API as literal text and rendered as-is. Your automation tool sent the expression itself instead of its result, usually because the field is in fixed-value mode. In n8n, switch the field from Fixed to Expression so the variable resolves before sending; Make behaves the same with {{1.field}} mappings. See the n8n integration guide. Simple placeholders like {{name}} (inline text parameters) and {{_page_number}} (flowing content) never trigger this warning.

Non-Scalar Value#

The value for 'X' should be a string, number, or boolean; received an array...

Modification values should be scalars; objects and arrays get stringified into the design ([object Object] or comma-joined text). Map the specific field you want, e.g. item.json.title instead of item.json. Exception: X.promptReferences accepts an array of image URLs.

Image Value#

'X' is an image parameter but "..." does not look like an image URL...

The template marks this parameter as an image source, but the value can't load as an image (e.g. plain text). Pass an http(s) URL or a data: URI. An empty string keeps the template's stored image; use X.prompt to generate the image with AI instead.

Modifications Not an Object#

'modifications' should be a JSON object mapping parameter keys to values...

modifications was sent as something other than a JSON object, most often a JSON-encoded string ("{\"title\": \"Hello\"}") or an array. It was ignored and the template rendered with its default values. Send the object itself, not a string.

Approved Version Rendered#

Rendered the approved version of this template. Its newer edits are waiting for approval.

The template is in an approved stage set to keep rendering the approved version while edits wait for approval, so the render used the design that was approved, not the latest edit. Once the edit is approved, renders use it. See Approval Errors.


Response Format Errors#

Unsupported Response Format#

Code400
MessageResponse format not supported. Supported formats: png, webp, avif, jpg, jpeg, pdf, mp4, webm, gif, mov, mkv

Why it happens: You specified an invalid response.format value.

Fix: Use one of the supported formats:

  • Images: png, jpeg, jpg, webp, avif
  • Documents: pdf
  • Videos: mp4, webm, mov, mkv, gif

See Response Format for details.


Unsupported Response Type#

Code400
MessageResponse type not supported. Supported types: binary, url, base64

Why it happens: You specified an invalid response.type value.

Fix: Use one of:

  • binary - Raw file data (best for direct downloads)
  • url - Hosted URL to the rendered file
  • base64 - Base64-encoded string

See Response Type for details.


Binary Not Supported for Multi-Page#

Code400
MessageBinary response type is not supported for multi-page templates unless format is PDF

Why it happens: You can't get binary output for multi-page templates (except PDFs). This also applies to templates with flowing content that generates overflow pages.

Fix: Either:

  • Use response.type: "url" or "base64" for multi-page templates
  • Use response.format: "pdf" to get a single binary PDF
  • Add response.includePages: [1] to render only one page as binary

Video/GIF Requires URL Response Type#

Code400
MessageVideo and GIF formats (mp4, webm, gif, mov, mkv) only support response.type "url". Received "..."

Why it happens: You requested response.type: "base64" or "binary" with a video or GIF response.format (mp4, webm, gif, mov, mkv). These formats only support response.type: "url".

Fix: Set response.type: "url" when rendering video or GIF output.


Smart Resize Errors#

Invalid Render Size#

Code400
Messagee.g. Unknown size '...'. Use a preset, a "WIDTHxHEIGHT" string, or explicit width/height values., one of several validation messages (see below)

Why it happens: Returned for an invalid Smart Resize request: either response.size (or response.width/height) can't be resolved, or response.extraSizes is combined with an unsupported option. The exact message names the cause:

  • size isn't a known preset slug or a "WIDTHxHEIGHT" string; or width/height aren't integers between 10 and 5000px (and must be sent together).
  • extraSizes with a response.type other than url or base64 (a binary stream can only return one file).
  • extraSizes on a template with flowing (auto-expanding) elements.
  • extraSizes with a PDF or video format. It currently supports image formats only (png, jpg, webp, avif).
  • The request would produce more than 50 extra outputs (pages × extra sizes). Each extra size is billed like a page.

Fix:

  • Pass size as a preset slug (e.g. instagram-story) or "1080x1920"; or send width + height together (10–5000px).
  • For extraSizes, use response.type url or base64, an image format, and a non-flow template.
  • Keep pages × extraSizes at 50 or fewer per call: reduce the sizes or pages, or split into multiple requests.

See Render from Studio Template → Smart Resize for the full reference.


Page Errors#

Invalid Page Numbers#

Code400
MessageInvalid page numbers in includePages: X. Valid range is 1-Y.

Why it happens: You specified page numbers that don't exist in the template.

Fix: Check your template's page count and use valid page numbers (1-based).

JavaScript
response: {
  includePages: [1, 2, 3]; // Only existing page numbers
}

Invalid Page Number (Dynamic URL)#

Code400
MessageInvalid page number 'X'. Page number must be a positive integer.

Why it happens: The page query parameter isn't a valid positive integer.

Fix: Use a positive integer for the page parameter: ?page=1


Dynamic URL Errors#

Template ID Missing (Dynamic URL)#

Code400
Message"templateId" is missing in the URL

Why it happens: The dynamic URL is missing the required templateId query parameter.

Fix: Include templateId in your URL: ?templateId=your-template-id&sign=...


Sign Parameter Missing#

Code400
Message"sign" parameter is missing in the URL

Why it happens: Dynamic URLs require a sign parameter for security.

Fix: Copy the complete dynamic URL from the Template Playground, which includes the sign parameter.


Sign Does Not Match#

Code404
MessageSign does not match for this dynamic URL

Why it happens: The signature doesn't match the template, possibly because URL parameters were modified.

Fix: Regenerate the dynamic URL from the Template Playground.


Dynamic URL Not Found#

Code404
MessageDynamic URL with ID 'X' not found

Why it happens: The template ID doesn't have a dynamic URL configured.

Fix: Enable dynamic URL for your template in the Template Playground settings.


Dynamic URL Inactive#

Code404
MessageDynamic URL for templateID 'X' is inactive. Enable it from Template playground page to start rendering

Why it happens: The dynamic URL has been disabled.

Fix: Go to your template's Playground page and toggle the dynamic URL to active.


Dynamic URL Render Limit Reached#

Code403
MessageThis dynamic URL has reached its maximum render limit. Increase Max Renders in the Template Playground for more renders

Why it happens: You've hit the maximum render count set for this dynamic URL.

Fix: Increase the "Max Renders" limit in your template's Playground settings.


Dynamic URL Expired#

Code403
MessageDynamic URL has expired. Please update the expiration time in the Template playground to continue rendering

Why it happens: The dynamic URL's validity period has passed.

Fix: Update the expiration date in your template's Playground settings.


Video Not Supported for Dynamic URLs#

Code400
MessageVideo generation is not supported for dynamic URLs

Why it happens: Dynamic URLs don't support video output formats.

Fix: Use the POST API endpoint (/v1/studio/render) for video generation instead.


Quality Parameter Errors#

Invalid Quality Value#

Code400
MessageInvalid quality value 'X'. Quality must be between 1 and 100.

Why it happens: The quality parameter must be an integer from 1-100.

Fix: Use a valid quality value: ?quality=85


Quality Only for JPEG/WebP/AVIF#

Code400
MessageQuality parameter is only supported for JPEG/JPG/WebP/AVIF formats. Current format: X

Why it happens: The quality parameter only works with JPEG, WebP, and AVIF output.

Fix: Either:

  • Remove the quality parameter, or
  • Set responseFormat to jpg, jpeg, webp, or avif

Signed URL Errors#

Signed URL Inactive#

Code401
MessageThis signed URL is inactive. Please activate it from the dashboard

Why it happens: The signed URL has been deactivated.

Fix: Reactivate the signed URL from your dashboard.


Signed URL Expired#

Code401
MessageSigned URL has expired. Please create a new signed URL.

Why it happens: The signed URL's expiration time has passed.

Fix: Generate a new signed URL with a longer expiration time.


Invalid Signature#

Code401
MessageInvalid signature. Please create a new signed URL.

Why it happens: The URL signature doesn't match, possibly due to parameter tampering.

Fix: Generate a fresh signed URL from the API or dashboard.


Missing Signed URL Parameters#

Code400
Message"templateId", "signature", "modifications" or "id" is missing in the URL

Why it happens: Required query parameters are missing from the signed URL.

Fix: Make sure all required parameters are included when generating the signed URL.


Website Screenshot Errors#

Invalid Website URL#

Code400
MessageMissing or invalid 'websiteUrl' value. Please provide a valid url for 'websiteUrl' modification

Why it happens: The websiteUrl modification is missing or not a valid URL.

Fix: Provide a valid URL:

JavaScript
modifications: {
  websiteUrl: "https://example.com";
}

Background Removal Errors#

Input Image Missing#

Code400
MessageMissing 'inputImage' value. Please provide a valid url for 'inputImage' modification

Why it happens: The inputImage modification is required for background removal.

Fix: Provide a valid image URL:

JavaScript
modifications: {
  inputImage: "https://example.com/image.png";
}

AI Generation Errors#

AI Credits Exhausted#

Code402
MessageInsufficient AI credits or errors containing AI generation

Why it happens: AI credits are a separate pool from render credits. They pay for AI images, AI video, voiceovers and background removal. The Free plan includes 50 AI credits as a one-time grant: unlike the 100 monthly render credits, AI credits do not come back at the start of the month. On a paid plan the pool is the plan's AI allowance plus any add-on credits purchased.

Typical costs: background removal 1 credit per image, AI image 2 credits, AI video 3 credits per second (6 at full HD).

Fix:

  • Check the pool with GET /v1/me. plan.ai_credits reports used, limit and remaining.
  • Price a clip before spending it with POST /v1/ai/video/estimate. Same body, returns the credits, generates nothing.
  • Add credits by upgrading or changing your plan at orshot.com/pricing, or buy add-on AI credits from Billing & Usage.
  • If you were expecting a monthly reset, you are thinking of the free render allowance. Different meter.

Templates with no AI layer do not touch this pool and keep rendering while AI credits are at zero.


Billing Errors#

API Key Paused#

HTTP status402
Error codeapi_key_paused
MessageThe original out-of-credits message, followed by We stopped answering this API key after N requests that had no credits behind them.

Why it happens: The workspace ran out of PAID credits and an integration kept calling anyway. After 1,000 refused requests we stop answering that key: the refusal comes straight back without looking anything up, so a retry loop cannot keep occupying the render queue.

The Free plan's monthly allowance never pauses a key, because it clears itself at the start of the next month. Running out of free credits returns Free Monthly Credits Used Up every time, and repeat rows are quieted in your logs rather than refused outright.

Only direct API calls count towards this. Anything rendered from inside Orshot or from one of its integrations, including the Studio playground, Post to social, embeds, Dynamic URLs, webhooks, workflows and the spreadsheet integration, is never counted and never sees this error.

Fix: Add credits by upgrading or changing your plan. The pause is not a flag on your key and there is nothing to un-pause. A plan change takes effect immediately, and otherwise one request every 15 minutes is checked against your real credit balance, so the key comes back on its own within 15 minutes of credits arriving by any route.


Rate Limit Errors#

Rate Limit Exceeded#

| HTTP status | 429 | | Error code | rate_limit_exceeded | | Message | Rate limit {count}/{limit} per minute exceeded. ... |

Why it happens: Your workspace sent more render requests in one minute than your plan's rate limit allows. The limit is shared across every render method: REST API, Dynamic URLs and webhooks all count against the same per-minute budget for the workspace owner.

Fix: Back off and retry using the response headers rather than a fixed sleep:

HeaderMeaning
Retry-AfterSeconds to wait before retrying
RateLimit-LimitRequests permitted in the current window
RateLimit-RemainingRequests left in the current window
RateLimit-ResetSeconds until the window resets

RateLimit-Limit and RateLimit-Remaining are also returned on successful render responses, so you can throttle before you hit the limit. The same values are sent under the legacy X-RateLimit-* names for existing integrations. The JSON body repeats them under info.

JavaScript
const res = await fetch(url, options);
if (res.status === 429) {
  const wait = Number(res.headers.get("Retry-After") ?? 60);
  await new Promise((r) => setTimeout(r, wait * 1000));
  // retry
}

If you need a higher ceiling, upgrade your plan or reach out at orshot.com/contact.

Approvals have limits of their own, separate from renders, and both send Retry-After:

LimitError codeMessage
Approvals API: 120 requests a minute per API key or person, in each workspacerate_limit_exceededRate limit exceeded: at most 120 approvals API requests per minute. ...
Comments in the Orshot app: 20 a minute and 200 an hour per personerror: "rate_limited", code: "approval.comment.rate_limited"You've added a lot of comments in a short time. Try again in ...

A comment edit counts toward the comment limit only when it mentions someone.


Routing Errors#

Route Not Found#

| HTTP status | 404 | | Error code | route_not_found | | Message | Not found: GET /v1/... See ... |

Why it happens: The path does not match any Orshot API endpoint. Usually a typo, a missing /v1 prefix, or an endpoint that was guessed rather than looked up.

Fix: Check the path against the API Reference. Every endpoint is served under https://api.orshot.com/v1. The machine-readable list of routes is published at orshot.com/openapi.json.


Server Errors#

General Server Error#

| HTTP status | 500 | | Error code | internal_error | | Message | Internal server error or Orshot Server Error(details). Reach out at ... |

Why it happens: An unexpected error occurred on our servers.

Fix:

  • Retry the request after a few seconds
  • If it persists, contact us at orshot.com/contact with the error details

Screenshot Generation Failed#

Code500
MessageOrshot Server Error: Failed to generate screenshots for multi-page template

Why it happens: The server couldn't generate the requested images.

Fix:

  • Check that your template and modifications are valid
  • Retry the request
  • Contact support if the issue continues

Storage Error#

Code500
MessageFailed to store page X: error details

Why it happens: The rendered image couldn't be saved to storage.

Fix: This is usually temporary. Retry the request or contact support.


Video Duration Errors#

These occur on video renders in either mode: as an HTTP 400 in sync mode, or as a failed job's error_code in async mode.

Invalid Video Duration#

Code400 / invalid-video-duration
MessagevideoOptions.duration must be a positive number of seconds (got X).

Why it happens: videoOptions.duration is zero, negative, or not a number.

Fix: Pass a positive number of seconds, or omit duration to use the template's own duration.


Video Duration Exceeds Plan#

Code400 / video-duration-exceeds-plan
MessageVideo duration (Xs) exceeds the maximum allowed length of Ys for your plan.

Why it happens: The requested video is longer than your plan's maximum video length.

Fix: Trim the video below your plan's limit, or upgrade to a bigger plan.


Async Render Job Errors#

Errors specific to async render jobs (response.mode: "async").

Async Requires URL#

Code400 / async-requires-url
Messageresponse.type "X" is not supported with response.mode "async"

Why it happens: Async results are delivered as hosted URLs, so base64 and binary response types can't be used.

Fix: Set response.type to "url" or omit it (it's the default).


Invalid Webhook URL#

Code400 / invalid-webhook-url
Messagewebhook_url must be publicly reachable — private and loopback addresses are not delivered to.

Why it happens: webhook_url isn't a valid http(s) URL, or points at a private/loopback address.

Fix: Use a publicly reachable https URL. For local development, use a tunnel (e.g. ngrok).


Metadata Too Long#

Code400 / metadata-too-long
Messagemetadata is X characters; the maximum is 1024.

Why it happens: The metadata passthrough string exceeds 1024 characters.

Fix: Store a reference (like your own database id) instead of the full payload.


Render Timeout#

Codefailed job, error_code: "render-timeout"
MessageRender exceeded the 15-minute limit.

Why it happens: The render ran longer than the 15-minute ceiling.

Fix: Reduce duration, fps or quality, or split pages into separate renders. Nothing is billed for a timed-out job.


Job Not Found#

Code404 / job-not-found
MessageNo render job X in this workspace.

Why it happens: The id doesn't exist in your workspace, or the job record expired (job records are kept for 7 days; the rendered file itself is unaffected).

Fix: Use the id from the 202 response of your async render, with a key from the same workspace.


Job Not Cancelable#

Code409 / job-not-cancelable
MessageJob X is already rendering and cannot be canceled

Why it happens: Only queued jobs can be canceled. This one is already rendering. It will complete and be billed normally.

Fix: Poll it, or simply ignore the result.


Job Already Finished#

Code409 / job-already-finished
MessageJob X already finished (status)

Why it happens: The job already reached a terminal status, so there's nothing to cancel.

Fix: Read the job's result or error instead.


Invalid Metadata#

Code400 / invalid-metadata
Messagemetadata must be a string

Why it happens: metadata was sent as something other than a string. It is passed through verbatim and echoed back to you.

Fix: Send metadata as a string (store a reference like your own database id).


Too Many Active Jobs#

Code429 / too-many-active-jobs
MessageYou already have N render jobs in progress (limit 200).

Why it happens: The workspace has too many queued/processing async jobs at once.

Fix: Wait for some to finish (poll or webhook), then retry. This is a safety limit, not a plan limit.


Render Interrupted / Orphaned#

Codefailed job, error_code: "render-interrupted" or "render-orphaned"
MessageThe server restarted while this render was running / did not start

Why it happens: A deploy or restart landed while the job was in flight. Nothing was billed for the incomplete render.

Fix: Retry the request.


Async Unavailable#

Code503 / async-unavailable
MessageAsync rendering is not available right now.

Why it happens: A temporary service issue on our side.

Fix: Retry with response.mode: "sync", or contact support if it persists.


Invalid Job Request Parameter#

Code400 / invalid-job-id, invalid-status-filter, invalid-cursor
MessageNames the parameter and the accepted values.

Why it happens: A render-job request had a bad :id (not a positive integer), an unknown status filter, or a cursor that is not a job id.

Fix: Use the id from the 202 response, a valid status (queued, processing, succeeded, failed, canceled), and the next_cursor from a previous list page.


Render Crashed#

Codefailed job, error_code: "render-crashed"
MessageInternal error while running the render …

Why it happens: The render threw an unexpected error server-side. Nothing usable was produced.

Fix: Retry. If it repeats, contact support with the job id.


Transient Job Errors#

Code500 / job-create-failed, job-read-failed, job-list-failed, job-cancel-failed
MessageCould not … the render job — retry.

Why it happens: A temporary database or service blip while creating, reading, listing, or canceling a job. For job-create-failed, nothing was rendered or billed.

Fix: Retry the request.


AI Video Not On Plan#

Code403 · ai-video-not-on-plan
MessageAI video is available on paid plans and this workspace is on the free plan.

Why it happens: The free plan's AI credits cover AI images and the design agent, not video. POST /v1/ai/video, POST /v1/ai/video/estimate and a render whose video element carries a .prompt modification all return this code. A render still succeeds: the element uses its saved clip and the response carries a warning. Nothing is charged. GET /v1/me reports it up front as plan.ai_credits.video.allowed: false.

Fix:

  • Upgrade to any paid plan at orshot.com/pricing.
  • Or stay on free and use real footage: GET /v1/stock/search for stock video, or a video already in your Brand Library.

AI Generation Errors#

Codeai-credits-insufficient (402), ai-daily-cap (429), ai-video-not-on-plan / ai-speech-not-on-plan (403), ai-speech-consent-required, ai-speech-too-long, ai-unknown-preset, ai-unknown-look, ai-invalid-aspect, ai-prompt-too-long, ai-script-too-long, invalid-media-ref, invalid-data-uri, input-too-large (400), ai-media-rejected, ai-media-provider-down, ai-video-unavailable, ai-media-busy, generation-timeout
MessageThe response says which input or limit is in the way.

Why it happens: AI video, audio and images draw from the AI credit pool and are capped per plan (plan.ai_credits.video and plan.ai_credits.audio on GET /v1/me): clip length, speech length, speech availability, and a daily per-workspace cap. Provider refusals (ai-media-rejected) and outages (ai-media-provider-down) never charge.

Fix: Check the position with GET /v1/me, estimate first with POST /v1/ai/video/estimate, fix the named input, or add credits / upgrade. For ai-media-busy wait for the running generation; for generation-timeout retry once.


Need Help?#

If you're still stuck:

  1. Check the Quick Start Guide for basic setup
  2. Review API Reference for correct request formats
  3. Reach out at orshot.com/contact
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 50+ integrations
  • White-label embed for your own app
  • 100 free credits a month, no credit card required