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

# Store a Stripe Checkout Session

> Bind a Stripe Checkout Session to the current Biqli click before payment.

Use this browser endpoint immediately after your server creates Stripe Checkout and before redirecting or mounting Checkout. It stores attribution only. It does not create a sale and does not prove payment.

```http theme={null}
POST https://biq.li/api/v1/integrations/stripe/checkout-sessions
```

The `@biqli/analytics` method `storeStripeSession()` is the recommended client. It reads the SDK's current click ID, adds a fresh idempotency key, and sends the authenticated request.

## SDK request

```ts theme={null}
const result = await biqli.storeStripeSession({
  stripe_account_id: 'acct_example123',
  stripe_environment: 'live',
  stripe_session_id: 'cs_live_example123',
  customer_email: 'buyer@example.com',
});

window.location.assign(checkoutUrl);
```

If you loaded the global browser script:

```js theme={null}
const result = await window.Biqli.storeStripeSession({
  stripe_account_id: 'acct_example123',
  stripe_environment: 'sandbox',
  stripe_session_id: 'cs_test_example123',
});
```

The SDK uses the stored `bq_id`. You can pass `bq_id` explicitly only when your application already obtained and validated the click ID through the same browser attribution flow.

## Authentication and origin

Send the workspace publishable key as a bearer token:

```http theme={null}
Authorization: Bearer biqli_pk_xxxxxxxxx
```

The request must come from a valid browser `Origin` whose hostname is allowed in the workspace tracking settings. The publishable key, allowed hostname, click, and Stripe connection must all belong to the same workspace.

Do not use a secret workspace API key for this browser endpoint. Never expose a Stripe secret or restricted key in the request.

## Headers

| Header                 | Required    | Description                                                                       |
| :--------------------- | :---------- | :-------------------------------------------------------------------------------- |
| `Authorization`        | Yes         | `Bearer` plus an active `biqli_pk_...` publishable key.                           |
| `Origin`               | Yes         | Browser-supplied origin. Its hostname must be allowed for the workspace.          |
| `Content-Type`         | Yes         | Must be `application/json`.                                                       |
| `Accept`               | Recommended | Use `application/json`.                                                           |
| `Idempotency-Key`      | Yes         | From 1 to 255 printable characters. The browser SDK generates one for every call. |
| `X-Biq-Client`         | Recommended | Client name for diagnostics.                                                      |
| `X-Biq-Client-Version` | Recommended | Client version for diagnostics.                                                   |
| `X-Biq-Request-Id`     | Recommended | Caller-generated safe request identifier. Biqli also returns its request ID.      |

The JSON request body cannot exceed 8,192 bytes.

## Request fields

The endpoint rejects unknown fields.

| Field                | Type                            | Required       | Description                                                                                                                                       |
| :------------------- | :------------------------------ | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |
| `stripe_account_id`  | `string`                        | Yes            | Connected Stripe account ID matching `acct_...`.                                                                                                  |
| `stripe_environment` | `'live' \| 'test' \| 'sandbox'` | Conditional    | Selects the connection environment. Required when the same account reference has more than one eligible active mode. Supplying it is recommended. |
| `stripe_session_id`  | `string`                        | Recommended    | Exact `cs_live_...` or `cs_test_...` Checkout Session ID created by your server. Its mode must match the connection.                              |
| `customer_email`     | `string`                        | No             | RFC-valid email up to 254 characters. Used only for safe identity enrichment or the bounded unique-email fallback.                                |
| `bq_id`              | `string`                        | Yes at the API | Valid Biqli click ID, up to 100 URL-safe identifier characters. The SDK supplies its current stored click when omitted from the method input.     |

### Exact mapping request

```bash theme={null}
curl --request POST \
  --url https://biq.li/api/v1/integrations/stripe/checkout-sessions \
  --header "Authorization: Bearer $BIQLI_PUBLISHABLE_KEY" \
  --header 'Origin: https://app.example.com' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: stripe-checkout-attempt-1842' \
  --data '{
    "stripe_account_id": "acct_example123",
    "stripe_environment": "live",
    "stripe_session_id": "cs_live_example123",
    "customer_email": "buyer@example.com",
    "bq_id": "fGQimsCLcpoa"
  }'
```

Use the raw request only for debugging a browser-equivalent request. A server without a browser `Origin` is rejected by design.

### Pending fallback request

You can omit `stripe_session_id` when it is not yet available:

```json theme={null}
{
  "stripe_account_id": "acct_example123",
  "stripe_environment": "sandbox",
  "customer_email": "buyer@example.com",
  "bq_id": "fGQimsCLcpoa"
}
```

