API Reference
Complete catalogue of every public Orbita Pay v1 endpoint. All paths are relative to the production base URL — https://liqfy.com.br/v1. (A dedicated sandbox host is not yet available.)
Authenticate every request with your API key in the apikey header — apikey: <LIQFY_API_KEY> (lq_test_*/lq_live_*). All bodies are JSON. All amounts are integers in the smallest currency unit.
The API is organised around a few resources:
- Charges (
/v1/charges) — create and read Pix charges:object: "charge",ch_…ids, headerIdempotency-Key,pixblock. This is where every integration starts. - Wallets (
/v1/wallets) — your account balance. - Payment operations (
/v1/payments) — refunds, cancellation, stats and other operations on a charge, addressed by its raw id (thech_prefix stripped). - Webhooks (
/v1/webhooks) — register endpoints and inspect deliveries.
#Charges
/v1/charges.
Public contract from API conventions §3. Every response is built by an allowlist serializer — no internal id, PSP name, cost or raw provider payload can appear here.
#Create charge
POST /v1/charges — Pix-first shortcut: POST /v1/pix/charges fixes payment_method: "pix" in the body and returns the exact same Charge.
Headers
| Header | Required | Notes |
|---|---|---|
apikey | yes | Your API key (lq_test_* / lq_live_*). |
Idempotency-Key | yes | 8–128 chars. A financial write — missing it is a 400. |
Body
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | yes | Smallest currency unit, > 0. |
currency | string | no | Defaults to "BRL". Pix charges must use BRL. |
payment_method | string | yes | "pix" — the only value accepted today. |
description | string | no | Up to 500 chars; folded into metadata.description on read. |
customer.name | string | no | |
customer.document | string | no | CPF or CNPJ. |
customer.email | string | no | |
customer.phone | string | no | |
metadata | object | no | Free-form; reserved/underscore-prefixed keys are stripped. |
Example
curl -X POST https://liqfy.com.br/v1/pix/charges \
-H "apikey: $LIQFY_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "BRL",
"description": "Pedido #12345",
"customer": { "name": "Maria Silva", "document": "12345678901" }
}'Response 201 Created
{
"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"
},
"checkout_url": "https://checkout.liqfy.com.br/a1b2c3d4-0000-0000-0000-000000000009",
"settlement": {},
"metadata": { "order_id": "12345" },
"created_at": "2026-07-23T14:30:00.000Z"
}pix.txid (when the provider bound one) and settlement.end_to_end_id (only once paid and settled) are contextual Pix data — see PIX. charge.id is never equal to charge.pix.txid.
checkout_url is Orbita Pay's hosted checkout for the charge — redirect the payer there instead of rendering your own Pix screen. It comes back on both create and GET /v1/charges/{id}, and can't be derived from charge.id (the checkout path drops the ch_ prefix).
Public status vocabulary: pending, processing, paid, failed, expired, cancelled, refunded, disputed.
#Get charge
GET /v1/charges/{id}
Accepts either the ch_…-prefixed id or the raw internal id. Scoped to the caller's account — a charge from another account 404s.
Response 200 OK — same Charge shape as creation.
#List charges
Status: the query contract below (
ListChargesDto+PaymentsService.listCharges) is implemented and unit-tested, but theGET /v1/chargesroute is not yet wired to an HTTP controller — calling it today 404s. Use theGET /v1/paymentslist until this ships. Tracked as a follow-up on the/v1/chargescontract.
GET /v1/charges?limit=25&starting_after=ch_01J…&status=paid| Param | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | 1–100. |
starting_after | string | — | A charge id (ch_…) of the same resource — cursor, not an offset. |
status | enum | — | Public status vocabulary (pending, paid, …). |
created_after | ISO 8601 | — | Inclusive lower bound on created_at. |
created_before | ISO 8601 | — | Inclusive upper bound on created_at. |
customer_id | string | — | Filters by payer document. |
{
"object": "list",
"data": [],
"has_more": false,
"next_cursor": null
}page/offset are never accepted on this cursor list — only starting_after.
#Wallets
/v1/wallets. Your account's balances. Read-only, scoped to your API key.
#Get balance
GET /v1/wallets/balance
The currency query param is optional. Without it (the recommended form) the response carries every currency on the account in balances[], plus the primary currency's fields (BRL by default) at the top:
curl "https://liqfy.com.br/v1/wallets/balance" \
-H "apikey: $LIQFY_API_KEY"Response 200 OK
{
"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 }
],
"primary": { "currency": "BRL", "available": 880, "pending": 0, "total": 880 }
}| Field | Description |
|---|---|
available | Spendable/payoutable balance of the primary currency, smallest unit (centavos for BRL). 880 = R$ 8,80. |
pending | Settled but still held — not spendable yet (primary currency). |
total | available + pending of the primary currency. |
retained | Total under retention (PENDING holds) of the primary currency. |
currency | Primary currency (ISO 4217). Defaults to BRL. |
next_release_at | When the next pending tranche releases, or null when nothing is scheduled. |
next_release_amount | Amount of that next release (smallest currency unit), or null. |
balances[] | One entry per currency: { currency, available, pending, total }. |
primary | The primary currency (same fields as a balances[] entry). |
The top-level fields (available/pending/total/currency) are the primary currency — kept for backward compatibility. For multi-currency accounts, iterate over balances[].
#Single currency (legacy shape)
GET /v1/wallets/balance?currency=BRL
Pass currency to get the flat single-currency shape:
curl "https://liqfy.com.br/v1/wallets/balance?currency=BRL" \
-H "apikey: $LIQFY_API_KEY"{
"available": 880,
"pending": 0,
"currency": "BRL",
"next_release_at": null,
"next_release_amount": null
}Auth & errors. Always send the
apikeyheader. A missing or invalid key →401(never404). If you get a404on this route, the request never reached Orbita Pay — it's almost always a wrong path (/v1/wallets/balance, plural, with the/v1prefix) or an intermediary proxy/gateway that doesn't forward/v1/wallets/*.
#List wallets
GET /v1/wallets
Every wallet on the account (operational, per-currency), same auth.
curl https://liqfy.com.br/v1/wallets \
-H "apikey: $LIQFY_API_KEY"Response 200 OK
[
{
"currency": "BRL",
"walletType": "OPERATIONAL",
"balance": 880,
"pendingBalance": 0
}
]balance/pendingBalance are in the smallest currency unit — the same amounts GET /v1/wallets/balance surfaces as available/pending.
#Payment operations
/v1/payments. These routes operate on the same charge you created via /v1/charges, addressed by its raw id (the ch_ prefix stripped). They cover operations the /v1/charges surface doesn't expose yet — refunds, cancellation, stats, receipts — plus create/read/list in the transaction wire format (tx_… ids, WAITING_PAYMENT/PAID statuses). To create a charge, prefer Charges.
#Create payment
POST /payments
Body
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | yes | Smallest currency unit, > 0. |
currency | string (3–8) | no | Defaults to "BRL". Examples: BRL, EUR, USD, USDT. |
paymentMethods | string[] | yes | One or more of: PIX, CREDIT_CARD, MBWAY, MULTIBANCO, BOLETO, CRYPTO. |
customerId | string | no | Your internal user id, echoed in webhooks. |
customerName | string | no | |
customerDocument | string | no | CPF, CNPJ, NIF, or other tax id. |
customerEmail | string | no | Validated as RFC 5322. |
metadata | object | no | Free-form. Reserved key: returnUrl (used by CREDIT_CARD). For MBWAY you must include phone (E.164). |
webhookUrl | string | no | Per-transaction webhook URL override. |
idempotencyKey | string | yes | Unique per intended request. Replays return the original. |
Response 201 Created
{
"id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"status": "WAITING_PAYMENT",
"amount": 24900,
"currency": "BRL",
"paymentMethods": ["PIX"],
"customerName": "Maria Silva",
"customerDocument": "12345678909",
"customerEmail": "maria@example.com",
"metadata": { "orderId": "ORD-7821" },
"createdAt": "2026-04-25T15:42:11.000Z"
}Method-specific fields (pixQrCode, cardRedirectUrl, etc.) are populated by GET /payments/{id} after Orbita Pay finalises the charge with the underlying acquirer (typically <3s).
#Get payment
GET /payments/{id}
Response 200 OK
{
"id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"status": "PAID",
"amount": 24900,
"currency": "BRL",
"paymentMethods": ["PIX"],
"paidWith": "PIX",
"pixQrCode": "data:image/png;base64,iVBOR...",
"pixCopyPaste": "00020126580014br.gov.bcb.pix...",
"pixExpiresAt": "2026-04-25T16:12:11.000Z",
"cardRedirectUrl": null,
"mbEntity": null,
"mbReference": null,
"mbExpiresAt": null,
"boletoBarcode": null,
"boletoLine": null,
"boletoPdfUrl": null,
"boletoExpiresAt": null,
"cryptoAddress": null,
"cryptoAmount": null,
"cryptoNetwork": null,
"cryptoCurrency": null,
"providerFee": 75,
"platformFee": 200,
"netAmount": 24625,
"metadata": { "orderId": "ORD-7821" },
"createdAt": "2026-04-25T15:42:11.000Z",
"paidAt": "2026-04-25T15:43:08.000Z"
}Fields are null when not applicable to the chosen paymentMethods.
#List payments
GET /payments
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
page | int | 1 | |
limit | int | 20 | Max 100. |
status | enum | — | See Status enum below. |
startDate | ISO 8601 | — | Inclusive lower bound on createdAt. |
endDate | ISO 8601 | — | Inclusive upper bound on createdAt. |
sortBy | string | createdAt | Any top-level field. |
sortOrder | enum | desc | asc or desc. |
Response 200 OK
{
"data": [ { "id": "tx_...", "...": "..." } ],
"total": 142,
"page": 1,
"limit": 20
}#Stats
GET /payments/stats?days=7
Response 200 OK
{
"totalTransactions": 142,
"paidTransactions": 119,
"todayTransactions": 8,
"successRate": 84,
"volumeByCurrency": [
{ "currency": "BRL", "volume": 1245000, "count": 95 },
{ "currency": "EUR", "volume": 89400, "count": 24 }
],
"todayVolumeByCurrency": { "BRL": 24900, "EUR": 8990 },
"dailyVolume": [
{ "date": "2026-04-19", "currencies": { "BRL": 89000 } },
{ "date": "2026-04-20", "currencies": { "BRL": 124500, "EUR": 4500 } }
],
"methodBreakdown": [
{ "method": "PIX", "volume": 980000, "count": 78 },
{ "method": "CREDIT_CARD", "volume": 265000, "count": 34 },
{ "method": "MULTIBANCO", "volume": 89400, "count": 7 }
]
}#Metrics
GET /payments/metrics?days=7¤cy=BRL
Conversion-funnel and volume metrics for your merchant account. days defaults to 7; currency is optional (filters to a single currency). Scoped to your apikey.
#Refund payment
POST /payments/{id}/refund
Refunds a settled transaction (PAID or APPROVED), full or partial. Live in production. For PIX this maps to a BACEN devolução (by endToEndId) or a PIX-out cashout, per strategy.
Body
| Field | Type | Required | Description |
|---|---|---|---|
idempotencyKey | string | yes | 8–128 chars. Replays return the original refund. |
amount | integer | no | Smallest currency unit, > 0. Omit for a full refund. Must be ≤ remaining refundable. |
reason | string | no | Up to 500 chars, stored for your records. |
strategy | enum | no | PIX only: devolution or cashout. Defaults to cashout. |
passFeeToTenant | boolean | no | Cashout refunds: debit the PIX-out fee from your wallet. Default false. (field name reflects the current v1 wire format — a merchant-scoped alias ships with the public API migration; see the glossary.) |
destinationKey | string | no | Cashout refunds: send to a specific PIX key instead of the payer's document. |
Response 201 Created
{
"id": "b2c3d4e5-f6a7-4890-9abc-def012345678",
"transactionId": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"amount": 24900,
"currency": "BRL",
"status": "PENDING",
"reason": "customer_request",
"createdAt": "2026-04-25T15:50:00.000Z"
}Refund status advances asynchronously (PENDING → IN_PROGRESS → REFUNDED/FAILED) as the acquirer confirms — subscribe to the payment.refunded webhook. The transaction only moves to REFUNDED once the refunded total reaches the original amount; partial refunds leave it PAID/APPROVED.
Refund support is provider-dependent: PIX (BrasilCash) and card (Stripe) are live. Other providers return a
REFUND_NOT_SUPPORTEDerror.
#List refunds
GET /payments/{id}/refunds
Returns every refund issued against a transaction (newest first).
#Cancel payment
POST /payments/{id}/cancel
Cancels an in-flight charge — valid only while WAITING_PAYMENT, PENDING, or PROCESSING. Settled (PAID/APPROVED) charges must be refunded, not cancelled. Returns the updated transaction and fires payment.failed.
#Resend webhook
POST /payments/{id}/resend-webhook
Re-emits the transaction's current status as a fresh webhook delivery — useful when your endpoint was down.
{ "resent": true, "status": "PAID" }#Provider status
GET /payments/{id}/provider-status
Live status straight from the acquirer (bypasses our cache) — for debugging stuck charges.
{
"localStatus": "WAITING_PAYMENT",
"provider": "brasilcash",
"providerTransactionId": "bc_...",
"providerStatus": "PENDING",
"rawResponse": { "...": "..." }
}Returns "error": "TRANSACTION_HAS_NO_PROVIDER_REFERENCE" when the charge never reached a provider.
#Receipt
GET /payments/{id}/receipt
Provider receipt (PDF) for a settled transaction, where the acquirer exposes one (e.g. BrasilCash PIX).
{ "contentType": "application/pdf", "base64": "JVBERi0xLjcK..." }#Webhooks
#Register endpoint
POST /webhooks/endpoints
{
"url": "https://merchant.example.com/hooks/liqfy",
"events": ["charge.paid", "charge.failed", "charge.expired"]
}Event names (charge-related): charge.created, charge.paid, charge.failed, charge.expired, payout.created, payout.paid, payout.failed. These are delivered in the versioned evt_ envelope and signed X-Liqfy-Signature: t=<unix>,v1=<hex> — see Webhooks. Subscribe to these.
The endpoint also accepts an older event family (payment.created, payment.completed, payment.failed, payment.expired, payment.refunded, withdrawal.*, and the payment.status_changed/withdrawal.status_changed aliases), delivered with a { event, data } envelope and X-Liqfy-Signature: sha256=<hex>. New integrations don't need these — use the charge.*/payout.* names above. The full, current list is authoritative at GET /webhooks/event-catalog.
Response 201 Created
{
"id": "e5f6a7b8-c9d0-4123-9ef0-123456789012",
"url": "https://merchant.example.com/hooks/liqfy",
"events": ["charge.paid", "charge.failed", "charge.expired"],
"secret": "b8f3a9c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
"status": "ACTIVE"
}secret is shown once — store it immediately.
#List endpoints
GET /webhooks/endpoints
{
"data": [
{
"id": "wh_...",
"url": "https://...",
"events": ["payment.status_changed"],
"status": "ACTIVE",
"createdAt": "...",
"updatedAt": "..."
}
]
}secret is never returned by this endpoint.
#Rotate secret
POST /webhooks/endpoints/{id}/rotate-secret
{ "id": "e5f6a7b8-c9d0-4123-9ef0-123456789012" }Response 200 OK
{ "id": "e5f6a7b8-c9d0-4123-9ef0-123456789012", "secret": "<64-char hex>" }The new secret is shown once. Rotation is an instantaneous server-side swap — there is no overlap window. Every webhook signed after this call uses the new secret, so make your verifier accept both the old and new secret across your deploy, then drop the old one (see webhooks.md).
#Update endpoint
PATCH /webhooks/endpoints/{id}
Update URL, subscribed events, or status (ACTIVE/INACTIVE). Send only the fields to change.
{ "url": "https://merchant.example.com/hooks/v2", "status": "ACTIVE" }Response 200 OK — { id, url, events, status }. Secret never returned.
#Delete endpoint
DELETE /webhooks/endpoints/{id}
Permanently removes the endpoint. All pending deliveries to it are abandoned.
Response 200 OK — { "deleted": true }.
#Event catalog
GET /webhooks/event-catalog
Returns the full, current list of subscribable public event names with descriptions (legacy: true on deprecated families). No auth required. Charge/payout-relevant excerpt:
[
{ "event": "charge.created", "description": "Cobrança criada (Pix/cartão gerado, aguardando pagamento)." },
{ "event": "charge.paid", "description": "Cobrança paga e confirmada (Pix/cartão liquidado)." },
{ "event": "charge.failed", "description": "Cobrança falhou ou foi cancelada." },
{ "event": "charge.expired", "description": "Cobrança expirou sem pagamento." },
{ "event": "payout.created", "description": "Saque solicitado." },
{ "event": "payout.paid", "description": "Saque liquidado com sucesso." },
{ "event": "payout.failed", "description": "Saque falhou ou foi rejeitado." },
{ "event": "payment.completed", "description": "[legado] Cobrança paga — use charge.paid.", "legacy": true },
{ "event": "withdrawal.completed", "description": "[legado] Saque liquidado — use payout.paid.", "legacy": true }
]#List deliveries
GET /webhooks/deliveries
| Param | Description |
|---|---|
status | PENDING, PROCESSING, DELIVERED, FAILED, CANCELLED |
eventType | Filter by event name (payment.completed, …). |
endpointId | Filter by registered endpoint. |
limit | Default 25, max 100. |
offset | Default 0. |
{
"data": [
{
"id": "wd_...",
"endpointId": "wh_...",
"eventType": "payment.completed",
"status": "DELIVERED",
"attempts": 1,
"maxAttempts": 15,
"lastStatusCode": 200,
"lastError": null,
"nextRetryAt": null,
"deliveredAt": "2026-04-25T15:43:09.000Z",
"createdAt": "2026-04-25T15:43:08.000Z",
"updatedAt": "2026-04-25T15:43:09.000Z",
"payload": { "event": "payment.completed", "data": { "...": "..." } }
}
],
"total": 142,
"limit": 25,
"offset": 0
}#Delivery stats
GET /webhooks/stats
{
"total": 1402,
"PENDING": 3,
"PROCESSING": 1,
"DELIVERED": 1380,
"FAILED": 12,
"CANCELLED": 6
}#Status enum
The table below is the detailed status enum returned by the /v1/payments transaction routes (and eventType/payload.data.status on their webhook deliveries). POST/GET /v1/charges never emit these values; they emit the smaller public vocabulary (pending, processing, paid, failed, expired, cancelled, refunded, disputed — see Getting Started §5), which the enum below maps onto.
| Transaction status | Description | Public charge.status |
|---|---|---|
PENDING | Internal — being created. Rarely surfaced. | pending |
WAITING_PAYMENT | Awaiting customer action. | pending |
PROCESSING | Card / 3DS in flight. | processing |
PAID | Settled — non-card methods. | paid |
APPROVED | Settled — card methods. | paid |
REFUSED | Acquirer or issuer declined. | failed |
CANCELLED | Cancelled before completion. | cancelled |
EXPIRED | Time window elapsed. | expired |
REFUNDED | Fully refunded. | refunded |
CHARGEBACK | Issuer raised a chargeback (cards). | disputed |
DISPUTE | Cardholder opened a dispute (cards). | disputed |
payment.completed fires for PAID and APPROVED; charge.paid fires for the same underlying transition. payment.failed fires for REFUSED, CANCELLED, EXPIRED, CHARGEBACK, DISPUTE; charge.failed and charge.expired split that older payment.* family by outcome.
#Webhook payload schema
#Canonical envelope (charge.* / payout.*)
{
"id": "evt_5f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c",
"object": "event",
"api_version": "2026-07-23",
"type": "charge.paid",
"created_at": "2026-07-23T14:31:00.000Z",
"data": {
"object": {
"id": "ch_a1b2c3d4-0000-0000-0000-000000000009",
"object": "charge",
"amount": 24900,
"currency": "BRL",
"status": "paid",
"payment_method": "pix",
"settlement": { "end_to_end_id": "E-END-TO-END-99" }
}
}
}| Field | Always present | Description |
|---|---|---|
id | yes | evt_… — stable per business event, identical across retries. Dedup on this. |
object | yes | Always "event". |
api_version | yes | Contract version, e.g. 2026-07-23. |
type | yes | charge.created | charge.paid | charge.failed | charge.expired | payout.created | payout.paid | payout.failed. |
data.object | yes | The same public Charge/Payout object the REST API returns — same serializer, same field names. |
#payment.* / withdrawal.* envelope
{
"event": "payment.completed",
"data": {
"transactionId": "tx_...",
"amount": 24900,
"status": "PAID",
"previousStatus": "WAITING_PAYMENT",
"paidWith": "PIX",
"providerFee": 75,
"platformFee": 200,
"netAmount": 24625,
"occurredAt": "2026-04-25T15:43:08.000Z"
}
}| Field | Always present | Description |
|---|---|---|
transactionId | yes | Matches the id returned at creation. |
amount | yes | Smallest currency unit. |
status | yes | Current status — see Status enum. |
previousStatus | yes | Status before this transition. |
paidWith | on PAID/APPROVED | The actual method used. |
providerFee | on PAID/APPROVED | Acquirer fee, smallest unit. |
platformFee | on PAID/APPROVED | Orbita Pay fee, smallest unit. |
netAmount | on PAID/APPROVED | amount - providerFee - platformFee. |
occurredAt | yes | ISO 8601 UTC. |
#Webhook headers
| Header | Description |
|---|---|
X-Liqfy-Signature | charge.*/payout.* events: t=<unix>,v1=<hex> HMAC of "<t>.<rawBody>". payment.*/withdrawal.* events: sha256=<hex> HMAC of the raw body. |
X-Liqfy-Delivery-Id | Unique per delivery attempt — stable across retries of the same attempt; use for transport-level dedup. |
X-Liqfy-Event-Type | Mirrors type / event. |
See webhooks.md for verification samples.
#HTTP status codes
| Code | Used for |
|---|---|
200 | Successful read |
201 | Resource created |
204 | No content (e.g. logout) |
400 | Validation error, or a financial write missing Idempotency-Key |
401 | Missing / invalid apikey |
403 | Blocked by the fraud / velocity guard (error code fraud.*) |
404 | Resource not found |
409 | Idempotency conflict — error.code: "idempotency_key_reused" |
422 | Business-rule rejection passed through from provider configuration (e.g. no PSP configured) |
429 | Rate limited |
500 | Server error — safe to retry (idempotency protects) |
503 | Upstream acquirer unavailable — retry with backoff |
See Errors for the full canonical error envelope (error.type/error.code/error.message, request_id, X-Request-Id).