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

# POST /v1/sessions

> Create a verification session for identity bootstrapping. Paid tier required.

<Warning>
  This endpoint requires a **paid plan** ([pricing](https://agentscore.sh/pricing)). Free-plan requests receive HTTP 402.
</Warning>

## 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 (paid or enterprise tier) |
| `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) |

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

```json theme={"dark"}
{
  "session_id": "sess_abc123...",
  "poll_secret": "poll_xyz789...",
  "verify_url": "https://agentscore.sh/verify?session=sess_abc123...",
  "poll_url": "https://api.agentscore.sh/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                                                                                                                                                                                  |
| `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 |

## 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.sh",
    "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.sh 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.
