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

# AgentScore Gate

> Verify the identity of agents and operators before completing transactions. KYC, age verification, sanctions screening, and jurisdiction checks in one API call.

If your service sells regulated goods, handles PII, or needs to know who's paying; AgentScore gives you identity verification for the agents and operators interacting with your API.

<Tip>
  Want a step-by-step tutorial using Martin Estate's wine commerce API as the worked example? See the [Agentic Commerce Quickstart](/guides/agent-commerce-quickstart); it walks through the AgentScore Gate middleware pattern end-to-end.
</Tip>

## How it works

1. An agent requests a purchase or action from your service
2. Your service calls AgentScore to verify the operator's identity
3. AgentScore returns `allow` or `deny` based on your compliance policy
4. If denied, the operator can self-serve verify via a URL you provide

You define the policy. AgentScore enforces it.

## Define your compliance policy

A policy specifies what identity checks the operator must pass:

```json theme={"dark"}
{
  "require_kyc": true,
  "require_sanctions_clear": true,
  "min_age": 21,
  "allowed_jurisdictions": ["US"]
}
```

| Field                     | Type      | Description                                                                                                                                                                                                                                                           |
| ------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `require_kyc`             | boolean   | Operator must have completed identity verification                                                                                                                                                                                                                    |
| `require_sanctions_clear` | boolean   | Operator must pass NAME-based sanctions screening on their KYC identity (separate from wallet-address OFAC screening, which fires automatically when a `signer` is in the request; see [POST /v1/assess](/api-reference/post-assess) for the signer\_sanctions block) |
| `min_age`                 | number    | Minimum age (18 or 21)                                                                                                                                                                                                                                                |
| `blocked_jurisdictions`   | string\[] | ISO country codes to block                                                                                                                                                                                                                                            |
| `allowed_jurisdictions`   | string\[] | ISO country codes to allow (denies all others)                                                                                                                                                                                                                        |

## Check identity at transaction time

When an agent sends a request to your service, extract the identity from the headers and call `POST /v1/assess`:

<CodeGroup>
  ```typescript Node.js theme={"dark"}
  import { AgentScore } from '@agent-score/sdk';

  const agentscore = new AgentScore({ apiKey: process.env.AGENTSCORE_API_KEY });

  const policy = {
    require_kyc: true,
    require_sanctions_clear: true,
    min_age: 21,
    allowed_jurisdictions: ['US'],
  };

  // In your request handler
  const walletAddress = req.headers['x-wallet-address'];
  const operatorToken = req.headers['x-operator-token'];

  const result = walletAddress
    ? await agentscore.assess(walletAddress, { policy })
    : await agentscore.assess(null, { operatorToken, policy });

  if (result.decision === 'deny') {
    return res.status(403).json({
      error: 'compliance_denied',
      reasons: result.decision_reasons,
      verify_url: result.verify_url,
    });
  }

  // Identity verified, proceed with transaction
  ```

  ```python Python theme={"dark"}
  from agentscore import AgentScore

  client = AgentScore(api_key=os.environ['AGENTSCORE_API_KEY'])

  policy = {
      'require_kyc': True,
      'require_sanctions_clear': True,
      'min_age': 21,
      'allowed_jurisdictions': ['US'],
  }

  wallet_address = request.headers.get('X-Wallet-Address')
  operator_token = request.headers.get('X-Operator-Token')

  if wallet_address:
      result = client.assess(wallet_address, policy=policy)
  elif operator_token:
      result = client.assess(operator_token=operator_token, policy=policy)
  else:
      return jsonify({'error': {'code': 'identity_required', 'message': 'Send X-Wallet-Address or X-Operator-Token.'}}), 403

  if result['decision'] == 'deny':
      return jsonify({
          'error': {'code': 'compliance_denied', 'message': 'Compliance policy denied this request.'},
          'reasons': result['decision_reasons'],
          'verify_url': result.get('verify_url'),
      }), 403

  # Identity verified, proceed with transaction
  ```
</CodeGroup>

Or use AgentScore Gate middleware for automatic enforcement:

