> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentscore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Operator Credentials

> Create, list, and revoke operator credentials for non-wallet agent identity.

## 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
```

```json theme={"dark"}
{
  "label": "claude-code-agent",
  "ttl_days": 1
}
```

| Field      | Type   | Required | Description                             |
| ---------- | ------ | -------- | --------------------------------------- |
| `label`    | string | No       | Human-readable label (max 100 chars)    |
| `ttl_days` | number | No       | Time to live in days (1-365, default 1) |

### Response

```json theme={"dark"}
{
  "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"]
  }
}
```

<Warning>
  The `credential` value is shown only once. Store it securely.
</Warning>

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**:

```json theme={"dark"}
{
  "error": {
    "code": "kyc_required",
    "message": "Complete identity verification before creating an operator credential."
  },
  "verify_url": "https://www.agentscore.com/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.

```json theme={"dark"}
{
  "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",
      "refreshable": false
    }
  ]
}
```

The sanctions gate is exposed as a paired field rather than a raw status:

| Field                  | Type              | Description                                                                                                                                                                                                  |
| ---------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sanctions_clear`      | `boolean \| null` | `true` 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_at` | `string \| null`  | ISO-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.                                            |

`credentials[].refreshable` is `true` when a live refresh token can still mint a
replacement for that credential. The list includes an expired credential while
this holds: revoking it is what stops the renewal, and a credential that stops
listing can never be revoked.

When the account has no KYC, `account_verification` contains only the status:

```json theme={"dark"}
{
  "account_verification": {
    "kyc_status": "none"
  },
  "credentials": []
}
```

## Revoke credential

```
DELETE /v1/credentials/{id}
X-API-Key: as_live_...
```

`{id}` is either the credential's `id` from `GET /v1/credentials` or the `opc_...`
value itself, so an agent holding only the credential can revoke it.

```json theme={"dark"}
{
  "id": "uuid",
  "revoked": true
}
```

Revoking also revokes the refresh token minted with the credential, and every
later rotation of that token along with the credentials those rotations minted,
in the same transaction. An agent in that chain cannot mint a replacement; it
has to run a fresh Passport login.

## Usage

Pass the credential as the `X-Operator-Token` header when calling `/v1/assess` or merchant endpoints:

```bash theme={"dark"}
curl -X POST https://api.agentscore.com/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
```

```json theme={"dark"}
{
  "operator_token": "opc_abc123...",
  "wallet_address": "0xabcdef1234567890abcdef1234567890abcdef12",
  "network": "evm"
}
```

| Field             | Type                   | Required | Description                                                                                                                                                                                                                                                                                               |
| ----------------- | ---------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `operator_token`  | string                 | Yes      | The credential the agent authenticated with on the gated endpoint.                                                                                                                                                                                                                                        |
| `wallet_address`  | string                 | Yes      | The 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`.       |
| `network`         | string                 | Yes      | Key-derivation family. `"evm"` covers every EVM chain (Base, Tempo, Ethereum, …) because EOAs share identity across them. `"solana"` is a separate namespace.                                                                                                                                             |
| `idempotency_key` | string (max 200 chars) | No       | Stable 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

```json theme={"dark"}
{
  "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:

```json theme={"dark"}
{
  "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

| Status | Code                 | Meaning                                                                                                                  |
| ------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| 400    | `bad_request`        | Missing `operator_token`, `wallet_address`, or `network`.                                                                |
| 400    | `invalid_network`    | Network must be `"evm"` or `"solana"`.                                                                                   |
| 400    | `invalid_wallet`     | Malformed wallet address for the given network, or the EVM zero address.                                                 |
| 401    | `signup_required`    | Missing or invalid API key.                                                                                              |
| 401    | `invalid_credential` | Operator credential not found, expired, or revoked. Returned as 401 (rather than 404) to prevent credential enumeration. |
| 429    | `rate_limited`       | Rate 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.
