Skip to main content
A Checkout Link (chk_) is a reusable billing template. You define it once (items, price, methods, branding) and share the URL as many times as you want — each buyer visit materializes an independent CheckoutSession. Ideal for an Instagram bio link, a landing page, or any mass distribution channel.
The examples use the sandbox: API at https://checkout-api.sandbox.z2pay.com and checkout page at https://pay.sandbox.z2pay.com. In production, use https://checkout-api.z2pay.com and https://pay.z2pay.com. All requests use the x-api-key header — see Authentication.

Endpoints

MethodRouteDescription
POST/checkout/linksCreates a billing template
GET/checkout/linksLists templates (paginated)
GET/checkout/links/{id}Retrieves a template by ID
PUT/checkout/links/{id}Updates a template
DELETE/checkout/links/{id}Archives (soft-deletes) a template
name
string
Internal name (≤ 255 chars), visible to the seller, not to the buyer. Useful for organizing the dashboard list.
description
string
Internal description (≤ 2000 chars).
slug
string
URL-friendly identifier. Regex ^[a-z0-9-]+$, 3–100 chars. Reserved for a future friendly URL feature.
mode
string
default:"payment"
required
Currently only "payment".
currency
string
default:"BRL"
required
ISO 4217 currency code. Currently only "BRL".
locale
string
default:"pt-BR"
required
Page language: "pt-BR", "en-US", or "es-ES".
items
array
required
Billing items (≥ 1). The sum of quantity × unitAmount is the total to charge. See Item.
paymentMethods
object
required
Accepted payment methods. At least one method must be enabled. See Payment methods.
splits
array
Multi-seller distribution. The sum of all percentage values must be exactly 100. See Split.
branding
object
Primary color, logo, and seller name. See Branding.
requiredFields
array
Buyer fields that are required in addition to the always-required ones. See Required fields.
customFields
array
Custom fields (≤ 20) to collect from the buyer. See Custom fields.
successUrl
string
Redirect URL (≤ 2000) after a successful payment.
cancelUrl
string
Redirect URL (≤ 2000) when the buyer cancels.
metadata
object
string → string map with arbitrary seller data (opaque to the server; returned in webhooks).
expirationMinutes
number
default:"1440"
Session validity period from creation, in minutes. Minimum 5, maximum 43200 (30 days). Default 1440 (24h).
Cents, always. Every monetary value is an integer in cents. R99.90=9990.Sending9.90isrejectedbyvalidation(integerrequired);9wouldbeacceptedas9cents(R 99.90 = `9990`. Sending `9.90` is rejected by validation (`integer` required); `9` would be accepted as **9 cents** (R 0.09). See Conventions.

Item (CheckoutItem)

name
string
required
Name displayed to the buyer (1–255 chars).
description
string
Description shown below the name (≤ 2000 chars).
imageUrl
string
Image displayed next to the item. Any public HTTPS URL (≤ 2000 chars).
quantity
number
required
Quantity (integer ≥ 1).
unitAmount
number
required
Unit amount in cents (integer ≥ 1). R$ 99.90 = 9990.
metadata
object
string → string map with the seller’s internal identifiers (sku, course_id, etc.).

Payment methods (paymentMethods)

card
object
Card configuration. card.enabled (boolean) toggles it on/off. card.installments defines installment options (see below). card.multipleCards (boolean) allows splitting the total between 2 cards.
card.installments
object
Installments. maxInstallments (1–12, required), freeInstallments (0–12, default 0, initial installments with no interest), interestRate (0–100, monthly rate in %, e.g. 2.99), interestType ("simple" or "compound", default "compound" = Price/Tabela).
pix
object
PIX configuration. pix.enabled (boolean) toggles it on/off. pix.expiresIn (60–86400) is the QR code expiration in seconds (60s to 24h).
boleto
object
Boleto configuration. boleto.enabled (boolean) toggles it on/off. boleto.dueDateDays (1–30) is the number of days until the due date.
combined
object
Combined payment (splitting the total across methods/cards). enabled (boolean, required), minAmountPerPayment (cents, default 1), maxPaymentsCount (2–8, default 4).
At least one method (card, pix, or boleto) must have enabled: true. Otherwise, validation rejects the body with a 400.

Split

Distributes the received amount among multiple recipients (marketplace, partners, platforms).
recipientId
string
required
Recipient ID (rec_*) that receives the share.
percentage
number
required
Percentage of the total amount (0.01–100). Accepts decimals (e.g. 33.33).
liable
boolean
Marks the recipient as liable for chargebacks (typically the main seller).
The sum of all split percentage values must be exactly 100 (numerical tolerance ±0.01 for rounding). Otherwise, validation returns 400 invalid_splits_sum. For the conceptual split model, see also Core API Splits.

Branding

primaryColor
string
Primary color in hex. Regex ^#([0-9a-f]{6}|[0-9a-f]{3})$/i, e.g. "#1e3b79" or "#FFF". Derived colors (hover, text contrast) are calculated automatically.
logoUrl
string
Seller logo displayed in the page header (≤ 2000 chars).
merchantName
string
Seller name displayed on the page (≤ 100 chars), when there is no logo or alongside it.
mobileLayout
string
"single-page" or "step-by-step" — controls the checkout layout on mobile.
faviconUrl
string
Page favicon, the browser tab icon (≤ 2000 chars).

Required fields

List of buyer fields that will be required in addition to the always-required ones.
"requiredFields": ["email", "document", "phone", "address"]
Accepted values: "email", "document", "phone", "address".
email, document, and phone are always required at confirm time, even if you do not list them here. Listing "address" forces the buyer to fill in their complete address.

Custom fields

Allows collecting additional information from the buyer (text, dropdown, checkbox).
key
string
required
Internal field identifier. Regex ^[a-z][a-z0-9_]*$/i, ≤ 50 chars.
label
string | object
required
Displayed label. Simple string ("Company") or a per-language map ({ "pt-BR": "Empresa", "en-US": "Company" }).
type
string
required
"text", "select", or "checkbox".
options
array
Required if type = "select". Accepts strings ("Option 1") or { value, label } objects with a localizable label.
required
boolean
If true, the buyer must fill in this field.

POST /checkout/links
Creates a persistent template. Returns 201. Supports idempotency via the Idempotency-Key header (see Conventions).
curl -X POST https://checkout-api.sandbox.z2pay.com/checkout/links \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -d '{
    "name": "Curso de Backend",
    "description": "Link para divulgação no Instagram",
    "items": [
      { "name": "Curso de Backend — vitalício", "quantity": 1, "unitAmount": 49900 }
    ],
    "paymentMethods": {
      "card": { "enabled": true, "installments": { "maxInstallments": 12, "freeInstallments": 3 } },
      "pix":  { "enabled": true, "expiresIn": 3600 }
    },
    "branding": { "primaryColor": "#1e3b79", "merchantName": "Henrique Cursos" },
    "metadata": { "campaign": "instagram-bio-2026-05" }
  }'
