Skip to main content
Reconciliation is how Invoke handles the worst moment in agent execution: the request reached the tool, and the answer never came back. The charge may have landed or not. The issue may exist or not.

Why not just retry?

  • Retry blindly and you risk doing it twice: two charges, two issues, two emails.
  • Give up and you risk leaving it half-done.
Neither is safe without knowing what happened, so Invoke never calls a lost answer “failed”. It marks the action unknown.

What Invoke does

1

The action is marked unknown

A timeout, a dropped connection, or an error after the tool accepted the request all count. The reserved budget stays held, since the money may have been spent.
2

Safe-to-repeat tools are reconciled

Reads, and tools that converge by construction such as linear.update_issue (read first, fixed ids), are safe to run again as the same action. The SDK re-sends the same action, Invoke re-runs it as the same effect, and it lands at most once. You get the receipt.
3

Everything else waits for a person

For a charge, an email or a new record, running again could act twice. So Invoke refuses to re-run it: the call throws OutcomeUnknownError with safeToRetry: false, and every retry with the same key is refused too. Someone checks the real system and settles it:
happened commits the action and seals its receipt. not_happened releases the budget, so an honest retry can run.

In code

The agent never writes reconciliation logic, and it never gets a false “failed” that invites a double charge.

Checking live state yourself

If you already hold the live state, such as a Stripe lookup you ran, POST /outcomes/reconcile tells you whether a retry is safe:

Next steps