Skip to guide
AgenticOrgDocsOpen app
Documentation/OACP — agentic commerce, made safe

OACP — agentic commerce, made safe

Safety and trust

Revocation everywhere, the arbiter's eight rules in plain words, the ledger and its offline check, agent request signatures in log and enforce mode, and how to read a refusal.

Merchants, risk and audit teams6 min readReviewed 2026-10-21
On this page

Revocation everywhere

Sometimes a fact must stop counting at once. A price was wrong. A product was recalled. A buyer withdrew consent.

With OACP you revoke it once. Grantex marks its slot on a public revocation list as invalid. Every place that relies on it checks that list.

One revoke, every channel

The merchant or operator revokes one fact. Grantex marks it invalid on the status list. AgenticOrg refuses the cached fact, the UCP profile stops offering what relied on it, the arbiter refuses new purchases, and revoking a mandate revokes the mandates built on it. If the list cannot be reached, everything refuses.

The merchant or operator revokes one fact. Grantex sets its status list slot to invalid and signs a revocation record. Verifiers check the list before trusting a fact. AgenticOrg refuses the cached fact, the UCP profile stops offering it, the arbiter refuses new purchases, and revoking a mandate revokes the mandates built on it. If the list cannot be reached, everything refuses.Merchant or operatorRevokes one fact, onceGrantexStatus list slot set to INVALIDplus a signed revocation recordVerifiers check the list before trusting a factAgenticOrgRefuses withartifact_revokedUCP profileStops offeringwhat relied on itArbiterRefuses newpurchasesMandatesRevoke one andits children go tooList unreachable? Everything refuses.

Good to know:

  • AgenticOrg keeps a copy of the list only briefly: never longer than the list itself allows, and never more than 10 minutes.
  • Revoking a mandate also revokes the mandates built on it.
  • A revocation stops new decisions. It does not undo a payment that already ran.

What the arbiter checks

Before a checkout completes, the arbiter runs these rules in a fixed order. Every rule runs. The first one that fails decides the refusal.

Eight rules, one answer

A purchase passes eight gates in order: mandate, consent, spending cap, verified buyer authority, valid facts, merchant limits, not-present limits and RBI AFA. Passing all gives allow. The first failure gives refuse, with that rule as the reason.

The first failure refuses, with that rule as the reason. The eight rules, in order: mandate present, consent active, spending cap, verified buyer authority, facts still valid, merchant limits, not-present limits, and RBI AFA. If all pass, the answer is allow.First failure: refuse, with that rule1Mandate presentA buyer mandate, not expired2Consent activeThe buyer's consent still stands3Spending capWithin the Commerce Passport cap4Verified buyer authorityChecked by AgenticOrg itself5Facts still validSigned, fresh, not revoked6Merchant limitsYour amount, currency, quantity7Not-present limitsStricter caps when the buyer is away8RBI AFAUPI AutoPay evidence when neededAll pass: allow, single use, 10 minutes
# Rule In plain words Example refusal
1 Mandate present There is a buyer mandate, and it has not expired. mandate_expired
2 Consent active The buyer's consent is still active. consent_missing
3 Spending cap The buyer's Commerce Passport allows this amount, in this currency. passport_amount_cap_exceeded
4 Verified buyer authority AgenticOrg verified the mandates itself. A claim in the request is not enough in production. buyer_authority_caller_asserted
5 Facts still valid Catalog, price, stock, policy and other facts are signed, fresh, unexpired and not revoked. artifact_revoked:price
6 Merchant limits The order is within your amount, currency and quantity limits. order_amount_cap_exceeded
7 Not-present limits If the buyer is away, your stricter limits and the Intent cap cover the order. not_present_limits_missing
8 RBI AFA A UPI AutoPay payment has verified AFA evidence when RBI rules need it. afa_evidence_missing

A refund has its own single rule: the refund must be positive, in the order's currency, and within what was captured.

Nobody waits for a person

The arbiter never answers "pending". It decides at once.

  • An allow is single use, bound to the exact cart, and expires after 10 minutes.
  • In production an allow is usable only with a Grantex countersignature. If that is missing, ask for a new decision.
  • You can choose to copy refusals into your review queue. That copy never changes or delays the decision.
  • An operator's note on a decision is only a note. It can never turn a refusal into an allow.

