Skip to content

Execution and delivery guarantee ​

Approval is safe to retry. An unconfirmed external write is not: it needs reconciliation. Axiom's delivery guarantee is built from four pieces, and only the last one lives in the provider.

  1. A client-chosen operation key makes submission idempotent.
  2. A single execution claim per action makes dispatch happen at most once from Axiom's side.
  3. A signed outcome attestation from the provider says what the target actually committed.
  4. The provider persists the operation key atomically with the business write, so a redelivered request cannot commit twice.

A shared key alone cannot guarantee one side effect across independent systems. The target must deduplicate on it, or offer equivalent idempotency and result lookup.

Operation keys ​

The agent chooses an operation_key (8 to 200 characters) once per business intent, before the first attempt, and reuses it for every retry of that intent. Keys are unique per tenant and service.

  • Same key, same material request: the existing action is returned. A lost response is recovered by simply sending the request again.
  • Same key, a changed material field: 409 idempotency_conflict with changed_fields; the original record is untouched.

On dispatch the key travels to the upstream as Idempotency-Key, and it is the key Axiom uses to reconcile. See Idempotency and conflicts.

Through the MCP proxy, where there is no idempotency header, a client can pass the key in _meta["com.sectigoaxiom/operation_key"]. Otherwise a write gets a key derived from the agent, service, tool and exact arguments, so a retried call converges on the same action. The flip side (still the current behaviour): two deliberate, identical write calls also converge on one action, so a client that means to repeat a write must pass its own key.

The single execution claim ​

approved → executing is one conditional update (… WHERE state = 'approved'). Only one caller can win it; concurrent resumes, duplicate callbacks and a failover instance all see the state already moved and get the existing record back. The winner records a claim_id and who claimed it, then dispatches.

Before claiming, the resume re-checks the exact request digest, the agent and credential status, the grant and its ancestors, emergency denies, current policy and approver eligibility.

One executor per project ​

A project has at most one executor: the Sectigo-hosted gateway, or the one customer-hosted gateway holding the executor flag. Moving the flag is a single update across the project's gateways, so a cutover or rollback never leaves two executors. See Fleet, gateways and cutover.

Every Axiom path enforces this. Before claiming, resumeAction refuses any claim that is not the project's executor: after a cutover, the REST resume, the hosted HTTP proxy, the hosted MCP proxy and the Axiom MCP server all answer 409 invalid_state ("execution for this project is owned by gateway …"). Immediate execution after an allowed submit is gated the same way (hostedMayExecute): the action is recorded as approved and waits for the executor.

Embedded-SDK services ​

For a service in sdk enforcement mode the provider executes. Only the provider's own POST /v1/authorize (a claim labelled sdk:…) can claim; a resume through any Axiom path returns 409 invalid_state ("repeat the original request to the provider with the same operation key"), so an action can never be claimed without being executed.

Not yet available

The customer-hosted gateway runtime and the Kong, Apigee and AWS API Gateway adapters are not in the repository yet. The fleet, heartbeat and bundle API they will use exists today.

Dispatch ​

The claimed request goes to the upstream with:

HeaderValue
Idempotency-KeyThe operation key.
Axiom-Action-IdThe action ID.
Axiom-Root-Action-IdThe root action ID (differs for delegated child operations).
Axiom-AuthorizationCompact JWS, typ: axiom-dispatch+jws, signed by Axiom's grant key, valid 120 s: exactly what was authorised.
Upstream credentialFrom the service's upstream_auth secret reference.

The axiom.dispatch.v1 claims include the action and root action IDs, operation key, operation, tenant, agent, resource, request_digest, the full approved canonical request and the claim_id. The provider recomputes the digest from the canonical request and compares the params with the body it received (AxiomProvider.verifyDispatch and assertMatches in the SDK).

The dispatch timeout is 20 seconds.

Classifying the result ​

What came backRecorded as
A valid axiom-outcome attestation bound to this actionIts stated state, target_confirmed or target_failed: destination confirmed.
A 2xx with no attestationsucceeded, reason_code: gateway_observed_success.
A 4xx (other than 408 and 429) with no attestationfailed, reason_code: gateway_observed_failure.
5xx, 408, 429, timeout, network error, or an invalid attestationoutcome_unknown, then reconciliation.

For MCP tools the attestation can also arrive in the tool result's _meta["com.sectigoaxiom/outcome"]; a tool result with isError or a JSON-RPC error is a failed, uncommitted call.

The receipt keeps the difference: phases.destination_confirmed is true only with a valid provider attestation, and an observed-only outcome records the HTTP status it saw.

Only a provider attestation produces target_confirmed or target_failed. The gateway_observed_* codes say the gateway saw an HTTP response and nothing more; providers that want authoritative failures should sign a failed attestation for business rejections, as the sample does.

Outcome attestations ​

The provider signs an axiom.outcome.v1 document with its own outcome key:

json
{
  "v": "axiom.outcome.v1",
  "action_id": "act_…",
  "operation_key": "refund-417-…",
  "request_digest": "sha-256:…",
  "state": "succeeded",
  "target_ref": "re_01kd…",
  "recorded_at": "2026-11-14T18:20:05.000Z",
  "detail": { "refund_id": "re_01kd…", "amount": "25000.00", "currency": "USD" }
}

It is a compact JWS with typ: axiom-outcome+jws. Axiom accepts it only when it verifies against a key in the service's outcome_keys and binds to the same action ID, operation key and request digest. The application, not Axiom, is the source for "the refund was posted".

In embedded SDK mode the provider reports the attestation with POST /v1/actions/{action_id}/outcome; a replay after the outcome is recorded returns the prior result.

Outcome unknown and reconciliation ​

outcome_unknown means the request may have reached the target and nobody knows whether it committed. There are no blind retries:

  1. Axiom issues a receipt recording the unknown outcome and queues reconciliation attempt 1 (after about 30 seconds).

  2. Reconciliation calls the provider's lookup by operation key, authenticated with a fresh Axiom-Authorization:

    http
    GET {upstream_url}/.well-known/axiom/operations/{operation_key}

    The provider answers { "state", "attestation", "target_ref" } for a committed key, or 404. AxiomProvider.handleOperationLookup implements this.

  3. A verified attestation moves the action to succeeded or failed with reason_code: reconciled and reissues the receipt with the same salts.

  4. Otherwise it queues the next attempt with a growing delay (30 × 2^attempt seconds, capped at one hour) up to 12 attempts, then stops. The action stays outcome_unknown for an operator to resolve; it is never retried blindly.

  5. The five-minute maintenance sweep re-drives only actions that look stalled (no change for an hour, for example after a lost queue message) and are still under the attempt limit.

Meanwhile the agent tracks the existing ID with status reads and tells its user the result is not yet confirmed. It never resubmits, resumes again or calls the target directly.

Accumulated budgets ​

For limits that accumulate across actions (a daily refund budget, a balance), the system of record must reserve or debit capacity atomically across parallel actions. Axiom evaluates each action's own material fields; it does not hold a running total. The sample provider rechecks the refundable balance inside the same database batch that records the operation key.

Developer preview. Contract version 2026-11-14.