<CodeGroup>
  ```typescript Hono theme={"dark"}
  import { Hono } from 'hono';
  import { agentscoreGate } from '@agent-score/commerce/identity/hono';

  const app = new Hono();
  app.use('*', agentscoreGate({
    apiKey: process.env.AGENTSCORE_API_KEY!,
    requireKyc: true,
    requireSanctionsClear: true,
    minAge: 21,
    allowedJurisdictions: ['US'],
  }));
  ```

  ```typescript Express theme={"dark"}
  import express from 'express';
  import { agentscoreGate } from '@agent-score/commerce/identity/express';

  const app = express();
  app.use('/purchase', agentscoreGate({
    apiKey: process.env.AGENTSCORE_API_KEY!,
    requireKyc: true,
    requireSanctionsClear: true,
    minAge: 21,
    allowedJurisdictions: ['US'],
  }));
  ```

  ```typescript Fastify theme={"dark"}
  import Fastify from 'fastify';
  import agentscoreGate from '@agent-score/commerce/identity/fastify';

  const app = Fastify();
  await app.register(agentscoreGate, {
    apiKey: process.env.AGENTSCORE_API_KEY!,
    requireKyc: true,
    requireSanctionsClear: true,
    minAge: 21,
    allowedJurisdictions: ['US'],
  });
  ```

  ```typescript Next.js theme={"dark"}
  // app/api/purchase/route.ts
  import { withAgentScoreGate } from '@agent-score/commerce/identity/nextjs';

  export const POST = withAgentScoreGate(
    {
      apiKey: process.env.AGENTSCORE_API_KEY!,
      requireKyc: true,
      requireSanctionsClear: true,
      minAge: 21,
      allowedJurisdictions: ['US'],
    },
    async (_req) => Response.json({ ok: true }),
  );
  ```

  ```typescript Web Fetch (Cloudflare Workers / edge) theme={"dark"}
  import { createAgentScoreGate } from '@agent-score/commerce/identity/web';

  const guard = createAgentScoreGate({
    apiKey: env.AGENTSCORE_API_KEY,
    requireKyc: true,
    requireSanctionsClear: true,
    minAge: 21,
    allowedJurisdictions: ['US'],
  });

  export default {
    async fetch(req: Request) {
      const r = await guard(req);
      if (!r.allowed) return r.response;
      return Response.json({ ok: true });
    },
  };
  ```

  ```python FastAPI (native) theme={"dark"}
  from fastapi import Depends
  from agentscore_commerce.identity.fastapi import AgentScoreGate

  gate = AgentScoreGate(
      api_key=os.environ['AGENTSCORE_API_KEY'],
      require_kyc=True,
      require_sanctions_clear=True,
      min_age=21,
      allowed_jurisdictions=['US'],
  )

  @app.post('/purchase', dependencies=[Depends(gate)])
  async def purchase():
      ...
  ```

  ```python Starlette / generic ASGI theme={"dark"}
  from agentscore_commerce.identity import AgentScoreGate

  app.add_middleware(
      AgentScoreGate,
      api_key=os.environ['AGENTSCORE_API_KEY'],
      require_kyc=True,
      require_sanctions_clear=True,
      min_age=21,
      allowed_jurisdictions=['US'],
  )
  ```

  ```python Flask theme={"dark"}
  from agentscore_commerce.identity.flask import agentscore_gate

  agentscore_gate(
      app,
      api_key=os.environ['AGENTSCORE_API_KEY'],
      require_kyc=True,
      require_sanctions_clear=True,
      min_age=21,
      allowed_jurisdictions=['US'],
  )
  ```

  ```python Django theme={"dark"}
  # settings.py
  AGENTSCORE_GATE = {
      'api_key': os.environ['AGENTSCORE_API_KEY'],
      'require_kyc': True,
      'require_sanctions_clear': True,
      'min_age': 21,
      'allowed_jurisdictions': ['US'],
  }
  MIDDLEWARE = [..., 'agentscore_commerce.identity.django.AgentScoreMiddleware']
  ```

  ```python AIOHTTP theme={"dark"}
  from aiohttp import web
  from agentscore_commerce.identity.aiohttp import agentscore_gate_middleware

  app = web.Application()
  app.middlewares.append(agentscore_gate_middleware(
      api_key=os.environ['AGENTSCORE_API_KEY'],
      require_kyc=True,
      require_sanctions_clear=True,
      min_age=21,
      allowed_jurisdictions=['US'],
  ))
  ```

  ```python Sanic theme={"dark"}
  from agentscore_commerce.identity.sanic import agentscore_gate

  agentscore_gate(
      app,
      api_key=os.environ['AGENTSCORE_API_KEY'],
      require_kyc=True,
      require_sanctions_clear=True,
      min_age=21,
      allowed_jurisdictions=['US'],
  )
  ```
