Change actions
Let customers confirm a change, request owner approval and track the real outcome.
Let customers confirm a change, request owner approval and track the real outcome.
Define a change#
Use a change action to update a record in your own system, such as cancelling an order. Open Agent → Actions, configure your agent secret in Signed-in customers, then add a Change action: a unique name, a description of when to use it, a public HTTPS endpoint and declared string inputs. A name such as cancel_order becomes the model tool change_cancel_order. Existing edits autosave; wait for the saved indicator.
Keep Requires signed-in customer enabled for private records. Your endpoint must still authorize the requested record against the verified customer ID. A name, email or order ID typed into chat is not proof of identity. If you disable this setting, customer can be null: permit only changes appropriate for anonymous visitors. Payments are outside this feature's scope.
Choose owner approval#
Customer confirmation is required in every mode:
| Mode | After the customer confirms |
|---|---|
No approval (none) | Execute without a separate owner decision. |
Always approve (always) | Wait for the owner. |
Over a limit (over_limit) | Wait when a declared numeric input is strictly greater than the limit; equality does not require approval. Invalid numeric input is rejected. |
Review waiting requests in Agent → Actions → Approvals. The site owner can approve or decline. If private Telegram owner alerts are linked, the alert also has Approve / Decline buttons. Only the linked owner's private chat can use them; expired or repeated taps cannot execute again. Approval expires 24 hours after customer confirmation. The customer receives the outcome, including decline or expiry. Recent runs show status, timestamps, reference and customer ID.
What your endpoint receives#
No request is sent when the model proposes the change. After customer confirmation and any required owner approval, Make Agent Fast sends:
POST /agent/cancel-order HTTP/1.1
Content-Type: application/json
x-maf-timestamp: <unix-seconds>
x-maf-signature: v1=<64-lowercase-hex-characters>
x-maf-idempotency-key: c5c1d94b-e88f-4bf6-8023-2dcd1e7ac67b{
"version": 1,
"action": "cancel_order",
"runId": "c5c1d94b-e88f-4bf6-8023-2dcd1e7ac67b",
"requestId": "c5c1d94b-e88f-4bf6-8023-2dcd1e7ac67b",
"siteId": "YOUR_SITE_ID",
"input": { "order_id": "A128" },
"customer": { "id": "customer_123", "email": "customer@example.com" }
}action excludes the change_ prefix. Both IDs and the idempotency header remain the same on retries. input contains only declared, trimmed strings; customer is null or the verified object with id and optional email/name.
Use the lookup signature verifier: HMAC-SHA256 with your agent secret over "maf-webhook-v1:" + timestamp + "." + rawBody, lowercase hex prefixed by v1=. Verify the original bytes, timestamp freshness, expected site/action and customer ownership before writing. Atomically deduplicate x-maf-idempotency-key with your change and retain the result. A repeated request must return the saved outcome without changing the record again. Platform locking cannot replace deduplication in your system.
Return an outcome#
Return 2xx JSON with required status (done, pending or failed) and optional message, reference and card:
{
"status": "done",
"message": "Order cancelled.",
"reference": "A128",
"card": {
"kind": "status",
"title": "Order A128",
"fields": [{ "label": "Order", "value": "A128" }]
}
}The optional status card requires title and fields; kind: "status" is optional. It supports status: { label, tone }, reference, asOf and link: { label, url }, as in lookups. Tones are neutral, success, warning or danger; links must be safe HTTPS URLs. Unknown schema fields are rejected. Keep text short and plain. The outer status determines the outcome; card wording cannot turn a pending result into success.
Use done only after the change is complete, pending when it is still processing, and failed for a known failure. Timeouts, network errors, 5xx and invalid responses are treated as uncertain (unknown). Reconciliation retries after one minute, then five minutes, with at most three execution attempts and the same key; queue scheduling can delay these times. HTTP 4xx fails without retry. On exhausted uncertainty the run fails and the owner is notified, but this does not prove your system made no change. Investigate before starting another run. pending does not trigger another execution.
Send a signed final callback#
When a pending or unknown run resolves, POST a final response to https://makeagent.fast/api/agent/actions/RUN_ID/callback. Send the same response contract with status: "done" or "failed"; pending is not accepted here.
Callback contract updated 2026-10-14: sign maf-callback-v1: + runId + : + timestamp + . + rawBody. If the body includes runId, it must match the path run ID; a mismatch returns 400. Signatures for another run or the previous unbound format return 403.
import { createHmac } from "node:crypto";
const body = JSON.stringify({ status: "done", reference: "A128" });
const timestamp = String(Math.floor(Date.now() / 1000));
const signature = createHmac("sha256", agentSecret)
.update(`maf-callback-v1:${runId}:${timestamp}.${body}`)
.digest("hex");
const response = await fetch(
`https://makeagent.fast/api/agent/actions/${encodeURIComponent(runId)}/callback`,
{
method: "POST",
headers: {
"content-type": "application/json",
"x-maf-timestamp": timestamp,
"x-maf-signature": `v1=${signature}`,
},
body,
},
);Use your server-held agent secret and the run ID received in the original request. The callback uses maf-callback-v1:, not the outbound request namespace. Timestamp skew must be within five minutes. Invalid signatures return 403; invalid bodies return 400; an already terminal or incompatible run returns 409. A repeated callback does not overwrite the final result. Rotate this secret together with your identity and endpoint signing configuration.
Customer confirmation and receipts#
The customer reviews the exact inputs and taps Confirm or Cancel on the proposal. A proposal expires after 15 minutes; a new proposal for the same action/conversation replaces the old one. Confirmation uses a token bound to that run, site and visitor. Typing “yes” or clicking a generic confirmation card does not authorize the change. Rehearsal buttons are inactive and do not call your endpoint.
On the web, the open widget polls nonterminal receipts, initially every five seconds and then every 30 seconds, for up to 24 hours. A receipt can show requested, waiting for owner, running, pending, checking an unknown result, done, failed, declined, cancelled or expired. Only done confirms completion.
Channel proposals work only in private chats, never groups or channels. Delayed receipts use the durable outbox: ordinary Telegram bot DMs are supported; WhatsApp, Messenger, Instagram and Telegram Business require a recorded inbound message within 24 hours, checked again at send time. Delayed delivery is unavailable for Telegram guest queries, Discord and KakaoTalk. Closed windows and disabled connections can prevent delivery; the run history still records the outcome.
Limits and testing#
| Item | Limit |
|---|---|
| Change actions | 5 per agent; names unique across changes and lookups |
| Names | Action: ^[a-z][a-z0-9_]{2,39}$; input: ^[a-z][a-z0-9_]{1,31}$; reserved action names unavailable |
| Descriptions | 300 characters |
| Inputs | 3 declared strings, each at most 200 characters |
| Endpoint | Public HTTPS, up to 500 characters, no URL credentials, private addresses or redirects |
| Execution timeout / response / callback body | 8 seconds / 32 KiB / 32 KiB |
| Status card title / field label / value / fields | 80 / 40 / 200 characters / 8 fields |
| Proposal / approval expiry | 15 minutes / 24 hours after confirmation |
Use a controlled endpoint to verify confirmation, cancellation, approval, timeout, duplicate-key handling and final callbacks. Check its records, not just the receipt text. Rehearsal proves no external write; it is not an endpoint execution test. Test every intended private channel before relying on receipts.