---
title: "Buy a credit pack within the owner's limits: charge the saved card off-session when allowed, else hand a person a Checkout link (next_action). Requires Idempotency-Key; a replay answers 200 with replayed true and Idempotent-Replayed: true; refusals leave no purchase. 10 per key and 20 per account per hour, per livemode"
method: POST
path: "/v1/billing/purchases"
---

# Buy a credit pack within the owner's limits: charge the saved card off-session when allowed, else hand a person a Checkout link (next_action). Requires Idempotency-Key; a replay answers 200 with replayed true and Idempotent-Replayed: true; refusals leave no purchase. 10 per key and 20 per account per hour, per livemode

`POST /v1/billing/purchases`

## Request

- Base URL: `https://api.roveapi.com`
- URL: `https://api.roveapi.com/v1/billing/purchases`
- Auth: HTTP bearer

## Headers

- `Idempotency-Key` string, required

## Request body

- object
  - `item_id` string, required: A current catalogue pack id, e.g. pack_1k (GET /v1/billing/catalogue).
  - `payment` 'auto' | 'checkout' | 'saved_method': auto (default): charge the saved card when the owner allowed it, else hand a person a Checkout link; checkout: always a link; saved_method: only the saved card (refused with suggested_payment auto when not eligible).

## Response `200`

Idempotency-Key replay: the purchase as it is now (reconciled with Stripe first when it was still created after 2 minutes)

- object
  - `id` string, uuid, required
  - `object` 'purchase', required
  - `status` 'created' | 'open' | 'requires_action' | 'processing' | 'paid' | 'refunded' | 'disputed' | 'reversed' | 'expired' | 'canceled' | 'failed', required
  - `livemode` boolean, required
  - `actor` 'owner_web' | 'api_key' | 'auto_topup' | 'legacy_link', required
  - `api_key_id` string, uuid, nullable, required
  - `api_key_name` string, nullable, required
  - `replayed` boolean, required
  - `channel` 'checkout' | 'off_session', required
  - `item` object, required
    - `id` string, required
    - `version` integer, required
    - `name` string, required
  - `amount_cents` integer, required
  - `currency` 'usd', required
  - `credits` integer, required: Whole credits the pack grants.
  - `amount_refunded_cents` integer, required
  - `millicredits_reversed` integer, required
  - `millicredits_held` integer, required
  - `next_action` object, nullable, required
    - `type` 'open_checkout_url', required
    - `url` string, required
    - `expires_at` string, date-time, required
    - `reason` 'checkout_requested' | 'no_saved_method' | 'off_session_not_allowed' | 'authentication_required' | 'card_declined', required
    - `instruction` string, required
  - `retry` 'same_key' | 'new_key', nullable, required
  - `failure_code` string, nullable, required
  - `created_at` string, date-time, required
  - `paid_at` string, date-time, nullable, required

## Other responses

- `201`: Successful response (Cache-Control: no-store)
- `400`: RFC 7807 idempotency-key-required, or invalid-params (body or a malformed Idempotency-Key)
- `401`: RFC 7807 invalid-api-key
- `402`: RFC 7807 payment-failed: the saved card was declined under payment 'saved_method', or Stripe refused the charge; the purchase is failed; retry new_key
- `403`: RFC 7807 purchase-not-permitted (reason agent_purchasing_off or key_not_granted with owner_action_url, retry none; or no_saved_method / off_session_not_allowed with suggested_payment auto, retry same_key) or spend-limit-exceeded
- `404`: RFC 7807 pack-not-found
- `409`: RFC 7807 idempotency-in-progress: the account's other purchase holds the lock; Retry-After: 1, retry same_key
- `422`: RFC 7807 idempotency-key-reused: the key was used for a different request; retry new_key
- `429`: RFC 7807 purchase-rate-limited (Retry-After until the oldest counted purchase leaves the hour; retry same_key) or rate-limit-exceeded (plan limiter)
- `503`: RFC 7807 billing-unavailable: Stripe unconfigured or unreachable; purchase_id when the row was kept created (a same-key retry reconciles it and never charges twice); retry same_key

---

[API](https://skmtc.dev/roveapi/apis/rove-api.md) · [All operations](https://skmtc.dev/roveapi/apis/rove-api/llms.txt) · [OpenAPI document](https://skmtc.dev/roveapi/apis/rove-api/revisions/0e5d69bde586?raw)