</CodeGroup>

## Per-product policy + soft mode (commerce SDK)

The single-gate setup above attaches one policy to a route. Multi-product
merchants where each item has different compliance needs (regulated wine ⨉
free-to-everyone merch ⨉ high-value print that wants KYC as a fraud signal
but won't block the sale) want the policy per product, not per route.

Both `agentscore-commerce` (Python) and `@agent-score/commerce` (Node) ship
parallel helpers for this. Field names follow each language's convention
(`snake_case` in Python, `camelCase` in Node) but the shape is identical.

**Python (`agentscore_commerce.identity.policy`):**

* **`PolicyBlock`**: typed shape carrying `enforcement` (`hard` | `soft` | absent), `require_kyc`, `require_sanctions_clear`, `min_age`, `allowed_jurisdictions`, `allowed_shipping_countries`, `allowed_shipping_states`. Vendors usually source these from a database row (one column per field).
* **`build_gate_from_policy(policy, *, api_key)`**; translates a block into an `AgentScoreGate`. Returns `None` when the block has no `enforcement` set, signalling "no gate; identity\_status='anonymous'".
* **`run_gate_with_enforcement(request, gate, *, enforcement)`**; runs the gate. On hard denial it returns `status="denied"` with a `denial_status` and `denial_body` for the caller to propagate. On soft denial it swallows the 403 and returns `status="unverified"` so the order completes with a degraded identity stamp. On success: `status="verified"`. No gate: `status="anonymous"`.

**Node (`@agent-score/commerce/identity/policy`):**

* **`PolicyBlock`**: same fields in camelCase (`enforcement`, `requireKyc`, `requireSanctionsClear`, `minAge`, `allowedJurisdictions`, `allowedShippingCountries`, `allowedShippingStates`).
* **`buildGateFromPolicy(policy, { apiKey })`**: translates a block into the options object the per-framework `agentscoreGate(...)` accepts. The Node SDK builds gates per framework (Hono, Express, Fastify, Next.js, Web), so the policy module emits options rather than a constructed gate, mirroring python's `build_gate_from_policy` verb pattern. Returns `null` when no `enforcement`.
* **`runGateWithEnforcement(enforcement, runGate)`**: wraps the per-framework middleware in the hard/soft enforcement runner. The vendor passes a `runGate` adapter that resolves to `{ ok: true }` on accept or `{ ok: false, status, body }` on deny; the runner returns a structured `GateResult`.

In both languages, also:

* **`shipping{Country,State}Allowed(...)`**: per-product shipping allowlists. Country list is hard-enforced regardless of identity strictness; state list only fires for US shipments (e.g. wine).

Three modes per product:

| `enforcement`   | Identity required?    | On gate denial                                          | Use case                                                |
| --------------- | --------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
| `hard`          | yes                   | propagate the gate's 403 (today's regulated path)       | wine, cannabis, anything regulated                      |
| `soft`          | offered, not required | swallow + stamp `identity_status="unverified"` on order | request KYC for fraud signal but accept anonymous sales |
| absent / `null` | no                    | gate never fires; `identity_status="anonymous"`         | unregulated merch, ship anywhere                        |

Persist `identity_status` on each order row (`verified` | `unverified` | `anonymous`) so ops/analytics can distinguish soft passes from hard passes from anonymous sales.

