Approvals API Overview
Set up approval flows, move templates through stages and approve them from your own code. Available on Enterprise plans, or through first-party apps.
Updated
Enterprise Only
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, each access level is turned on separately. See Enterprise pricing to get access.
The Approvals API does what the Approvals page in your workspace does. You set up approval flows and their stages, add templates, move them between stages, approve them or request changes, comment, and read the activity.
When a flow needs approval for automation, its templates render through the API only once they're approved. A blocked render renders nothing and costs no credits: see Approval Errors for what it returns.
Base URL: https://api.orshot.com/v1
Authentication#
Every request sends Authorization: Bearer <token>, with an API key or an OAuth access token.
| Credential | Acts as | Good for |
|---|---|---|
| API key | The workspace. The activity history shows its changes "via API". With the X-Orshot-User-Id header, one person on the team (see Acting for a team member). | Automations and backend jobs: adding templates to a flow, moving them, reading approval status before a render. Internal tools that act for the person using them. |
| OAuth access token | The person who connected your app, with their own access in each approval flow. | Apps people sign in to, and endpoints that act for a person: Waiting on me, following a template, notifications. |
A few endpoints act for one person: Waiting on me, following a template, notifications and notification settings. They take that person's OAuth token, or an API key with their user id in X-Orshot-User-Id. An API key without the header gets 403 permission_denied, code approval.person_required. Their pages say so.
A token that covers several workspaces picks one with the x-workspace-id header. API keys always act in their own workspace.
OAuth apps ask for these scopes when the person connects. See Scopes for the others.
| Scope | Allows |
|---|---|
workspace:approvals:read | See approval flows, stages, where templates sit, comments and activity |
workspace:approvals:write | Add and move templates, comment, and manage your notifications |
workspace:approvals:decide | Approve or request changes where you're an approver |
workspace:approvals:admin | Create and change approval flows, stages, groups and approvers |
admin includes write and read, and write and decide each include read. No scope includes decide: approving is always asked for on its own.
Access levels#
Your Enterprise plan turns on each level separately. A call without the level it needs gets 403 enterprise_api_required, with the level in required. The callout at the top of every endpoint page names its level.
| Level | What it allows |
|---|---|
read | List and read approval flows, stages, access, groups, templates in flows, a template's approval status, comments, people to mention and the activity history. Your own OAuth app also needs it for Waiting on me, following a template, notifications and notification settings, and an API key acting for a team member needs it to read their Waiting on me, notifications and notification settings. |
move | Add templates to a flow, move them between stages, and write, edit, resolve and delete comments. An API key acting for a team member needs it as well as read to follow or mute a template for them, mark or archive their notifications, or change their notification settings. |
decide | Approve, request changes and withdraw an approval. Also needed to move a template into a stage that only approvers can put templates in. Never used by a call acting for a team member: approvals come from the person. |
manage | Create, change, copy and archive approval flows and stages, set who has access, manage groups, and take templates out of a flow. |
Levels don't include each other. An automation that reads a flow and then moves templates needs read and move.
With an API key, a level covers every approval flow in the workspace. With an OAuth token, or an API key acting for a team member, the person's own access applies too: someone without Approve access in a stage can't approve there, whatever levels the workspace has. Reviewers stay inside the approval flows they were given, and get 404 not_found for everything else.
Acting for a team member#
An API key acts for the workspace. To make a call for one person instead, send their user id in the X-Orshot-User-Id header:
await fetch("https://api.orshot.com/v1/approvals/inbox", {
headers: {
Authorization: "Bearer <ORSHOT_API_KEY>",
"X-Orshot-User-Id": "9a4c7e2b-1d6f-4e3a-8b5c-2f7d0e9a4c61",
},
});- Who. An owner, admin or member of the key's workspace, as they are now. Anyone else gets
403 permission_denied, codeapproval.member_header_not_member: reviewers, people who left, and people outside the workspace. Reviewers use their own OAuth token or the Orshot app. A value that isn't a user id gets422 validation_failed, codeapproval.member_header_invalid. - What it can do. The call needs both the key's access level and that person's own access, so it never does more than either. An automation with
moveacting for someone who can't move templates into a stage can't move them there either. - What is theirs. Waiting on me, notifications, following a template and notification settings answer for that person, as their own OAuth token would get them, in the key's workspace only. Reading them needs
read. Changing them (following or muting a template, marking or archiving notifications, notification settings) needsreadandmove, so a read-only key only reads. Like your own OAuth app, the key changes notification settings for its workspace only (sendworkspaceId) and can't change the person's time zone. - Credit. Comments, moves and other changes are credited to the person: people see "Priya Shah, via API key" in comments, the activity history, notifications and emails. The person isn't notified about their own change. The key is recorded too: comments and events carry it in
postedVia, and webhooks keep it as the actor with the person inonBehalfOf. - Comments. The key can edit and delete the comments it posted. The person can delete a comment posted in their name in Orshot, but not edit it. Owners and admins can hide it, as any comment.
- Approving. Approving, requesting changes, withdrawing an approval, and putting or reordering a template in an approved stage always come from the person, through their OAuth token or the Orshot app. With the header they get
403 permission_denied, codeapproval.decision_person_required. - OAuth tokens. They already act for the person who connected them, so they can't send the header:
403 permission_denied, codeapproval.member_header_not_allowed.
Rate limits count the key, whoever it acts for.
Retries and idempotency keys#
Writes that aren't safe to repeat, like adding, moving or approving a template, accept an Idempotency-Key header. Use up to 255 visible characters, one key per request, and send the same key again when you retry it.
- A repeat within 24 hours returns the first response with the
Idempotent-Replayed: trueheader, and changes nothing. - The same key with a different request gets
422 validation_failed, codeapproval.idempotency_key_reused. - A retry while the first request is still running gets
409 state_conflict, codeapproval.request_in_progress. Try again in a moment. - A refused request doesn't use up its key, so you can fix it and send it again with the same key.
Reads, and writes that are safe to repeat as they are (updates, archiving, marking notifications read), don't take a key. Each endpoint's Headers table shows whether it takes one.
Revisions and 409 conflicts#
Each template in a flow has a revision that goes up every time it moves. Send the revision you last saw when you move a template. If someone moved it first, you get 409 state_conflict, and current carries its stage and revision now:
{
"error": "state_conflict",
"code": "state_conflict",
"message": "Someone else moved this template first. Refresh to see where it is now.",
"current": {
"item": 1,
"revision": 3,
"reviewRound": 1,
"stage": { "id": 3, "slug": "legal-review", "name": "Legal review", "category": "review" }
},
"helpUrl": "https://orshot.com/docs/error-reference#state-conflict"
}Check that the move still makes sense, then send it again with the new revision. Bulk moves list conflicting templates in failed and move the rest.
Approving and requesting changes pin the design the same way. Send reviewedVersion, the template.currentVersion you looked at. If the design changed since, you get 409 stale_review, and current.currentVersion is the version to review instead.
Destructive calls and lists#
Archiving a flow, stage or group, and taking a template out of a flow, need "confirm": true in the body. Agents should ask the person before they send it.
Lists come in pages. Pass nextCursor back as cursor until it's null. limit is 1 to 100. A cursor the API can't read starts again from the first page, and one longer than 200 characters is refused with 422 validation_failed.
Errors#
Every approvals error has the same body, on the API, in the dashboard and in agents:
error: the family, likepermission_deniedorstate_conflict. Branch on this.code: more specific, likeapproval.item.decide_deniedorinsufficient_scope.message: plain words you can show to people as they are.helpUrl: the error's section in the error reference.- When they apply:
required(the missing permission, scope or level),role,context,issues(invalid fields),current(conflicts),blockersandviolations(blocked renders).
Writes also return warnings, notices that didn't stop the change, like no_approver when a stage has nobody who can approve yet. It's always there on writes, and empty when there's nothing to say.
More than 120 approvals requests in a minute from one person or key get 429 rate_limit_exceeded. Wait for the Retry-After header, then continue.
Where to start#
List Approval Flows
Find a flow's slug before working with its stages or templates
Add a Template to an Approval Flow
Put a template into an approval flow to start its review
Move a Template to Another Stage
Submit for review, or move it on with the revision you saw
Get a Template's Approval Status
Check whether API renders of a template are blocked, and why
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