Response (201):
{
  "id": "chk_abc123...",
  "tenantId": "ten_xyz...",
  "name": "Curso de Backend",
  "description": "Link para divulgação no Instagram",
  "slug": null,
  "status": "active",
  "mode": "payment",
  "currency": "BRL",
  "locale": "pt-BR",
  "items": [
    {
      "id": "ci_...",
      "name": "Curso de Backend — vitalício",
      "quantity": 1,
      "unitAmount": 49900
    }
  ],
  "paymentMethods": { "...": "..." },
  "splits": null,
  "branding": { "...": "..." },
  "requiredFields": null,
  "customFields": null,
  "successUrl": null,
  "cancelUrl": null,
  "metadata": { "campaign": "instagram-bio-2026-05" },
  "expirationMinutes": 1440,
  "createdAt": "2026-06-24T12:00:00.000Z",
  "updatedAt": "2026-06-24T12:00:00.000Z",
  "deletedAt": null,
  "version": 0,
  "url": "https://pay.sandbox.z2pay.com/c/chk_abc123..."
}
The returned url is the payment link ready to share. Each visit materializes a new Session.

Common errors

HTTPCodeWhen
400validation_failedMalformed body (missing items, non-integer value, etc.)
400no_itemsEmpty items array
400amount_too_lowSum of items results in 0
400invalid_splits_sumSplits do not add up to 100
401unauthenticatedx-api-key missing, invalid, or revoked
409slug_takenA Link with that slug already exists
409idempotency_conflictSame Idempotency-Key used with a different body
See Errors for the full error format.
GET /checkout/links
Paginated list of the tenant’s templates.
status
string
Filter by status (CSV). E.g.: ?status=active or ?status=active,archived.
Search in name and description (≤ 255 chars).
page
number
default:"1"
Page number (≥ 1).
limit
number
default:"20"
Items per page (1–100).
curl -G https://checkout-api.sandbox.z2pay.com/checkout/links \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -d status=active \
  -d search=curso \
  -d page=1 \
  -d limit=20
Response (200):
{
  "data": [
    { "id": "chk_...", "name": "...", "url": "https://pay.sandbox.z2pay.com/c/chk_...", "...": "..." }
  ],
  "total": 12,
  "page": 1,
  "limit": 20
}

