---
title: "Start checkout for a reviewed batch campaign"
method: POST
path: "/campaigns/{id}/checkout"
tags: ["Billing", "Human only"]
---

# Start checkout for a reviewed batch campaign

`POST /campaigns/{id}/checkout`

## Request

- Base URL: `https://gtmswag.com/api/v1`
- URL: `https://gtmswag.com/api/v1/campaigns/{id}/checkout`
- Auth: API key in cookie `gtm_session`

Human only: A signed-in owner or admin does this in the browser at gtmswag.com. API keys and OAuth connections receive 403 HUMAN_APPROVAL_REQUIRED with a `url` to hand to that person. Hand the campaign review URL to them. Starts a Stripe checkout for the reviewed people without a gift; playbooks are paid from the balance instead (409 ALWAYS_ON_BALANCE_FUNDED).

## Path parameters

- `id` string, required

## Request body

- object
  - `expectedPolicyVersion` integer, required: The campaign's current policyVersion; a stale value returns POLICY_CHANGED.
  - `idempotencyKey` string, required: Same key for identical retries only.

## Response `201`

Success: `{"data": Checkout}`.

- CheckoutResponse: The success body: `{"data": Checkout}`.
  - `data` Checkout, required: A campaign checkout with its state, amounts and invoice links.
    - `id` string, required
    - `campaignId` string, required
    - `state` 'creating' | 'open' | 'paid' | 'consumed' | 'expired' | 'cancelled', required
    - `amountCents` integer, required
    - `remainingCount` integer, required

## Other responses

- `400`: Bad request: `VALIDATION_ERROR`, `INVALID_JSON`. Each example says when.
- `401`: Not authenticated: `AUTH_REQUIRED`. no credentials, or an OAuth token on a route that only takes workspace keys; the key is malformed, unknown or revoked.
- `403`: Forbidden: `HUMAN_APPROVAL_REQUIRED`, `TEAM_MANAGER_REQUIRED`. Each example says when.
- `404`: Not found: `NOT_FOUND`. Each example says when.
- `409`: Conflict: `ALWAYS_ON_BALANCE_FUNDED`, `IDEMPOTENCY_CONFLICT`, `BUDGET_EXCEEDED`, `COUNT_EXCEEDED`, `REPEAT_REVIEW_REQUIRED`, `POLICY_CHANGED`, `CAMPAIGN_INACTIVE`, `NO_NEW_RECIPIENTS`, `CHECKOUT_LOCKED`, `GIFT_RETIRED`, `GIFT_UNAVAILABLE`, `PRICE_QUOTE_REQUIRED`. Each example says when.
- `413`: Request too large: `INVALID_REQUEST`. the body exceeds 1 MiB.
- `415`: Unsupported media type: `INVALID_REQUEST`. a write with a body that is not `Content-Type: application/json` (a key request with no body needs no Content-Type).
- `429`: Too many requests: `RATE_LIMIT`. more than 240 requests in the current minute for this workspace (REST and MCP share the limit); wait `retryAfterMs`.
- `503`: Unavailable: `SANDBOX_CHECKOUT_DISABLED`, `PREPAID_DISABLED`, `STRIPE_UNAVAILABLE`. Each example says when.
- `default`: Any other refusal or failure, such as 403 CSRF (a cross-origin `Origin` header), 403 HTTPS_REQUIRED or 500 INTERNAL_ERROR, with what to do next.

---

[API](https://skmtc.dev/gtmswag/apis/gtm-swag-api.md) · [All operations](https://skmtc.dev/gtmswag/apis/gtm-swag-api/llms.txt) · [OpenAPI document](https://skmtc.dev/gtmswag/apis/gtm-swag-api/revisions/9ff86fa99afe?raw)
