Cobranças
Cobranças (billings) são o recurso central da API: representam um valor a receber de um contato ou grupo, emitido via uma regra bancária (bank_rule) que define qual banco/convênio gera o boleto.
Veja conventions.md para paginação, autenticação, erros, rate limit e isolamento entre contas. Os campos do recurso
contactegroupaninhados na resposta seguem o que está em contacts.md e groups.md.
Modelo
Tipos: billing_type
A API só opera com cobranças avulsas — toda cobrança criada via API tem billing_type="billing".
Cobranças recorrentes (schedule) e carnês (carne) podem aparecer no GET se já existirem (criadas pela interface web), mas o POST força sempre billing e ignora qualquer valor enviado em billing_type. Para gerenciar schedule/carne via API, abra uma issue.
Modos de emissão: billing_issue_type
| Valor | Comportamento |
|---|---|
individual (default) | Cobrança endereçada a um único contato (contact_id obrigatório). group_id é zerado pelo servidor |
bulk | Cobrança em massa para todos os contatos de um grupo (group_id obrigatório). contact_id é zerado pelo servidor |
Status
status é a coluna persistida; virtual_status é o status mostrado nas respostas, e equivale a status exceto quando status="opened" e due_date < hoje — nesse caso virtual_status="expired".
status | Significado |
|---|---|
opened | Cobrança em aberto, aguardando pagamento |
under_review | Em análise pelo banco (intermediário) |
awaiting_capture | Aguardando captura no provedor (gateway digital) |
protested | Protestada |
in_dispute | Em disputa |
paid | Paga via gateway |
available | Valor disponível (compensado) |
discharged | Baixada manualmente (POST/pay_off) |
canceled | Cancelada |
returned | Devolvida pelo banco |
Valores adicionais expostos apenas em virtual_status:
- expired — derivado, não persiste.
Campos retornados
| Campo | Tipo |
|---|---|
id, code, your_code, reference | int / string |
status | string (ver tabela acima) |
virtual_status | string (status persistido + derivação expired) |
billing_type, billing_issue_type | string |
amount | decimal — calculado pelo servidor a partir dos billing_items (sum(quantity * amount)) |
due_date | date — se cair em sábado, ajustado para segunda; se domingo, para segunda |
competence | string MM/YYYY (ou null) |
evincive | string — texto descritivo, 10..80 chars, sem caracteres especiais |
observation | string até 255 |
discount_type | percentage ou fixed_amount |
discount, discount2 | decimal 0..999999999 |
discount_antecedence, discount_antecedence2 | inteiro 0..365 dias antes do vencimento. O segundo desconto vale para uma janela menor: discount_antecedence > discount_antecedence2 (ou discount_antecedence2 = 0) e discount > discount2 |
penalty_type | percentage ou fixed_amount |
penalty | decimal 0..999999999 |
cancel_penalty | boolean — quando true, anula juros e multa após vencido |
interest_type | percentage ou fixed_amount |
interest_period | monthly ou daily |
interest | decimal 0..999999999 |
instruction_type | protest ou devolution |
instruction_days | inteiro 0..99 |
bank_rule_id, contact_id, group_id | FKs (precisam ser da mesma conta) |
installments, installment_type, installment | int / string — só preenchidos em carnês legados criados pelo admin web. Sempre 1 / "none" / 1 em cobranças criadas via API |
gw_payment_link | string | null — link gerado pelo gateway externo (PagSeguro, MercadoPago) para o pagamento. Use isso pra redirecionar o cliente ou abrir em iframe |
gw_received_with | string | null — método com que o pagamento foi recebido (credit_card, bank_billet, pix, etc.) |
gw_payment_date | date | null — data do pagamento no gateway |
gw_gross_amount | decimal — valor bruto recebido |
gw_fee_amount | decimal — tarifa cobrada pelo gateway |
gw_net_amount | decimal — valor líquido (gross - fee) |
gw_discount_amount | decimal — desconto aplicado pelo gateway |
gw_extra_amount | decimal — acréscimos (juros/multa) aplicados pelo gateway |
gw_escrow_end_date | datetime | null — fim do período de retenção (escrow) no gateway |
gw_installment_count | int | null — número de parcelas escolhidas no checkout |
gw_payment_method_type | int | null — código numérico do tipo de método de pagamento no gateway |
gw_payment_method_code | int | null — código numérico do método de pagamento no gateway |
gw_type_id | int | null — identificador do tipo no gateway |
discount_amount, extra_amount, amount_payable | calculados com base em hoje (descontos antecipados, juros/multa por atraso) |
days_overdue | inteiro (negativo se ainda não venceu) |
overdue | boolean — days_overdue > 0 e virtual_status in (opened, expired) |
paid | boolean — virtual_status in (paid, available, discharged) |
display_amount_payable | decimal — versão "congelada" de amount_payable: usa a data do registro do boleto AFPAY quando aplicável, ou gw_gross_amount - gw_discount_amount + gw_extra_amount quando pago. Use este valor para exibir ao cliente final no checkout |
return_date | due_date + instruction_days se instruction_type=devolution; caso contrário null |
redirect_url | URL para onde o pagador é redirecionado após pagar no checkout do Sacador (eco do que foi configurado) |
notification_url | URL adicional de notificação configurada na cobrança (eco do que foi configurado) |
created_at, updated_at | ISO-8601 |
contact | objeto {id, description, email, document} ou ausente |
group | objeto {id, description} ou ausente |
billing_items | array — só aparece em show, create, update, cancel e pay_off. Não retornado na listagem (GET/v1/billings). Ver billing_items |
billing_reminder_deliveries | array de tentativas de notificação — só aparece em show, create, update, cancel e pay_off. Ver billing_reminder_deliveries |
bank_billets | array de boletos bancários — só aparece em show, create, update, cancel e pay_off. Formato detalhado em bank_billets.md. Para listagem dedicada use GET/v1/billings/:billing_id/bank_billets |
billing_items
Cada cobrança tem pelo menos 1 item.
| Campo | Tipo | Regras |
|---|---|---|
id | int | Atribuído pelo servidor após criação |
description | string | Obrigatório, até 100 chars |
quantity | int | 1..9999999 |
amount | decimal | ≥ 0,01 |
O amount total da cobrança é sempre sum(quantity * amount) dos itens — não envie amount no nível da cobrança, é ignorado.
billing_reminder_deliveries
Histórico de envios de lembretes (email/WhatsApp). Cada item:
{
"id": 5,
"channel": "email",
"status": "delivered",
"delivered_at": "2026-04-12T09:00:00.000Z",
"error_message": null,
"attempts_count": 1,
"created_at": "2026-04-12T09:00:00.000Z",
"updated_at": "2026-04-12T09:00:01.000Z"
}
Read-only. Use para auditoria, não para disparar lembretes.
Checkout transparente
Para construir um checkout próprio (sem redirecionar para o Sacador), use a seguinte combinação de campos:
| Necessidade | Onde está |
|---|---|
| Valor a cobrar (já com desconto/juros aplicados) | display_amount_payable em GET/v1/billings/:id |
| Boleto bancário (linha digitável, barcode, QR Pix) | Itens do array bank_billets — ver bank_billets.md |
| Link do gateway (PagSeguro/MercadoPago) | gw_payment_link |
| Confirmação de pagamento | Webhook billing.updated (ver webhooks.md) — quando o status muda para paid/available/discharged |
| Reconciliação financeira do que efetivamente entrou | Campos gw_gross_amount, gw_fee_amount, gw_net_amount, gw_payment_date, gw_received_with |
| Redirecionar pagador pós-pagamento | Configure redirect_url no POST ou PATCH — o Sacador redireciona ao final do fluxo |
| Notificação adicional além dos webhooks | Configure notification_url |
Fluxo típico:
- POST
/v1/billingscomredirect_urlapontando para sua página de obrigado eyour_codepara correlação. - Mostre
display_amount_payablee ofereça os métodos disponíveis a partir debank_billets[].pix_qr_code,bank_billets[].digitable_linee/ou redirect paragw_payment_link. - Aguarde o webhook
billing.updatedpara confirmar e baixar internamente no seu sistema. - Para reconciliar valores recebidos, use
gw_gross_amountegw_net_amount.
Quando uma cobrança é editável
Determina o que pode ser alterado em PATCH/v1/billings/:id. Uma cobrança é editável quando todas as condições abaixo valem:
- não é
carne(carnês nunca são editáveis); e - é
schedule(sempre editável), ou ébillingno statusopened/expiredcom o boleto bancário (quando existir) ainda editável e não está em gateway PagSeguro.
Em cobranças não-editáveis, o PATCH aceita somente dois campos, e apenas se o status atual permitir (opened ou expired):
due_date— reagendamentocancel_penalty— zerar juros/multa
Qualquer outro campo enviado nessa situação é silenciosamente ignorado.
Endpoints
GET/v1/billings
Lista cobranças da conta. Ordem: id DESC. Pré-carrega bank_billet, contact, group, billing_items e billing_reminder_deliveries para evitar N+1.
Query params:
| Param | Tipo | Comportamento |
|---|---|---|
billing_type | string | Default "billing". Use "schedule" ou "carne" para listar recorrências/carnês legados criados pelo admin web |
status | string ou string separada por espaços | Aceita os valores de status + os agregados opened (apenas com due_date >= hoje), expired (status=opened, due_date < hoje), paid (paid+available). Múltiplos valores: ?status=opened%20under_review |
contact_id | int | Filtra por contato |
group_id | int | Filtra cobranças endereçadas a contatos do grupo |
billing_id | int | Filtra parcelas filhas de um carnê (campo billing_id próprio = id do carnê pai) |
your_code | string | Igualdade exata |
start_created_at, end_created_at | date YYYY-MM-DD | Faixa por data de criação (ambos obrigatórios para o filtro ativar) |
start_due_date, end_due_date | date YYYY-MM-DD | Faixa por data de vencimento (ambos obrigatórios) |
s | string | Busca por documento (CPF/CNPJ) OU description do contato OU evincive (LIKE) |
page, per_page | int | Padrão de paginação |
Exemplo cURL:
curl -X GET 'https://api.sacador.com.br/v1/billings?status=opened&page=1&per_page=25' \
-H 'Authorization: Bearer sct_seu_token_aqui'
Resposta 200 OK:
{
"billings": [
{
"id": 123,
"code": "ABC123",
"your_code": "ERP-2026-0001",
"reference": "Mensalidade",
"status": "opened",
"virtual_status": "opened",
"billing_type": "billing",
"billing_issue_type": "individual",
"amount": "150.00",
"due_date": "2026-06-10",
"competence": "05/2026",
"evincive": "Mensalidade de maio",
"observation": null,
"penalty_type": "percentage",
"penalty": "2.0",
"cancel_penalty": false,
"interest_type": "percentage",
"interest_period": "monthly",
"interest": "1.0",
"instruction_type": "protest",
"instruction_days": 5,
"bank_rule_id": 7,
"contact_id": 101,
"group_id": null,
"discount_amount": "0.0",
"extra_amount": "0.0",
"amount_payable": "150.00",
"days_overdue": -30,
"overdue": false,
"return_date": null,
"created_at": "2026-05-11T10:00:00.000Z",
"updated_at": "2026-05-11T10:00:00.000Z",
"contact": {
"id": 101,
"description": "Maria Silva",
"email": "maria@exemplo.com",
"document": "12345678901"
}
}
]
}
Note que
billing_items,billing_reminder_deliveriesebank_billetsnão aparecem na listagem. Para obtê-los use GET/v1/billings/:id(que retorna tudo) ou os endpoints dedicados.
GET/v1/billings/:id
Retorna uma cobrança individual. Esta é a resposta canônica usada também por POST, PATCH, POST/cancel e POST/pay_off — todos retornam o mesmo formato. É o que você consome para construir o checkout transparente.
Exemplo cURL:
curl -X GET 'https://api.sacador.com.br/v1/billings/123' \
-H 'Authorization: Bearer sct_seu_token_aqui'
Resposta 200 OK:
{
"id": 123,
"code": "0F7B91A2C3D4E5F60718293A4B5C6D7E",
"your_code": "ERP-2026-0001",
"reference": "Mensalidade",
"status": "opened",
"virtual_status": "opened",
"billing_type": "billing",
"billing_issue_type": "individual",
"amount": "150.00",
"due_date": "2026-06-10",
"competence": "05/2026",
"evincive": "Mensalidade de maio",
"observation": null,
"discount_type": "percentage",
"discount": "10.0",
"discount_antecedence": 5,
"discount_type2": null,
"discount2": "0.0",
"discount_antecedence2": 0,
"penalty_type": "percentage",
"penalty": "2.0",
"cancel_penalty": false,
"interest_type": "percentage",
"interest_period": "monthly",
"interest": "1.0",
"instruction_type": "protest",
"instruction_days": 5,
"bank_rule_id": 7,
"contact_id": 101,
"group_id": null,
"installments": 1,
"installment_type": "none",
"installment": 1,
"redirect_url": "https://store.example.com/thanks",
"notification_url": null,
"gw_payment_link": null,
"gw_received_with": null,
"gw_payment_date": null,
"gw_gross_amount": "0.0",
"gw_fee_amount": "0.0",
"gw_net_amount": "0.0",
"gw_discount_amount": "0.0",
"gw_extra_amount": "0.0",
"gw_escrow_end_date": null,
"gw_installment_count": null,
"gw_payment_method_type": null,
"gw_payment_method_code": null,
"gw_type_id": null,
"created_at": "2026-05-12T10:00:00.000Z",
"updated_at": "2026-05-12T10:00:00.000Z",
"virtual_status": "opened",
"return_date": null,
"discount_amount": "15.0",
"extra_amount": "0.0",
"amount_payable": "135.0",
"display_amount_payable": "135.0",
"days_overdue": -29,
"overdue": false,
"paid": false,
"contact": {
"id": 101,
"description": "Maria Silva",
"email": "maria@exemplo.com",
"document": "12345678901"
},
"billing_items": [
{
"id": 555,
"description": "Mensalidade",
"quantity": 1,
"amount": "150.00"
}
],
"billing_reminder_deliveries": [
{
"id": 5,
"channel": "email",
"status": "delivered",
"delivered_at": "2026-05-12T09:00:00.000Z",
"error_message": null,
"attempts_count": 1,
"created_at": "2026-05-12T09:00:00.000Z",
"updated_at": "2026-05-12T09:00:01.000Z"
}
],
"bank_billets": [
{
"id": 9001,
"status": "opened",
"bank_symbol": "bb",
"amount": "150.00",
"due_date": "2026-06-10",
"barcode": "00190000000000010000000000000000000000000000",
"digitable_line": "00190.00009 00000.000009 00000.000009 0 00000000000000",
"pix_qr_code": "00020101021226...",
"our_number": "1234567",
"days_overdue": -29,
"overdue": false,
"editable": false,
"created_at": "2026-05-12T10:00:00.000Z",
"updated_at": "2026-05-12T10:00:00.000Z"
}
]
}
Campos do array
bank_billetsforam resumidos acima — o formato completo (recipient/payer, datas de pagamento, valores líquidos, provider, etc.) está em bank_billets.md.
Erros: 404 se inexistente ou de outra conta.
POST/v1/billings
Cria uma cobrança.
Body:
{
"billing": {
"bank_rule_id": 7,
"contact_id": 101,
"billing_issue_type": "individual",
"due_date": "2026-06-10",
"evincive": "Mensalidade de maio",
"instruction_type": "protest",
"instruction_days": 5,
"penalty_type": "percentage",
"penalty": 2,
"interest_type": "percentage",
"interest_period": "monthly",
"interest": 1,
"billing_items": [
{ "description": "Mensalidade", "quantity": 1, "amount": 150.00 }
]
}
}
Campos aceitos (qualquer outro é ignorado, incluindo account_id, code, status):
bank_rule_id, contact_id, group_id, billing_issue_type, evincive, observation, reference, your_code, competence, due_date, penalty, penalty_type, interest, interest_type, interest_period, instruction_type, instruction_days, redirect_url, notification_url, billing_items.
O campo
amountno nível da cobrança é somente leitura — sempre recalculado pelo servidor comosum(quantity * amount)dosbilling_items. Se você enviar, é descartado.
billing_typenão é aceito: a API sempre cria combilling_type="billing". Qualquer valor enviado é descartado.
Exemplo cURL (cobrança individual):
curl -X POST 'https://api.sacador.com.br/v1/billings' \
-H 'Authorization: Bearer sct_seu_token_aqui' \
-H 'Content-Type: application/json' \
-d '{
"billing": {
"bank_rule_id": 7,
"contact_id": 101,
"billing_issue_type": "individual",
"due_date": "2026-06-10",
"evincive": "Mensalidade de maio",
"your_code": "ERP-2026-0001",
"competence": "05/2026",
"penalty_type": "percentage",
"penalty": 2,
"interest_type": "percentage",
"interest_period": "monthly",
"interest": 1,
"billing_items": [
{ "description": "Mensalidade", "quantity": 1, "amount": 150.00 }
]
}
}'
Exemplo cURL (cobrança em massa para um grupo):
curl -X POST 'https://api.sacador.com.br/v1/billings' \
-H 'Authorization: Bearer sct_seu_token_aqui' \
-H 'Content-Type: application/json' \
-d '{
"billing": {
"bank_rule_id": 7,
"group_id": 12,
"billing_issue_type": "bulk",
"due_date": "2026-06-10",
"evincive": "Mensalidade junho",
"billing_items": [
{ "description": "Mensalidade", "quantity": 1, "amount": 99.90 }
]
}
}'
billing_items em POST
Cada elemento aceita: description, quantity, amount. Não envie id na criação.
Validações
Resumo das regras que mais aparecem em 422:
bank_rule_idobrigatório e pertencente à contacontact_idobrigatório sebilling_issue_type=individual; precisa ser da contagroup_idobrigatório sebilling_issue_type=bulk; precisa ser da contadue_dateobrigatóriobilling_items.length >= 1, e cada item válido (descrição até 100 chars, quantidade 1..9999999, valor ≥ 0,01)evincivese preenchido: 10..80 chars, sem@#$%^&*"{}|<>\[]interest,penalty,discount,discount2≤ 999.999.999- Quando ambos os descontos são usados:
discount > discount2ediscount_antecedence > discount_antecedence2(o segundo desconto deve ter valor menor e janela mais próxima do vencimento) - Conta não verificada (sem KYC aprovado): valor da cobrança limitado a
R$ 10,00
Resposta 201 Created: corpo idêntico a GET/v1/billings/:id.
Observação sobre boleto bancário
A criação do bank_billet no banco é assíncrona. O POST retorna a cobrança imediatamente com status=opened; o boleto fica disponível assim que o banco confirma o registro. Reconsulte via GET/v1/billings/:id se precisar do provider_id.
PATCH/v1/billings/:id
Atualiza uma cobrança.
- Cobranças editáveis (ver Quando uma cobrança é editável): aceita os mesmos campos do
POST. - Cobranças não editáveis em status
opened/expired: aceita apenasdue_dateecancel_penalty. - Demais casos: o
PATCHé aceito (200 OK), mas nenhuma alteração é efetuada.
Exemplo cURL (prorrogar vencimento):
curl -X PATCH 'https://api.sacador.com.br/v1/billings/123' \
-H 'Authorization: Bearer sct_seu_token_aqui' \
-H 'Content-Type: application/json' \
-d '{
"billing": {
"due_date": "2026-07-10"
}
}'
billing_items em PATCH — semântica de destruição implícita
Quando você envia billing_items no body de um PATCH:
- Itens com
idexistentes na cobrança são atualizados. - Itens sem
idsão criados. - Itens existentes na cobrança que não aparecem no array são removidos automaticamente.
Exemplo: cobrança tem itens [1, 2, 3]. Você envia:
{
"billing": {
"billing_items": [
{ "id": 1, "description": "Mensalidade atualizada", "quantity": 1, "amount": 200 },
{ "description": "Item novo", "quantity": 1, "amount": 50 }
]
}
}
Resultado: item 1 atualizado, item novo criado, itens 2 e 3 removidos. Para manter um item, inclua-o (ao menos com id) no array.
Para apagar todos os itens, envie
billing_items: []— mas oPOST/PATCHfalhará com422porque a cobrança precisa de ao menos 1 item. Use o cancelamento se o objetivo for descontinuar a cobrança.
Erros: 404 (outra conta), 422 (validações).
DELETE/v1/billings/:id
Apaga uma cobrança.
Restrição: somente cobranças com billing_type=schedule (recorrências legadas) podem ser deletadas. Cobranças billing criadas via API não podem ser deletadas — use POST/v1/billings/:id/cancel para descontinuá-las. Tentativa de delete retorna:
{
"status": 422,
"errors": ["Não é possível alterar esta cobrança"]
}
Exemplo cURL:
curl -X DELETE 'https://api.sacador.com.br/v1/billings/123' \
-H 'Authorization: Bearer sct_seu_token_aqui'
Resposta 204 No Content quando bem-sucedido.
Erros: 404 (outra conta), 422 (não é schedule).
POST/v1/billings/:id/cancel
Cancela uma cobrança.
Body:
{
"billing": {
"cancellation_reason": "Cliente desistiu da compra"
}
}
| Campo | Regras |
|---|---|
cancellation_reason | Obrigatório, mínimo 3 caracteres, máximo 1000 |
Exemplo cURL:
curl -X POST 'https://api.sacador.com.br/v1/billings/123/cancel' \
-H 'Authorization: Bearer sct_seu_token_aqui' \
-H 'Content-Type: application/json' \
-d '{
"billing": {
"cancellation_reason": "Cliente desistiu da compra"
}
}'
Resposta 200 OK: corpo idêntico a GET/v1/billings/:id, com status="canceled".
Erros: 404 (outra conta), 422 (motivo curto/ausente, ou cobrança em status que não permite cancelamento).
POST/v1/billings/:id/pay_off
Dá baixa manual (descarregamento).
Body:
{
"billing": {
"bank_account_id": 42,
"discharge_reason": "bank_deposit",
"gw_gross_amount": 150.00
}
}
| Campo | Regras |
|---|---|
bank_account_id | Obrigatório. Precisa pertencer à mesma conta. Não pode ser conta do tipo sacador |
discharge_reason | Obrigatório. "bank_deposit" ou "payment_in_hand" |
gw_gross_amount | Valor efetivamente recebido (pode diferir de amount se houve juros/desconto manual) |
Após baixa o status vira discharged e um lançamento de caixa (Cashier) é criado automaticamente na conta bancária informada.
Exemplo cURL:
curl -X POST 'https://api.sacador.com.br/v1/billings/123/pay_off' \
-H 'Authorization: Bearer sct_seu_token_aqui' \
-H 'Content-Type: application/json' \
-d '{
"billing": {
"bank_account_id": 42,
"discharge_reason": "bank_deposit",
"gw_gross_amount": 150.00
}
}'
Resposta 200 OK: corpo idêntico a GET/v1/billings/:id, com status="discharged".
Erros: 404 (outra conta), 422 (bank_account_id inválido, conta sacador, discharge_reason inválido, ou status que não permite baixa).