A typical multi-product setup mixes enforcement modes; e.g. a regulated wine SKU with `enforcement="hard"` (KYC + 21 + US-only state allowlist) alongside unregulated merch (tee, sticker pack) with no policy. Single-file runnable examples: [`per_product_policy_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/per_product_policy_merchant.py) (Python) and [`per-product-policy-merchant.ts`](https://github.com/agentscore/node-commerce/blob/main/examples/per-product-policy-merchant.ts) (Node).

## Handle unverified operators

When an operator isn't verified, the assess response includes a `verify_url`. Return it to the agent so the operator can self-serve:

```json theme={"dark"}
{
  "decision": "deny",
  "decision_reasons": ["kyc_required"],
  "verify_url": "https://www.agentscore.com/dashboard/verify?address=0x..."
}
```

The agent should tell the operator: *"Identity verification is required. Visit \[verify\_url] to get verified."*

### Session-based verification (recommended)

For a smoother flow, create a verification session before returning the deny. This lets the agent poll for the result instead of requiring the operator to copy-paste credentials:

```typescript theme={"dark"}
// No identity provided, create a verification session
const session = await agentscore.createSession({
  context: 'purchase',
  product_name: 'Premium API Access',
});

return res.status(403).json({
  error: 'identity_required',
  verify_url: session.verify_url,
  session_id: session.session_id,
  poll_secret: session.poll_secret,
  poll_url: session.poll_url,
  agent_instructions: {
    action: 'poll_for_credential',
    steps: [
      'Direct the user to visit verify_url',
      'Poll poll_url with X-Poll-Secret header every 5 seconds',
      'When status is "verified", extract operator_token from response',
      'Retry the original request with X-Operator-Token header',
    ],
  },
});
```

After the operator verifies, the agent polls and receives an `operator_token`. It retries the request; this time assess returns `allow`. The user closes the AgentScore tab; the agent finishes the transaction in the background.

## What's checked

| Check                     | What it verifies                                                                                                                                                                                                                |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `require_kyc`             | Government-issued photo ID via Stripe Identity                                                                                                                                                                                  |
| `require_sanctions_clear` | Operator name not on OFAC/sanctions lists (OpenSanctions @ KYC), AND payment-signer wallet address not on the OFAC SDN crypto list (refreshed hourly from `sdn_advanced.xml`). A hit on either axis flips `decision` to `deny`. |
| `min_age`                 | Age bracket derived from ID (18+ or 21+)                                                                                                                                                                                        |
| `blocked_jurisdictions`   | Country from ID is not in blocked list                                                                                                                                                                                          |
| `allowed_jurisdictions`   | Country from ID is in allowed list                                                                                                                                                                                              |

## AgentScore Gate denial codes

When AgentScore Gate rejects a request before hitting `/v1/assess`, the 403 body uses one of the codes below. **Every code carries a structured `agent_instructions` payload (JSON-encoded `{action, steps, user_message}`)** so agents can recover deterministically from the response alone; no discovery-doc round trip required.

| Code                                  | When                                                                                                                                                                                                                           | `agent_instructions.action` + key fields                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `missing_identity`                    | Neither `X-Wallet-Address` nor `X-Operator-Token` present, and no auto-session was created                                                                                                                                     | `probe_identity_then_session`: try wallet on signing rails → stored `opc_...` → session flow; `agent_memory` cross-merchant bootstrap hint included                                                                                                                                                                                                                                                                                                                                                                                          |
| `identity_verification_required`      | No identity header, but the merchant's gate auto-minted a verification session: body includes `verify_url`, `session_id`, `poll_secret`, `poll_url`                                                                            | `deliver_verify_url_and_poll`: share `verify_url` with the user, poll `poll_url` with `X-Poll-Secret` until status=verified, retry with the returned `opc_...`                                                                                                                                                                                                                                                                                                                                                                               |
| `token_expired`                       | `X-Operator-Token` was valid but is no longer (covers both TTL-expired and explicitly revoked: the API deliberately doesn't disclose which). 401 body carries an auto-minted session so the agent recovers without an API key. | `deliver_verify_url_and_poll`: same flow as `identity_verification_required`; poll returns a fresh `opc_...`                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `invalid_credential`                  | `X-Operator-Token` doesn't match any credential: typo, fabricated, or never minted. Permanent: retrying the same token will keep failing, no auto-session issued.                                                              | `switch_token_or_restart_session`: try a different stored `opc_...`, or drop the header and re-bootstrap via the merchant's `createSessionOnMissing` flow                                                                                                                                                                                                                                                                                                                                                                                    |
| `wallet_signer_mismatch`              | `X-Wallet-Address` was sent but the payment signer resolves to a different operator                                                                                                                                            | `resign_or_switch_to_operator_token`; body also carries `claimed_operator`, `actual_signer_operator`, `expected_signer`, `actual_signer`, `linked_wallets`: re-sign from `expected_signer` (or any `linked_wallets` entry), or switch to `X-Operator-Token`                                                                                                                                                                                                                                                                                  |
| `wallet_auth_requires_wallet_signing` | `X-Wallet-Address` was sent with a rail that has no wallet signature (Stripe SPT, card)                                                                                                                                        | `switch_to_operator_token`: drop `X-Wallet-Address`, retry with `X-Operator-Token`, or use a wallet-signing rail (Tempo MPP, x402 EIP-3009 on Base, or Solana MPP)                                                                                                                                                                                                                                                                                                                                                                           |
| `wallet_not_trusted`                  | `/v1/assess` returned `deny` with an UNFIXABLE policy reason (`sanctions_flagged` / `age_insufficient` / `jurisdiction_restricted`). Body includes `reasons[]`.                                                                | `contact_support`: re-verification won't fix this; surface the merchant's support contact. **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, identical UX to `missing_identity`.** Note: `jurisdiction_restricted` is unfixable because the API only emits it after KYC is verified (the user's KYC'd country is in the merchant's blocked list: re-doing KYC won't change the country). |
| `payment_required`                    | The merchant's AgentScore plan doesn't include `/v1/assess`. Merchant-side misconfig: not recoverable agent-side.                                                                                                              | `contact_merchant`: surface to the merchant via their support channel; no agent-side fix available                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `api_error`                           | AgentScore API failure. Body's `agent_instructions.action` discriminates: `retry_with_backoff` for transient 5xx / network timeout, `contact_merchant` for a 429 (merchant-side issue).                                        | Read `agent_instructions.action`. For `retry_with_backoff`: exponential backoff over 5–30s, surface to user after \~5min sustained. For `contact_merchant`: DO NOT retry; surface the merchant's support contact to the user. Identity headers remain valid in both cases; this is NOT a compliance denial. See [errors › Retryable infra errors](/api-reference/errors#retryable-infra-errors-api_error)                                                                                                                                    |

### Wallet-mode response fields

When `identity_mode: "wallet"`, 402 and 403 bodies also include these fields so agents know exactly which address must sign:

| Field               | Type                             | Description                                                                                                                                                             |
| ------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `identity_mode`     | `"wallet"` \| `"operator_token"` | Which identity path was used                                                                                                                                            |
| `required_signer`   | string                           | Wallet address normalized per network: EVM lowercased, Solana base58 preserved verbatim. The payment MUST be signed by this address or a `linked_wallets` entry.        |
| `linked_wallets`    | string\[]                        | Same-operator sibling wallets, normalized per network. May mix EVM (`0x...`) and Solana (base58) for multi-chain operators; any may sign in place of `required_signer`. |
| `signer_constraint` | string                           | Human-readable explanation of the same-operator rule                                                                                                                    |

## Fail-open behavior (opt-in)

By default AgentScore Gate fails **closed**: any AgentScore-side infrastructure failure (HTTP 429, 5xx, network timeout) returns 503 to the buyer. This is the correct posture for regulated commerce; better to outage than to ship to a sanctioned wallet because our API blipped.

Some merchants (low-stakes commerce, high-uptime SLAs) prefer graceful degradation. Set `failOpen: true` (Node) / `fail_open=True` (Python) to opt in. When opted in AND the failure is infra-shape, the buyer passes through and the gate state carries a `degraded` flag merchants can log/alert on:

<CodeGroup>
  ```ts node-commerce (Hono / Express / Fastify) theme={"dark"}
  import { agentscoreGate, getGateDegradedState } from '@agent-score/commerce/identity/hono';

  const gate = agentscoreGate({ apiKey: process.env.AGENTSCORE_API_KEY!, failOpen: true });

  app.use('/purchase', gate);

  app.post('/purchase', async (c) => {
    const { degraded, infraReason } = getGateDegradedState(c);
    if (degraded) {
      // Compliance was NOT enforced this request. Log, alert, or refund async.
      console.warn(`[gate] degraded: ${infraReason}`);
    }
    // ...
  });
  ```

  ```python python-commerce (FastAPI) theme={"dark"}
  from agentscore_commerce.identity.fastapi import AgentScoreGate, get_gate_degraded_state

  gate = AgentScoreGate(api_key="ask_...", fail_open=True)

  @app.post("/purchase", dependencies=[Depends(gate)])
  async def purchase(request: Request):
      state = get_gate_degraded_state(request)
      if state["degraded"]:
          logger.warning("gate degraded: %s", state["infra_reason"])
      # ...
  ```
</CodeGroup>

`infraReason` / `infra_reason` is one of:

| Value             | When                                                             |
| ----------------- | ---------------------------------------------------------------- |
| `quota_exceeded`  | AgentScore returned 429                                          |
| `api_error`       | AgentScore returned 5xx (transient infra issue)                  |
| `network_timeout` | Request to `/v1/assess` timed out or failed at the network layer |

**`failOpen` does NOT bypass compliance denials.** `sanctions_flagged`, `age_insufficient`, `jurisdiction_restricted`, `wallet_signer_mismatch`, `kyc_required` and other real policy outcomes still return 403 regardless of the flag. `failOpen` only covers "we couldn't reach AgentScore to ask," never "AgentScore said no but we'll allow anyway."

For Web Fetch / Next.js (`createAgentScoreGate` / `withAgentScoreGate`), the `degraded` + `infraReason` fields land directly on the `GuardResult.allowed` variant / handler's `gate` parameter; no separate getter needed.

### Recommendations by vertical

| Vertical                                                                   | Recommended posture                                                     |
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Regulated commerce (alcohol, cannabis, age-gated, sanctioned-jurisdiction) | `failOpen: false` (default): better to outage than to bypass compliance |
| Low-stakes commerce (cheap API calls, content access)                      | `failOpen: true` is reasonable: accept the trade for uptime             |
| Enterprise customers with tight SLAs                                       | `failOpen: true` with `degraded` logging as the audit trail             |

## What sellers see

Sellers receive binary decisions; `allow` or `deny`. You never see the operator's name, address, date of birth, or ID documents. The only data exposed:

* Verification level (none / claimed / verified)
* Whether each policy check passed or failed

```json theme={"dark"}
{
  "decision": "allow",
  "operator_verification": {
    "level": "kyc_verified",
    "operator_type": "individual"
  },
  "policy_result": {
    "all_passed": true,
    "checks": [
      { "rule": "require_kyc", "passed": true },
      { "rule": "require_sanctions_clear", "passed": true },
      { "rule": "min_age", "passed": true, "required": 21 },
      { "rule": "allowed_jurisdictions", "passed": true }
    ]
  }
}
```

## Privacy

* AgentScore does not store ID documents; they are processed by [Stripe Identity](https://stripe.com/identity) and never leave Stripe
* We store derived facts only: verification status, jurisdiction (country code), age bracket, sanctions status
* If our database is breached, attackers see "operator X is verified, US, individual"; no identity data

## Sandbox testing

Use `test: true` with reserved test addresses to simulate compliance scenarios:

```bash theme={"dark"}
# Always allow (verified individual, US, 21+)
curl -X POST https://api.agentscore.com/v1/assess \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"address": "0x0000000000000000000000000000000000000001", "test": true, "policy": {"require_kyc": true, "min_age": 21}}'

# Always deny (unverified)
curl -X POST https://api.agentscore.com/v1/assess \
  -H "X-API-Key: your-api-key" \
  -d '{"address": "0x0000000000000000000000000000000000000002", "test": true, "policy": {"require_kyc": true}}'

# Sanctions flagged
curl -X POST https://api.agentscore.com/v1/assess \
  -H "X-API-Key: your-api-key" \
  -d '{"address": "0x0000000000000000000000000000000000000003", "test": true, "policy": {"require_sanctions_clear": true}}'

# Verified entity (DE jurisdiction)
curl -X POST https://api.agentscore.com/v1/assess \
  -H "X-API-Key: your-api-key" \
  -d '{"address": "0x0000000000000000000000000000000000000004", "test": true, "policy": {"require_kyc": true}}'
```

## Pricing

See [pricing](https://www.agentscore.com/pricing) for plans, quotas, and which tier includes compliance gating.

## Next steps

<CardGroup cols={2}>
  <Card title="POST /v1/assess" icon="shield-check" href="/api-reference/post-assess">
    Full assess endpoint reference.
  </Card>

  <Card title="Sessions" icon="arrows-rotate" href="/api-reference/post-sessions">
    Create verification sessions for agent polling.
  </Card>

  <Card title="Credentials" icon="key" href="/api-reference/credentials">
    Create and manage operator credentials.
  </Card>

  <Card title="AgentScore Passport" icon="id-card" href="/passport">
    How operators verify their identity.
  </Card>
</CardGroup>
