API documentation

Card Authorization

How a bank puts execution authority between an agent's card purchase and the issuer's authorization: the decision is taken before the card is submitted, the cardholder is asked through a presence provider when policy says so, and the authorization is matched to a held grant in milliseconds.

Who does what

Four parties keep four responsibilities. The agent proposes; it neither proves its own approval nor issues its own authority.

PartyOwns
The agent platformStates the intent: who acts, the card (by token reference), the amount, the merchant. Sends it to the bank's AgentSafe.
DecionisEvaluates the bank's versioned policy, verifies admitted human proof, issues one single-use grant bound to the exact intent, signs the dossier.
AgentSafe (in the bank)Holds the grant, matches the issuer's authorization to it, claims it once, records the result as the effect. Open source, Apache-2.0.
A presence provider (for example Koard)Proves the cardholder tapped their own physical card on their own phone inside the issuer's app, and signs that over the intent.
The issuerTakes the final approve or decline, enforces card controls, reports the result.
Why the decision is taken before the card is submitted

An issuer has seconds to answer an authorization before the network stands in for it; a person takes from thirty seconds to several minutes. So Decionis decides, and the cardholder is asked, while the agent waits and before anything reaches the rails. At the authorization, AgentSafe only matches and claims.

1. The purchase is proposed

The agent platform proposes card.purchase to the bank's AgentSafe, which captures it as an agent-safe.intent/1 intent and asks the authority.

POST/v1/authority/enforce-and-bindOrg API key (decision:write)

Evaluate the exact intent in ENFORCEMENT. ALLOW carries a single-use grant AgentSafe holds; ESCALATE carries where the hold goes; BLOCK carries nothing, and the agent never submits the card.

POST https://api.decionis.com/v1/authority/enforce-and-bind
Authorization: Bearer dcy_org_xxx
Idempotency-Key: 7d2c6b1e-...   (the intent_id)

{
  "protocol_version": "agent-safe.intent/1",
  "tenant_id": "<bank org id>",
  "intent_id": "7d2c6b1e-...",
  "captured_at": "2026-10-12T12:00:00.000Z",
  "expires_at": "2026-10-12T12:05:00.000Z",
  "actor": { "id": "shopping-agent", "type": "AI_AGENT" },
  "action": {
    "type": "card.purchase",
    "resource": "card:tok_7f3a",
    "parameters": { "cardTokenRef": "tok_7f3a", "amountMinor": 4250, "currency": "EUR", "merchantId": "M-17" }
  },
  "context": { "caller_principal": "agent-platform", "card_velocity": { "window_seconds": 600, "holds": 0, "claims": 0 } },
  "downstream_target": { "system": "issuer", "operation": "card_purchase" },
  "expected_effect_digest": "sha256:...",
  "intent_hash": "sha256:...",
  "mode": "ENFORCEMENT"
}
FieldMeaning
parameters.cardTokenRefThe issuer's token reference for the card. Never a card number or a device card number: a value shaped like one is refused before anything is asked or recorded.
parameters.amountMinorThe ceiling in minor units of the currency. The issuer may authorise less, never more.
context.card_velocityHow many purchases the bank's AgentSafe held for this card and how many authorizations claimed one in the last ten minutes. Counted by the runtime, so a policy rule can hold a burst for the cardholder.
expires_atAt most five minutes after capture for a card purchase; a held grant never outlives it.

2. Policy names the cardholder

An ESCALATE rule's authority block says who must approve and how that person is proved. For the cardholder, the method is a card tap and the provider is a presence provider the bank admitted.

{
  "action": "ESCALATE",
  "authority": {
    "reason_codes": ["CARDHOLDER_TAP_REQUIRED"],
    "selector": { "kind": "ROLE", "role_id": "CARDHOLDER" },
    "verification_requirements": {
      "level": "STANDARD",
      "methods": ["CARD_TAP"],
      "hardware_pki_required": false,
      "disallow_virtual_cameras": false,
      "provider": "koard"
    },
    "resolution": { "quorum": 1, "delegation": "DISALLOWED", "fallback_role_ids": [] },
    "expires_in_seconds": 300
  }
}
FieldRule
methodsWEBAUTHN, MOBILE_DEVICE and ACTIVE_LIVENESS are ceremonies Decionis Presence runs for a bank role. CARD_TAP is proved by a third-party provider. A requirement never mixes the two kinds.
providerWho proves the person. Absent or decionis: Decionis Presence. Any other id names a provider from the directory that the bank admitted; its signed attestation is the proof. presence is reserved and refused.
The second evaluationThe tap is evidence, never permission. After it, policy is evaluated again with the approval facts (agent_safe.human_approval.provider, .method, .approver_role) and only a rule matching them turns the hold into an ALLOW with a grant.

