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#
| Code | 403 |
|---|---|
| Message | Access 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:
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#
| Code | 403 |
|---|---|
| Message | Access Forbidden: API Key is missing in the request |
| Error code | api_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#
| Code | 400 |
|---|---|
| Message | Access 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#
| Code | 400 |
|---|---|
| Message | Access Forbidden: The API Key or User ID do not exist |
| Error code | invalid_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#
| Code | 401 |
|---|---|
| Message | Access 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#
| Code | 401 |
|---|---|
| Message | Access 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#
| Code | 403 |
|---|---|
| Message | Insufficient 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#
| Code | 403 |
|---|---|
| Error code | insufficient_role |
| Message | This 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#
| Code | 403 |
|---|---|
| Error code | workspace_not_accessible |
| Message | You 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#
| Code | 403 |
|---|---|
| Message | This 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#
| Code | 403 |
|---|---|
| Message | Subscription 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#
| Code | 403 |
|---|---|
| Error code | free_monthly_credits_exhausted |
| Message | 100 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_creditsreportsused,limitandremainingfor 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#
| Code | 403 |
|---|---|
| Error code | billing_account_unresolved |
| Message | We 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#
| Code | 403 |
|---|---|
| Message | Video 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 status | 403 |
|---|---|
| Error code | voice_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 status | 403 |
|---|---|
| Error code | voice_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 status | 403 or 429 |
|---|---|
| Error code | voice_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 status | 400 |
|---|---|
| Error code | voice_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#
| Code | 429 |
|---|---|
| Message | Partner 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#
| Code | 403 |
|---|---|
| Message | This 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#
| Code | 403 |
|---|---|
| Message | Restoring versions needs a paid plan |
| Code id | PLAN_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#
| Code | 403 |
|---|---|
| Message | This version is older than your plan can restore |
| Code id | PLAN_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#
| Code | 409 |
|---|---|
| Message | Your plan keeps up to N named versions per template. Delete one to name another. |
| Code id | NAMED_CAP |
Fix: Delete a named version on the template (DELETE …/versions/:versionId) or upgrade the plan.
Template Changed While Restoring#
| Code | 409 |
|---|---|
| Message | The template changed while restoring. Reload and try again. |
| Code id | TEMPLATE_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#
| Code | 400 |
|---|---|
| Code id | invalid_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#
| Code | 400 |
|---|---|
| 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:
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#
| Code | 403 |
|---|---|
| Message | Studio 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#
| Code | 400 |
|---|---|
| Message | templateId 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#
| Code | 400 |
|---|---|
| Message | Modifications 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:
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:
{
"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-storeand the code in theX-Orshot-Errorheader, 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#
| Code | 403 |
|---|---|
| Error code | template_not_approved |
| Message | This 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:
| Reason | Meaning |
|---|---|
not_in_render_stage | Waiting for approval in a stage that doesn't render. |
design_only | The design is approved, but not for automation yet. See Design Approved, Not for Automation. |
approval_missing | In an approved stage with no approval that counts, for example after the approval was withdrawn. |
approved_version_missing | The 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#
| Code | 403 |
|---|---|
| Error code | template_not_approved |
| Message | This 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#
| Code | 403 |
|---|---|
| Error code | template_not_approved |
| Message | This 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#
| Code | 403 |
|---|---|
| Error code | output_not_approved |
| Message | This 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#
| Code | 403 |
|---|---|
| Error code | edited_since_approval |
| Message | This 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#
| Code | 403 |
|---|---|
| Error code | condition_violated |
| Message | This 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#
| Code | 503 |
|---|---|
| Error code | approvals_unavailable |
| Message | Approvals 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#
| Code | 403 |
|---|---|
| Error code | permission_denied |
| Message | You 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:
| Code | Meaning |
|---|---|
approval.item.decide_denied and other _denied codes | Your access doesn't cover this action. The code names it. |
approval.stage.enter_denied | Templates in that stage can't move straight to this one. |
approval.cross_site_request | A write to the Orshot app's approvals routes came from a page on another site. |
insufficient_scope | The OAuth token lacks the scope in required. Reconnect the app and allow approvals. |
approval.person_required | This 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_member | X-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_allowed | An OAuth token sent X-Orshot-User-Id. It already acts for the person who connected it. |
approval.decision_person_required | An 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_required | The key or token has no workspace. Pick one with the x-workspace-id header. |
approval.workspace_not_accessible | The request names another workspace that this key or token can't open. |
approval.sandbox_workspace | Sandbox 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:
| Code | Message |
|---|---|
approval.item.move_denied | Reviewers can't move templates between stages. They approve or request changes. |
approval.item.remove_denied | Reviewers can't take templates out of a flow. They approve or request changes. |
approval.item.add_denied | Reviewers can't add templates to a flow. They approve or request changes. |
approval.flow.manage_denied | Reviewers can't change approval flows. They approve or request changes. |
approval.flow.duplicate_denied | Reviewers 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#
| Code | 404 |
|---|---|
| Error code | not_found |
| Message | This 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#
| Code | 423 |
|---|---|
| Error code | stage_locked |
| Message | This 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#
| Code | 409 |
|---|---|
| Error code | stale_review |
| Message | The 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#
| Code | 409 |
|---|---|
| Error code | state_conflict |
| Message | Someone 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#
| Code | 403 |
|---|---|
| Error code | plan_required |
| Message | Approvals 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#
| Code | 403 |
|---|---|
| Error code | enterprise_api_required |
| Message | Using 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#
| Code | 403 |
|---|---|
| Error code | flow_limit_reached |
| Message | Your 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#
| Code | 422 |
|---|---|
| Error code | no_approver |
| Message | Nobody 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#
| Code | 422 |
|---|---|
| Error code | comment_required |
| Message | Add 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#
| Code | 422 |
|---|---|
| Error code | validation_failed |
| Message | Some 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#
| Code | 400 |
|---|---|
| Message | Request body is not valid JSON: ... |
| Error code | invalid_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#
| Code | 413 |
|---|---|
| Message | Request body too large (max 10mb) |
| Error code | payload_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#
| Code | 400 |
|---|---|
| Message | Invalid 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#
| Code | 424 |
|---|---|
| Message | Sheets 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#
| Code | 503 |
|---|---|
| Message | Voice 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.typeofurlorbase64) include a top-levelwarningsarray when there is at least one warning:
{
"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-Warningsheader (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#
| Code | 400 |
|---|---|
| Message | Response 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#
| Code | 400 |
|---|---|
| Message | Response 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 filebase64- Base64-encoded string
See Response Type for details.
Binary Not Supported for Multi-Page#
| Code | 400 |
|---|---|
| Message | Binary 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#
| Code | 400 |
|---|---|
| Message | Video 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#
| Code | 400 |
|---|---|
| Message | e.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:
sizeisn't a known preset slug or a"WIDTHxHEIGHT"string; orwidth/heightaren't integers between 10 and 5000px (and must be sent together).extraSizeswith aresponse.typeother thanurlorbase64(a binary stream can only return one file).extraSizeson a template with flowing (auto-expanding) elements.extraSizeswith 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
sizeas a preset slug (e.g.instagram-story) or"1080x1920"; or sendwidth+heighttogether (10–5000px). - For
extraSizes, useresponse.typeurlorbase64, an image format, and a non-flow template. - Keep
pages × extraSizesat 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#
| Code | 400 |
|---|---|
| Message | Invalid 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).
response: {
includePages: [1, 2, 3]; // Only existing page numbers
}Invalid Page Number (Dynamic URL)#
| Code | 400 |
|---|---|
| Message | Invalid 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)#
| Code | 400 |
|---|---|
| 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#
| Code | 400 |
|---|---|
| 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#
| Code | 404 |
|---|---|
| Message | Sign 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#
| Code | 404 |
|---|---|
| Message | Dynamic 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#
| Code | 404 |
|---|---|
| Message | Dynamic 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#
| Code | 403 |
|---|---|
| Message | This 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#
| Code | 403 |
|---|---|
| Message | Dynamic 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#
| Code | 400 |
|---|---|
| Message | Video 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#
| Code | 400 |
|---|---|
| Message | Invalid 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#
| Code | 400 |
|---|---|
| Message | Quality 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
responseFormattojpg,jpeg,webp, oravif
Signed URL Errors#
Signed URL Inactive#
| Code | 401 |
|---|---|
| Message | This 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#
| Code | 401 |
|---|---|
| Message | Signed 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#
| Code | 401 |
|---|---|
| Message | Invalid 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#
| Code | 400 |
|---|---|
| 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#
| Code | 400 |
|---|---|
| Message | Missing 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:
modifications: {
websiteUrl: "https://example.com";
}Background Removal Errors#
Input Image Missing#
| Code | 400 |
|---|---|
| Message | Missing '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:
modifications: {
inputImage: "https://example.com/image.png";
}AI Generation Errors#
AI Credits Exhausted#
| Code | 402 |
|---|---|
| Message | Insufficient 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_creditsreportsused,limitandremaining. - 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 status | 402 |
|---|---|
| Error code | api_key_paused |
| Message | The 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:
| Header | Meaning |
|---|---|
Retry-After | Seconds to wait before retrying |
RateLimit-Limit | Requests permitted in the current window |
RateLimit-Remaining | Requests left in the current window |
RateLimit-Reset | Seconds 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.
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:
| Limit | Error code | Message |
|---|---|---|
| Approvals API: 120 requests a minute per API key or person, in each workspace | rate_limit_exceeded | Rate limit exceeded: at most 120 approvals API requests per minute. ... |
| Comments in the Orshot app: 20 a minute and 200 an hour per person | error: "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#
| Code | 500 |
|---|---|
| Message | Orshot 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#
| Code | 500 |
|---|---|
| Message | Failed 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#
| Code | 400 / invalid-video-duration |
|---|---|
| Message | videoOptions.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#
| Code | 400 / video-duration-exceeds-plan |
|---|---|
| Message | Video 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#
| Code | 400 / async-requires-url |
|---|---|
| Message | response.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#
| Code | 400 / invalid-webhook-url |
|---|---|
| Message | webhook_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#
| Code | 400 / metadata-too-long |
|---|---|
| Message | metadata 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#
| Code | failed job, error_code: "render-timeout" |
|---|---|
| Message | Render 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#
| Code | 404 / job-not-found |
|---|---|
| Message | No 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#
| Code | 409 / job-not-cancelable |
|---|---|
| Message | Job 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#
| Code | 409 / job-already-finished |
|---|---|
| Message | Job 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#
| Code | 400 / invalid-metadata |
|---|---|
| Message | metadata 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#
| Code | 429 / too-many-active-jobs |
|---|---|
| Message | You 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#
| Code | failed job, error_code: "render-interrupted" or "render-orphaned" |
|---|---|
| Message | The 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#
| Code | 503 / async-unavailable |
|---|---|
| Message | Async 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#
| Code | 400 / invalid-job-id, invalid-status-filter, invalid-cursor |
|---|---|
| Message | Names 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#
| Code | failed job, error_code: "render-crashed" |
|---|---|
| Message | Internal 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#
| Code | 500 / job-create-failed, job-read-failed, job-list-failed, job-cancel-failed |
|---|---|
| Message | Could 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#
| Code | 403 · ai-video-not-on-plan |
|---|---|
| Message | AI 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/searchfor stock video, or a video already in your Brand Library.
AI Generation Errors#
| Code | ai-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 |
|---|---|
| Message | The 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:
- Check the Quick Start Guide for basic setup
- Review API Reference for correct request formats
- Reach out at orshot.com/contact
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