Callbacks — trigger.complete / trigger.reverse
Two REST endpoints (not MCP tools) that your handler calls to report execution outcome and any subsequent reversal. Calling back is what writes the ledger entry, trains trust, and lets the substrate compute its next decision.
Why call back at all?
A 200 OK from your handler tells Cortex the dispatch was received. The callback tells Cortex the action was actually executed — with what external reference, in what channel, at what time. This is what closes the loop and writes the ledger entry; it's also what trains trust over time. A handler that always returns 200 but never calls back will, after the configured success-window, have its trust pair flagged.
POST /v1/triggers/{trigger_id}/complete
Reports successful execution (or an explicit user decision in ask mode).
POST https://mcp.cortex.jakeselby.com/v1/triggers/{trigger_id}/complete
Authorization: Bearer <principal_token>
Content-Type: application/json
Idempotency-Key: cb_01HXP9...
{
"outcome": "executed",
"external_ref": "slack_msg_123",
"external_url": "https://your-app.com/messages/123",
"executed_at": "2026-05-27T14:32:10Z",
"notes": "Optional human notes; appended to ledger entry."
}Outcome values
| Value | Meaning | Trust impact |
|---|---|---|
executed | Action executed successfully on your side (act / notify_after modes). | Reinforces the pair's graduation streak. |
approved | User approved the action in your app's UI (ask mode); you then executed it. | Reinforces the pair. |
rejected | User explicitly rejected the proposal (ask mode). | Demotes trust for the (action × counterparty) pair. |
deferred | User chose “not now”; no action taken, but no negative signal. | Neutral; the same envelope may re-dispatch later. |
Response
{
"ok": true,
"ledger_entry_id": "led_01HXP9COMM",
"trust_delta": { "action_class": "communicate", "counterparty_id": "entity_01HXSARAH", "change": "reinforce" }
}POST /v1/triggers/{trigger_id}/reverse
Reports that the executed action was undone — either by the user inside the reversal window, or by your app for an operational reason (delivery bounce, recipient deletion, opt-out).
POST https://mcp.cortex.jakeselby.com/v1/triggers/{trigger_id}/reverse
Authorization: Bearer <principal_token>
Content-Type: application/json
Idempotency-Key: cbr_01HXP9...
{
"reason": "user_undo",
"reason_text": "User clicked Undo within 60 minutes",
"reversed_at": "2026-05-27T14:35:00Z"
}Reason values
| Value | Meaning | Trust impact |
|---|---|---|
user_undo | User clicked Undo / Reverse in your UI within the reversal window. | Demotes the pair. Repeated user_undo within a short window can drop trust by two levels. |
user_correction | User corrected the action (different recipient, different content) after execution. | Demotes the pair; logs the correction against the rule that fired. |
operational | Reversal for operational reason (bounce, opt-out, channel error). Not user intent. | Neutral for trust; appended to ledger for audit. |
Idempotency
Both callbacks accept an optional Idempotency-Key header. If your handler retries a callback after a network error, set the same key to ensure the substrate treats it as a no-op rather than recording the outcome twice.
Errors
| Code | Meaning |
|---|---|
signature_invalid | Your callback is itself authenticated, not signed (you use the principal bearer token). But Cortex can return this if you attempt to callback a trigger that was dispatched to a different handler. |
invalid_decision_id | The trigger id does not exist or was not dispatched to your handler. |
already_decided | This trigger was already resolved (callback already recorded). |
decision_expired | The trigger's expires_at has passed. Reversal is still accepted within the broader reversal window; complete is not. |
See the full error catalog for resolution steps.