Browse the manual
Automation rules and webhook tools
Read and change owned rules, inspect runs and request exact human approval for signed webhook delivery and recovery.
On this page
For: developers connecting an HTTP or MCP client, or implementing a webhook receiver. What you need: an active delegated staff connection, Automations access and the grants required below.
HTTP and MCP call the same rule, run and delivery actions as the browser. Read the Automations manual for the controls and recovery steps.
Rules and endpoints belong to their creator. Source, target and result records need current access. Guests cannot use these tools.
Choose grants and discover schemas
Use automations:read for reads, automations:prepare for ordinary saved changes, and automations:execute for sensitive approvals and webhook recovery.
The person always needs automations.use. Tools marked manage below also need automations.manage. Source actions retain their own permissions.
Read GET /api/v1/tools or MCP tools/list for the connection's exact input and output schemas. Unknown input fields are refused.
HTTP uses each route below. Send the record ID in the path. Every write needs an Idempotency-Key header.
MCP uses the tool name below. Put the record id and idempotency_key in its arguments when required.
Keys accept 1–80 letters, digits, underscores or hyphens. Use a new key for each intended change. Retry uncertain responses with the same key and unchanged input.
Reuse with different input is refused. Replays recheck current access before returning saved results. They cannot restore a lost permission.
Tool and route reference
All HTTP paths start with /api/v1. Use and manage refer to the permissions above.
| MCP tool | HTTP route | Grant | Permission | Input and result |
|---|---|---|---|---|
automations_catalog |
GET /api/v1/automations/catalog |
automations:read |
Use | No input; permitted triggers, conditions and actions |
automations_rules |
GET /api/v1/automations/rules |
automations:read |
Use | Optional search, state, page; the browser's owned-rule register |
automations_rule_get |
GET /api/v1/automations/rules/{id} |
automations:read |
Use | Rule ID; current rule and readable graph |
automations_rule_create |
POST /api/v1/automations/rules |
automations:prepare |
Manage | name, graph, optional description; inactive rule |
automations_rule_update |
PUT /api/v1/automations/rules/{id} |
automations:prepare |
Manage | version, complete input; changed inactive rule |
automations_activation |
POST /api/v1/automations/rules/{id}/activation |
automations:prepare |
Manage | version, Boolean active; current rule |
automations_stop |
POST /api/v1/automations/rules/{id}/stop |
automations:prepare |
Manage | version, Boolean stopped; current rule |
automations_runs |
GET /api/v1/automations/rules/{id}/runs |
automations:read |
Use | Rule ID; up to 50 recent permitted runs |
automations_run_get |
GET /api/v1/automations/runs/{id} |
automations:read |
Use | Run ID; current inputs, steps and results |
automations_run_retry |
POST /api/v1/automations/runs/{id}/retry |
automations:prepare |
Manage | Current run version; run ID, version and status |
automations_endpoints |
GET /api/v1/automations/endpoints |
automations:read |
Manage | No input; up to 100 owned endpoint records |
automations_endpoint_get |
GET /api/v1/automations/endpoints/{id} |
automations:read |
Manage | Endpoint ID; metadata without its secret |
automations_endpoint_create |
POST /api/v1/automations/endpoints |
automations:prepare |
Manage | name, url; endpoint with one-time secret |
automations_endpoint_rotate |
POST /api/v1/automations/endpoints/{id}/rotate |
automations:prepare |
Manage | Current endpoint version; endpoint with one-time replacement secret |
automations_webhook_get |
GET /api/v1/automations/webhooks/{id} |
automations:read |
Manage | Delivery ID; current state, attempts and recovery availability |
automations_webhook_recovery_prepare |
POST /api/v1/automations/webhooks/{id}/recovery |
automations:execute |
Manage | Current delivery version; a new rule recovery run and pending approval ID |
automations_webhook_send_prepare |
POST /api/v1/approvals/automations-webhook-send/{id} |
automations:execute |
Manage | Endpoint ID and exact body; pending approval, review URL and nonce |
automations_webhook_send |
POST /api/v1/approvals/{id}/automations-webhook-send |
automations:execute |
Manage | Approval ID and nonce; executed approval and current delivery |
automations_webhook_retry_prepare |
POST /api/v1/approvals/automations-webhook-retry/{id} |
automations:execute |
Manage | Delivery ID; fresh pending approval, review URL and nonce |
automations_webhook_retry |
POST /api/v1/approvals/{id}/automations-webhook-retry |
automations:execute |
Manage | Approval ID and nonce; executed approval and current delivery |
views_automation_rule |
GET /api/v1/views/automation_rule |
automations:read |
Use | Registered view inputs; permitted rule view |
views_automation_endpoint |
GET /api/v1/views/automation_endpoint |
automations:read |
Use | Registered view inputs; only owned endpoints with manage permission |
The rule register accepts state: all, active, inactive or stopped, and a search of up to 120 characters. It returns 50 rows per page.
Most tools return {"data": ...}. Rule-register data includes rows, pagination, filters, row actions and permitted links.
A rule includes id, name, description, version, active, stopped, graph, graph_available, actor and updated_label.
An unreadable saved declaration or target returns graph: null and graph_available: false. Keep that distinction. An empty graph would misrepresent the saved rule.
The view tools keep their ordinary view format, including root layout, view, total, totals and next_cursor fields. They do not use the rule-register envelope.
Create and activate a rule
This HTTP example creates a weekly task. Replace the example date and time zone before use.
POST /api/v1/automations/rules
Authorization: Bearer YOUR_CONNECTION_TOKEN
Idempotency-Key: weekly-planning-1
Content-Type: application/json
{
"name": "Weekly planning",
"graph": {
"nodes": [
{
"id": "when",
"kind": "trigger",
"key": "schedule",
"config": {
"starts_on": "2026-11-02",
"time": "09:00",
"timezone": "Europe/Amsterdam",
"frequency": "weekly",
"interval": 1
}
},
{
"id": "plan",
"kind": "action",
"key": "task.create",
"config": {
"title": "Review next week's work",
"starts_on": {"binding": "occurrence.date"},
"due_on": {"binding": "occurrence.date"},
"priority": 3
}
}
],
"edges": [
{"id": "next", "source": "when", "target": "plan", "source_handle": "next"}
]
}
}
Keep the returned rule id and version. Creation saves an inactive rule and performs no action.
Send {"version": 1, "active": true} to POST /api/v1/automations/rules/RULE_ID/activation with a new key. Use the returned version if it differs.
For MCP, call automations_rule_create with the same JSON fields and idempotency_key. Activate with automations_activation, adding id, version, active and another key.
Rule names accept 120 characters. Descriptions accept 2,000 characters. Versions are integers from 1 to 4,294,967,295.
Updates replace the complete input: {name, description?, graph}. They deactivate the rule and cancel unfinished work. Reactivate after review.
stopped: true blocks activation. Clearing it leaves the rule inactive. Activation excludes earlier event history; an old schedule start can produce catch-up work.
Graph fields and limits
The graph has exactly nodes and edges. It accepts 2–32 nodes, 64 edges and 131,072 bytes of encoded graph data.
Each node has id, kind, key and config. Optional position contains finite x and y values between −100,000 and 100,000.
Node IDs start with a letter. The remaining characters can be letters, digits, underscores or hyphens. The maximum length is 64 characters.
Use exactly 1 trigger. Every other node has exactly 1 incoming edge. Every node must be reachable. Cycles and joins are refused.
Each edge has unique id, source, target and source_handle. Use next for ordinary steps, or true and false for conditions.
Each source handle has at most 1 target. A condition can leave either branch empty to end that path.
| Kind | Keys and configuration |
|---|---|
| Event trigger | task.created, task.completed, deal.won, invoice.finalized, calendar_event.created, doc.approved, form.submitted; empty configuration |
| Schedule trigger | schedule; starts_on, time as HH:mm, IANA timezone, frequency, interval, optional ends_on |
| Title condition | source.title; operator is equals, not_equals or contains, with string value |
| Priority condition | source.priority; operator is equals, gt or lt, with integer value from 1 to 4 |
| Create task | task.create; required title; optional project_id, starts_on, due_on, priority |
| Open project | deal.open_project; required deal_id, optional title |
| Issue document | approval.documents.issue; required document id |
| Send document | approval.documents.send; document id, template_key, subject, body, recipient selection below, optional cc |
| Send webhook | webhook.send; owned endpoint_id and exact JSON-object string body |
Read the catalogue's fields, required, options, bindings and outputs before constructing input. Only task triggers provide priority and project outputs.
Task priorities are 1 Urgent, 2 High, 3 Normal and 4 Low. Priority comparisons use the numeric value.
Schedule cadence is daily, weekly or monthly, with interval from 1 to 365. Dates use YYYY-MM-DD. Read the wall-clock rules.
A binding is an object such as {"binding":"source.title"}. Use only compatible declared values. step.NODE_ID.id must refer to an earlier connected action with the required record type.
For send steps, optional recipient_policy accepts selected or primary_billing. Omitting it keeps the selected behavior.
With selected, supply to as a JSON-encoded array of offered recipient tokens from documents_email_draft. Use current returned tokens; do not substitute addresses or guess contact IDs.
With primary_billing, omit to. Pepper resolves the current eligible primary recipient, or the first offered recipient, when it prepares the action. Combining that policy with to is refused.
The approval displays the exact resolved addresses. An address change after preparation makes the proposal stale. Optional cc is a JSON-encoded array of permitted additional email addresses.
Rule activation grants no approval to issue or send a document. Each run prepares its own exact proposal through the shared approval system.
The internal webhook.retry action belongs to recovery. It is refused in an ordinary saved graph.
Inspect runs and retry ordinary work
A run returns its ID, current version, rule ID and version, status, actor ID, inputs, steps, error code and retry availability.
Each step returns its node ID, status, inputs, result, attempt count, approval ID and error code. Created records retain their type and ID.
Read access rechecks source records, fixed targets, resolved inputs, results and approvals. An inaccessible run is not disclosed, even through an old response replay.
automations_runs returns up to 50 recent permitted runs. Use automations_run_get for current evidence before acting.
Retry only when retryable is true. Pass the current run version to automations_run_retry. Completed steps keep their results.
A failed sensitive step cannot become an ordinary resend. Read the delivery and use its reviewed recovery path.
Repeated event processing does not create another run for the same rule and source event. Replayed steps do not create another ordinary effect.
Rules cannot re-enter themselves through their own event chain. Chains stop at 8 ancestor rules. One root event allows at most 64 ordinary action effects.
Prepare a direct webhook send
- Create an endpoint with
automations_endpoint_create. - Store its one-time
secretat your receiver. - Call
automations_webhook_send_preparewith the endpoint ID and exact JSON-object stringbody. - Show the person the returned
review_url. - Wait for the person to approve the destination, exact body and retry policy.
- Call
automations_webhook_sendwith the approval ID, nonce and a new key. - Read the returned delivery and its current status.
Endpoint names accept 120 characters. API addresses accept 4,096 characters. Saving an endpoint performs no HTTP send.
The endpoint output includes id, name, url, version and has_secret. Create and rotate also return one-time secret output.
That secret is ephemeral. Keep it out of model transcripts, logs and saved receipts. A replay returns secret: null. Rotate if you missed it.
Rotation changes the endpoint version and makes old unexecuted preparations stale. Configure the receiver with the replacement before approving another delivery.
The body must be a JSON object of at most 262,144 bytes. Pepper preserves the exact bytes, including whitespace, for approval, signing and retries.
Only public HTTPS destinations on port 443 are allowed. Credentials, fragments, redirects and private, local or reserved addresses are refused.
Each attempt resolves DNS again, rejects unsuitable answers and pins an admitted address while verifying TLS. Connection and total deadlines are 3 and 10 seconds.
Response bodies have a 16,384-byte limit. Pepper stores the status and result code, not the receiver's response body.
Verify a webhook
Check the signature against the raw request body before parsing JSON. Use the stored signing secret as its text bytes.
| Header | Value |
|---|---|
Content-Type |
application/json |
X-Pepper-Webhook-Version |
1 |
X-Pepper-Delivery-Id |
Stable delivery ID |
Idempotency-Key |
The same stable delivery ID |
X-Pepper-Timestamp |
Unix timestamp in seconds for this attempt |
X-Pepper-Signature |
v1= followed by the lowercase hexadecimal HMAC-SHA256 |
Compute the signature over these exact bytes:
v1.TIMESTAMP.DELIVERY_ID.EXACT_BODY
For example, verify with a constant-time comparison in PHP:
$signed = 'v1.'.$timestamp.'.'.$deliveryId.'.'.$rawBody;
$expected = 'v1='.hash_hmac('sha256', $signed, $signingSecret);
$valid = hash_equals($expected, $receivedSignature);
Validate the version and both delivery-ID headers too. Apply a timestamp-age policy suitable for your receiver.
Store processed delivery IDs and reject duplicate processing. Retries keep the ID and body, but use a fresh timestamp and signature.
HTTP 2xx means the receiver accepted the request. It does not confirm completion of the receiver's business operation.
Recover a delivery
Read automations_webhook_get before recovery. It returns version, status, lifetime attempts, cycle_attempts, HTTP status and next-attempt time.
It also returns can_retry, recovery_origin, approval IDs, up to 50 attempt records and more_attempts when older attempts remain.
| Result | Processing policy |
|---|---|
| HTTP 2xx | accepted; no automatic retry |
| HTTP 425, 429, 5xx or a known refused connection | Retry within the approved cycle |
| HTTP 408, timeout, ambiguous transport or an oversized response | unknown; fresh human review required |
| Redirect or other HTTP 4xx | rejected; no automatic retry |
| Worker stopped after claiming a send | Mark unknown after 10 minutes; do not resend without review |
One approval cycle allows at most 5 attempts. Retry delays are 60, 300, 1,800 and 7,200 seconds after each known retryable failure.
An earlier attempt may have reached the receiver. Check it before preparing recovery. A fresh approval permits up to 5 further attempts with the same ID and body.
For recovery_origin: automation, call automations_webhook_recovery_prepare with the delivery ID, current version and a new key.
It returns a fresh recovery run's id, version, status and approval_id. Open that run or its exact approval in Pepper. Nothing sends before human approval.
The rule must remain current, active and accessible. Stopping it, changing its version or losing the source or endpoint prevents recovery.
For recovery_origin: agent, use the original still-active connection with automations_webhook_retry_prepare. Show its new review URL, then execute automations_webhook_retry after approval.
A replacement connection cannot take over the original request. The internal recovery tool refuses an externally requested delivery.
Run completion and delivery completion are separate. A finished step can have a later retryable or unknown delivery. Read current delivery evidence before reporting success.
Handle refusals
| Response | Next step |
|---|---|
| 401 or 403 | Check connection expiry, delegate status, grants and current permissions |
| 404 | Check ownership and current source, target and result access |
| 409 | Read current versions and state. Review changed input, authority or an existing approval before retrying |
| 410 | Prepare a fresh approval after checking the current action |
| 422 | Correct unknown fields, invalid graph structure, incompatible bindings or invalid values |
Keep valid input after a refusal. Do not convert a timeout into a success message or automatic sensitive retry.
Use the normal hosting scheduler for rules and delivery retries. It runs automations:process every minute when the app is available.
Need help with the product?
Contact support