Skip to main content

Payments Integration

Try signed requests in your browser at the API Sandbox — paste your API key and secret, and the playground signs requests automatically.
Examples below show the wire-format Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE header. To compute the signature in your code, use the signedRequest helper in Authentication.
The Payments integration is a provider-agnostic REST API for the core financial operations every backoffice needs:
  1. 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.
  2. Cancel an invoicePOST /payments/invoices/{id}/cancel (voids unpaid charge).
  3. 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.
  4. Pay a beneficiary (a “prize” or any payout)POST /payments/payouts.
  5. Read a bank statementGET /payments/balance-transactions.
Every transaction carries first-class control fields used by your finance team for reconciliation: 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’s country and currency. You do not need to choose. Outbound payouts route by destination: BRL → PIX cashout; other currencies → international transfer.

How It Works

  1. You get an API Key in the SalesOS Dashboard (Admin > Integrations > API Keys) with the right scopes (payments:write, payments:read).
  2. 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.
  3. (Optional) A tax document is attached. When currency = BRL and you set tax_document.auto_issue: true, SalesOS issues an NFS-e automatically after the charge is confirmed paid. You can also call POST /invoices/{id}/tax-documents later to issue or retry.
  4. You pay beneficiaries (employees, partners, prize winners). SalesOS routes BRL via PIX and other currencies via international transfer.
  5. 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.
  6. You listen to webhooks for terminal events (invoice.paid, invoice.canceled, tax_document.issued, payout.paid, …) instead of polling.

Quick Start

1

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...
2

Issue your first invoice (BR — PIX charge with auto NFS-e)

Create a PIX charge for a Brazilian customer. The optional tax_document block tells SalesOS to issue the NFS-e automatically after the charge is paid.
Response (201 Created):
Status flows from openpaid (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.
3

(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 501 provider_not_configured.
No 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.
4

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.
5

Read your statement

List every entry tagged with cost_center=CC-AWARDS for the month:
Response (200 OK):

Authentication

All requests require an API Key in the Authorization header:
See the Authentication page for details on creating and managing API Keys.

Environments

Base URL: https://api.play2sell.comDashboard: https://dashboard.play2sell.com

Endpoint 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

object
required
Customer that will be billed.
string
required
ISO-3166-1 alpha-2 (BR, US, DE, …). Drives charge-provider routing.
string
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.
string
required
Full legal name (max 255 chars).
string
required
Valid email — used for receipts and (when applicable) fiscal-document delivery.
string
Optional phone number (max 20 chars). E.164 format recommended (e.g. +5511999000111). Used by some providers for AML/KYC matching and transactional notifications.
integer
required
Total amount in minor units (e.g. 12345 = R$ 123,45). Must be positive.
string
required
ISO-4217 code. Drives charge-provider routing alongside customer.country.
string
required
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").
object
required
How the charge will be collected.
string
required
One of pix, boleto, bolepix, card (BR); card, ach_debit, bank_transfer (US — coming soon); card, sepa_debit, bank_transfer (EU — coming soon).
integer
For pix / boleto / bolepix: how long the payment artifact stays valid. Defaults to 3600 (PIX) / 3 days (boleto).
string
For card: where to send the customer after card-checkout completion.
object
Optional. When set, SalesOS will issue a fiscal document automatically after the charge is paid. BR-only today.
boolean
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.
string
"nfse" (BR services — live), "nfe" (BR products — coming soon), "peppol" (EU — coming soon).
string
Municipal service code, required when type="nfse" (e.g. "01.05" for São Paulo consulting).
string
Your internal cost center code (max 64 chars). Optional — when omitted, the charge is filed against the tenant’s default cost center. Sending a code that is not registered for the tenant returns 422 unknown_cost_center. See Common Control Fields.
string
External customer/contract reference (max 64 chars).
object
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

The response always includes the current 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

If the Invoice has an attached 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 of auto_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

string
required
"nfse" today. Future: "nfe", "peppol".
string
Required when type="nfse".
object
Optional free-form metadata, persisted on the document row.
Response (202 Accepted):
Issuance is asynchronous. Listen for 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

Subject to the issuing municipality’s cancellation window (typically the day of issuance for NFS-e). A 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.
object
required
Recipient of the payout.
string
required
Full legal name (max 255 chars).
string
required
Tax/identity document of the beneficiary. CPF (11 digits) or CNPJ (14 digits) for BR; passport or local tax ID for international. Mandatory.
string
required
Phone number in E.164 format (e.g. "+5511999000111"). Mandatory — used for AML/KYC matching and payout-status notifications.
string
Optional email address. When provided, payout receipts are sent to this address.
object
required
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.
integer
required
Amount in minor units. Must be positive.
string
required
ISO-4217. Drives backend routing.
string
required
One of "prize", "commission", "vendor_payment", "other".
string
Internal cost center code (max 64 chars). Optional — when omitted, the payout is filed against the tenant’s default cost center. Sending a code that is not registered for the tenant returns 422 unknown_cost_center.
string
External reference (max 64 chars).
object
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:

GET /functions/v1/payments/balance-transactions/{id} — retrieve

Each row exposes:
string
Stable ledger-entry id (btxn_...).
string
charge | payout | fee | adjustment.
integer
Signed minor units. Negative = money out, positive = money in.
string
ISO-4217.
integer
amount - fee for outgoing; amount + fee ignored for incoming.
integer
Provider fee in minor units, always positive.
string
ISO-8601 date the funds settle.
string
ISO-8601 timestamp of the entry.
object
{ resource, id } — points back to the originating invoice/payout.
string
Inherited from the source resource.
string
Inherited from the source resource.
string
Inherited from the source resource.

Common Control Fields

Every Invoice, Payout and Balance Transaction carries the same control fields. They round-trip on GET and are filterable on LIST.
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

All POST 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: true response 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: Currencies follow ISO-4217 (3 uppercase letters). Always pair amount with currency. Reject any payload that mixes scales (e.g. sending decimals).

Error Handling

All errors follow RFC 9457 Problem Details:
Malformed JSON or missing required headers. Fix the request and resend.
API key missing, invalid or expired. Check the Authorization header. Generate a new key if expired.
Key is valid but lacks the required scope (payments:write or payments:read). Edit the key in the Dashboard.
The resource id does not exist or belongs to another tenant.
Same Idempotency-Key used with a different body. Either use a fresh key or send the original body.
Too many requests. The Retry-After response header tells you how many seconds to wait.
The downstream backend returned an error. The detail field contains a sanitized message. Retry safely with the same Idempotency-Key.
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: Each request includes:
  • X-Pay-Event — event type (e.g. payout.paid).
  • X-Pay-Signaturet=<unix>,v1=<hex-hmac-sha256>. Verify with the secret you set in the Dashboard.
  • X-Pay-Delivery — unique delivery id, useful for deduplication.
Signature verification (Node.js):
Sample payload:

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.
Joining the two at month-end gives you a one-click reconciliation between the bank, the SalesOS Payments API and your ERP.

Region routing

SalesOS auto-routes by customer.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 verify X-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-After says.
  • 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. Rate-limited responses include a 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.
Never expose your API key in client-side code (JavaScript running in the browser, mobile apps, or public repos). The Payments API must be called only from your backend server.

Next Steps

Authentication

Create and manage API Keys

Activities Integration

Send CRM activities to SalesOS

Support

Need help? Contact our support team