# Start a Stripe Checkout top-up

`POST /v1/console/checkout-sessions` (operation id `createCheckoutSession`)

Creates a Stripe Checkout session for a top-up between EUR 10 and EUR 2,500 (at most EUR 500 in total during
an account's first 30 days). The balance is credited with the net amount, excluding VAT, when Stripe reports
the payment as settled.

Authentication: Console session cookie (`__Host-dex_session`). State-changing routes also need the `x-dex-csrf` header.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `x-dex-csrf` | header | string | yes | The `csrf_token` returned by `createSession`. Required on every state-changing console route. (32 to 64 characters) |
| `idempotency-key` | header | string | no | Makes retries safe. Within 24 hours, a repeat with the same key and the same body is never charged again. It answers with `idempotent-replayed: true` and the original response: the same `id`, `created`, `model`, answers and `usage`, including the original charge fields. The same key with a different body answers 409.  (1 to 255 characters; pattern ^[\x21-\x7E]+$) |

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount_cents` | integer | yes | Top-up amount in euro cents, excluding VAT (EUR 10 to EUR 2,500). (1,000 to 250,000) |
| `return_path` | string | no | Console path to return to after checkout. (pattern ^/[A-Za-z0-9/_-]{0,200}$) |

## Responses

### 201

Checkout session created; send the browser to `url`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `object` | string | yes | (always "checkout_session") |
| `url` | string | yes | (format uri) |
| `amount_cents` | integer | yes |  |
| `currency` | string | yes | (always "EUR") |
| `expires_at` | string | yes | (format date-time) |

### 400

The request could not be read. Codes: `invalid_json` (not JSON, not UTF-8, or a `\u` escape that is half of a
surrogate pair, such as `"\ud800"` without its low half), `duplicate_key` (a JSON object
repeats a key; `param` is the JSON pointer of the repeated member, such as `/questions/q1/options/a`),
`unsupported_media_type` (not `application/json`) and `invalid_header` (a malformed `idempotency-key` or
`x-client-request-id`; `param` names the header).


### 401

Missing, unknown, revoked or expired credentials.

### 403

The credentials are valid but not allowed to do this (missing scope, suspended account or failed CSRF check).

### 409

The idempotency key was used with a different body, or the first request with this key is still running.

### 422

The request is well-formed JSON but breaks a validation rule. `param` names the field as a dotted path.
Codes: `unknown_field`, `missing_field`, `invalid_type` (wrong JSON type), `invalid_value` (right type, value
out of range: an empty or over-long string, a state, `instructions`, `criteria` string or option description
of only whitespace, an empty `questions` or `options` object, an empty state object or array, a pattern or
allowed-value mismatch such as a label or level with outer whitespace, a state nested deeper than 32 levels, a
bad date or date range), `field_not_allowed` (`options` or `levels` on the wrong question type),
`invalid_question_id`, `too_many_questions`, `too_many_options`, `invalid_levels` (including two levels equal
after Unicode NFC normalisation and lower-casing), `invalid_min_confidence`, `duplicate_label` (two option
labels equal after Unicode NFC normalisation and lower-casing, such as `Billing` and `billing`),
`state_path_not_found`, `state_not_json` and, on the console, `top_up_limit_exceeded`.


### 429

A rate limit or quota is exhausted. Retry after the given number of seconds. For `test_daily_quota` that is
the time until 00:00 UTC; clients should not retry it automatically.


### 500

An unexpected error. Any charge was refunded. Retry with the same idempotency key.

### 503

`maintenance`: the console's database, sign-in or payments are unavailable for a moment. Retry after
`retry-after`. Nothing was changed.


## Examples

Illustrative values. Examples show the shape of requests and responses; the numbers in them are not measured results.

Request: fifty

```json
{
  "amount_cents": 5000,
  "return_path": "/console/billing"
}
```

Response: session

```json
{
  "id": "cs_live_a1B2c3D4e5F6g7H8",
  "object": "checkout_session",
  "url": "https://checkout.stripe.com/c/pay/cs_live_a1B2c3D4e5F6g7H8",
  "amount_cents": 5000,
  "currency": "EUR",
  "expires_at": "2026-10-20T12:30:00Z"
}
```