The ledger and offline checks

Every commerce write and every decision is recorded in the commerce ledger, in the same transaction as the change itself.

Check the record yourself

Export the ledger, run the verifier on your own computer, and read one of two answers: verified, or the first break, such as a missing, reordered, edited or truncated row.

Export the ledger as a file. Run scripts/verify_ledger.py on your own computer, with no database and no network. Exit code 0 means verified. Exit code 1 names the first break, such as sequence_gap, link_hash or truncated.Tenant administratorExports the ledger fileON YOUR OWN COMPUTERverify_ledger.py export.jsonNo database. No network.EXIT 0VerifiedLinks, anchors andwitnessed headsall matchEXIT 1First breaksequence_gaplink_hashtruncated
  1. ExportOwner: Tenant administrator

    A tenant administrator downloads the ledger export from the ledger routes.

    Blocked / exception

    Only administrators of that tenant can export.

  2. Verify offlineOwner: Anyone holding the file

    Run python scripts/verify_ledger.py export.json. It uses no database and no network.

    Blocked / exception

    Exit code 2 means the file is not a readable export.

  3. Read the answerOwner: Auditor or merchant

    Exit code 0 means verified. Exit code 1 reports the first break, such as sequence_gap or link_hash.

    Blocked / exception

    Treat a break as an irregularity, re-export and compare.

Three honest details:

  • Rows are signed with a platform secret (HMAC). The offline check verifies the chain links and the witnessed heads. It checks row signatures only when you supply the secret.
  • Rows are chained only for tenants where audit-chain sealing is on. That switch starts off.
  • To catch someone who rewrites the whole file, compare with a head kept outside it: a Grantex witness (its own switch, off by default) or a head you saved yourself with --expected-head.

Request signing: which agent is calling?

Agents can sign their HTTP requests (RFC 9421). AgenticOrg understands two public profiles:

  • Visa Trusted Agent Protocol (TAP): browsing and payment tags, a short window and a one-time nonce.
  • Web Bot Auth: the agent's key is found through its published signature agent.

Log mode and enforce mode

In log mode, the default, every request is answered and its signature status is logged. In enforce mode, unsigned or invalid requests to the commerce API get 401. Public pages are never refused in either mode.

Two modes for agent request signatures. In log mode, the default, signed requests, unsigned or invalid requests and public pages are all answered, and unsigned ones are logged. In enforce mode, chosen deliberately, signed requests and public pages are answered and unsigned or invalid requests to the commerce API are refused with 401.logthe defaultSigned and validansweredUnsigned or invalidanswered, loggedPublic pagesansweredenforceonly by choiceSigned and validansweredUnsigned or invalid401 refusedPublic pagesanswered
Mode Unsigned or bad signature Good signature
log (default) Answered, and the status is logged Answered, logged, and recorded on write routes
enforce Refused with 401 on the commerce API surfaces Answered, logged, and recorded on write routes

Enforce mode covers only the catalog and product JSON reads and the A2A routes. Your public pages, structured data, sitemap and llms.txt are never refused for a missing signature.

What a refusal looks like

A refusal always tells you what failed and why. Here is a shortened, synthetic example for a purchase over the buyer's cap:

{
  "code": "arbiter_refused",
  "failing_rule": "passport_amount_cap",
  "reason": "passport_amount_cap_exceeded",
  "decision_id": "arbdec_5b0e1c7d2a9f",
  "action": "The commerce arbiter refused: passport_amount_cap (passport_amount_cap_exceeded).",
  "required_config": "buyer_authority.passport_ref, passport_max_amount_minor_units, passport_currency, passport_expires_at"
}

Read it like this:

  1. failing_rule is the rule from the table above.
  2. reason is the exact cause.
  3. decision_id finds the sealed decision in the ledger.
  4. required_config says what must change before a new attempt.

In a UCP or ACP checkout the decision comes back inside the checkout answer, as a business message. Nothing is charged.

Implementation references

This guide is checked against repository sources. Enabled features and deployed versions may differ by environment.