This creates a short-lived pending candidate. Biqli later accepts it only when the customer email matches exactly one eligible, unconsumed candidate. Multiple candidates are ambiguous and fail closed. Prefer the exact mapping request.

## Response

A new registration returns `201 Created`:

```json theme={null}
{
  "checkout": {
    "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
    "status": "pending",
    "expiresAt": "2026-12-26T10:30:00+00:00"
  },
  "request_id": "7ed1e7ea-8e49-4d4f-9eb8-f3221fc54251"
}
```

An existing compatible registration can return `200 OK`. `checkout.id` is Biqli's public registration ID, not the Stripe Checkout Session ID.

| Response field       | Meaning                                                                                                                                        |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| `checkout.id`        | Public Biqli checkout-attribution registration ID.                                                                                             |
| `checkout.status`    | Mapping state: `pending`, `consumed`, `awaiting_payment`, `paid`, or `payment_failed`. A normal pre-Checkout registration starts as `pending`. |
| `checkout.expiresAt` | UTC expiration of this attribution registration.                                                                                               |
| `request_id`         | Biqli request identifier for diagnostics. Also returned in `X-Biq-Request-Id`.                                                                 |

Exact Checkout mappings default to a 90-day eligibility window. Pending registrations without a Session ID default to 24 hours. Treat these as upper bounds, not a reason to delay payment.

## Idempotency

The endpoint requires `Idempotency-Key`.

* Repeating the same key and canonical request returns the original response and adds `Idempotency-Replayed: true`.
* Reusing the key with different request data returns `409 idempotency_conflict`.
* Attempting to bind an existing Stripe Checkout identity to another click returns `409 stripe_attribution_conflict`.
* Idempotency records are retained for seven days.

The browser SDK creates a new idempotency key per method call. If your UI retries after an unknown network outcome and you need response replay, call the API with a stable attempt key or retain the first successful response in your application state.

## Errors

Errors use Biqli's structured API error format unless a readiness failure occurs before the controller.

| Status | Code                           | Meaning                                                                                |
| :----- | :----------------------------- | :------------------------------------------------------------------------------------- |
| `401`  | `invalid_token`                | Publishable key is missing, invalid, or revoked.                                       |
| `403`  | `origin_required`              | No valid browser `Origin` was supplied.                                                |
| `403`  | `hostname_not_configured`      | The workspace has no allowed hostname.                                                 |
| `403`  | `origin_not_allowed`           | The request hostname is not allowed for this workspace.                                |
| `403`  | `tracking_disabled`            | Client conversion tracking is disabled or unavailable.                                 |
| `405`  | `method_not_allowed`           | The endpoint only accepts `POST`.                                                      |
| `409`  | `idempotency_conflict`         | The idempotency key was reused with different data.                                    |
| `409`  | `stripe_account_not_connected` | No active connection matches the workspace and Stripe account.                         |
| `409`  | `stripe_environment_required`  | More than one active mode is eligible; specify the environment.                        |
| `409`  | `stripe_attribution_conflict`  | The Stripe Checkout identity is already linked to another click.                       |
| `422`  | `idempotency_key_required`     | No `Idempotency-Key` header was supplied.                                              |
| `422`  | `invalid_click_id`             | The click is invalid, expired, inactive, or belongs to another workspace.              |
| `422`  | `stripe_mode_mismatch`         | The `cs_live_...` or `cs_test_...` ID conflicts with the connection mode.              |
| `422`  | `validation_error`             | JSON, body size, field names, types, formats, or idempotency-key syntax are invalid.   |
| `429`  | `rate_limit_exceeded`          | This publishable key and IP exceeded the client tracking limit. Respect `Retry-After`. |
| `503`  | —                              | Stripe App connection handling is temporarily unavailable.                             |

The client tracking limit is 120 requests per 60 seconds for a publishable-key and IP pair. Responses include standard and `X-RateLimit-*` diagnostic headers.

## SDK errors

The browser method throws `BiqliError`. Two client-side errors can occur before an HTTP request:

* `consent_required` when browser tracking consent is disabled;
* `missing_click_id` when neither an explicit nor stored valid click ID exists.

Handle them before opening Checkout:

```ts theme={null}
try {
  await biqli.storeStripeSession(input);
  window.location.assign(checkoutUrl);
} catch (error) {
  // Keep the customer on your page and offer a safe retry.
  showCheckoutAttributionError(error);
}
```

Read [Implement Stripe Checkout](/developers/integrations/stripe/checkout) for the complete server and browser sequence.