3. The hold is routed

On ESCALATE in ENFORCEMENT, Decionis tells two parties where the hold goes: the runtime that holds the intent, on the decision, and the bank's webhook subscriber, by event.

// On the decision, for agent-safe-pipeline/0.3.7 and later only
"escalation_requirement": {
  "requirement_id": null,
  "expires_at": "2026-10-12T12:05:00.000Z",
  "selector": { "kind": "ROLE", "role_id": "CARDHOLDER" },
  "verification": { "provider": "koard", "methods": ["CARD_TAP"], "level": "STANDARD" }
}

// authority.escalation_required, to the bank's subscribers
{
  "tenant_id": "<bank org id>",
  "intent_id": "7d2c6b1e-...",
  "intent_hash": "sha256:...",
  "expires_at": "2026-10-12T12:05:00.000Z",
  "decision_id": "...", "dossier_id": "...",
  "reason_codes": ["CARDHOLDER_TAP_REQUIRED"],
  "authority_requirement": null,
  "selector": { "kind": "ROLE", "role_id": "CARDHOLDER" },
  "verification": { "provider": "koard", "methods": ["CARD_TAP"], "level": "STANDARD" },
  "action": { "type": "card.purchase", "resource": "card:tok_7f3a", "parameters": { "...": "..." } }
}
What the runtime does with it

The bank's AgentSafe answers the agent with ESCALATE_PENDING and an ATTESTATION handoff carrying the same routing as requirement. A provider other than decionis means no Presence ceremony is asked for: the hold waits for that provider's attestation. The bank chooses the webhook subscriber, and so decides who receives the parameters; no credential, token or attestation is ever in the event.

4. The tap resumes the hold

The provider signs a presence attestation bound to the intent. Whoever holds it, the agent platform or the issuer app's backend, presents it to the bank's AgentSafe, which sends it to Decionis as evidence for a fresh evaluation of the same intent.

// Protected header
{ "alg": "EdDSA", "typ": "decionis-presence-attestation+jwt", "kid": "koard-2026-10" }

// Claims
{
  "iss": "koard",
  "aud": "<bank org id>",
  "sub": "tok_7f3a",
  "iat": 1760270460, "exp": 1760270580, "jti": "tap-0001",
  "intent_id": "7d2c6b1e-...",
  "intent_hash": "sha256:...",
  "presence": {
    "method": "card_tap",
    "result": "VERIFIED",
    "approver_role": "cardholder",
    "card_token_ref": "tok_7f3a",
    "token_match": true,
    "network_validated": true
  }
}
POST/v1/authority/enforce-and-bindOrg API key (decision:write)

The same intent again, with the attestation as evidence.humanApproval. A verified attestation puts its facts into the evaluation; a refused one is a 409 with its code and no evaluation runs.

{
  "...": "the intent exactly as first sent",
  "evidence": { "humanApproval": { "provider": "attestation", "attestation": "<compact JWS>" } }
}
RefusalMeaning
PRESENCE_ATTESTATION_MALFORMEDNot a compact EdDSA JWS of the expected type, or a claim missing or ill-shaped (a subject shaped like a card number is malformed).
PRESENCE_ATTESTATION_KEY_UNKNOWNNo active key under that kid: neither registered by the bank nor published by an admitted provider.
PRESENCE_ATTESTATION_SIGNATURE_INVALIDThe signature does not verify under the key.
PRESENCE_ATTESTATION_EXPIREDexp has passed, within a 300 s tolerance.
PRESENCE_ATTESTATION_AUDIENCE_MISMATCHaud is not the bank's organisation.
PRESENCE_ATTESTATION_INTENT_MISMATCHintent_id or intent_hash is not the intent presented: a tap for one purchase cannot approve another.
PRESENCE_ATTESTATION_ISSUER_MISMATCHiss is not the issuer the key was registered or published for.
PRESENCE_ATTESTATION_NOT_VERIFIEDpresence.result is not VERIFIED.
On the runtime side