GET /checkout/links/{id}
Returns the full data of a Link (same format as the POST response).
curl https://checkout-api.sandbox.z2pay.com/checkout/links/chk_abc123 \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"
Possible statuses: 200 (found) · 404 (not found).
PUT /checkout/links/{id}
Updates an existing Link. All fields are optional — only the fields sent are modified. Returns 200. Supports idempotency via Idempotency-Key.
Updating a Link does not affect already-materialized Sessions — they retain the snapshot from the moment of creation. Changes only apply to future Sessions.
curl -X PUT https://checkout-api.sandbox.z2pay.com/checkout/links/chk_abc123 \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{ "name": "Curso de Backend — vitalício", "quantity": 1, "unitAmount": 39900 }]
  }'
Possible statuses: 200 (updated) · 404 (not found) · 400 (validation error).
DELETE /checkout/links/{id}
Soft-delete. The public URL (/c/chk_...) will start returning 404 and new Sessions can no longer be created from it. Already created Sessions continue to work normally. Returns 200 with the entity containing a populated deletedAt. Supports idempotency.
curl -X DELETE https://checkout-api.sandbox.z2pay.com/checkout/links/chk_abc123 \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"
Response (200):
{
  "id": "chk_abc123...",
  "status": "archived",
  "deletedAt": "2026-06-24T13:00:00.000Z",
  "...": "..."
}
Possible statuses: 200 (archived) · 404 (not found).
Ready-to-use payloads for POST /checkout/links. Open the returned url to preview the result visually.
{
  "name": "Apenas cartão",
  "items": [{ "name": "Produto Teste", "quantity": 1, "unitAmount": 19900 }],
  "paymentMethods": {
    "card": { "enabled": true, "installments": { "maxInstallments": 6, "freeInstallments": 1 } }
  }
}
A single “Card” tab. The selector shows 1× interest-free, 2–6× with interest.
{
  "name": "Apenas PIX",
  "items": [{ "name": "Doação", "quantity": 1, "unitAmount": 5000 }],
  "paymentMethods": { "pix": { "enabled": true, "expiresIn": 3600 } }
}
Single PIX tab, QR code expires in 1h.
{
  "name": "Apenas boleto",
  "items": [{ "name": "Mensalidade", "quantity": 1, "unitAmount": 12000 }],
  "paymentMethods": { "boleto": { "enabled": true, "dueDateDays": 5 } }
}
Single boleto tab, due in 5 days.
{
  "name": "Todos os métodos",
  "items": [{ "name": "E-book Premium", "quantity": 1, "unitAmount": 9900 }],
  "paymentMethods": {
    "card":   { "enabled": true, "installments": { "maxInstallments": 12, "freeInstallments": 12 } },
    "pix":    { "enabled": true, "expiresIn": 1800 },
    "boleto": { "enabled": true, "dueDateDays": 3 }
  }
}
3 tabs; card up to 12× interest-free; PIX 30 min; boleto 3 days.
{
  "name": "Múltiplos itens",
  "items": [
    { "name": "Curso Backend", "quantity": 1, "unitAmount": 49900 },
    { "name": "Material PDF", "quantity": 1, "unitAmount": 4900 },
    { "name": "Mentoria (3x)", "quantity": 3, "unitAmount": 15000 }
  ],
  "paymentMethods": { "card": { "enabled": true } }
}
3 item lines, total R$ 998.00 (49900 + 4900 + 3 × 15000).
{
  "name": "Item com imagem",
  "items": [{
    "name": "Camiseta Tech — Tamanho M",
    "description": "100% algodão, estampa em silk",
    "imageUrl": "https://exemplo.com/camiseta-m.jpg",
    "quantity": 2,
    "unitAmount": 7900
  }],
  "paymentMethods": { "card": { "enabled": true }, "pix": { "enabled": true } }
}
Thumbnail on the left; description below the name; quantity 2; total R$ 158.00.
{
  "name": "Branding completo",
  "items": [{ "name": "Produto", "quantity": 1, "unitAmount": 5000 }],
  "paymentMethods": { "pix": { "enabled": true } },
  "branding": {
    "primaryColor": "#16a34a",
    "merchantName": "Loja Verde",
    "logoUrl": "https://exemplo.com/logo-verde.png"
  }
}
Buttons/actions in green; logo in the header; name “Loja Verde” displayed.
{
  "name": "Inglês",
  "locale": "en-US",
  "items": [{ "name": "Online Course", "quantity": 1, "unitAmount": 29900 }],
  "paymentMethods": { "card": { "enabled": true } }
}
Use "locale": "es-ES" for the page in Spanish. The entire page follows the chosen language.
{
  "name": "Endereço obrigatório",
  "items": [{ "name": "Produto físico", "quantity": 1, "unitAmount": 8900 }],
  "paymentMethods": { "card": { "enabled": true } },
  "requiredFields": ["email", "document", "phone", "address"]
}
The full address form appears before payment.
{
  "name": "Custom fields",
  "items": [{ "name": "Workshop", "quantity": 1, "unitAmount": 19900 }],
  "paymentMethods": { "pix": { "enabled": true } },
  "customFields": [
    { "key": "company_name", "label": "Nome da empresa", "type": "text", "required": false },
    {
      "key": "experience_level",
      "label": { "pt-BR": "Nível de experiência", "en-US": "Experience level" },
      "type": "select",
      "options": [
        { "value": "junior", "label": "Júnior" },
        { "value": "pleno",  "label": "Pleno" },
        { "value": "senior", "label": "Sênior" }
      ],
      "required": true
    },
    { "key": "newsletter", "label": "Quero receber novidades por e-mail", "type": "checkbox" }
  ]
}
3 extra fields; select with 3 options; optional checkbox. Blocks confirm without experience_level.
{
  "name": "Marketplace 70/30",
  "items": [{ "name": "Pedido marketplace", "quantity": 1, "unitAmount": 10000 }],
  "paymentMethods": { "card": { "enabled": true }, "pix": { "enabled": true } },
  "splits": [
    { "recipientId": "rec_seller_principal", "percentage": 70, "liable": true },
    { "recipientId": "rec_plataforma",       "percentage": 30 }
  ]
}
The buyer sees a total of R100.00;afterpayment,R 100.00; after payment, R 70 go to the main seller and R$ 30 to the platform.
{
  "name": "Juros compostos",
  "items": [{ "name": "Notebook", "quantity": 1, "unitAmount": 350000 }],
  "paymentMethods": {
    "card": {
      "enabled": true,
      "installments": {
        "maxInstallments": 10,
        "freeInstallments": 3,
        "interestRate": 2.99,
        "interestType": "compound"
      }
    }
  }
}
1×/2×/3× interest-free; 4–10× at 2.99% per month (Price/Tabela). The buyer sees the installment amount and the final total.
{
  "name": "Com redirects",
  "items": [{ "name": "Produto", "quantity": 1, "unitAmount": 5000 }],
  "paymentMethods": { "card": { "enabled": true } },
  "successUrl": "https://meu-site.com/pedido/obrigado?session={CHECKOUT_SESSION_ID}",
  "cancelUrl":  "https://meu-site.com/carrinho"
}
After approval, the buyer is redirected to successUrl. You can use {CHECKOUT_SESSION_ID} as a placeholder — we replace it with the cs_* before redirecting.
{
  "name": "Kitchen-sink",
  "description": "Link com tudo configurado",
  "mode": "payment",
  "currency": "BRL",
  "locale": "pt-BR",
  "items": [
    { "name": "Produto A", "description": "...", "imageUrl": "https://...", "quantity": 2, "unitAmount": 9900 },
    { "name": "Produto B", "quantity": 1, "unitAmount": 4900 }
  ],
  "paymentMethods": {
    "card":   { "enabled": true, "installments": { "maxInstallments": 12, "freeInstallments": 6, "interestRate": 1.99 } },
    "pix":    { "enabled": true, "expiresIn": 3600 },
    "boleto": { "enabled": true, "dueDateDays": 3 },
    "combined": { "enabled": true, "maxPaymentsCount": 3, "minAmountPerPayment": 1000 }
  },
  "splits": [
    { "recipientId": "rec_principal", "percentage": 80, "liable": true },
    { "recipientId": "rec_afiliado",  "percentage": 20 }
  ],
  "branding": {
    "primaryColor": "#1e3b79",
    "merchantName": "Empresa X",
    "logoUrl": "https://...",
    "mobileLayout": "step-by-step"
  },
  "requiredFields": ["email", "document", "phone", "address"],
  "customFields": [
    { "key": "company", "label": "Empresa", "type": "text", "required": false },
    { "key": "newsletter", "label": "Quero novidades", "type": "checkbox" }
  ],
  "successUrl": "https://meu-site.com/obrigado",
  "cancelUrl":  "https://meu-site.com/cancelado",
  "metadata": { "campaign": "lancamento-2026" },
  "expirationMinutes": 4320
}
2 items, 3 methods, combined up to 3 entries, 80/20 split, full branding, required address, 2 custom fields, redirects, expires in 3 days.

See also

Checkout Overview

Concepts, use cases, and endpoint map.

Checkout Sessions

Materialize Sessions from a Link.

Webhooks

checkout.link.* and checkout.session.* events.

Errors

Error format and how to handle 400 / 404 / 409.