Reference

Verification API

Last updated: 14 June 2026API version 0.1.0

The short answer

Send POST /v1/verify to https://verify.mandategate.com with a bearer merchant key. The body carries the agent's signature headers, the cart your server built, and your policy. The response is always HTTP 200 with a decision of allow, deny, or review, plus reason codes. The 4xx codes mean the request itself was broken, never that an agent was denied.

Authentication

Every /v1/verify call is authenticated with a merchant API key sent as a bearer token. A key is bound to one merchant_id and is rejected if it tries to verify for any other merchant. Keep it server-side.

Authorization: Bearer mg_live_xxxxxxxxxxxxxxxxxxxx

Missing or invalid key returns 401. A valid key used with the wrong merchant_id in the body returns 403. The /healthz and /v1/scan endpoints take no auth.

Endpoints

POST/v1/verifybearer key required
GET/healthzpublic
GET/v1/scan?url=<store>public, read-only

This page documents /v1/verify. The scan endpoint powers the public agent-ready scanner; /healthz returns {"status":"ok","version":"0.1.0"}.

Request body

A JSON object. Only merchant_id and request_context are required; the rest are supplied as the relevant layers apply to your setup.

merchant_id required
Your merchant identifier, for example mch_store_01. Must match the merchant your API key is bound to.
request_context required
The signature-relevant context of the agent's HTTP request: method, target_uri (the exact RFC 9421 @target-uri), the raw headers (lowercase keys, including signature-input and signature), and an optional received_at timestamp that anchors clock-skew and expiry windows.
checkout optional
The checkout the merchant is about to settle. checkout.cart is the server-built basket (amount in minor units, ISO 4217 currency, optional line items). checkout.checkout_jwt is the merchant-signed checkout terms; when present, the mandate's checkout_hash is verified against it and the signed amount is cross-checked against the cart.
mandates optional
AP2 v0.2 mandates in SD-JWT form. checkout_mandate is the agent's signed spending mandate; open_checkout_mandate is the user-signed open mandate used in autonomous (human-not-present) mode.
acp optional
Agentic Commerce Protocol context for ACP-mode merchants: the Checkout Session session_status, an allowance object (max_amount, currency, expires_at), and the shared_payment_token.
policy optional
Your verification policy: mode (strict, standard, or monitor), required_layers, an optional trusted_agents allow-list, and max_clock_skew_seconds. Defaults to standard with [transport, mandate] required. See policy modes.

Example request

curl -X POST https://verify.mandategate.com/v1/verify \
  -H "Authorization: Bearer mg_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "mch_store_01",
    "request_context": {
      "method": "POST",
      "target_uri": "https://shop.example.com/wp-json/wc/store/v1/checkout",
      "headers": {
        "signature-agent": "\"https://agent.example/.well-known/http-message-signatures-directory\"",
        "signature-input": "sig1=(\"@authority\" \"@method\" \"@target-uri\" \"content-digest\");created=1781370000;keyid=\"ed25519-key-1\";tag=\"web-bot-auth\"",
        "signature": "sig1=:K2qGT5srn2OGbOIDzQ6kYT+ruaycnDAAUpKv+ePFfD0=:",
        "content-digest": "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:"
      },
      "received_at": "2026-06-14T08:20:05Z"
    },
    "checkout": {
      "checkout_jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6...",
      "cart": { "amount": 4999, "currency": "GBP" }
    },
    "mandates": { "checkout_mandate": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZjK3NkLWp3dCJ9..." },
    "policy": { "mode": "standard", "required_layers": ["transport", "mandate"] }
  }'

Response body

Always HTTP 200 for a well-formed, authorized request, including a deny.

decision
allow deny review. The verdict your plugin enforces.
verification_id
Unique id for this verification, used as the audit and replay-ledger key.
reason_codes
The granular signals that drove the decision, for example transport.signature_valid or mandate.checkout_hash_mismatch. The full set is in the reason code reference.
layers
Per-layer results (transport, mandate, allowance), each with evaluated, a result of pass / fail / indeterminate / skipped, and the individual checks.
audit
The tamper-evident record: receipt_hash of this decision, prev_hash of the previous one, and logged_at.
{
  "decision": "allow",
  "verification_id": "vrf_01J8Z3K4",
  "reason_codes": [
    "transport.signature_valid",
    "mandate.signature_valid",
    "mandate.exp_valid",
    "mandate.checkout_hash_match",
    "mandate.constraint_satisfied"
  ],
  "layers": {
    "transport": { "evaluated": true, "result": "pass" },
    "mandate":   { "evaluated": true, "result": "pass" },
    "allowance": { "evaluated": false, "result": "skipped" }
  },
  "audit": { "receipt_hash": "sha256:9f2c...", "prev_hash": "sha256:00aa...", "logged_at": "2026-06-14T08:20:05Z" }
}

Status codes

CodeMeaning
200A verification decision. This includes deny and review, not only allow.
400The request body is malformed or missing a required field.
401The merchant API key is missing or invalid.
403The key is valid but used for a merchant_id it is not bound to.
422The body is well-formed but logically contradictory, for example mode: strict with no layer input at all.

What it does not do

The verify API returns a decision and nothing more. It does not capture payment, hold funds, or touch your gateway; settlement stays where it is today. It does not see or store a human shopper's identity. Enforcement is the merchant plugin's job: the API judges, the plugin acts. That separation is what lets you deploy in monitor mode with no risk to live sales.

Frequently asked questions

Does a deny return an HTTP error?

No. A deny is a successful verification, so it returns HTTP 200 with decision set to deny. The 4xx codes are only for a broken or unauthorized request: 400 for a malformed body, 401 for a bad API key, 403 when a key is used for a merchant_id it is not bound to, and 422 for a logically contradictory body.

Does Agent Gateway charge the card?

No. The verify API only returns a decision. Settlement stays with your existing payment gateway, and your store plugin enforces the decision by allowing or stopping the checkout.

What does the audit field contain?

Every decision is written to a tamper-evident chain. The response audit object carries the canonical hash of this record and the hash of the previous one, so the log cannot be altered without breaking the chain.

Get a key and call it

Request a merchant key to start sending real verifications, or read the reason code reference to learn what each signal means.