---
title: "Start a plan upgrade"
method: POST
path: "/billing/checkout"
tags: ["Billing"]
---

# Start a plan upgrade

`POST /billing/checkout`

## Request

- Base URL: `https://api.griffnode.com/v1`
- URL: `https://api.griffnode.com/v1/billing/checkout`
- Auth: HTTP bearer

## Request body

- object
  - `tier` 'starter' | 'business' | 'professional' | 'enterprise', required
  - `billing_months` integer
  - `payment_method` string

## Response `201`

Billing transaction created.

- TransactionEnvelope
  - `success` unknown, required
  - `data` Transaction, required: The canonical, curated public view of a transaction (same shape from create, get and list). Internal fields (capability tokens, client_ip, address_index, derivation/db internals) are deliberately NOT exposed.
    - `transaction_id` string, required
    - `status` 'pending' | 'awaiting_confirmation' | 'completed' | 'partial' | 'expired' | 'failed', required: Public transaction status (internal sub-states are mapped to these).
    - `exception` 'paid_late' | 'needs_review': Present only when the payment waits for the merchant's decision. paid_late: coins arrived within 6h after the window closed (status stays expired; complete it with Mark as paid in the dashboard, which also sends payment.completed). needs_review: ETH/ERC-20 deposit address balance dropped while open, payment unknown (status pending).
    - `type` 'basic' | 'detailed'
    - `crypto` 'BTC' | 'LTC' | 'DOGE' | 'DASH' | 'ETH' | 'USDT' | 'USDC' | 'EURC' | 'DAI' | 'WBTC' | 'SHIB' | 'PEPE' | 'LINK' | 'UNI' | 'AAVE' | 'MKR', required: Supported coin/token symbol. /cryptos/list is authoritative at runtime.
    - `deposit_address` string
    - `amount_crypto` number
    - `amount_fiat` number, required
    - `amount_usd` number, required
    - `amount_paid` number
    - `amount_remaining` number
    - `currency_fiat` 'USD' | 'PLN' | 'EUR' | 'GBP', required
    - `fiat_to_usd_rate` number
    - `exchange_rate` number: USD per unit of crypto, locked at creation.
    - `confirmations_required` integer
    - `payment_url` string, uri
    - `order_id` string, nullable
    - `customer_email` string, nullable
    - `items` LineItem[], nullable
      - `name` string, required
      - `quantity` integer
      - `price` number, required
    - `payments` PaymentSplit[]: On-chain payments detected toward this transaction.
      - `txid` string: On-chain transaction hash (blockchain id, NOT the GriffNode transaction_id).
      - `amount_crypto` number
      - `confirmations` integer
      - `status` string
      - `detected_at` string, date-time
      - `confirmed_at` string, date-time, nullable
    - `metadata` Metadata, nullable: Free-form key/value (≤20 keys, string values ≤500 chars, ≤4 KB total).
    - `success_url` string, nullable
    - `cancel_url` string, nullable
    - `created_at` string, date-time, required
    - `updated_at` string, date-time
    - `expires_at` string, date-time, required

## Other responses

- `401`: Missing or invalid API key (UNAUTHORIZED).
- `403`: Key lacks permission — e.g. a publishable key on a secret-only endpoint, or plan too low (FORBIDDEN, USE_PAY_ENDPOINT, PLAN_UPGRADE_REQUIRED, NO_ACTIVE_PLAN).

---

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