OACP_STATUS_LIST_PUBLIC_ENABLED=true. Without the key, issuance answers 503 oacp_issuer_key_unavailable; without the public status list, AgenticOrg’s verifier refuses every artifact and mandate with revocation_status_unavailable. Order: operator runbook.
Runtime Flow
Request Contract
AgenticOrg sends:- tenant, merchant, seller agent, and source identifiers;
- merchant self-service config refs for source connector, buyer channels, provider-owned payment rails, public publishing, and POS metadata;
- requested OACP artifact families;
- source observed timestamp;
- public-safe connector evidence;
- no raw Shopify credential, raw provider payload, checkout URL, or payment URL.
- no raw POS payload, raw payment payload, order-created claim, or POS paid-state claim.
201 artifact_issuance_readywith artifact families when ready;202 receivedwhen connector evidence is still required;422when the request is private, stale, executable, or otherwise unsafe.
refusal_record_id); a 202 received intake is not.
Synthetic Demo Source
A demonstration merchant whose catalogue is invented sendsconnector_choice: "synthetic_demo"
and synthetic_data: true in the request, and source_system: "synthetic_demo" with
synthetic_data: true in the connector evidence, never shopify. Grantex accepts it only for a
tenant on both COMMERCE_C6Z_DEMO_SOURCE_TENANTS and COMMERCE_C6Z_AUTHORITY_SERVICE_TENANTS;
otherwise it answers 422 unsupported_or_enabling_authority_request, as for any unsupported
source, and records the refusal. Accepted artifacts carry source_system: "synthetic_demo" and
synthetic_data: true in the signed payload and in source_lineage, with
merchant_system_source_of_record: false. AgenticOrg should label answers and public surfaces
built from them as demonstration data. Public discovery for such a tenant follows the usual
opt-in, and its public_discovery_state artifact carries synthetic_data: true.
Verifying Artifacts
AgenticOrg must verify every artifact before caching it, as OACP artifact authority describes:- pin the Grantex issuer (
JWT_ISSUER) and JWKS URI (<issuer>/.well-known/jwks.json) in configuration; never follow a URL taken from an artifact; - run the eight verification steps with the refusal codes listed there (the shared vectors in
spec/examples/oacp/oacp-artifact-jws-v1.jsoncover steps 1-6); - read the status list at
status.status_list.uri, verify it against the same JWKS, cache it no longer than itsttl(30 s), and refuse withrevocation_status_unavailablewhen it cannot be read; - treat
schema_versionoacp.artifact.v1as the signed profile, and artifact ids as unique per issuance (oacp_art_<ULID>), not stable per family.
GET /v1/commerce/oacp/artifacts?tenant_id=... with the
same service token, following next_cursor. Revocation uses
POST /v1/commerce/oacp/artifacts/:artifactId/revoke.
Public Catalog Publishing Boundary
AgenticOrg publishes public buyer-safe seller and product surfaces after it has valid Shopify/OACP evidence and the operator enables public catalog publishing. Grantex does not host these catalog pages and does not sit in the hot path for non-binding buyer questions. Expected AgenticOrg public surfaces:
Those outputs must include source and freshness labels, must not expose Shopify credentials or raw provider payloads, and must not claim checkout, payment, mandate, order, certification, or external platform approval. If the AgenticOrg cache is stale, revoked, or source-mismatched, the public surface must block or label the missing requirement instead of inventing facts.
Required Configuration
Failure Handling
If Grantex is unavailable, AgenticOrg may answer non-binding buyer questions from valid cached artifacts. Commitment-bound or POS-bound requests must refresh, prepare no execution, use only valid cached policy when risk permits, or refuse.Arbiter Decisions
AgenticOrg’s policy arbiter is authoritative and deterministic: it sealsallow or refuse from the mandate, consent, Commerce Passport cap and OACP artifact evidence, and never waits for a person. It posts every sealed decision to Grantex, which only records and countersigns it.
POST /v1/commerce/oacp/arbiter-decisions uses the same service token and tenant allowlist as the authority route; developer, merchant and agent credentials are refused. The body:
AgenticOrg verifies the returned
decision_grant against the Grantex JWKS (typ decision+jwt, aud urn:grantex:decision, approver_auth policy_arbiter, action_hash equal to its own payload_hash) before storing the jti beside its decision row. Grantex’s human decision-grant verifiers (POST /v1/decisions/consume, SDK verifyDecisionGrant(s) and enforce()) refuse a policy_arbiter grant, so it can never stand in for a human approval. The profile is specified in spec/decision-grant.md section 10.
Witness Checkpoints
AgenticOrg anchors the head of its hash-chainedaudit_log and asks Grantex to witness it, so a later rewrite below a witnessed head is provable outside AgenticOrg.
POST /v1/audit/witness-checkpoints (same AgenticOrg service token) takes {source: "agenticorg", tenant_ref, chain: "audit_log", head_seq, head_hash, anchored_at} (head_hash lowercase hex SHA-256) and answers 201 {witness_id, countersignature, witnessed_at, created: true}. countersignature is a compact JWS (typ audit-witness+jwt, platform key, kid in /.well-known/jwks.json) over those fields plus witnessed_at, with jti = witness_id and no exp.
GET /v1/audit/witness-checkpoints?source=agenticorg&tenant_ref=...&limit=... returns {checkpoints, forks}, newest head_seq first. AgenticOrg verifies the countersignature against the Grantex JWKS before storing the witness_id beside its anchor row, and treats a WITNESS_FORK as an irregularity of its own chain.
UCP And ACP Surfaces
Each route takes the same service token with its own route scope and the tenant allowlist. Details: OACP runtime authority and adapter mappings.Mandates
Grantex issues Intent, Cart and Payment mandates as ES256 JWTs signed with the OACP issuer key. Each mandate is on the OACP Token Status List. Why plain JWTs, and how they map to AP2: OACP mandates decision. Each route takes the service token with its own route scope and the tenant allowlist, or an operator of the tenant.tenant_id is the Commerce Passport’s commerce tenant.
An issue response is
{mandate, jti, mandate_type, exp, status: {status_list: {idx, uri}}, mandate_hash, claims, audit_event_id}; a refusal carries error.audit_event_id when the tenant is a commerce tenant. Store jti and mandate_hash (lowercase hex SHA-256 of the compact JWT) in the ledger and in the arbiter evidence (mandate_ref, mandate_hash). Put the three mandate hashes in the Plural order metadata.
Verify every mandate locally before relying on it. Use the pinned Grantex issuer and JWKS, and check the status list. Order and refusal codes:
mandate_malformed,mandate_algorithm_unsupported,mandate_type_mismatch(check the expectedtyp);issuer_key_untrusted,mandate_signature_invalid,mandate_issuer_mismatch,mandate_claims_invalid;mandate_not_yet_valid(60 s skew),mandate_expired,mandate_lifetime_exceeded(exp - iatover 600 s);mandate_audience_mismatch(audis the merchant id);mandate_revoked,mandate_suspended,revocation_status_unavailable(fail closed).
- the cart’s
intent_mandate_jtiand the payment’scart_mandate_jti; payment.transaction_id == cart.checkout_hash;- the payment amount and currency equal the cart total;
- the cart total is within the intent cap;
- the same tenant, merchant,
sub, buyer agent andconsent_ref.
mandate_chain_mismatch. The SDK sources carry this as verifyOacpMandate / checkOacpMandateChain (TypeScript), verify_oacp_mandate / check_oacp_mandate_chain (Python) and VerifyOacpMandate / CheckOacpMandateChain (Go); they are not yet in a published package release. The shared vectors are in spec/examples/oacp/oacp-mandates-v1.json and oacp-mandates-v1-extra.json.
Issuance refuses, and records the refusal (the error details.refusal_record_id), with these codes:
- passport and consent:
passport_invalid,passport_revoked,passport_not_checkout,passport_scope_missing,passport_cap_missing,consent_not_active,passport_cap_exceeded,currency_mismatch,merchant_not_allowed; - presence:
presence_mismatch(ahuman_not_presentmandate under ahuman_presentconsent or parent); - parent mandate:
parent_mandate_expired,parent_mandate_revoked,parent_mandate_suspended,parent_mandate_invalid,mandate_chain_mismatch,intent_cap_exceeded; - passkey:
passkey_evidence_required(ahuman_presentcart without an assertion),passkey_evidence_invalid,passkey_evidence_replayed; - request content:
checkout_invalid,card_data_refused,payment_rail_unsupported,token_binding_invalid,intent_invalid.
revocation_unavailable. A mandate never outlives its passport or its parent, and a checkout passport lives 600 s, so request the three mandates within one passport’s life.
Mandate Capability Evidence
Send the result of the provider capability verifier (verify_plural_pine_mandate_capability) with the C6Z authority request. It is a top-level mandate_capability_evidence member beside request and connector_evidence. Grantex binds it into the signed mandate_capability artifact. The object is closed: unknown members are refused, and it must hold no secrets.
result_statusandredacted_evidence_refare the verifier’s own values.railsandcapsuse the mandate rail namesplural,upi_autopayandcard_network_token, with integer minor units.verified_atmust be at most 6 h old and not in the future.availableneedsexternal_validation_performed: trueand at least one rail.
mandate_capability_status: "verified". It carries mandate_capability_ref (the redacted reference) and mandate_capability_evidence: the object without tenant and merchant, plus evidence_object_hash, Grantex’s SHA-256 of the RFC 8785 form of what it received.
- Any other
result_statusgivesunavailable. - With no evidence the artifact says
unverifiedand carries no reference. The placeholderprovider:mandate_rail:capability:pending:redactedis gone. - Malformed, stale, out-of-scope or placeholder evidence is refused as
mandate_capability_evidence_invalid(422, recorded). - The UCP profile emits checkout and order only while the live
mandate_capabilityartifact isverified.
Settlement Receipts
When a payment settled over x402 or MPP, AgenticOrg can bind the receipt to the commitment:POST /v1/commerce/oacp/commitment-evidence/settlement-receipts with the service token (scope commerce.oacp.commitment_evidence.write, tenant allowlist; operators and the platform admin are refused). Grantex issues a signed commitment_evidence artifact with the receipt hash, network and transaction reference, never the payer, and records the receipt as reported, not verified, because neither receipt format is signed. The same commitment and receipt again answers 200; another receipt for that commitment answers 409. settlementReceiptEvidence in @grantex/x402 shapes an x402 receipt for this body (a source addition, not yet released); an MPP receipt is sent as {protocol: "mpp", receipt: <the decoded Payment-Receipt object>}.
Agent Request Signatures
AgenticOrg verifies and signs agent HTTP requests withgrantex-agent-httpsig (verify_with_profile / sign_with_profile, profiles grantex, tap and web-bot-auth). The library, the shared vectors and the registry’s key directory are described in agent network identity. The registry key directory is served only with HTTP_SIGNATURE_DIRECTORY_ENABLED on, and its keys carry the registry’s countersignature rather than per-key signatures.
Presence And Authentication Methods
A consent request namespresence: human_present (the buyer confirms each cart with a passkey) or human_not_present (the buyer delegated purchases within the consented caps). The default is human_not_present. The buyer sees the mode on the consent page and it is part of the payload whose hash they approve. Approval records amr: ["consent", "otp"]. The Commerce Passport carries both as presence and amr claims, and mandates copy them. A passport issued before these claims existed reads as human_not_present with no methods. AgenticOrg’s arbiter applies its stricter not-present rule set to human_not_present; neither mode adds a human step to a delegated purchase.