AccountingKitDocs

Operations

The durable execution model behind every automation, sync, and webhook delivery.

Every unit of background work in AccountingKit is an operation: a persisted record with its input, step results, attempt history, lease, and terminal state. Automations, connection synchronization, webhook delivery, and retention all run through the same model, so one set of rules explains how any of them behaves under failure.

Lifecycle

StateTerminalMeaning
queued—Committed and waiting to be claimed.
running—Claimed by a worker under a lease that expires if the worker is lost.
retrying—An attempt failed and the next one is scheduled with backoff.
succeeded✓Every step completed. The result stays readable for the life of the record.
failed✓The attempt failed in a way that will not be retried.
cancelled✓The operation was stopped before it completed.
dead_letter✓The retry budget is exhausted. The terminal error is preserved for review.

Read the current state with GET /v1/operations/{operation_id}, or subscribe to automation.succeeded and automation.failed and avoid polling entirely.

Retries

Failed attempts are retried with bounded exponential backoff. Input and error history are preserved across attempts, and completed steps are not repeated: an operation resumes from its last successful step rather than starting over. When the budget is exhausted the operation moves to dead_letter rather than disappearing, so a failure is always something you can inspect and act on.

Provider calls run under a per-connection request budget. When a connection exhausts it, the request returns 429 with code RATE_LIMITED and a retry_after_seconds value; the budget protects your accounting provider's own rate limits from a burst on one tenant.

The reconciliation safety net

Provider webhooks are the fast path, not the guarantee. AccountingKit reconciles invoices for every active connection on a 15-minute schedule using the provider's modified-since filter, and that schedule runs through the same payment-transition, signed-delivery, and PDF-refresh path as a live webhook. A provider notification that never arrives still produces invoice.payment_changed, invoice.paid, and the refresh instruction — late, but not lost.

Trigger an unbounded scan at any time:

POST /v1/connections/{connection_id}/sync
Idempotency-Key: reconcile-invoices-2026-09-01T1200Z
Content-Type: application/json

{"scope":"invoices"}

{"scope":"all"} reconciles every supported resource family instead of invoices alone.

Replaying a failed operation

Correct the root cause first — a replay repeats the work, it does not repair the reason it failed.

POST /v1/operations/{operation_id}/replay
Idempotency-Key: replay-operation-1042
Content-Type: application/json

{"reason":"Xero connection was reauthorized"}

Replay preserves the original operation and its successful step results, resets only the failed steps, and opens a fresh retry budget. The reason, actor, previous terminal state, and previous attempt count are written to the audit log.

Replaying a webhook delivery

Each outbound delivery records its attempts, response status, a response body excerpt, the next scheduled attempt, and its terminal state. List deliveries by state with GET /v1/webhook-deliveries to find what needs attention, then requeue one after your endpoint recovers:

POST /v1/webhook-deliveries/{delivery_id}/replay
Idempotency-Key: replay-delivery-1042
Content-Type: application/json

{"reason":"Customer endpoint recovered"}

The original delivery and its attempt history stay intact. Both replay endpoints require an Idempotency-Key and a non-empty reason.

On this page