Gallery Platform API

Invoicing & payments

Create invoices through the API and collect payment on the gallery's Stripe — get a hosted checkout link in the create response and settle via webhooks.

You can issue invoices and get paid entirely through /api/v1. Buyers pay on a Stripe-hosted checkout page on the gallery's own connected account, so the gallery is the merchant of record and your integration never touches card data. This is the Shopify-style pattern: you orchestrate, the platform is the system of record and the payment rail.

The flow

  1. Create a sale — an invoice always belongs to a sale (POST /gallery/sales).
  2. Issue an invoice for that sale with create_checkout_link: true (POST /gallery/invoices). The response includes a hosted checkout_url.
  3. Send the buyer to checkout_url to pay.
  4. Receive webhookspayment.recorded when a payment settles and invoice.paid once the invoice is fully paid (see Webhooks).

Issue and collect in one call

curl -X POST https://app.example.com/api/v1/gallery/invoices \
  -H "Authorization: Bearer $GALLERY_API_KEY" \
  -H "X-Gallery-Slug: $GALLERY_SLUG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sale_id": "…",
    "currency": "USD",
    "total_cents": 250000,
    "create_checkout_link": true
  }'

The response is the invoice plus a checkout_url:

{
  "id": "…",
  "invoice_number": "INV-0007",
  "total_cents": 250000,
  "amount_paid_cents": 0,
  "currency": "USD",
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_…"
}

Redirect the buyer to checkout_url. When they pay, the platform records the payment against the invoice and fires your webhooks — no polling required.

When checkout_url is null

The link is minted only when the gallery can actually collect. checkout_url is null if:

  • the gallery hasn't connected Stripe (or charges aren't enabled yet), or
  • the invoice has no outstanding balance (e.g. you created a header and will add line items in later calls, so the total is still 0).

In that second case, build up the invoice with POST /gallery/invoices/{id}/line-items, then mint a link on demand with POST /gallery/invoices/{id}/payment-sessions — the same hosted checkout, minted whenever you're ready.

Payment plans (installments)

Collect an invoice in stages — a deposit plus scheduled installments. Plan the installments in one call; the plan's total may not exceed the invoice total, and each installment is collected on its own hosted checkout.

# Plan a deposit + two installments
curl -X POST https://app.example.com/api/v1/gallery/invoices/$INVOICE_ID/payment-schedule \
  -H "Authorization: Bearer $GALLERY_API_KEY" \
  -H "X-Gallery-Slug: $GALLERY_SLUG" \
  -H "Content-Type: application/json" \
  -d '{
    "installments": [
      { "label": "Deposit",        "amount_cents": 100000, "due_date": "2026-08-01" },
      { "label": "Installment 1",  "amount_cents":  75000, "due_date": "2026-09-01" },
      { "label": "Installment 2",  "amount_cents":  75000, "due_date": "2026-10-01" }
    ]
  }'

Each installment comes back with an id, a sequence (0 = deposit), and a status (scheduledpaid when it settles). To collect one, mint a checkout link for it:

curl -X POST \
  https://app.example.com/api/v1/gallery/invoices/$INVOICE_ID/payment-schedule/$SCHEDULE_ID/collect \
  -H "Authorization: Bearer $GALLERY_API_KEY" \
  -H "X-Gallery-Slug: $GALLERY_SLUG" \
  -H "Idempotency-Key: $(uuidgen)"

Send the buyer to the returned url. When they pay, the platform records the payment against the invoice, marks that installment paid, and fires your payment.recorded webhook — the same settlement path as a one-shot checkout.

GET …/payment-schedule lists the plan; DELETE …/payment-schedule/{scheduleId} cancels an unpaid installment. Re-planning is refused once any installment has been paid.

Scopes

Issuing invoices needs the sales:write scope; minting payment links and reading payment state need payments:write / payments:read. Grant a key only the scopes your integration uses (see Authentication).

On this page