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

# POST /v1/sessions

> Create a verification session for identity bootstrapping. Available on all tiers, gated by monthly quota.

<Info>
  This endpoint is available on all tiers, gated by monthly quota ([pricing](https://www.agentscore.com/pricing)).
</Info>

## Overview

Verification sessions let merchants bootstrap identity for agents that don't have a wallet or operator credential. When an agent tries to perform a gated action without identity, the merchant creates a session and returns the verification URL to the agent.

The session returns two secrets:

* **`session_id`**: goes in the verification URL (user-facing)
* **`poll_secret`**: given to the agent for polling (never in URLs)

## Request

### Headers

| Header | Required | Description |
| - | - | - |
| `X-API-Key` | Yes | Your API key (any tier, including Free) |
| `Content-Type` | Yes | `application/json` |

### Body

```json theme={"dark"}
{
  "context": "wine_purchase",
  "product_name": "2022 Martin Estate Rose"
}
```

| Field | Type | Required | Description |
| - | - | - | - |
| `context` | string | No | Freeform context label (e.g., "wine\_purchase") |
| `product_name` | string | No | Product name shown during verification (max 200 chars) |
| `kind` | string | No | `"kyc"` (default) runs full identity verification. `"sign_in"` is registration-only: the user signs in with their AgentScore account, no identity documents, and the session mints a `sign_in` credential that identifies the account but passes no compliance policy. |

The merchant name shown on the verify page is derived from your account's merchant name (set in dashboard Settings), not from the request.

Verification is agent-driven: after the user completes KYC they close the tab, and the requesting agent retrieves the credential by polling `GET /v1/sessions/:token`. There is no browser redirect back to the merchant; this API does not accept `return_url` or any payment-method metadata.

## Response (201)

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "poll_secret": "poll_xyz789...",
  "kind": "kyc",
  "verify_url": "https://www.agentscore.com/verify?session=sess_abc123...",
  "poll_url": "https://api.agentscore.com/v1/sessions/sess_abc123...",
  "expires_at": "2026-04-09T13:00:00Z",
  "next_steps": {
    "action": "deliver_verify_url_and_poll",
    "poll_interval_seconds": 5,
    "poll_secret_header": "X-Poll-Secret",
    "steps": [
      "Deliver verify_url to the end user so they can complete identity verification in the browser.",
      "Poll poll_url every 5 seconds, sending poll_secret in the X-Poll-Secret header.",
      "When status becomes verified, the operator_token is returned in that poll response exactly once, save it and retry the original merchant request with X-Operator-Token."
    ],
    "user_message": "Share verify_url with the user so they can verify identity, then poll poll_url with X-Poll-Secret until an operator_token is issued."
  },
  "agent_memory": {
    "pattern_summary": "...",
    "identity_check_endpoint": "...",
    "identity_paths": { "...": "..." },
    "bootstrap": { "...": "..." },
    "do_not_persist_in_memory": ["operator_token", "poll_secret"],
    "persist_in_credential_store": ["operator_token"]
  }
}
```

Sessions expire after 1 hour.

### Response fields

| Field | Type | Description |
| - | - | - |
| `session_id` | string | Opaque session identifier, shown in `verify_url` (user-facing) |
| `poll_secret` | string | Agent-only secret for the `X-Poll-Secret` header when polling |
| `kind` | string | `kyc` or `sign_in`, as requested |
| `verify_url` | string | Ready-to-open URL the end user visits to complete KYC |
| `poll_url` | string | Polling endpoint for session status |
| `expires_at` | string | ISO 8601 session expiry (1 hour TTL) |
| `next_steps` | object | Structured agent instructions for consuming the session (`action: "deliver_verify_url_and_poll"`, polling cadence, step-by-step) |
| `agent_memory` | object | Cross-merchant pattern hint: LLM-hosted agents should persist the pattern (never secrets) so future AgentScore merchants skip the cold-start flow. Also emitted on `POST /v1/credentials` success and on first-mint `missing_identity` denials |

### Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `bad_request` | `kind` is set to something other than `kyc` or `sign_in` |
| 401 | `signup_required` | Missing or invalid `X-API-Key` |
| 402 | `passport_quota_exceeded` | The Free plan's monthly Passport verifications are used up (see [pricing](https://www.agentscore.com/pricing)); paid plans have no cap |

## Polling

```
GET /v1/sessions/{session_id}
X-Poll-Secret: poll_xyz789...
```

| Status | Meaning |
| - | - |
| `pending` | User hasn't completed verification yet |
| `verified` | User verified: `operator_token` included in response |
| `consumed` | Token already issued to a previous poll |
| `expired` | Session expired (1 hour TTL) |
| `failed` | Stripe Identity verification did not succeed (unreadable ID, selfie mismatch, etc.) |
| `flagged` | Identity verified but sanctions screening flagged a potential match: credential issuance is blocked pending manual review |

### Poll response by status

**Pending**; user hasn't completed verification yet:

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "status": "pending",
  "retry_after_seconds": 5,
  "next_steps": {
    "action": "continue_polling",
    "poll_interval_seconds": 5,
    "eta_message": "Users typically complete identity verification in 2-5 minutes. Continue polling every 5 seconds until status becomes verified, consumed, or expired."
  }
}
```

**Verified**; user verified, one-time `operator_token` included:

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "status": "verified",
  "operator_token": "opc_newtoken...",
  "completed_at": "2026-04-09T12:30:00Z",
  "token_ttl_seconds": 86400,
  "next_steps": {
    "action": "retry_merchant_request_with_operator_token",
    "header_name": "X-Operator-Token",
    "user_message": "Verification complete. Retry the original merchant request with X-Operator-Token set to the operator_token value."
  }
}
```

The token is returned exactly once. Subsequent polls return `consumed` without the token.

**Consumed**; token already issued to a previous poll:

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "status": "consumed",
  "next_steps": {
    "action": "use_stored_operator_token",
    "user_message": "The operator_token was already delivered on a previous poll. If you did not save it, ask the merchant to create a new session."
  }
}
```

**Expired**; session expired (1 hour TTL):

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "status": "expired",
  "next_steps": {
    "action": "create_new_session",
    "user_message": "This verification session has expired."
  }
}
```

**Failed**; Stripe Identity verification did not succeed:

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "status": "failed",
  "next_steps": {
    "action": "verification_failed",
    "user_message": "Identity verification was not successful. The user may need to try again with a clearer photo or different ID. Create a new purchase to start a fresh verification session."
  }
}
```

**Flagged**; identity verified but sanctions screening flagged a potential match. The user must contact support before a credential can be issued:

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "status": "flagged",
  "next_steps": {
    "action": "contact_support",
    "support_email": "support@agentscore.com",
    "support_subject": "Sanctions screening dispute",
    "user_message": "Identity was verified, but sanctions screening flagged a potential match. Credentials cannot be issued automatically. If you believe this is in error, contact support@agentscore.com to dispute."
  }
}
```

### Poll response fields

| Field | Type | When present | Description |
| - | - | - | - |
| `next_steps` | object | Always | Action guidance that varies by status |
| `retry_after_seconds` | number | `pending` | Recommended poll interval in seconds |
| `token_ttl_seconds` | number | `verified` | TTL of the issued operator token in seconds |

## Rate limits

The poll endpoint is rate-limited to 30 requests per minute per IP. Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.