Skip to content
DEMO PAYdocs
PTEN
Go to dashboard

Getting Started

Five minutes to your first Cobrança (charge).

#1. Get your API key

Log in to the Demo Pay Dashboard → Settings → API Keys → Create key, or via HTTP / CLI as documented in API Keys.

You will receive a key that looks like this:

text
dm_test_8f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c

dm_test_* keys hit the same production infrastructure with test-safe behavior for integration work; dm_live_* keys move real money. Treat both like a password. Anyone with a key can create charges billed to your account. Store it server-side only — never ship it to a browser or mobile app. The cleartext is shown exactly once at issuance — see API Keys for rotation if you lose it.

#2. Pick your environment

EnvironmentBase URLNotes
Productionhttps://demo.zentry.cloud/v1Real money. Real settlements.
Sandboxnot yet availableA dedicated test environment is planned but not live — there is no separate sandbox host yet. Test against production with small amounts and a dm_test_* key.

All examples in this guide use the production base URL.

#3. Make your first call

bash
curl -X POST https://demo.zentry.cloud/v1/charges \
  -H "apikey: $DEMO_API_KEY" \
  -H "Idempotency-Key: order-12345" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1000,
    "currency": "BRL",
    "payment_method": "pix",
    "description": "Pedido #12345",
    "customer": { "name": "Maria Silva", "document": "12345678901" },
    "metadata": { "order_id": "12345" }
  }'

Successful response (201 Created):

json
{
  "id": "ch_a1b2c3d4-0000-0000-0000-000000000009",
  "object": "charge",
  "amount": 1000,
  "currency": "BRL",
  "status": "pending",
  "payment_method": "pix",
  "customer": { "name": "Maria Silva", "document": "12345678901" },
  "pix": {
    "br_code": "000201BRCODEPIX",
    "qr_code_url": "https://qr.example/img.png",
    "expires_at": "2026-07-23T15:00:00.000Z"
  },
  "settlement": {},
  "metadata": { "order_id": "12345" },
  "created_at": "2026-07-23T14:30:00.000Z"
}

The id (ch_…) is your charge handle — use it to fetch status and reconcile webhooks. For Pix the BR Code and QR Code are already in the creation response: no follow-up poll is needed to render the checkout.

pix.txid and settlement.end_to_end_id are BACEN-contextual Pix data, not identifiers — see PIX. settlement stays {} until the charge is paid.

#4. Conventions

These rules apply to every Demo Pay /v1/charges-family endpoint.

#Authentication

Every request to a merchant-scoped endpoint carries your API key in the apikey header:

text
apikey: dm_live_...

Missing or invalid keys return 401 Unauthorized.

#Amounts are integers in the smallest currency unit

CurrencyWhat 1000 means
BRLR$ 10,00

Pix charges settle in BRL only today. Never send decimals — 10.00 is rejected.

#Idempotency is mandatory on writes

Every POST /v1/charges must include the Idempotency-Key header (8–128 characters, scoped by merchant + operation + key — the same key from a different merchant never conflicts).

Replaying the same key with the same request body returns the stored response, guaranteeing a network retry, mobile-app double-tap, or background-job restart never charges twice. Reusing the key with a different body returns 409 and error.code: "idempotency_key_reused" (see Errors). The server retains the record for at least 24 hours.

Recommended: use your internal order id (e.g. order-12345) or a UUID v4.

#Timestamps are ISO 8601 UTC

text
2026-07-23T14:30:00.000Z

#Identifiers

A charge id is always prefixed ch_ followed by an opaque UUID-shaped string — for example ch_a1b2c3d4-e5f6-4789-9abc-def012345678. Treat it as an opaque string: don't parse, don't sort lexicographically, don't infer ordering from the value. ch_… is always the Demo Pay id; the BACEN pix.txid and settlement.end_to_end_id are contextual Pix data and never substitute it.

#Errors

All errors return the canonical envelope:

json
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_amount",
    "message": "amount deve ser um inteiro positivo em centavos.",
    "param": "amount"
  },
  "request_id": "req_01J..."
}

Every response — success or error — also carries X-Request-Id. See Errors for the full envelope and status code map.

#Rate limits

200 req/sec, 5000 req/min, 50000 req/hour per authenticated API key by default (Kong's rate-limiting plugin, scoped by consumer). Need more? Email suporte@demo.zentry.cloud.

#5. Charge lifecycle

text
                  ┌──────────────────────┐
                  │        pending        │  ← created, awaiting payer
                  └──────────┬───────────┘
                             │
                ┌────────────┼────────────┐
                ▼            ▼            ▼
          ┌─────────┐  ┌──────────┐  ┌─────────┐
          │  paid   │  │ expired  │  │ failed  │
          └────┬────┘  └──────────┘  └─────────┘
               │
               ▼
          ┌──────────┐
          │ refunded │
          └──────────┘

Public status vocabulary: pending, processing, paid, failed, expired, cancelled, refunded, disputed. These are stable states you can branch on; the internal states they map from never appear in a /v1/charges response.

Your fulfillment hook should fire on paid. Everything else is informational — prefer webhooks over polling for that transition.

#6. Check your balance

Once charges start settling, your available balance is one call away — same apikey, no body. The currency query param is optional; without it you get every currency on the account:

bash
curl "https://demo.zentry.cloud/v1/wallets/balance" \
  -H "apikey: $DEMO_API_KEY"
json
{
  "available": 880,
  "pending": 0,
  "total": 880,
  "retained": 0,
  "currency": "BRL",
  "next_release_at": null,
  "next_release_amount": null,
  "balances": [
    { "currency": "BRL", "available": 880, "pending": 0, "total": 880 }
  ]
}

Amounts are in centavos (880 = R$ 8,80). available is what you can spend or pay out right now; pending is settled-but-not-yet-released. When a release is scheduled, next_release_at / next_release_amount tell you when and how much. Each currency on the account shows up in balances[].

Just one currency? Add ?currency=BRL for the flat shape. Need every wallet (by type)? GET /v1/wallets returns the full list. See the API Reference.

#Next

Build the PIX flow