SalesOS

Approvals

Multi-step approval processes on any record — entry criteria, approvers by user, profile or manager, record locking while pending, and status write-back.

An approval process routes a record through one or more approval steps. Processes run on leads, contacts, accounts, deals (opportunities), cases, tasks and custom objects, as well as the deal-desk entities (quotes, discounts, orders, contracts). Manage them under Settings → Sales process → Approval workflows; people decide in Approvals (/dashboard/approvals) or on the record page. Approvals are part of the Enterprise plan.

A process

SettingMeaning
entity (+ customObjectName)What it runs on: LEAD, CONTACT, ACCOUNT, OPPORTUNITY, CASE, TASK, CUSTOM_OBJECT, QUOTE, DISCOUNT, ORDER, CONTRACT.
conditionsEntry criteria: [{ field, operator, value }], all must match. Operators EQUALS, NOT_EQUALS, GREATER_THAN(_OR_EQUALS), LESS_THAN(_OR_EQUALS), CONTAINS, IN. Fields are the record's own (API names; custom object fields by name).
priorityWhen several processes apply, the highest priority whose criteria match is used.
stepsIn order. Each step's approver is a USER, anyone with a profile (ROLE), the requester's MANAGER or SKIP_LEVEL_MANAGER; a step can require a comment and allow delegation.
lockRecordWhile a request is pending, only the current step's approvers and admins can edit the record. Default on for records; never for the deal-desk entities.
allowRecallWhether the requester can recall (cancel) a pending request.
statusField, statusValuesA field that mirrors the request: written PENDING, APPROVED, REJECTED or RECALLED (or your own values per outcome).
fieldUpdates{ onSubmit, onApprove, onReject, onRecall: [{ field, value }] }. Values are literals or $Now, $Today, $Approver, $Submitter.

Status and field updates are written as the process's author through the entity services, so validation rules, before-save hooks, field-level security, workflows, webhooks and the audit trail apply; they pass the process's own lock. Writable fields are the record's editable fields and, on leads, contacts, accounts and deals, custom fields as customFields.<name> — anything else is 400 INVALID_APPROVAL_FIELD. A write that is refused is logged on the request (entityDetails.writeBackError) and does not undo the decision.

Quotes keep their deal-desk behaviour: submitting sets the quote to PENDING_APPROVAL, approval makes it APPROVED, rejection returns it to DRAFT with the reason, and editing its pricing resets it to draft and cancels the open request.

Record locking

A locked record answers 423 RECORD_LOCKED to edits and deletes by anyone else, wherever they come from: the REST API and inline edits, GraphQL mutations, the Bulk API, bulk edits, imports, workflows, flows and custom field values. The body names the request:

{
  "statusCode": 423,
  "code": "RECORD_LOCKED",
  "message": "This record is locked while \"Large deal review\" approval is pending. …",
  "approvalRequestId": "cm…"
}

Org owners and admins (and system admins), and whoever can decide the current step, can still edit. System jobs without a user are not locked out. The lock ends when the request is approved, rejected or recalled.

Submitting and deciding

GET  /v1/approval-requests/record/{entityType}/{entityId}   processes, pending request, lock, canDecide, canRecall, history
POST /v1/approval-requests/submit        { "entityType": "OPPORTUNITY", "entityId": "…", "comment": "…" }
POST /v1/approval-requests/{id}/decide   { "action": "APPROVE" | "REJECT" | "DELEGATE", "comment": "…" }
POST /v1/approval-requests/{id}/cancel   recall
GET  /v1/approval-requests/pending       waiting for you

The submitter must be able to see the record. A record has at most one pending request (409 APPROVAL_ALREADY_PENDING); a record that matches no process's entry criteria is 400 ENTRY_CRITERIA_NOT_MET. For custom objects pass entityType: "CUSTOM_OBJECT" and the record id. Flows submit with the Submit for approval element.

Events

approval.approved and approval.rejected are in the event catalog: data.record is the request, data.relatedTo the record ({ object, id }), plus workflowName and the approver's comment.

Metadata

Processes move as APPROVAL_PROCESS components (approval-processes/<ENTITY>.<name>.approval.json). Approvers travel as the user's email and the profile's name; a process with pending requests can't have its steps replaced by a deploy. New processes arrive inactive unless the deploy activates them.

On this page