Approve an approval request
The approvals.approve operation: POST /api/v1/approvals/{id}/approve in the REST API and the approvals_approve tool in the MCP server.
/api/v1/approvals/{id}/approveApproves a pending approval request. Approvers are people in the console (never API keys, agents, apps or
an OAuth or MCP client acting for a person) other than the requester (for an app: the person who installed
it) who may run the operation themselves at its entity or brand and in its mode, and who signed in within
the last 15 minutes. A request by an agent or app that a two-person rule covers needs two approvers
(requiredApprovals: 2).
When the request has the approvals it needs, the operation runs as originally requested: the same
input and Idempotency-Key, as the person, key, agent or app that asked, with their current access. The response is the
request afterwards: executed (with resultRef), or stale when it can no longer run as requested
(failure says why; the requester must ask again). A retryable failure leaves it approved with
failure set: approve again to retry. Approving a request that is already approved or executed changes
nothing.
At a glance
- REST:
POST /api/v1/approvals/{id}/approve - MCP tool:
approvals_approve - Operation ID:
approvals.approve - Scope: Organization
- Permission:
approvals:write - Risk tier:
security - Dry run: Supported (send
Ownrecord-Dry-Run: true) - Idempotency:
Idempotency-Keyrequired - Modes: Test and live
- Authentication: API key with the
approvals:writescope - Fresh sign-in: In the console, a sign-in within the last 15 minutes
- Since: M2
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | apr_… ID |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
comment | string | No |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | apr_… ID | |
object | "approval_request" | |
status | one of pending, approved, rejected, expired, executed, stale | |
operationId | string | |
summary | string | |
orgId | org_… ID | |
entityId | ent_… ID or null | |
brandId | brd_… ID or null | |
livemode | boolean | |
requestedBy | object | |
requestedBy.type | one of user, api_key, agent, app, system, public, customer | |
requestedBy.id | string | |
requestedBy.via | one of console, api, mcp, assistant, portal, job or null | |
requestedBy.clientId | string or null | |
requestedBy.onBehalfOfUserId | string or null | |
requestedBy.appInstallationId | string or null | |
amount | money or null | |
input | any | |
dryRun | object | |
dryRun.output | any | |
dryRun.events | array of string | |
diff | object or null | |
requiredApprovals | integer (1 to 2) | |
approvals | array of object | |
approvals[].userId | usr_… ID | |
approvals[].at | date-time | |
approvals[].comment | string or null | |
rejection | object or null | |
failure | object or null | |
resultRef | string or null | |
approveUrl | string | |
expiresAt | date-time | |
executedAt | date-time or null | |
createdAt | date-time | |
updatedAt | date-time |
Examples
Approve a brand change
POST /api/v1/approvals/apr_01m4ttwd3veh9apbewxheq5xh7/approve
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"comment": "Checked with legal"
}
Response (200):
{
"id": "apr_01m4ttwd3veh9apbewxheq5xh7",
"object": "approval_request",
"status": "executed",
"operationId": "brands.update",
"summary": "Update a brand",
"orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
"entityId": "ent_01m4ttwd3veh9apbewxheq5xh4",
"brandId": "brd_01m4ttwd3veh9apbewxheq5xh5",
"livemode": true,
"requestedBy": {
"type": "user",
"id": "usr_01m4ttwd3veh9apbewxheq5xh6",
"via": "console",
"clientId": null,
"onBehalfOfUserId": null,
"appInstallationId": null
},
"amount": null,
"input": {
"id": "brd_01m4ttwd3veh9apbewxheq5xh5",
"statementDescriptor": "ACME APP"
},
"dryRun": {
"output": {
"id": "brd_01m4ttwd3veh9apbewxheq5xh5",
"statementDescriptor": "ACME APP"
},
"events": ["brand.updated"]
},
"diff": {
"before": {
"statementDescriptor": "ACME"
},
"after": {
"statementDescriptor": "ACME APP"
}
},
"requiredApprovals": 1,
"approvals": [
{
"userId": "usr_01m4ttwd3veh9apbewxheq5xha",
"at": "2026-10-13T14:30:00.000Z",
"comment": "Checked with legal"
}
],
"rejection": null,
"failure": null,
"resultRef": "brd_01m4ttwd3veh9apbewxheq5xh5",
"approveUrl": "https://ownrecord.io/console/acme/approvals/apr_01m4ttwd3veh9apbewxheq5xh7?mode=live",
"expiresAt": "2026-10-16T12:00:00.000Z",
"executedAt": "2026-10-13T14:30:00.000Z",
"createdAt": "2026-10-13T12:00:00.000Z",
"updatedAt": "2026-10-13T14:30:00.000Z"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
resource_not_found | 404 | No approval request with this ID exists in this organization and mode. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
self_approval_not_allowed | 403 | You made the request yourself. | Ask another person who may run this operation to approve it. |
approver_not_eligible | 403 | You can't run the operation yourself at its entity or brand in this mode, you are an API key, agent or app, or you call through an OAuth or MCP client. | Approval requests are decided by people in the Ownrecord console (not API keys, agents, apps or MCP clients) who may run the operation themselves, at its entity or brand and in its mode. Ask such a person. |
approval_expired | 409 | Nobody approved the request within 72 hours. | Send the request again with a new Idempotency-Key to create a new approval request. Requests expire after 72 hours. |
approval_not_pending | 409 | The request was rejected or went stale. | Read the request (GET /approvals/{id}) to see its status and outcome. |
fresh_auth_required | 403 | You last signed in more than 15 minutes ago. | Sign in again, then repeat the action within 15 minutes. |
version_conflict | 409 | Someone decided at the same moment; retry. | Fetch the object again and retry the change. |
permission_denied | 403 | You can't decide on approval requests here. | Ask an organization owner or admin for a role or scope that allows this action. |
It can also return these common errors: validation_failed, api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, rate_limited, internal_error.