Skip to main content

What is the intent state machine?

Every validated transaction in Mandate is tracked as an intent. The intent moves through a strict state machine from validation to on-chain confirmation (or failure). The entry state depends on which endpoint the agent uses: action-based validation creates an allowed intent, raw validation creates a reserved intent.

State diagram

State reference

StateDescriptionTTLTerminalEntry Point
allowedValidated via action-based endpoint (POST /validate). The transaction is approved by policy and ready to execute through the agent’s wallet.24 hoursYesAgent calls validate
reservedValidated via raw endpoint (POST /validate/raw). Quota is reserved. Awaiting broadcast.15 minNoAgent calls raw validate
approval_pendingOne or more approval triggers fired. Waiting for the owner to approve or reject in the dashboard, Slack, or Telegram.1 hourNoSystem detects approval trigger
approvedOwner approved the transaction. The agent has a 10-minute window to broadcast.10 minNoOwner clicks approve
rejectedOwner rejected the transaction. Quota is released.N/AYesOwner clicks reject
broadcastedAgent posted a transaction hash via POST /intents/{id}/events. Waiting for the envelope verifier to confirm on-chain.NoneNoAgent posts txHash
confirmedOn-chain transaction matches the validated parameters. Quota reservation converts to a permanent spend record.N/AYesEnvelope verifier confirms
failedTransaction reverted on-chain, was dropped, or the envelope verifier detected a parameter mismatch. An envelope mismatch also trips the circuit breaker.N/AYesOn-chain failure or mismatch
expiredTTL exceeded without the intent progressing to the next state. Quota reservation is released.N/AYesScheduled expiration job

Transitions

FromToTriggerActor
(new)allowedAgent calls POST /validateAgent
(new)reservedAgent calls POST /validate/rawAgent
reservedapproval_pendingPolicy engine detects an approval triggerSystem
reservedbroadcastedAgent calls POST /intents/{id}/events with txHashAgent
reservedexpired15 minutes pass without broadcastSystem
approval_pendingapprovedOwner approves via dashboard, Slack, or TelegramOwner
approval_pendingrejectedOwner rejectsOwner
approval_pendingexpired1 hour passes without a decisionSystem
approvedbroadcastedAgent calls POST /intents/{id}/events with txHashAgent
approvedexpired10 minutes pass without broadcastSystem
broadcastedconfirmedEnvelope verifier confirms on-chain matchSystem
broadcastedfailedOn-chain revert or envelope mismatchSystem

Terminal vs non-terminal states

Terminal states end the intent lifecycle. No further transitions are possible. Terminal states: allowed, confirmed, failed, expired, rejected Non-terminal states: reserved, approval_pending, approved, broadcasted Non-terminal states have TTLs enforced by a scheduled expiration job. When the TTL expires, the intent moves to expired and any reserved quota is released back to the agent’s budget.

Quota behavior by terminal state

Terminal StateQuota Action
confirmedReservation converts to permanent spend record
failedReservation released, budget restored
expiredReservation released, budget restored
rejectedReservation released, budget restored
allowedNo reservation (action-based validation does not reserve quota)

Polling for status

Use GET /api/intents/{id}/status to check the current state. The SDK provides convenience methods:
// Poll for approval decision (5s interval, 1h timeout)
const status = await client.waitForApproval(intentId);

// Poll for on-chain confirmation (3s interval, 5min timeout)
const status = await client.waitForConfirmation(intentId);
Both methods throw MandateError if the intent reaches a terminal failure state.

Next Steps

Intent Lifecycle Concepts

Conceptual explanation of how intents move through the system.

MandateClient

SDK methods for validation, event posting, and status polling.

Check Status (CLI)

Poll intent status from the command line.