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:

StatusMeaning
compensatingDispatched, waiting for result
compensatedCompleted successfully
comp_failedPermanently 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 compensating state are rescheduled using their stored retry time.
  • Work items still in succeeded state 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: -1 with care: unlimited retries on an unavailable service block flow deactivation indefinitely.
  • If a step has no real side effects, omit compensate rather than implementing a no-op endpoint.