Custom actions
Register your own HTTPS endpoint as a named action and call it from workflows, the assistant and record pages, with signed requests, schemas, retries and a full invocation log.
A custom action is an HTTPS endpoint you run, registered in SalesOS under a
stable apiName with an input schema and an output schema. SalesOS never runs
your code; it signs a POST to your endpoint, validates what you send back,
and uses the result:
- as a workflow step (Automations and Cross-object workflows): record fields fill the inputs, and outputs are written back to the record;
- as an assistant tool, when you expose it with a description;
- from a Run action button on Lead, Contact, Account and Deal pages, when you flag it for manual use;
- from agents, which propose it for a person to approve.
Manage actions under Settings → Developer → Custom actions, or with
/v1/custom-actions (org owners and admins; API access is part of the
Enterprise plan, and of every developer org).
Register an action
The response includes the action's signing secret once (secret). Store it
in your endpoint's configuration; POST /v1/custom-actions/{id}/rotate-secret
issues a new one.
| Field | Meaning |
|---|---|
apiName | Lowercase letters, digits and _, 2–48 characters. Never changes; it is how workflows and change sets refer to the action. |
kind | READ (looks something up, changes nothing) or WRITE. Write actions always need the user's confirmation in the assistant and an approver for agents. |
idempotent, maxRetries | Safe-to-repeat actions are retried (up to 3 times) on timeouts, network errors, 429 and 5xx, with the same Idempotency-Key. Other actions are called once. |
timeoutMs | Default 10 000, maximum 30 000. All attempts share one deadline of at most 45 s. |
objectTypes | LEAD, CONTACT, ACCOUNT, OPPORTUNITY, TASK or custom:<object api name>. Where the Run action button appears, and what a workflow step may run on. |
inputMapping | { "input": "recordField" }, a dotted path into the record (including customFields.<name>), or { "input": { "value": … } } for a constant. |
outputMapping | { "output": "recordField" }. Written back after a record run. |
Schemas
The input and output schemas are JSON Schema objects of named fields — the shape
the field picker in Settings produces: string (optionally with format
date, date-time, email or uri, or an enum), number, integer,
boolean, and arrays of strings. Inputs are validated (and lightly coerced:
"42" → 42) before anything is sent; unknown input keys are refused. The
response must match the output schema; extra keys are ignored.
The request you receive
source is MANUAL, WORKFLOW, COPILOT, AGENT, TEST or REPLAY. The
invocation id is stable across retries of one call; dedupe on it (or on
Idempotency-Key).
Your response
Reply 2xx with JSON, either the outputs themselves or wrapped in output:
Anything else — another status, a body that isn't JSON or doesn't match the
output schema, a timeout — fails the invocation; nothing is written back.
Only 307/308 redirects are followed (at most two), each to a target that
passes the same checks.
Verifying the signature
The scheme is the same as webhooks,
with the action's own secret: v1 is the hex HMAC-SHA256 of "<t>.<raw body>".
Reject requests whose t is more than 300 seconds from your clock.
Calling through a named credential
Instead of a raw url, an action can target a
named credential and a path under it:
The request, signature, schemas, retries and log are the same; SalesOS also adds the credential's authentication (an API key header, bearer token, basic auth or an OAuth 2.0 client-credentials token), keeps redirects under the credential's base URL, and logs the call on the credential too. Rotating the partner's key then means updating one credential, not every action.
Running a function instead
Set functionId (one of your serverless functions)
instead of url, and the action runs your code on SalesOS with no endpoint to
host. The schemas, workflow step, assistant tool, Run action button and
invocation log work unchanged; the function's input is the validated input
and its return value is checked against the output schema.
Where actions run
Workflow step
Add a Custom action step in Automations or Cross-object workflows. The
step's config is { "actionApiName": "enrich_company" }, optionally with its own
inputMapping / outputMapping (otherwise the action's apply). Outputs are
written as the workflow's creator, through the same services as the REST
API: validation rules, before-save hooks, field-level security, ownership,
the audit trail, workflows and webhooks all apply, and a rejected write marks
the step failed with the reason. A step's write-back never re-triggers the
workflow it belongs to, and chained write-backs stop after three workflows.
Assistant tool
With exposeAsTool, the assistant sees the action as a tool:
read_custom_<apiName> for READ actions and custom_action_<apiName> for
WRITE actions, described by toolDescription and typed by the input schema.
Write actions are always held until the user confirms in a later message,
whatever the model says. Inputs and outputs pass through the AI trust layer
like any other tool: personal data is masked before it reaches the model and
restored before your endpoint is called, and the output is fenced as
untrusted data.
Run action button
With allowManual, people who can see a record of one of the action's
objectTypes get a Run action button. Mapped inputs are filled from the
record; the person types the rest. POST /v1/custom-actions/{id}/run
{ "objectType", "recordId", "input" } does the same over the API.
Agents
Agents propose a CUSTOM_ACTION with { "actionApiName", "input", "objectType", "recordId" };
every such proposal waits in the approvals inbox, and runs as the approver.
Protection SalesOS applies
- https only, no credentials in the URL, and no internal hostnames or private, internal or reserved addresses. The check is made on the address actually connected to, at connect time and on every redirect.
- Responses over 256 KB are cut off and fail.
- After repeated consecutive failures, calls to an action are paused
briefly (logged as
CIRCUIT_OPEN) before SalesOS tries it again. - Inactive actions are refused (tests and replays still work).
Invocation log, test and replay
Every call is logged with its request headers and body, the response status
and the first 4 KB of the body, latency, attempts and outcome, for 30 days.
The outcome is SUCCEEDED, FAILED, TIMEOUT, BLOCKED (the URL failed the
checks above), REJECTED or CIRCUIT_OPEN.
A replay sends the same input again under a new invocation id and does not write back to the record.
Developer orgs
Build and test actions in a developer org: a separate org with sample data, where API access, webhooks, OAuth apps, custom actions and hooks are available on any plan and email only reaches you. Promote what you built with a change set.