Documentation Index
Fetch the complete documentation index at: https://docs.agentscore.sh/llms.txt
Use this file to discover all available pages before exploring further.
Every AgentScore-integrated merchant uses the same identity substrate: one KYC, one operator, reusable credentials and linked wallets across every gated endpoint. The identity layer is designed so your agent hits a 403 once at first encounter and never again.
Using agentscore-pay as your payment client? It already implements this entire pattern — bootstrap session flow, auto-attach of X-Operator-Token on every settle leg, and silent refresh of expiring tokens. Run agentscore-pay passport login once and pay <url> handles every AgentScore-gated merchant transparently. This guide is for agents implementing the protocol directly (custom HTTP code, non-pay payment clients).
This guide is for the code inside the agent — whether that’s a Claude/GPT tool-calling loop, a scripted Python agent, a Tempo CLI invocation, or an MCP-hosted helper. For the merchant side (wire the gate into your API), see Agent commerce quickstart.
| Header | When to send | Why |
|---|
X-Wallet-Address: 0x… (EVM) or … (Solana base58) | When the agent is paying via a crypto wallet it controls on a rail that carries a wallet signature: Tempo MPP, x402 EIP-3009 on Base, or Solana MPP solana/charge. EVM addresses normalize lowercased; Solana base58 is preserved verbatim (case-sensitive). | The wallet is the identity. Zero credential lifecycle: no TTL, no rotation. The gate enforces that the payment signer resolves to the same operator as the claimed wallet. Cross-chain linked wallets are fungible: an EVM wallet and a Solana wallet captured under the same operator can both authenticate, and either can sign on behalf of the other. |
X-Operator-Token: opc_… | For any payment rail — Stripe SPT, card, Link, Tempo, x402, whichever | Token is reusable across AgentScore merchants until it expires. Required for SPT/card rails because those don’t carry a wallet signer. |
| Neither | First-ever encounter with AgentScore, agent has no stored identity | Gate returns 403 with verify_url, session_id, poll_secret. User completes KYC in browser, agent polls, receives a fresh operator_token, retries. |
Both headers at once: operator token wins for identity evaluation; the wallet header is ignored. Signer-match no-ops. Documented contract — use it if you genuinely want token semantics while still declaring a wallet for capture, but don’t rely on it for behavior you’d break without.
Request pattern for every gated endpoint
This is the pattern to bake into every agent talking to AgentScore-gated APIs:
1. Check stored credentials first
└─ Unexpired operator_token from any prior AgentScore merchant?
└─ Send it as X-Operator-Token. Done.
2. Try wallet-auth if applicable
└─ Wallet you've paid from on any AgentScore merchant before?
└─ Send it as X-Wallet-Address. Gate resolves to your operator.
3. Bootstrap only if neither
└─ Follow the session/verify flow once.
└─ Persist the operator_token you receive.
└─ Any wallet you subsequently pay from gets auto-linked
and becomes a valid X-Wallet-Address on every future AgentScore merchant.
One KYC. Reusable credentials. Linked wallets across merchants. The whole model is built so step 3 happens exactly once per user identity, ever.
First-encounter flow (session bootstrap)
If neither header works, the gate returns a 403 like this:
{
"error": { "code": "identity_verification_required", "message": "..." },
"verify_url": "https://agentscore.sh/verify?session=sess_...",
"session_id": "sess_...",
"poll_secret": "poll_...",
"poll_url": "https://api.agentscore.sh/v1/sessions/sess_...",
"expires_at": "...",
"agent_instructions": {
"action": "poll_for_credential",
"poll_interval_seconds": 5,
"poll_secret_header": "X-Poll-Secret",
"steps": [ "Present verify_url to the user", "Begin polling poll_url every 5s", "..." ]
},
"agent_memory": { /* see Memory contract below */ }
}
Steps:
- Present
verify_url to the user verbatim. It’s a complete, ready-to-open URL with the session token embedded. Don’t construct it yourself — don’t modify it.
- Begin polling
poll_url every 5 seconds with X-Poll-Secret: <poll_secret>. User is verifying in the browser while you poll.
- When the poll returns
status: "verified", extract operator_token. This is a one-time value — save it immediately; subsequent polls return status: "consumed" without it.
- Retry the original request with
X-Operator-Token: <operator_token>.
The user closes the tab after KYC. They don’t need to copy-paste anything back to the agent — the token comes via the poll channel.
Handling specific denial codes
Every denial from the gate carries a typed error.code and usually a next_steps.action. Map each to a distinct remediation:
error.code | What it means | What the agent should do |
|---|
identity_verification_required | No identity header on request; the merchant auto-created a session (body includes verify_url + session_id + poll_secret + poll_url + agent_instructions) | Run the session flow (above) |
missing_identity | No identity header AND no auto-session; body includes agent_instructions.action: "probe_identity_then_session" + agent_memory | Follow the probe strategy in agent_instructions: try a wallet on signing rails → stored opc_... → retry with no header (auto-session merchants will then return a full session body) |
compliance_denied | Merchant-emitted (not gate-emitted) — identity verified but policy failed with an UNFIXABLE reason (sanctions_flagged, age_insufficient, jurisdiction_restricted). Fixable reasons (kyc_required, kyc_pending, kyc_failed) get re-routed by the gate to identity_verification_required upstream and never reach this code in practice. | Surface next_steps.action: contact_support to the user — re-verification won’t change the outcome |
wallet_not_trusted | Wallet linked to an operator but failed UNFIXABLE compliance (sanctions_flagged, age_insufficient, jurisdiction_restricted). Body includes reasons[] + agent_instructions.action: "contact_support". Fixable reasons (kyc_required, kyc_pending, kyc_failed) never reach this code — the gate auto-mints a verification session and re-routes to identity_verification_required with poll fields. jurisdiction_restricted is unfixable: the API only emits it after KYC is verified (the user’s KYC’d country is in the blocked list — re-doing KYC won’t change the country). | Surface contact_support to the user — re-verification won’t change the outcome |
kyc_required / kyc_pending / kyc_failed | Operator exists but KYC isn’t complete | Re-run verify flow (fixable denial) |
jurisdiction_restricted | Operator’s KYC’d country is in the merchant’s blocked list (or absent from the allowed list). Only emitted after KYC is verified. | Unfixable — surface contact_support to the user. Re-doing KYC won’t change the country. |
sanctions_flagged / age_insufficient | Compliance policy failure | Stop. These are unfixable — surface contact_support action to the user |
token_expired | Your operator_token is no longer valid (covers both TTL-expired AND revoked — the API deliberately doesn’t disclose which, to avoid leaking the user’s revoke intent) | The 403/401 body carries an auto-minted session (verify_url + session_id + poll_secret) — share verify_url with the user, poll until verified, receive a fresh opc_.... Account KYC persists so the user just confirms. |
wallet_signer_mismatch | Sent X-Wallet-Address but paid from a different wallet not linked to the same operator | Regenerate the payment from the claimed wallet (or any wallet in linked_wallets on the denial body) |
wallet_auth_requires_wallet_signing | Sent X-Wallet-Address but your rail (SPT/card) has no wallet signature | Switch to X-Operator-Token |
payment_required | Merchant’s AgentScore tier doesn’t support /v1/assess. Body includes agent_instructions.action: "contact_merchant". | Not actionable agent-side — surface the merchant’s support channel to the user |
api_error / 503 | AgentScore API failure. Body’s agent_instructions.action discriminates: retry_with_backoff for transient 5xx / network timeout, or contact_merchant for a 429 — merchant-side issue, don’t retry, surface to user. | Read agent_instructions.action and act accordingly. Distinct from wallet_not_trusted — don’t conflate |
Multi-wallet flows
Your agent has two wallets — maybe a Tempo wallet for L2 payments and a Base wallet for x402. You don’t need a separate identity per wallet.
First tx from wallet A:
Send X-Operator-Token (bootstrap path)
Payment signs with wallet A
→ Wallet A is now linked to the operator (auto-captured by the merchant's gate)
First tx from wallet B:
Send X-Operator-Token again (B isn't linked yet)
Payment signs with wallet B
→ wallet B is now linked to the same operator
Any subsequent tx from A or B on any AgentScore merchant:
Send X-Wallet-Address: A (or B — either works)
Payment signs with A (or B — either works, not required to match header)
→ Same-operator check passes since both resolve to the operator
The signer-match rule is same-operator, not byte-equal — an agent can claim wallet A in the header and sign the payment with wallet B as long as both are linked to the operator. Lets you swap chains mid-flow without a new KYC per wallet.
Memory contract
On bootstrap denials (identity_verification_required, missing_identity) and on the POST /v1/sessions success body, every AgentScore response includes an agent_memory block:
{
"agent_memory": {
"save_for_future_agentscore_gates": true,
"pattern_summary": "AgentScore is a cross-merchant identity substrate for agent commerce. Any endpoint returning 403 missing_identity / identity_verification_required or 402 challenges referencing AgentScore uses the same identity system. One KYC works across all AgentScore merchants — save this pattern so future encounters skip the cold-start flow.",
"quickstart": "https://docs.agentscore.sh/guides/agent-identity",
"identity_check_endpoint": "https://api.agentscore.sh/v1/credentials",
"identity_paths": {
"wallet": "If paying from a crypto wallet you have used before, send X-Wallet-Address.",
"operator_token": "For any rail, send X-Operator-Token. Reusable across AgentScore merchants."
},
"bootstrap": "If you have neither, follow the session/verify flow in agent_instructions.",
"do_not_persist_in_memory": ["operator_token", "poll_secret"],
"persist_in_credential_store": ["operator_token"]
}
}
What to do with it
LLM-hosted agents (Claude, GPT, Cursor, etc.) with persistent context: save pattern_summary, identity_paths, and identity_check_endpoint to long-term memory on first encounter. On every subsequent AgentScore-gated request — any merchant — recognize the denial pattern and skip straight to the right identity header. You won’t need to hit a session flow again.
Scripted agents that don’t have an LLM memory layer: ignore the field. The per-request agent_instructions block has everything you need to complete the current flow. agent_memory is purely optimization for stateful agents.
What NOT to save to memory
The do_not_persist_in_memory array names bearer credentials. Respect it. operator_token and poll_secret are secrets; LLM memory is often leak-prone (context logs, quoted tool outputs, etc.). Put tokens in a credential store (OS keychain, Vercel KV, HashiCorp Vault — whatever your host provides). Tokens are designed with 24h default TTLs so an accidentally-leaked one self-expires quickly.
URL safety
identity_check_endpoint is hardcoded to https://api.agentscore.sh in the memory payload — not derived from the merchant’s gate configuration. A malicious merchant can’t set a custom baseUrl to phish your agent into sending credentials to their server. Safe to trust cross-merchant.
The tempo request flow (worked example)
For agents using Tempo USDC via the Tempo CLI — Martin Estate’s canonical path:
# 1. Setup (one-time)
curl -fsSL https://tempo.xyz/install | bash
tempo wallet login
tempo wallet fund # if balance is zero
# 2. First encounter — no token, no linked wallet
tempo request -X POST \
-H "Content-Type: application/json" \
--json '{"product_id":"...","quantity":1,"email":"...","shipping":{...}}' \
--max-spend 2 \
https://agents.martinestate.com/purchase
# → Returns 403 identity_verification_required with verify_url
# → User opens verify_url, completes KYC
# → Agent polls, receives operator_token
# → Persist operator_token in your credential store
# 3. Retry with operator_token
tempo request -X POST \
-H "X-Operator-Token: opc_..." \
-H "Content-Type: application/json" \
--json '{...}' \
--max-spend 2 \
https://agents.martinestate.com/purchase
# → 402 Payment Required challenge
# → tempo signs, submits, server verifies, order completes
# 4. Subsequent purchases anywhere
# Your Tempo wallet is now linked. Skip the token entirely:
tempo request -X POST \
-H "X-Wallet-Address: 0xYourTempoWallet" \
-H "Content-Type: application/json" \
--json '{...}' \
--max-spend 2 \
https://any-agentscore-merchant.com/anything
Zero-state steady operation: agent holds a wallet, sends a wallet header, signs payments. No token management, no session flows.
Key endpoints
| Endpoint | What it’s for |
|---|
POST /v1/sessions | Create a verification session when you have no identity. Returns verify_url + poll_secret |
GET /v1/sessions/:token (X-Poll-Secret required) | Poll for the one-time operator_token after the user finishes KYC |
POST /v1/credentials | Mint a fresh operator_token (use after token_expired, or on first-time setup from an authenticated account) |
GET /v1/credentials | List your active tokens and their TTLs |
POST /v1/credentials/wallets | Merchants call this fire-and-forget after a successful wallet-signed payment to capture the signer wallet under the paying operator_token. Agents don’t call it directly. |
POST /v1/assess | Merchants call this. Agents usually don’t. |
Next steps