Skip to main content

Overview

Operator credentials (opc_...) let agents identify their human operator without a wallet. After the operator completes KYC, they (or their agent) can create credentials via the API or MCP tools. Credentials expire after a configurable TTL (default 24 hours). Agents can self-serve new credentials via the API.

Create credential

POST /v1/credentials
X-API-Key: as_live_...
Content-Type: application/json
{
  "label": "claude-code-agent",
  "ttl_days": 1
}
FieldTypeRequiredDescription
labelstringNoHuman-readable label (max 100 chars)
ttl_daysnumberNoTime to live in days (1-365, default 1)

Response

{
  "id": "uuid",
  "credential": "opc_abc123...",
  "prefix": "opc_abc1",
  "label": "claude-code-agent",
  "expires_at": "2026-04-10T12:00:00Z",
  "created_at": "2026-04-09T12:00:00Z",
  "agent_memory": {
    "pattern_summary": "...",
    "identity_paths": { "...": "..." },
    "bootstrap": { "...": "..." },
    "do_not_persist_in_memory": ["operator_token", "poll_secret"],
    "persist_in_credential_store": ["operator_token"]
  }
}
The credential value is shown only once. Store it securely.
The agent_memory block is a cross-merchant pattern hint; LLM-hosted agents should persist the pattern (never secrets) to long-term memory so future AgentScore-gated endpoints become returning-customer encounters. Same block is emitted on POST /v1/sessions, on missing_identity bootstrap denials (when no auto-session is created), and inline on the first-mint 402 Payment Required from merchants that echo the hint.

409: KYC required

If the account has not completed identity verification, credential creation returns 409 Conflict:
{
  "error": {
    "code": "kyc_required",
    "message": "Complete identity verification before creating an operator credential."
  },
  "verify_url": "https://agentscore.sh/dashboard/verify",
  "next_steps": {
    "action": "complete_kyc_then_retry",
    "user_message": "Visit the verify_url, complete Stripe Identity verification, then retry this request."
  }
}

List credentials

GET /v1/credentials
X-API-Key: as_live_...
Returns active (non-expired, non-revoked) credentials for your account, along with the account’s verification status.
{
  "account_verification": {
    "kyc_status": "verified",
    "kyc_verified_at": "2026-04-07T17:13:56.525Z",
    "jurisdiction": "US",
    "age_verified": true,
    "age_bracket": "21+",
    "sanctions_clear": true,
    "sanctions_checked_at": "2026-04-07T17:13:56.525Z",
    "operator_type": "individual"
  },
  "credentials": [
    {
      "id": "uuid",
      "prefix": "opc_abc1",
      "label": "claude-code-agent",
      "expires_at": "2026-04-10T12:00:00Z",
      "last_used_at": "2026-04-09T14:30:00Z",
      "created_at": "2026-04-09T12:00:00Z"
    }
  ]
}
The sanctions gate is exposed as a paired field rather than a raw status:
FieldTypeDescription
sanctions_clearboolean | nulltrue only when the account has been screened AND the screening is within the freshness window. false when the account is on a sanctions list. null when never screened or when the screening is stale.
sanctions_checked_atstring | nullISO-8601 timestamp of the last sanctions screening, or null if never screened. Lets clients apply their own freshness logic if the default window is too loose.
When the account has no KYC, account_verification contains only the status:
{
  "account_verification": {
    "kyc_status": "none"
  },
  "credentials": []
}

Revoke credential

DELETE /v1/credentials/{id}
X-API-Key: as_live_...
{
  "id": "uuid",
  "revoked": true
}

Usage

Pass the credential as the X-Operator-Token header when calling /v1/assess or merchant endpoints:
curl -X POST https://api.agentscore.sh/v1/assess \
  -H "X-API-Key: as_live_..." \
  -H "Content-Type: application/json" \
  -d '{"operator_token": "opc_abc123...", "policy": {"require_kyc": true, "min_age": 21}}'
Or via AgentScore Gate middleware; agents send X-Operator-Token alongside requests to gated services.

Report a captured wallet

Merchants call this after a successful payment to report the signer wallet back to AgentScore. Builds a cross-merchant credential↔wallet profile that powers unified reputation, cross-merchant sanctions, and already-verified detection on future credentials from known wallets.
POST /v1/credentials/wallets
X-API-Key: as_live_...
Content-Type: application/json
{
  "operator_token": "opc_abc123...",
  "wallet_address": "0xabcdef1234567890abcdef1234567890abcdef12",
  "network": "evm"
}
FieldTypeRequiredDescription
operator_tokenstringYesThe credential the agent authenticated with on the gated endpoint.
wallet_addressstringYesThe signer wallet recovered from the payment payload (EIP-3009 from for x402, Tempo MPP DID address for mppx, base58 Solana pubkey). EVM addresses are lowercased server-side; Solana base58 addresses are kept verbatim. The EVM zero address (0x0000…0000) is rejected with invalid_wallet.
networkstringYesKey-derivation family. "evm" covers every EVM chain (Base, Tempo, Ethereum, …) because EOAs share identity across them. "solana" is a separate namespace.
idempotency_keystring (max 200 chars)NoStable per-payment key (e.g., Stripe PI id, x402 tx hash). When the same key is reported again for the same (credential, wallet, network), the server no-ops: agent retries of the same logical payment don’t inflate transaction_count. Values longer than 200 characters are truncated server-side.

Response

{
  "associated": true,
  "first_seen": true
}
first_seen is true the first time this (credential, wallet, network) tuple is reported, and false on subsequent observations (internal transaction_count is incremented). When an idempotency_key matches the most recent capture’s key, the response is:
{
  "associated": true,
  "first_seen": false,
  "deduped": true
}
and no state change happens (no transaction_count bump, no last_seen update).

Auth & tier

Any authenticated tier (free or paid). Rate limits still apply. Call this fire-and-forget; a failed capture must never block a payment response.

Errors

StatusCodeMeaning
400bad_requestMissing operator_token, wallet_address, or network.
400invalid_networkNetwork must be "evm" or "solana".
400invalid_walletMalformed wallet address for the given network, or the EVM zero address.
401signup_requiredMissing or invalid API key.
401invalid_credentialOperator credential not found, expired, or revoked. Returned as 401 (rather than 404) to prevent credential enumeration.
429rate_limitedRate limit exceeded.

SDK helpers

  • node-sdk: client.associateWallet({ operatorToken, walletAddress, network })
  • python-sdk: client.associate_wallet(operator_token, wallet_address, network) (plus async aassociate_wallet)
  • node-commerce identity adapters expose captureWallet(ctx, { walletAddress, network }) which reads the operator_token from the gate state; no need to re-pass it.