AgentSafe accepts { mode: 'ATTESTATION', intent, attestation } on POST /v1/escalations from the proposer, or from an OPERATOR holding escalations.resume (the issuer app's backend, where the tap lands). The operator adds no authority: the intent is re-hashed on the way in and the attestation is bound to that hash. An ALLOW becomes HELD_FOR_AUTHORIZATION; the grant then waits for the issuer's hook.

Presence providers

A provider's keys are trusted in two ways: a key the bank registers itself, or the JWKS of a provider the bank admits from the Decionis directory. Registered keys are consulted first; the claim boundary looks the key up again, so a revocation at either level stops a grant from being claimed.

GET/v1/presence-providersOrg API key (policy:read)

The directory: each provider's id, the iss it signs with, the methods it proves, and the https JWKS it publishes its Ed25519 keys at.

{ "service": "decionis", "providers": [
  { "provider_id": "koard", "display_name": "Koard", "issuer": "koard",
    "jwks_url": "https://keys.koard.example/.well-known/jwks.json", "methods": ["CARD_TAP"], "revoked_at": null }
] }
PUT/v1/execution/presence-providers/{provider_id}/admissionOrg API key (policy:write)

Admit a provider for this organisation. From then on an attestation signed under a key its JWKS publishes verifies, when its iss is the provider's. DELETE revokes the admission and keeps its record.

POST/v1/execution/provider-keysOrg API key (policy:write)

Pin a provider's key by hand instead: the public half of an Ed25519 key, with purpose presence_attestation and the iss it signs with. A key only ever verifies its own purpose.

{ "kid": "koard-2026-10", "issuer": "koard", "algorithm": "EdDSA", "purpose": "presence_attestation",
  "public_jwk": { "kty": "OKP", "crv": "Ed25519", "x": "..." } }

5. The authorization is matched

Inside the bank, the issuer's real-time hook asks AgentSafe whether the authorization is the purchase a held grant authorised. The match is pure and local; the claim is one call to Decionis, made once.

POSThttps://<bank agentsafe>/v1/card-authorizationsAgentSafe OPERATOR with cards.authorize

Match on card token reference, currency, merchant (and category when the purchase named one) and an amount at or below the ceiling; a match claims the grant through POST /v1/execution/claim-token exactly once.

{ "authorization_id": "auth_01J...", "card_token_ref": "tok_7f3a", "amount_minor": 4000,
  "currency": "EUR", "merchant_id": "M-17", "mcc": "5812" }

// APPROVE: { "decision": "APPROVE", "intent_id", "intent_hash", "decision_id", "dossier_id", "grant_id", "lease_expires_at" }
// else:    { "decision": "NO_MATCH", "code": "NO_GRANT" | "GRANT_ALREADY_USED" | "GRANT_EXPIRED" | "MERCHANT_MISMATCH" | ... }
POSThttps://<bank agentsafe>/v1/card-authorizations/{authorization_id}/resultAgentSafe OPERATOR with cards.authorize

What the issuer did. An approval finalizes the grant COMMITTED with the effect compared against the ceiling; a decline finalizes FAILED; a result after the claim's lease finalizes INDETERMINATE. Recorded once, into the dossier through POST /v1/execution/finalize-token.

{ "status": "APPROVED", "approved_amount_minor": 4000, "auth_code": "A1B2C3" }
NO_MATCH is a statement, not a decline

It says no cardholder-approved intent exists for this authorization. What that means for the purchase is the issuer's own policy: decline, or the issuer's usual scoring. APPROVE says the intent leg succeeded; the issuer keeps the final card decision.

Events

Subscribe to these with the WEBHOOK integration provider (see Webhooks). Deliveries are at-least-once and signed when a secret is configured; none affects a decision.

EventWhenCarries
authority.escalation_requiredAn ENFORCEMENT intent is held for a person.Intent id, hash and expiry; decision and dossier ids; reason codes; selector and verification (provider, methods, level); the action and its parameters. Never a token.
authority.escalation_resolvedA held intent is presented again with human approval evidence.The fresh decision's outcome (ALLOW, BLOCK, ESCALATE) or REFUSED with the refusal code; the proof's facts (provider, method, approver_role, attestation_id, key_id). Never the proof.
managed_escalation.*Decionis orchestrates a Presence ceremony for a bank role (escalation.mode MANAGED).The escalation's lifecycle: created, presence_requested, awaiting_approver, presence_verified, presence_rejected, reauthorizing, grant_issued, blocked, cancelled, expired, failed.

Where a hold can wait

An authorization is a live message on a clock measured in seconds, so the question for each bank is where the held item waits while a person answers.

Where the item waitsKoard's tapWhat happensStatus
Before the authorization existsTap to Verify, for a held purchaseThe agent platform states the intent; the decision and the tap happen before the card is submitted; the issuer's hook matches the authorization to the held grant in milliseconds.Built. The flow on this page, and the cleanest.
In the bank's fraud queueTap to Verify, for a flagged authorizationWith EXECUTOR_CARD_NO_GRANT=evaluate the hook evaluates an authorization that has no hold at the commit boundary, as a card.purchase intent of the commit actor the deployment names. ALLOW is held and claimed in the same call; ESCALATE is answered PENDING with the handoff and authority.escalation_required fires; the platform pends the authorization, the issuer app's backend presents the tap through POST /v1/escalations, authority.escalation_resolved fires, and the re-presented authorization matches the hold the tap left. BLOCK is NO_MATCH.Built, for a platform that can pend; the pending answer lives as long as the intent (EXECUTOR_INTENT_TTL_SECONDS).
After the authorizationTap to Confirm, called by the platformNothing can pend, so the authorization goes through; the cardholder confirms or rejects it afterwards in the issuer's app, and a rejection is a reversal the bank's own rail carries. Decionis's part is evidence: the same evaluation in shadow, here or on the wire through @decionis/iso8583, records what policy would have said.Evidence only; the easiest to build and the weakest, since the money has moved.
A standing permission, no amount behind itTap to AuthenticateGranting an agent permission to buy on the cardholder's behalf, a new device, account recovery: a signal policy requires through required_signals, not an intent to hold.A producer's signature on the signal is verified: the token is presented as evidence, the authority verifies it against the producer's registered key, writes the facts under agent_safe.signed_signals, and policy names them with required_signals.

Signed producer signals: Tap to Authenticate

A standing permission is not an intent to hold. The cardholder taps once to let an agent buy on their behalf, within limits, for a while; the producer signs that as a signal; the agent's intents carry it as evidence; the authority verifies the producer's signature, writes what the signal says and what of each intent it covers as facts, and policy decides. Nothing is pre-authorised: every purchase is still its own intent, evaluated and bound on its own.

// Protected header
{ "alg": "EdDSA", "typ": "decionis-signed-signal+jwt", "kid": "koard-2026-10" }

// Claims
{
  "iss": "koard",
  "aud": "<bank org id>",
  "sub": "cardholder:ch_91ad",
  "iat": 1760270460, "exp": 1762862460, "jti": "delegation-0001",
  "signal": {
    "name": "card_delegation",
    "method": "card_tap",
    "result": "VERIFIED",
    "agent": "shopping-agent",
    "scope": { "actions": ["card.purchase"] },
    "limits": { "currency": "EUR", "amount_minor": 50000 },
    "card_token_ref": "tok_7f3a"
  }
}

// Presented with every intent it is meant to cover, with or without a tap
{
  "...": "the intent",
  "evidence": { "signals": [{ "token": "<compact JWS>" }] }
}
ClaimMeaningRule
issThe producer.Equal to the issuer registered with the key; never presence; at most 100 characters.
audThe organisation the signal is for.The bank's organisation id.
subWhom the signal is about.An opaque reference of at most 200 characters; a value shaped like a card number is refused.
iat, expWhen it was signed and until when it stands.exp after iat and at most thirty days later; iat at most 300 s in the future; an expired signal is refused, with a 300 s tolerance.
jtiThe signal's id.At most 200 characters; recorded as signal_id.
signal.nameThe fact the signal is written under.A lower-case name of at most 64 characters; two signals under one name in the same intent are refused.
signal.methodHow the producer established it.An open name such as card_tap, at most 64 characters.
signal.resultVERIFIED or FAILED.FAILED is refused with SIGNED_SIGNAL_NOT_VERIFIED.
signal.agentThe actor the permission is for.Optional, at most 200 characters; compared with actor.id.
signal.scope.actionsThe action types permitted.Optional; 1 to 32 names; compared with action.type.
signal.limitsA ceiling: currency and amount_minor.Optional; a three-letter currency and a whole number of minor units; compared with parameters.currency and parameters.amountMinor.
signal.card_token_refThe card the permission is for.Optional; compared with parameters.cardTokenRef or a card: resource; never a card number.
Any other key of signalNot part of the contract.Refused as SIGNED_SIGNAL_MALFORMED: the shape is closed.
// Written by the authority for each verified signal, never by the caller;
// anything the caller put under agent_safe is overwritten.
"agent_safe": {
  "signed_signals": {
    "card_delegation": {
      "verified": true,
      "producer": "koard", "method": "card_tap", "subject": "cardholder:ch_91ad",
      "signal_id": "delegation-0001", "key_id": "koard-2026-10",
      "issued_at": "2026-10-12T12:01:00.000Z", "expires_at": "2026-11-11T12:01:00.000Z",
      "evidence_digest": "sha256:...",
      "agent": "shopping-agent",
      "scope": { "actions": ["card.purchase"] },
      "limits": { "currency": "EUR", "amount_minor": 50000 },
      "card_token_ref": "tok_7f3a",
      "covers": { "action": true, "agent": true, "amount": true, "currency": true, "card": true },
      "covers_intent": true
    }
  }
}
FactMeaning
covers.action, .agent, .amount, .currency, .cardWhat of this intent the signal covers: true or false where the signal asserts it (scope, agent, limits, card_token_ref) and null where it does not. A limit the intent does not name, no amountMinor or no currency, is not covered.
covers_intentTrue when no entry of covers is false: the signal, as signed, covers this intent. A purchase over the ceiling, in another currency, on another card, by another agent or of another action type is not covered, and the rule below does not match.
evidence_digestThe SHA-256 of the token. The token itself is never recorded: not in the evaluation, not in the dossier, not in the binding.
{
  "required_signals": ["agent_safe.signed_signals.card_delegation"],
  "rules": [
    {
      "rule_id": "delegated-purchase",
      "when": { "field": "agent_safe.signed_signals.card_delegation.covers_intent", "operator": "EQ", "value": true },
      "then": { "action": "AUTO_APPROVE" },
      "priority": 200
    },
    {
      "rule_id": "cardholder-tap",
      "when": { "field": "agent_safe.action.type", "operator": "EQ", "value": "card.purchase" },
      "then": {
        "action": "ESCALATE",
        "authority": {
          "reason_codes": ["CARDHOLDER_TAP_REQUIRED"],
          "selector": { "kind": "ROLE", "role_id": "CARDHOLDER" },
          "verification_requirements": {
            "level": "STANDARD",
            "methods": ["CARD_TAP"],
            "hardware_pki_required": false,
            "disallow_virtual_cameras": false,
            "provider": "koard"
          },
          "resolution": { "quorum": 1, "delegation": "DISALLOWED", "fallback_role_ids": [] },
          "expires_in_seconds": 300
        }
      },
      "priority": 100
    }
  ]
}
LineWhat it does
required_signalsA dotted path under the facts. An intent that carries no verified signal of that name resolves to ESCALATE as missing_required_signals before any rule runs: a negative predicate never fires against a fact that was never supplied.
delegated-purchaseThe signal covers this intent: ALLOW, with a grant bound to this purchase. The authority wrote covers_intent; the caller cannot.
cardholder-tapOtherwise the hold goes to the cardholder's tap, exactly as in step 2.
POST/v1/execution/provider-keysOrg API key (policy:write)

The producer's key: the public half of an Ed25519 key with purpose signed_signal and the iss it signs with. A key only ever verifies its own purpose, so an attestation key signs no signal and a signal key signs no attestation. A provider the bank admitted from the directory needs no registration: the keys its JWKS publishes sign its signals too.

{ "kid": "koard-2026-10", "issuer": "koard", "algorithm": "EdDSA", "purpose": "signed_signal",
  "public_jwk": { "kty": "OKP", "crv": "Ed25519", "x": "..." } }
RefusalMeaning
SIGNED_SIGNAL_MALFORMEDNot a compact EdDSA JWS of the expected type, a claim missing or ill-shaped, a key of signal outside the contract, or a lifetime over thirty days.
SIGNED_SIGNAL_KEY_UNKNOWNNo active key under that kid for purpose signed_signal: neither registered by the bank nor published by an admitted provider.
SIGNED_SIGNAL_SIGNATURE_INVALIDThe signature does not verify under the key.
SIGNED_SIGNAL_EXPIREDexp has passed, within a 300 s tolerance.
SIGNED_SIGNAL_AUDIENCE_MISMATCHaud is not the bank's organisation.
SIGNED_SIGNAL_ISSUER_MISMATCHiss is not the issuer the key was registered or published for.
SIGNED_SIGNAL_NOT_VERIFIEDsignal.result is not VERIFIED.
SIGNED_SIGNAL_DUPLICATE_NAMETwo signals in one intent carry the same signal.name.
What is recorded, and what is checked again

The dossier and the signed execution binding record the facts: the producer, the method, the subject, the signal id, the key id, the window, what the signal asserted, what it covered, and the SHA-256 of the token as evidence_digest. Never the token. A refused signal is a 409 with its code; no evaluation runs and no event fires, since a signal is the producer's standing word rather than a person's answer to a hold. At the claim, the authority looks the producer's key up again under purpose signed_signal, requires its issuer to be the signal's producer and the signal's expiry to lie ahead, and rebuilds the same facts for the policy re-run: a key revoked since, or a delegation that ran out, stops the grant from being claimed. After a managed Presence ceremony the signals are verified again before the re-evaluation: one that has run out contributes no fact, and the policy decides without it.

Hosting tiers and key custody

Written once, the same on the runtime's deployment guide and in the bank brief, so a security review reads one answer.

TierWho runs the authority that mints grantsWhere Presence runsThe grant signing key
HostedDecionis, multi-tenant, at api.decionis.com, in one Microsoft Azure region (Central US today; regional residency planned).The Decionis Presence service, for escalations to a bank role. A cardholder's tap never goes through Presence: it is the provider's attestation, verified by the authority.A Key Vault secret of the Decionis deployment, with purge protection, rotated with an overlap: one active key for the deployment.
Dedicated single-tenantDecionis, in a deployment for one bank: its own database, its own Key Vault and signing keys, in a region agreed with the bank. An explicitly agreed deployment, not self-serve.The Decionis Presence service.The bank's own Key Vault in that deployment: one key per bank.
Self-managed under commercial licenceThe bank, from the versioned Helm chart, in its own cluster with its own Postgres, egress-only or air-gapped; decisions, dossiers and policy stay inside.The Decionis Presence service, connected by a service token and pinned key thumbprints; a self-managed Presence is not offered, and a bank whose escalations go only to the cardholder's tap needs no Presence connection.The bank's secret manager, supplied to the chart as the API's Secret; the policy bundle for the edge evaluator stays Decionis-signed, and the bank pins the public JWKS.
Every other key

Decionis signs grants, dossiers and policy bundles with Ed25519, which the HSM-backed keys of Azure Key Vault and Managed HSM do not hold, so those keys are software keys kept as Key Vault secrets today; HSM custody for a dedicated or self-managed deployment is a change of key type made per deployment on request, not a switch. A presence provider's keys are the provider's own, and Decionis holds only the public halves, in the directory and in each bank's admissions. Everything the bank's AgentSafe holds, its workspace key, its TLS and evidence signing keys, the card hold key and the ISO 8583 tokenizer key, lives in the bank's own secret store in every tier and never reaches Decionis.

Limits of this version

Stated so a security review can plan around them.

LimitConsequence
Held grants live in memory by default, or in a Postgres store every replica shares.With EXECUTOR_CARD_HOLDS_STORE=postgres a hold made on one replica is matched on another and its claim settled on a third, and holds survive a restart; what a row keeps of a grant is sealed under the deployment's hold key. In memory, a restart loses only grants nobody claimed, which expire with no effect.
One pending purchase per card and merchant at a time.A second purchase on the same card at the same merchant waits for the first to be authorised or to expire (five minutes by default); purchases at other merchants hold in parallel.
An authorization id is one amount, unless the issuer increments.EXECUTOR_CARD_AUTHORIZATIONS=incremental answers the same id re-presented for more, within the ceiling and before its result, from the same claim. Split authorizations are not offered: a grant finalizes once.
The intent comes from the agent platform, or from the authorization itself.An authorization with no grant is NO_MATCH by default, or evaluated at the commit boundary with EXECUTOR_CARD_NO_GRANT=evaluate; on the wire, @decionis/iso8583 derives the intent from the ISO 8583 message in its evaluate mode.
The claim is one call to Decionis inside the authorization's budget.What AgentSafe adds is measured on every run: under 50 microseconds to match and under a millisecond in the store at the 99th percentile. The claim is one round trip; the budget AgentSafe states per placement is 150 ms p99 with Decionis in the same cloud region, 300 ms across regions, and 10 ms with a local claim at the edge, each confirmed on the bank's own link before enforcement. What happens when the claim cannot complete is the bank's fail policy, per issuer.
A managed escalation cannot orchestrate a card tap.escalation.mode MANAGED with a CARD_TAP rule is refused with CEREMONY_REQUIREMENT_MISMATCH; the tap runs through the attestation path.