A gateway in front of your API
Reserve your gateway and connect your API under Connections → Deployments. Your gateway lives at https://<your-label>.decionisedge.com and forwards to the one API origin you proved you control.
Keep the n8n, Zapier or Make workflow you already run. Send the step that calls your API through your gateway, and your policy decides it before your API sees it.
You change one thing in your workflow: the address the step calls.
Reserve your gateway and connect your API under Connections → Deployments. Your gateway lives at https://<your-label>.decionisedge.com and forwards to the one API origin you proved you control.
Reveal the ingress key once in your workspace and keep it in your automation tool's credential store. Every request sends it as AgentSafe-Tenant-Key; your gateway strips it before forwarding.
Point a POST, PUT, PATCH or DELETE at your gateway, such as a payout, a refund or a deploy. GET, HEAD and OPTIONS pass through and are never decided.
| Capability | Status | What it means for your workflow |
|---|---|---|
| Hosted gateway in shadow mode | Private beta | Your step's request reaches your API as before, and what your policy would have decided is recorded in your workspace. |
| Hosted gateway enforcement | Private beta | Opened per workspace on a plan that may enforce. BLOCK and ESCALATE stop the request before it reaches your API. |
| Presence approvals for a held request on a hosted gateway | Planned | Today an ESCALATE on a hosted gateway is held and reported, and nobody can release it. Branch to a person and stop the step. |
Branch on the agentsafe-execution header where your tool shows headers, and on state in the gateway's own JSON answer otherwise. A forwarded answer is your API's own body and status.
| Verdict or state | Status | agentsafe-execution | Meaning | Your branch |
|---|---|---|---|---|
| ALLOW | Your API's status | FORWARDED | Your policy allowed it and the request reached your API once. The answer is your API's, with agentsafe-decision: ALLOW and agentsafe-dossier-id added. | Continue the workflow. |
| ALLOW | Your API's 4xx | FAILED | Allowed and sent, and your API refused it. Nothing was done. | Handle it as your API's own error. |
| BLOCK | 403 | NOT_FORWARDED | Your policy refused it. Nothing reached your API. | Stop. Do not retry the same request. |
| ESCALATE | 202 | HELD | A person must decide. The request is held until expires_at, two minutes by default, and nothing reached your API. | Stop and notify a person with decision_id and dossier_id. Keep resume_token out of logs and messages. |
| AUTHORITY_UNAVAILABLE | 503 | NOT_FORWARDED | No decision could be obtained, so nothing was forwarded. Retry-After is set. | Retry after Retry-After with the same Idempotency-Key. |
| EXECUTION_INDETERMINATE | 502 | INDETERMINATE | The request was sent and no answer came back. Your API may have acted on it. | Do not retry. Check your API for the result first. |
| Shadow | Your API's status | PASSTHROUGH | Shadow mode: forwarded unchanged, with agentsafe-mode: SHADOW. The verdict is recorded in your workspace, not returned to your step. | Continue. Review the recorded decisions before you enforce. |
HTTP/1.1 403 Forbidden
agentsafe-state: BLOCK
agentsafe-execution: NOT_FORWARDED
{
"version": "agent-safe.gateway/1",
"state": "BLOCK",
"verdict": "BLOCK",
"reason_codes": ["<reason code>"],
"execution": "NOT_FORWARDED",
"intent_id": "<intent id>",
"decision_id": "<decision id>",
"dossier_id": "<dossier id>"
}Use the trigger's own event or record ID. A retry after 503 AUTHORITY_UNAVAILABLE then carries the same key. Never retry an INDETERMINATE answer: your API may already have acted.
Point the HTTP Request node that calls your API at your gateway, keep the full response, and add a Switch on the agentsafe-execution header.
In the HTTP Request node that calls your API, replace your API's origin with your gateway URL. The path, method and body stay the same: https://<your-label>.decionisedge.com/payouts.
Use a Custom Auth credential that sends AgentSafe-Tenant-Key. In shadow mode it also sends your API's own Authorization header; remove that once your gateway enforces, because your gateway adds it.
Under Options → Response, turn on Include Response Headers and Status and Never Error, so a 403 or 202 reaches the next node instead of failing this one.
Add a Switch on {{ $json.headers['agentsafe-execution'] }}. FORWARDED and PASSTHROUGH continue; anything else goes to a branch that stops.
HTTP Request node (paste into the n8n canvas)
{
"nodes": [
{
"parameters": {
"method": "POST",
"url": "https://<your-label>.decionisedge.com/payouts",
"authentication": "genericCredentialType",
"genericAuthType": "httpCustomAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Idempotency-Key",
"value": "={{ $json.event_id }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.payout) }}",
"options": {
"response": {
"response": {
"fullResponse": true,
"neverError": true
}
}
}
},
"name": "Payout through your gateway",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
0,
0
]
}
],
"connections": {}
}Custom Auth credential (shadow mode)
{
"headers": {
"AgentSafe-Tenant-Key": "<AGENTSAFE_TENANT_KEY>",
"Authorization": "<YOUR_API_CREDENTIAL>"
}
}| When | Then |
|---|---|
| FORWARDED or PASSTHROUGH | Continue with {{ $json.body }}, your API's answer. |
| FAILED | Your API refused the request. Handle it as you do today. |
| NOT_FORWARDED with body.state BLOCK | Stop. Send body.decision_id and body.dossier_id to the person who owns the step. |
| HELD (body.state ESCALATE) | Stop and notify a person. On a hosted gateway the hold cannot be released yet. |
| NOT_FORWARDED with body.state AUTHORITY_UNAVAILABLE | Wait for the Retry-After header, then run again with the same Idempotency-Key. |
| INDETERMINATE | Do not retry. Check your API for the result. |
Keep the ingress key in an n8n credential, never in the node, a sticky note or a workflow export that leaves your instance.
Replace the action that calls your API with a Webhooks by Zapier Custom Request to your gateway, then add Paths on the gateway's State field.
Add a Webhooks by Zapier action, event Custom Request, where your step called your API.
Method POST, URL https://<your-label>.decionisedge.com/payouts, Data your mapped JSON body, and the headers below. Data Pass-Through stays off.
After the request, add Paths. Path A continues only when State does not exist; Path B runs when State exactly matches ESCALATE.
Leave the step's error handling as it is: a 403 BLOCK or a 5xx answer fails the Custom Request, so the Zap stops before the next step runs.
Webhooks by Zapier · Custom Request
Method: POST
URL: https://<your-label>.decionisedge.com/payouts
Data: {"amount": <mapped amount>, "currency": "<mapped currency>", "recipient": "<mapped recipient>"}
Headers:
Content-Type application/json
AgentSafe-Tenant-Key <AGENTSAFE_TENANT_KEY>
Idempotency-Key <mapped trigger ID>
Authorization <YOUR_API_CREDENTIAL> (shadow mode only; remove once your gateway enforces)
Unflatten: yes
Data Pass-Through: noPaths
Path A · Continue State (from the Custom Request) Does not exist
Path B · Hold State (Text) Exactly matches ESCALATE
→ notify a person with Decision Id and Dossier Id, then stop| When | Then |
|---|---|
| State does not exist | Your API answered; continue the Zap. |
| State is ESCALATE | Notify a person with Decision Id and Dossier Id. The Zap ends on this path. |
| Step error: 403 BLOCK | The Zap stops. The refusal and its Decision Dossier are in your workspace. |
| Step error: 503 AUTHORITY_UNAVAILABLE | Replay the Zap run later; the same trigger ID keeps the Idempotency-Key. |
| Step error: 502 INDETERMINATE | Do not replay. Check your API for the result first. |
Webhooks by Zapier keeps headers in the Zap itself, so anyone who can edit the Zap can read the ingress key. Limit who can edit it, and contact support to replace the key if someone who should not have it could read it.
Point the HTTP module that calls your API at your gateway, keep non-2xx answers as output, and add a Router with filters on the gateway's state.
In the HTTP Make a request module that calls your API, set the URL to https://<your-label>.decionisedge.com/payouts. Method and body stay the same.
Store the ingress key as an API key credential with the header name AgentSafe-Tenant-Key. Keep your API's own credential where it is until your gateway enforces.
Set Parse response to Yes and Evaluate all states as errors to No, so a 403 or a 202 reaches the Router.
Add a Router with one filter per route below. The fallback route stops the scenario.
HTTP · Make a request
URL: https://<your-label>.decionisedge.com/payouts
Method: POST
Authentication: API key · header AgentSafe-Tenant-Key = <AGENTSAFE_TENANT_KEY>
Headers: Idempotency-Key = {{1.id}}
Authorization = <YOUR_API_CREDENTIAL> (shadow mode only)
Body content type: application/json
Body: <your mapped JSON body>
Parse response: Yes
Evaluate all states as errors: NoRouter filters
Continue data.state Does not exist AND statusCode Less than 400
Hold data.state Equal to ESCALATE → notify a person, then stop
Blocked data.state Equal to BLOCK → record decision_id, then stop
Retry data.state Equal to AUTHORITY_UNAVAILABLE → Break, retry later
Fallback anything else (including statusCode 502) → stop and check your API| When | Then |
|---|---|
| data.state does not exist and status below 400 | Your API answered; continue. |
| data.state is ESCALATE | Notify a person with data.decision_id, then stop. |
| data.state is BLOCK | Stop. Record data.decision_id and data.dossier_id. |
| data.state is AUTHORITY_UNAVAILABLE | Use a Break error handler to retry later with the same Idempotency-Key. |
| Anything else, including 502 | Stop and check your API. An INDETERMINATE answer may have acted. |
Keep the ingress key in Make's keychain as an API key credential, never in the URL, the body, scenario notes or a blueprint you share.
Your policy decides every request your gateway receives. Write it in Policy Writer, in plain English.
Under Policies → Draft, open Write and describe the rule, such as: block payouts over 5,000 and send payouts to a new recipient to a person. The proposal joins your draft only when you add it, and becomes policy after review.
Your gateway names each request by its method: http.post, http.put, http.patch or http.delete, with its path and JSON body. Write rules against those names and the fields in the body your step sends.
The Policy field under Deployments names the published policy your gateway uses, or leave it empty for your workspace's default. Every request through that gateway is decided by it.
An ESCALATE means a person must decide. Your step receives 202 with execution HELD, and nothing reaches your API.
Branch an ESCALATE to a person with decision_id and dossier_id, and stop the step. The hold expires at expires_at; it cannot be released, because hosted gateways do not yet name a Presence approver.
Presence is where the named approver confirms with a passkey. When a hosted gateway can name one, a held request runs once after a fresh ALLOW, and only then.
Every gateway starts in shadow mode. Run your workflow unchanged for a while, then decide whether to enforce.
Your step's request reaches your API exactly as before, with agentsafe-mode: SHADOW. Keep your API's own credential in the step. Each request is decided and recorded in your workspace with its Decision Dossier.
Read what your policy would have blocked or held. Adjust it in Policy Writer until the decisions match what you expect.
Switch your gateway to enforcement under Deployments, give it your API's credential, and remove that credential from your workflow. BLOCK and ESCALATE now stop the request.
What these recipes cannot do today. Plan your workflow around it.
A hosted gateway forwards to one HTTPS origin, and only after you prove control of it with /.well-known/agentsafe-upstream or a TXT record. To govern a call to a provider you do not control, call your own API, which then calls the provider.
A hosted gateway has no named routes yet. A governed request reaches your policy as http.post, http.put, http.patch or http.delete with its path and body, and one policy decides every request through the gateway.
An ESCALATE is held until expires_at and then dropped. Resuming it answers 409 PRESENCE_NOT_CONFIGURED, because hosted gateways do not yet name a Presence approver.
The gateway answers the request that was sent and does not call your workflow back. Holds live in memory for at most five minutes.
In shadow, your step receives your API's answer only. Read what your policy would have decided in your workspace, not in the workflow.
Calling Decionis directly instead of through a gateway? The Zapier, Make and n8n connectors evaluate a decision without forwarding anything. Decionis Auto, which runs these steps for you, is Planned.