Compensation
Compensation undoes succeeded work items when a step ultimately fails. Configure a compensate endpoint on any sync or async HTTP step, and Argyll will call it for each work item that already completed.
When Compensation Fires
Compensation is triggered at step failure, not flow failure. When a step fails after some work items have already succeeded, the engine schedules compensation for every work item whose status is succeeded.
Compensation runs even after the flow reaches a terminal state (failed). The flow is held until all compensation work items settle.
Configuring a Compensate Endpoint
Add compensate to the step’s http block:
{
"id": "charge-card",
"name": "Charge Card",
"type": "async",
"http": {
"endpoint": "https://payment-service.example.com/charge",
"timeout": 1000,
"compensate": "https://payment-service.example.com/refund/{charge_id}"
},
"attributes": {
"amount": { "role": "required", "type": "number" },
"charge_id": { "role": "output", "type": "string" }
}
}
The compensate URL supports {param} placeholders resolved from the work item’s inputs and outputs, with outputs taking priority when names collide.
What the Engine Sends
The engine sends a POST to the resolved compensate URL:
{
"input": { "amount": 49.99 },
"output": { "charge_id": "ch_abc123" }
}
The same Argyll-Flow-ID, Argyll-Step-ID, and Argyll-Receipt-Token headers sent to the work endpoint are also sent to the compensate endpoint. Use the receipt token as the idempotency key for compensation side effects.
Retry Behavior
Compensation uses the same work_config retry settings as normal work. The engine treats 5xx responses as transient and retries with the configured backoff. 4xx responses are permanent failures. When max_retries is exhausted, the compensation is marked comp_failed.
See Retries for work_config options.
Memoizable Steps Cannot Be Compensated
Steps with memoizable: true declare no side effects, so compensate is not allowed alongside memoizable. Configuring both is a validation error.
Work Item States
Compensation adds three states to the normal work item lifecycle:
| Status | Meaning |
|---|---|
compensating | Dispatched, waiting for result |
compensated | Completed successfully |
comp_failed | Permanently failed |
Compensation retries produce a comp_retry_scheduled event and follow the same backoff as work retries.
The flow is not deactivated until all compensation work items reach a terminal state.
Startup Recovery
If the engine restarts while compensations are in flight:
- Work items already in
compensatingstate are rescheduled using their stored retry time. - Work items still in
succeededstate on a failed step are detected and compensation starts from the beginning.
Design Tips
- Implement idempotent compensate endpoints keyed on the receipt token.
- Use
max_retries: -1with care: unlimited retries on an unavailable service block flow deactivation indefinitely. - If a step has no real side effects, omit
compensaterather than implementing a no-op endpoint.