---
title: "Charge the saved card for credits, or start Stripe Checkout"
method: POST
path: "/billing/team/{team_id}/purchase-credits"
tags: ["billing"]
---

# Charge the saved card for credits, or start Stripe Checkout

`POST /billing/team/{team_id}/purchase-credits`

Charge the saved card, or start Checkout when that is not possible.

Teams with a default payment method are charged in place and credits are
granted here. Teams without a usable card, or whose bank requires a 3DS
challenge, still go through hosted Checkout; the
``checkout.session.completed`` webhook applies those credits.

Args:
    request: Raw request, required by the rate limiter.
    team_id: Team the credits are bought for.
    body: Purchase amount, attempt id, and the Checkout return URLs.
    auth: Caller identity; payment enforcement is off because this route is
        how a team with no funds acquires them.

Returns:
    ``checkout_url`` set when the buyer must finish on Stripe; ``None``
    when the saved card was charged and credits are already on the balance.

Raises:
    HTTPException: 403 without MANAGE_BILLING; 400 when a Stripe customer
        cannot be resolved; 402 when the saved card is declined; 500 when
        payment succeeded but the grant failed; 502 or 503 when Stripe
        cannot start a session.

## Path parameters

- `team_id` string, required

## Request body

- PurchaseCreditsRequest — Request to buy credits, in place on the saved card or via Checkout.
  - `amount_usd` number, required — Amount in USD to purchase (max $500 per transaction)
  - `success_url` string, required — URL Stripe redirects to after a successful payment
  - `cancel_url` string, required — URL Stripe redirects to if the purchase is cancelled
  - `attempt_id` string, nullable — Caller-generated id for this purchase attempt, stable across retries of it (e.g. a UUID). Guarantees the card is charged once per attempt; omit and same-minute retries are collapsed instead.

## Response `200`

Successful Response

- PurchaseCreditsResponse — Response after a credit purchase attempt. ``checkout_url`` is set when the buyer must finish on Stripe Checkout. It is ``None`` when a saved card was charged and credits are already granted.
  - `success` boolean, required
  - `checkout_url` string, nullable — Stripe-hosted Checkout URL to redirect the user to
  - `message` string — Human-readable status detail

## Other responses

- `422` — Validation Error

## Changes

- **2026-09-24** `1cffaad2a921` — 1 warning, 2 info
  - removed the optional property `detail` from the response with the `422` status
  - added the new optional request property `attempt_id`
  - added the required property `error` to the response with the `422` status

[Change history](https://skmtc.dev/pioneer/apis/brain-api/changes/billing/team/:team_id/purchase-credits/post.md)

---

[API](https://skmtc.dev/pioneer/apis/brain-api.md) · [All operations](https://skmtc.dev/pioneer/apis/brain-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pioneer/apis/brain-api/revisions/1cffaad2a921?raw)
