---
title: "Create a Checkout Session on the organization's Stripe account"
method: POST
path: "/projects/v1/{project_id}/stripe/checkout-sessions"
tags: ["Projects"]
---

# Create a Checkout Session on the organization's Stripe account

`POST /projects/v1/{project_id}/stripe/checkout-sessions`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/projects/v1/{project_id}/stripe/checkout-sessions`
- Auth: one of:
  - HTTP bearer
  - API key in header `SIGN-IN-WITH-X`
  - HTTP bearer
  - HTTP bearer

A Stripe Checkout Session created ON the connected account as a direct charge: mode subscription or payment; line_items each an existing price id on the account or inline price_data (currency, unit_amount in the minor unit, product_name, and recurring {interval, interval_count} in subscription mode); success_url and cancel_url on the project's own hosts (the onboarding-link URL rule); optional customer or customer_email, client_reference_id, metadata (keys beginning run402_ are reserved), automatic_tax {enabled} (the organization's own Stripe Tax registrations apply), subscription {trial_period_days, metadata}, locale, allow_promotion_codes, expires_at. There is no payment-method selection: the organization's own Stripe payment-method settings apply (cards, SEPA Direct Debit where enabled). Run402 stamps run402_project_id, run402_org_id and run402_checkout_id into the session's (and the subscription's) metadata so the Stripe events that follow attribute to this project. The outcome arrives as stripe_checkout_completed (and stripe_invoice_paid for a subscription) on the project events feed, or read it with GET .../checkout-sessions/:checkout_session_id. The session url is never logged or cached. Refusals: 403 STRIPE_ACCOUNT_REQUIRED / STRIPE_ACCOUNT_ONBOARDING_INCOMPLETE (details.requirements) / STRIPE_ACCOUNT_RESTRICTED (details.disabled_reason), 422 VALIDATION_FAILED naming the path (a subscription item without recurring, a payment item with it, reserved metadata), 422 STRIPE_RETURN_URL_NOT_ALLOWED, 502 STRIPE_UPSTREAM_ERROR with Stripe's code and param. Behind the lifecycle gate. Direct charges only: the organization is the merchant of record, Stripe bills it for processing and dispute fees from its own balance, Run402 takes no application fee (platform_fee_bps 0) and never holds, moves, or refunds the money. Refunds and disputes live in the organization's own Stripe dashboard. 503 STRIPE_CONNECT_UNAVAILABLE when the platform's Stripe configuration is absent or Stripe is unreachable.

## Path parameters

- `project_id` string, required: Run402 project ID. Public IDs are not UUIDs.

## Headers

- `SIGN-IN-WITH-X` string, required
- `Idempotency-Key` string

## Request body

- StripeCheckoutSessionCreateRequest
  - `mode` 'subscription' | 'payment', required
  - `line_items` StripeCheckoutLineItem[], required
    - `price` string: An existing price on the organization's account. Not with price_data.
    - `quantity` integer
    - `price_data` object
      - `currency` string, required: Lowercase ISO 4217 (eur, usd). SEPA Direct Debit needs eur.
      - `unit_amount` integer, required: In the currency's minor unit (12000 = €120.00).
      - `product_name` string, required
      - `recurring` object: Required in mode subscription, refused in mode payment.
        - `interval` 'day' | 'week' | 'month' | 'year', required
        - `interval_count` integer
  - `success_url` string, uri, required: HTTPS on one of the project's own hosts or the console (http://localhost in test mode).
  - `cancel_url` string, uri, required
  - `customer` string: An existing customer on the organization's account; not with customer_email.
  - `customer_email` string, email
  - `client_reference_id` string: Your own id for the payer (for example member:42); echoed on every event about the session and its subscription.
  - `metadata` object: Up to 40 string values; keys beginning run402_ are reserved.
  - `automatic_tax` object: Stripe Tax under the organization's own registrations; off unless the organization set them up.
    - `enabled` boolean
  - `subscription` object: Mode subscription only.
    - `trial_period_days` integer
    - `metadata` object
  - `locale` string: A Checkout locale (de, fr-FR, auto).
  - `allow_promotion_codes` boolean
  - `expires_at` string, date-time: Between 30 minutes and 24 hours from now.

## Response `200`

Idempotency-Key replay: the original session (deduplicated true).

- StripeCheckoutSession
  - `checkout_session_id` string, required
  - `url` string, uri, required: Send the payer here. One-time; never cached.
  - `expires_at` string, date-time, nullable
  - `mode` 'subscription' | 'payment', required
  - `account_id` string, required
  - `project_id` string, required: Run402 project ID. Public IDs are not UUIDs.
  - `status` 'open', required
  - `deduplicated` boolean, required: true when an Idempotency-Key replay returned the original session.
  - `next_actions` NextAction[], required
    - `type` string, required: Advisory machine-readable action type such as retry, authenticate, submit_payment, fund_wallet, renew_tier, check_usage, resume_deploy, edit_request, edit_migration, contact_support, upgrade_client, poll (poll the project events feed — appended when the envelope carries a correlated_platform_incident), or hand_to_member (on a commit/promote response that just activated a public site: show the human urls.site and urls.console, relay Run402's offer to promote the build on @run402com, and ask yes or no — see `credited_as` below).
    - `method` string
    - `path` string
    - `auth` string
    - `why` string
    - `credited_as` string, nullable: `hand_to_member` only. The caller's own live presence name in the project's default room, else that room's most recently active live presence, else `null` — in which case `why` tells the agent that joining the room with a name (`run402 rooms join --name ...`) is how the promotion credits it.
    - `body` object: `hand_to_member` only. A ready-to-send `POST /feedback/v1` template: `{ project_id, message: "promote: yes", handle? }`.

## Other responses

- `201`: Session created.
- `403`: STRIPE_ACCOUNT_REQUIRED, STRIPE_ACCOUNT_ONBOARDING_INCOMPLETE, STRIPE_ACCOUNT_RESTRICTED, not authorized, or the lifecycle gate.
- `409`: IDEMPOTENCY_KEY_REUSED (different body) or IDEMPOTENCY_KEY_IN_PROGRESS (retry).
- `422`: VALIDATION_FAILED (details.field) or STRIPE_RETURN_URL_NOT_ALLOWED (details.field, details.allowed_hosts).
- `502`: STRIPE_UPSTREAM_ERROR (details.code, details.param).
- `503`: STRIPE_CONNECT_UNAVAILABLE.

---

[API](https://skmtc.dev/run402/apis/run402-api.md) · [All operations](https://skmtc.dev/run402/apis/run402-api/llms.txt) · [OpenAPI document](https://skmtc.dev/run402/apis/run402-api/revisions/893607557f50?raw)
