Payments Integration
The Payments integration is a provider-agnostic REST API for the core financial operations every backoffice needs:- Issue an invoice (charge) —
POST /payments/invoices. An Invoice here is a payment request — a charge collected via PIX, boleto, card, ACH or SEPA depending on region. - Cancel an invoice —
POST /payments/invoices/{id}/cancel(voids unpaid charge). - Issue a tax document (NFS-e/NF-e/PEPPOL) —
POST /payments/invoices/{id}/tax-documents. Optional, region-specific (Brazil today, Europe later). Can also be auto-issued from the Invoice creation call. - Pay a beneficiary (a “prize” or any payout) —
POST /payments/payouts. - Read a bank statement —
GET /payments/balance-transactions.
cost_center and contractor_reference you provide on the request; our_number is generated by SalesOS and returned in the response (and on every statement entry). Behind the scenes, SalesOS routes the request to the right provider — you do not pick the provider unless you want to.
Charge ≠ tax document. An Invoice is the request for payment (boleto, PIX, card). The fiscal document (NFS-e in BR, PEPPOL in EU) is a separate, optional resource attached to the Invoice. This split keeps the API portable across BR / US / EU.
The API follows accepted market conventions: REST resource model, integer minor-unit money, ISO-8601 timestamps, RFC 9457 Problem Details for errors,
Idempotency-Key header for safe retries, cursor pagination, signed webhooks. If you have integrated with any major payments API you will feel at home.Region routing
SalesOS picks the right backend automatically from the customer’scountry and currency. You do not need to choose.
| Customer country | Currency | Status | Methods |
|---|---|---|---|
| BR | BRL | live | pix, boleto, bolepix, card (card-as-PIX) |
| US | USD | coming soon | card, ach_debit, bank_transfer |
| EU member states | EUR / local | coming soon | card, sepa_debit, bank_transfer |
| Region | Tax document type | Status |
|---|---|---|
| BR | nfse (services) | live |
| BR | nfe (products) | coming soon |
| EU | peppol (e-invoice) | coming soon |
| US | n/a | sales tax via Invoice line metadata; no fiscal document |
How It Works
- You get an API Key in the SalesOS Dashboard (Admin > Integrations > API Keys) with the right scopes (
payments:write,payments:read). - You issue invoices (charges). SalesOS creates the payment request for the customer’s region (BR live; US/EU coming soon). You receive a payable artifact: PIX QR-code/copy-paste, boleto barcode, card payment URL or hosted checkout link.
- (Optional) A tax document is attached. When
currency = BRLand you settax_document.auto_issue: true, SalesOS issues an NFS-e automatically after the charge is confirmed paid. You can also callPOST /invoices/{id}/tax-documentslater to issue or retry. - You pay beneficiaries (employees, partners, prize winners). SalesOS routes BRL via PIX and other currencies via international transfer.
- You read a unified statement — every invoice, payout, fee and tax-document fee in one ledger, filterable by
cost_center,our_number,contractor_reference, period and provider. - You listen to webhooks for terminal events (
invoice.paid,invoice.canceled,tax_document.issued,payout.paid, …) instead of polling.
Quick Start
Get your API Key
Go to Admin > Integrations > API Keys in the SalesOS Dashboard. Create a new key with the scopes
payments:write and payments:read. Copy the key — it will only be shown once.Your key looks like: sk_live_a1b2c3d4e5f6g7h8i9j0...Issue your first invoice (BR — PIX charge with auto NFS-e)
Create a PIX charge for a Brazilian customer. The optional Response (201 Created):Status flows from
tax_document block tells SalesOS to issue the NFS-e automatically after the charge is paid.open → paid (on PIX confirmation, ~seconds) → tax_document.status: issued (NFS-e emission, seconds to minutes). Listen to webhooks invoice.paid and tax_document.issued instead of polling.(Optional) Issue an invoice for a US customer (coming soon)
For US/EU customers SalesOS routes the charge to its international backend. This path is queued behind the Play2Sell LLC onboarding — calls today return No
501 provider_not_configured.tax_document block — US has no national fiscal document. Sales tax goes inside metadata or future line_items. The customer’s SSN is not collected by this endpoint: regular B2C/B2B charges in the US do not require it. SSN/EIN handling for 1099 reporting (when paying out US contractors) is a separate flow under Payouts, not Invoices.Pay a beneficiary
Pay a prize winner via PIX (BRL — routed automatically):Response (201 Created):You will receive a
payout.paid webhook within seconds for sandbox or under a minute for production PIX.Authentication
All requests require an API Key in theAuthorization header:
| Property | Details |
|---|---|
| Header | Authorization: Bearer sk_live_xxx |
| Scopes | payments:write (create/cancel), payments:read (list/retrieve) |
| Rate limit | Configurable per key (default: 1000 requests/hour) |
| Key format | sk_live_ (production) or sk_test_ (testing) |
Environments
- Production
- Staging
Base URL:
https://api.play2sell.comDashboard: https://dashboard.play2sell.comEndpoint Reference
Base path:/functions/v1/payments. All endpoints accept and return JSON.
Invoices
An Invoice is a payment request — a charge that will be collected from your customer via PIX, boleto, card or another method depending on region. The Invoice does not include a fiscal document by default; see Tax Documents below.POST /functions/v1/payments/invoices — create a charge
Customer that will be billed.
ISO-3166-1 alpha-2 (
BR, US, DE, …). Drives charge-provider routing.CPF (11 digits) or CNPJ (14 digits) for BR (required when
tax_document is set). For US/EU charges this field is not required — leave it out unless your accounting workflow specifically needs it. SSN/EIN are not collected here for normal charges.Full legal name (max 255 chars).
Valid email — used for receipts and (when applicable) fiscal-document delivery.
Total amount in minor units (e.g.
12345 = R$ 123,45). Must be positive.ISO-4217 code. Drives charge-provider routing alongside
customer.country.Human description shown on the payment artifact and (when issued) the fiscal document body (max 1000 chars).
The
description field is shown to the payer. For PIX charges it appears in the payer’s bank/wallet app next to the QR code; for boleto it prints on the slip; for fiscal documents it goes into the document body. Pick a value that reads well in all three contexts (e.g. "Consulting — April/2026", not "INV-2026-000123 internal-billing-row").How the charge will be collected.
One of
pix, boleto, bolepix, card (BR); card, ach_debit, bank_transfer (US — coming soon); card, sepa_debit, bank_transfer (EU — coming soon).For
pix / boleto / bolepix: how long the payment artifact stays valid. Defaults to 3600 (PIX) / 3 days (boleto).For
card: where to send the customer after card-checkout completion.Optional. When set, SalesOS will issue a fiscal document automatically after the charge is paid. BR-only today.
When
true, the fiscal document is issued the moment the charge transitions to paid. When false (or the field is omitted), use POST /v1/invoices/{id}/tax-documents later."nfse" (BR services — live), "nfe" (BR products — coming soon), "peppol" (EU — coming soon).Municipal service code, required when
type="nfse" (e.g. "01.05" for São Paulo consulting).Your internal cost center code (max 64 chars). See Common Control Fields.
External customer/contract reference (max 64 chars).
Free-form key-value pairs (max 50 keys, value max 500 chars).
our_number is not part of the request — SalesOS generates it on creation and returns it on the response (format: INV-<YYYY>-<sequence>). Store it for reconciliation.GET /functions/v1/payments/invoices/{id} — retrieve
payment_method artifact (PIX QR, boleto barcode, card checkout URL) and the inline tax_document summary when present.
GET /functions/v1/payments/invoices — list
Filters: status (open|paid|canceled|failed), payment_method.type, cost_center, our_number, contractor_reference, created[gte], created[lte]. Cursor pagination via limit (≤ 100), starting_after, ending_before.
POST /functions/v1/payments/invoices/{id}/cancel — void unpaid charge
tax_document already in issued state, the cancellation cascades into the fiscal document when the issuing municipality is still inside its cancellation window. A cancellation_window_expired error is returned otherwise.
Refunding a paid charge is not exposed in v1. If you need to reverse a settled payment, contact support — the team can process it manually. A future version of the API may expose a programmatic refund flow once the use-cases stabilize.
Tax Documents
A TaxDocument is the fiscal artifact attached to an Invoice — currently NFS-e (BR services). It has its own lifecycle and can be issued, retrieved or canceled independently of the underlying charge. Use this resource when you opted out ofauto_issue at Invoice creation, when an auto-issue attempt failed and you want to retry, or when you need to cancel only the fiscal document.
POST /functions/v1/payments/invoices/{id}/tax-documents — issue/retry
"nfse" today. Future: "nfe", "peppol".Required when
type="nfse".Optional free-form metadata, persisted on the document row.
tax_document.issued (or tax_document.failed) webhooks. On success the document carries document_number, xml_url, pdf_url and issued_at.
GET /functions/v1/payments/invoices/{id}/tax-documents/{doc_id} — retrieve
POST /functions/v1/payments/invoices/{id}/tax-documents/{doc_id}/cancel — cancel
cancellation_window_expired error is returned otherwise.
Payouts
POST /functions/v1/payments/payouts — pay a beneficiary
Routing is automatic: currency = "BRL" → PIX cashout; any other currency → international transfer.
Recipient of the payout.
Full legal name (max 255 chars).
Tax/identity document of the beneficiary. CPF (11 digits) or CNPJ (14 digits) for BR; passport or local tax ID for international. Mandatory.
Phone number in E.164 format (e.g.
"+5511999000111"). Mandatory — used for AML/KYC matching and payout-status notifications.Optional email address. When provided, payout receipts are sent to this address.
Destination account. For BR PIX, set
type: "pix" and provide key_type (cpf|cnpj|email|phone|evp) and key. For international, set type: "bank_transfer" and provide country, iban or account_number + routing_number per the destination country’s banking requirements.Amount in minor units. Must be positive.
ISO-4217. Drives backend routing.
One of
"prize", "commission", "vendor_payment", "other".Internal cost center code (max 64 chars).
External reference (max 64 chars).
Free-form key-value (max 50 keys, value max 500 chars).
our_number is not part of the request — SalesOS generates it on creation and returns it on the response (format: PO-<YYYY>-<sequence>). Store it for reconciliation.GET /functions/v1/payments/payouts/{id} — retrieve
GET /functions/v1/payments/payouts — list
Filters: status, provider, cost_center, our_number, contractor_reference, created[gte], created[lte]. Cursor pagination as above.
POST /functions/v1/payments/payouts/{id}/cancel — cancel
Only available for international transfers in created/incoming_payment_waiting state. PIX cashout is sync-final; once accepted, it cannot be cancelled — issue a payout in the opposite direction to compensate.
Balance Transactions
A unified ledger across providers. Read-only.GET /functions/v1/payments/balance-transactions — list
Filters:
| Filter | Values |
|---|---|
account | omie, rinne, wise |
type | charge, payout, fee, adjustment |
cost_center | any string |
our_number | any string |
contractor_reference | any string |
currency | ISO-4217 code |
created[gte] / created[lte] | ISO-8601 timestamps |
GET /functions/v1/payments/balance-transactions/{id} — retrieve
Each row exposes:
Stable ledger-entry id (
btxn_...).charge | payout | fee | adjustment.Signed minor units. Negative = money out, positive = money in.
ISO-4217.
amount - fee for outgoing; amount + fee ignored for incoming.Provider fee in minor units, always positive.
ISO-8601 date the funds settle.
ISO-8601 timestamp of the entry.
{ resource, id } — points back to the originating invoice/payout.Inherited from the source resource.
Inherited from the source resource.
Inherited from the source resource.
Common Control Fields
Every Invoice, Payout and Balance Transaction carries the same control fields. They round-trip onGET and are filterable on LIST.
| Field | Direction | Format | Max | Indexed | Use |
|---|---|---|---|---|---|
cost_center | input (required) | string, FK to your cost-centers table | 64 | yes | File every transaction against a budget line. |
contractor_reference | input (optional) | string | 64 | yes | External customer / contract reference (PO, contract id, event id). |
metadata | input (optional) | Record<string, string> | 50 keys, value 500 chars | no | Free-form. Use sparingly; not searchable. |
our_number | output only | string, format <PREFIX>-<YYYY>-<sequence> | 64 | yes | SalesOS-generated reference returned at creation. Distinct from id (ULID for API use). Stable, sequential per resource type per tenant. Use it as the bank-side reconciliation key. |
Why
our_number is server-generated. In Brazilian banking the cedente assigns “nosso número” — but in this API SalesOS is the issuance gateway, so we assign the value and hand it back. If you need to carry your own internal id, put it in contractor_reference (top-level, indexed) or metadata.* (free-form).Idempotency
AllPOST endpoints require an Idempotency-Key header — a unique string of your choosing (max 255 chars; we recommend a UUIDv4 or a deterministic key derived from your domain).
- The first call with a given key processes normally and the response is stored for 24 h.
- Replays with the same key and the same body return the same response, with a
Idempotent-Replay: trueresponse header. - Replays with the same key but a different body return
409 idempotency_key_reused.
Money & Currency
Amounts are integers in minor units to avoid floating-point errors:| Currency | Minor unit | 12345 means |
|---|---|---|
BRL | centavo | R$ 123,45 |
USD | cent | $123.45 |
EUR | cent | €123.45 |
amount with currency. Reject any payload that mixes scales (e.g. sending decimals).
Error Handling
All errors follow RFC 9457 Problem Details:400 — bad_request
400 — bad_request
Malformed JSON or missing required headers. Fix the request and resend.
401 — unauthorized
401 — unauthorized
403 — insufficient_scope
403 — insufficient_scope
Key is valid but lacks the required scope (
payments:write or payments:read). Edit the key in the Dashboard.404 — not_found
404 — not_found
The resource id does not exist or belongs to another tenant.
409 — idempotency_key_reused
409 — idempotency_key_reused
Same
Idempotency-Key used with a different body. Either use a fresh key or send the original body.422 — validation_error
422 — validation_error
Body validation failed. The
errors[] array lists field-level problems.429 — rate_limited
429 — rate_limited
Too many requests. The
Retry-After response header tells you how many seconds to wait.502 — provider_error
502 — provider_error
The downstream backend returned an error. The
detail field contains a sanitized message. Retry safely with the same Idempotency-Key.500 — server_error
500 — server_error
Internal error. Retry with exponential backoff (2s, 4s, 8s). Contact support if it persists.
Webhooks
Configure a webhook endpoint per environment in Admin > Integrations > Webhooks. SalesOS will POST signed JSON for every terminal event:| Event | Resource | Triggered when |
|---|---|---|
invoice.paid | invoice | Charge confirmed paid by the provider |
invoice.canceled | invoice | Unpaid charge voided |
invoice.failed | invoice | Charge failed permanently (e.g. card declined, boleto expired) |
tax_document.issued | tax_document | Fiscal document accepted by the municipality |
tax_document.canceled | tax_document | Fiscal document cancellation accepted |
tax_document.failed | tax_document | Issuance failed permanently |
payout.paid | payout | Funds confirmed delivered |
payout.failed | payout | Provider rejected the transfer |
payout.canceled | payout | Cancel request accepted (international transfers only) |
X-Pay-Event— event type (e.g.payout.paid).X-Pay-Signature—t=<unix>,v1=<hex-hmac-sha256>. Verify with the secret you set in the Dashboard.X-Pay-Delivery— unique delivery id, useful for deduplication.
Complete Code Examples
Best Practices
Cost-center hygiene
Pick a small, stable set of cost-center codes (≤ 50). Document them in your finance wiki. Reject API calls server-side that reference an unknown code — better to fail loudly than to silently miscategorize.Use both keys together for reconciliation
our_number(server-generated) is your bank-side key — printed on the boleto / sent to the provider, present in your bank statement file.contractor_reference(you provide) is your contract-side key — the PO, vendor agreement id or event id you owe against; present in your ERP.
Region routing
SalesOS auto-routes bycustomer.country and currency. You do not need to pick a backend; trust the routing.
Webhook reliability
Treat webhooks as the source of truth for terminal status. Polling works but burns rate-limit budget. Always verifyX-Pay-Signature and dedupe by X-Pay-Delivery.
Handling failures
- 422: fix the data and resend with a fresh
Idempotency-Key. - 429: back off as
Retry-Aftersays. - 502 (provider_error): retry with the same
Idempotency-Key— the original request did not commit, so retrying is safe. - 5xx: exponential backoff (2s, 4s, 8s). Contact support if it persists.
Rate Limits
Each API key has a configurable limit (default: 1000 requests per hour). The counter resets every hour.| Limit | Value |
|---|---|
| Default requests per hour | 1000 |
| Max payload size | 1 MB |
| Max list page size | 100 |
| Timeout per request | 60 seconds |
Retry-After header (seconds).
Security
- API keys are hashed with bcrypt — never stored in plaintext.
- Each key is scoped to a single tenant — no cross-tenant access.
- IP allowlists can be configured per key.
- All requests are logged for audit purposes (immutable, 7-year retention).
- Keys can be revoked instantly from the Dashboard.
Next Steps
Authentication
Create and manage API Keys
Activities Integration
Send CRM activities to SalesOS
Support
Need help? Contact our support team

