Despesas (expenses)
O recurso expenses expõe as despesas do seu financeiro. Cada despesa traz os
valores (original, desconto e final), os vínculos com categoria, fornecedor,
centro de custo, conta bancária e cartão, as datas de vencimento e pagamento,
tags livres e as informações de recorrência e parcelas. É um recurso de leitura
e escrita.
Para criar despesas com parcelas e rateios consistentes, use os endpoints
/expenses/operations. Eles confirmam o conjunto em uma transação, trabalham
com centavos inteiros BRL e retornam um recibo recuperável pelo mesmo
Idempotency-Key. As rotas originais continuam disponíveis para despesas que
ainda não entraram nesse fluxo operacional.
| Módulo | despesas |
| Escopos | expenses:read para leitura, expenses:write para escrita |
| Operações | leitura e escrita |
Sem o módulo no contrato, a resposta é 403 module_not_enabled. Sem o escopo na
chave, é 403 insufficient_scope. expenses:write concede também a leitura.
Endpoints
Seção intitulada “Endpoints”| Método | Endpoint | Escopo | Descrição |
|---|---|---|---|
GET |
/v1/expenses |
expenses:read |
Lista despesas, com paginação por cursor |
GET |
/v1/expenses/{id} |
expenses:read |
Busca uma despesa pelo id, com ?include=recurring opcional |
POST |
/v1/expenses |
expenses:write |
Cria uma despesa e responde 201 |
PATCH |
/v1/expenses/{id} |
expenses:write |
Atualização parcial: envie só os campos que mudam |
DELETE |
/v1/expenses/{id} |
expenses:write |
Desativa a despesa e responde 204 |
POST |
/v1/expenses/operations |
expenses:write |
Cria cabeçalho, parcelas e rateios juntos |
PATCH |
/v1/expenses/{id}/operations |
expenses:write |
Edita o conjunto preservando o histórico financeiro |
POST |
/v1/expenses/{id}/cancel |
expenses:write |
Cancela com motivo após reverter pagamentos |
GET |
/v1/expenses/{id}/installments |
expenses:read |
Consulta parcelas e valor liquidado em centavos |
GET |
/v1/expenses/{id}/allocations |
expenses:read |
Consulta rateios por centro de custo |
POST |
/v1/expenses/recurrences |
expenses:write |
Cria regra a partir de uma despesa operacional |
GET |
/v1/expenses/recurrences |
expenses:read |
Lista regras gerenciadas por este fluxo |
GET |
/v1/expenses/recurrences/{id} |
expenses:read |
Consulta regra e próxima competência |
PATCH |
/v1/expenses/recurrences/{id} |
expenses:write |
Altera frequência, intervalo ou término |
POST |
/v1/expenses/recurrences/{id}/pause |
expenses:write |
Pausa novas ocorrências |
POST |
/v1/expenses/recurrences/{id}/resume |
expenses:write |
Retoma regra pausada |
POST |
/v1/expenses/recurrences/{id}/end |
expenses:write |
Encerra definitivamente a regra |
POST |
/v1/expenses/recurrences/{id}/generate |
expenses:write |
Gera a próxima ocorrência já vencida ou de hoje |
GET |
/v1/expenses/recurrences/{id}/occurrences |
expenses:read |
Consulta despesas geradas por competência |
Operação composta em centavos
Seção intitulada “Operação composta em centavos”Todas as novas escritas da tabela acima exigem Idempotency-Key com UUID e
retornam 200, com data e meta.request_id, meta.command_id, meta.action.
Use o mesmo UUID e corpo ao repetir uma requisição sem resposta. Mudar o corpo
com esse UUID resulta em 409 idempotency_conflict.
Exemplo de corpo de POST /v1/expenses/operations para R$ 100,00:
{ "description": "Equipamentos para o escritório", "expense_type": "one_time", "amount_cents": 10000, "due_date": "2026-10-31", "installments": 3, "payment_method": "bank_slip", "allocations": [ { "cost_center_id": "5d4e3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b", "basis_points": 10000 } ]}expense_type aceita fixed, variable ou one_time. amount_cents deve ser
inteiro positivo, no máximo 9.999.999.999, e deve comportar pelo menos um centavo
por parcela. installments aceita de 1 a 120 e assume 1. Nesse exemplo, as parcelas
são 3333, 3333 e 3334 centavos; o resíduo fica na última parcela.
Os campos opcionais são purchase_date, category_id, subcategory_id,
supplier_id, bank_account_id, credit_card_id, payment_method, notes e
allocations. Categoria, fornecedor, conta, cartão e centros devem estar ativos
na mesma equipe. A subcategoria deve pertencer à categoria informada. Não envie
equipe, autor, estado de pagamento, saldo ou vínculo com Produção/Repasses.
Um rateio usa somente um dos formatos por requisição:
amount_cents: os valores devem somar exatamente o total da despesa.basis_points: os pesos inteiros devem somar 10000, equivalentes a 100%.
Centros duplicados são rejeitados. No formato percentual, cada parcela de rateio
é truncada em centavos; o último centro informado recebe o resíduo. Omitir rateios
na criação deixa a despesa sem rateio. allocations: [] remove o rateio na edição;
omitir o campo preserva seus valores. Ao mudar o total de uma despesa já rateada,
envie também o rateio correspondente ao novo total.
O PATCH /{id}/operations recebe somente os campos alterados. Ele preserva a
identidade das parcelas existentes. Parcelas com histórico de baixa não podem
ser apagadas nem ter a quantidade da série alterada. Uma baixa pendente de estorno
impede mudanças financeiras; parcelas que já participaram de pagamento de fatura
mantêm valor e vínculo com essa fatura. Edições descritivas continuam disponíveis.
Para cancelar, envie {"reason":"Compra registrada em duplicidade"} a
POST /{id}/cancel. Primeiro reverta as baixas ou pagamentos de fatura aplicáveis.
O motivo fica registrado; repetir o cancelamento não gera novas reversões nem
apaga parcelas. A despesa recebe status: "cancelled".
Baixas e cartão
Seção intitulada “Baixas e cartão”Registre baixas parciais ou totais em
POST /v1/expenses/installments/{id}/settlements, indicando amount_cents,
payer_kind (bank_account ou wallet), payer_id, effective_date e
payment_method. O histórico fica em GET /v1/expenses/{id}/settlements;
estornos usam POST /v1/expenses/settlements/{id}/reversals com valor, data e motivo.
Essas operações registram o movimento interno e não executam um pagamento no banco.
Despesas credit_card ou credit_card_installments exigem credit_card_id.
purchase_date determina a primeira fatura, usando due_date se omitida. A data
de fechamento é inclusiva, dias 29–31 são limitados ao último dia do mês, e o
vencimento é a primeira ocorrência posterior ao fechamento. Uma fatura fechada
encaminha novas obrigações à próxima aberta. Cada parcela pertence à sua fatura;
a baixa direta da parcela de cartão é recusada. O caixa muda no
pagamento da fatura.
Leituras operacionais
Seção intitulada “Leituras operacionais”Parcelas, rateios, regras e ocorrências aceitam limit de 1 a 100 (padrão 50) e
cursor UUID. A resposta inclui pagination.has_more e pagination.next_cursor.
Essas consultas usam projeções fixas; não aceitam os filtros e fields das rotas
legadas. Parcelas incluem amount_cents, settled_cents, vencimento, estado,
número, quantidade da série e credit_card_invoice_id. settled_cents corresponde
às baixas diretas, líquidas de estornos; para cartão, consulte o histórico da fatura.
Recorrências operacionais
Seção intitulada “Recorrências operacionais”Crie a regra com uma despesa já gerenciada pelas operações acima:
{ "expense_template_id": "2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a", "frequency": "monthly", "interval": 1, "start_date": "2027-01-31", "end_date": "2027-12-31"}frequency aceita daily, weekly, monthly e yearly; interval vai de 1 a
120. start_date é a primeira nova competência. A despesa usada como modelo
continua existindo e não é contada automaticamente como ocorrência da regra.
Cada competência gera cabeçalho, parcelas e rateios com a autoria da regra. A
data permanece ancorada: uma série mensal iniciada em 31 de janeiro passa por
28/29 de fevereiro e volta a 31 de março. O gerador existente e o endpoint
generate compartilham a mesma proteção contra duplicação. Cada chamada gera no
máximo uma competência; datas futuras são recusadas. Ao ultrapassar end_date, a
regra é encerrada e a resposta contém a regra, em vez de uma nova despesa.
Envie {} em pause, resume, end e generate, sempre com UUID
Idempotency-Key. Pausar não muda ocorrências anteriores. Retomar mantém a próxima
competência pendente, inclusive se estiver atrasada. Encerrar é definitivo.
O PATCH aceita frequency, interval e end_date (null remove a data final);
alterações na frequência se aplicam ao avanço após a próxima competência já agendada.
A geração revalida a chave criadora, o autor e o acesso ao módulo. Revogar ou expirar essa chave interrompe novas gerações; não altera as despesas existentes. As consultas operacionais de regras listam somente regras criadas por esse fluxo. Regras anteriores continuam acessíveis pelo contrato legado de recorrência.
Conflitos de histórico, origem incompatível, regra pausada/encerrada ou geração
antecipada retornam 409 resource_conflict. Falhas transitórias de concorrência
retornam 503; repita com o mesmo UUID.
Os campos e exemplos a seguir descrevem as rotas originais /expenses e
/expenses/{id}, que mantêm valores decimais em reais.
| Campo | Tipo | Descrição |
|---|---|---|
id |
uuid |
Identificador da despesa |
expense_number |
string |
Número da despesa. Se omitido no POST, é gerado como API-<timestamp>-<sufixo> |
expense_type |
string |
Tipo da despesa. Obrigatório na criação |
description |
string |
Descrição da despesa. Obrigatório na criação |
original_amount |
number |
Valor original, maior ou igual a zero. Obrigatório na criação |
discount_amount |
number | null |
Desconto aplicado, maior ou igual a zero |
final_amount |
number |
Valor final, maior ou igual a zero. Obrigatório na criação |
category_id |
uuid | null |
Categoria da despesa |
subcategory_id |
uuid | null |
Subcategoria da despesa |
cost_center_id |
uuid | null |
Centro de custo |
supplier_id |
uuid | null |
Fornecedor vinculado |
payment_method |
string | null |
Forma de pagamento, por exemplo pix, bank_slip ou credit_card |
bank_account_id |
uuid | null |
Conta bancária usada no pagamento |
credit_card_id |
uuid | null |
Cartão de crédito usado no pagamento |
check_number |
string | null |
Número do cheque, quando aplicável |
installments |
integer | null |
Quantidade de parcelas, maior ou igual a 1 |
status |
string | null |
Status da despesa, por exemplo pending ou paid |
payment_date |
date (YYYY-MM-DD) | null |
Data do pagamento |
due_date |
date (YYYY-MM-DD) | null |
Data de vencimento |
notes |
string | null |
Observações livres |
tags |
string[] | null |
Tags livres para organização |
is_recurring |
boolean | null |
Indica se a despesa veio de uma recorrência |
is_active |
boolean | null |
false quando a despesa foi desativada |
payment_reference |
string | null |
Referência externa do pagamento |
recurring_expense_id |
uuid | null |
Recorrência que gerou esta despesa |
installment_number |
integer | null |
Número desta parcela. Somente leitura |
total_installments |
integer | null |
Total de parcelas da série. Somente leitura |
created_at |
timestamp |
Data de criação, em ISO 8601 |
updated_at |
timestamp | null |
Última atualização, em ISO 8601 |
recurring |
object | null |
Recorrência embutida. Só aparece com ?include=recurring |
No POST, os campos obrigatórios são expense_type, description,
original_amount e final_amount. No PATCH, todos os campos aceitos no POST
são opcionais: envie apenas o que quer alterar. installment_number e
total_installments não são aceitos na escrita.
Nos GET, o parâmetro fields devolve campos parciais:
?fields=id,description,final_amount,due_date,status.
Filtros e ordenação
Seção intitulada “Filtros e ordenação”A sintaxe do filtro é filter[campo][operador]=valor, com os operadores eq,
neq, gt, gte, lt, lte, in, nin, ilike e is_null. Os detalhes
estão em Paginação, filtros e ordenação.
| Campo | Exemplo |
|---|---|
expense_number |
filter[expense_number][eq]=DESP-2026-0142 |
expense_type |
filter[expense_type][eq]=fixed |
description |
filter[description][ilike]=%aluguel% |
category_id |
filter[category_id][eq]=3f2a7b1c-8d9e-4f0a-b1c2-d3e4f5a6b7c8 |
subcategory_id |
filter[subcategory_id][eq]=1c2b3a4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d |
cost_center_id |
filter[cost_center_id][eq]=5d4e3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b |
supplier_id |
filter[supplier_id][eq]=9a0f8e7d-6c5b-4a3f-2e1d-0c9b8a7f6e5d |
payment_method |
filter[payment_method][in]=pix,bank_slip |
bank_account_id |
filter[bank_account_id][eq]=0d4f2c9a-1b7e-4c3d-9a52-8e6f1b2c3d4e |
credit_card_id |
filter[credit_card_id][is_null]=true |
status |
filter[status][eq]=pending |
payment_date |
filter[payment_date][gte]=2026-07-01 |
due_date |
filter[due_date][lte]=2026-07-31 |
is_recurring |
filter[is_recurring][eq]=true |
is_active |
filter[is_active][eq]=true |
created_at |
filter[created_at][gte]=2026-01-01 |
A ordenação padrão é -created_at, da mais recente para a mais antiga. Os campos
aceitos em sort são created_at, updated_at, payment_date e due_date.
Um exemplo de combinação, para as despesas pendentes e ativas que vencem em julho de 2026:
curl -G "$LIGGA_BASE/v1/expenses" \ -H "Authorization: Bearer $LIGGA_API_KEY" \ --data-urlencode "filter[status][eq]=pending" \ --data-urlencode "filter[is_active][eq]=true" \ --data-urlencode "filter[due_date][gte]=2026-07-01" \ --data-urlencode "filter[due_date][lte]=2026-07-31" \ --data-urlencode "sort=due_date"Relações incluídas
Seção intitulada “Relações incluídas”GET /v1/expenses/{id} aceita ?include=recurring. Quando a despesa veio de uma
recorrência, ou seja, quando recurring_expense_id está preenchido, a resposta
embute o objeto recurring com os dados da série. Caso contrário, recurring
vem como null.
Campo de recurring |
Tipo |
|---|---|
id |
uuid |
expense_template_id |
uuid |
recurrence_frequency |
string, por exemplo monthly |
recurrence_interval |
integer |
start_date |
date |
end_date |
date | null |
next_generation_date |
date |
last_generated_expense_id |
uuid | null |
is_active |
boolean | null |
created_at |
timestamp |
Exemplos
Seção intitulada “Exemplos”Os exemplos abaixo supõem as duas variáveis de ambiente do Início rápido:
export LIGGA_API_KEY='ligga_live_…'export LIGGA_BASE='https://api.ligga.app/functions/v1/api'curl -G "$LIGGA_BASE/v1/expenses" \ -H "Authorization: Bearer $LIGGA_API_KEY" \ --data-urlencode "limit=2" \ --data-urlencode "sort=-due_date" \ --data-urlencode "filter[is_active][eq]=true"{ "data": [ { "id": "2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a", "expense_number": "DESP-2026-0142", "expense_type": "fixed", "description": "Aluguel da loja, julho de 2026", "original_amount": 3500, "discount_amount": null, "final_amount": 3500, "category_id": "3f2a7b1c-8d9e-4f0a-b1c2-d3e4f5a6b7c8", "subcategory_id": null, "cost_center_id": "5d4e3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b", "supplier_id": "9a0f8e7d-6c5b-4a3f-2e1d-0c9b8a7f6e5d", "payment_method": "pix", "bank_account_id": "0d4f2c9a-1b7e-4c3d-9a52-8e6f1b2c3d4e", "credit_card_id": null, "check_number": null, "installments": null, "status": "pending", "payment_date": null, "due_date": "2026-07-10", "notes": "Reajuste anual previsto para agosto.", "tags": ["aluguel", "loja-centro"], "is_recurring": true, "is_active": true, "payment_reference": null, "recurring_expense_id": "b8c7d6e5-f4a3-4b2c-9d1e-0f9a8b7c6d5e", "installment_number": null, "total_installments": null, "created_at": "2026-07-01T08:00:12.334Z", "updated_at": null }, { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "expense_number": "API-1751925600000-a1b2c3", "expense_type": "variable", "description": "Ração premium, reposição de estoque", "original_amount": 1280.5, "discount_amount": 80.5, "final_amount": 1200, "category_id": "3f2a7b1c-8d9e-4f0a-b1c2-d3e4f5a6b7c8", "subcategory_id": "1c2b3a4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "cost_center_id": null, "supplier_id": "9a0f8e7d-6c5b-4a3f-2e1d-0c9b8a7f6e5d", "payment_method": "bank_slip", "bank_account_id": null, "credit_card_id": null, "check_number": null, "installments": 2, "status": "paid", "payment_date": "2026-07-03", "due_date": "2026-07-05", "notes": null, "tags": ["estoque"], "is_recurring": false, "is_active": true, "payment_reference": "NF-e 12345", "recurring_expense_id": null, "installment_number": 1, "total_installments": 2, "created_at": "2026-06-30T15:42:51.902Z", "updated_at": "2026-07-03T10:11:23.556Z" } ], "pagination": { "next_cursor": "eyJpZCI6IjdmMWUyZDNjLS4uLiIsInYiOiIyMDI2LTA3LTA1In0", "has_more": true, "limit": 2 }, "meta": { "request_id": "req_01JZXQ8V2K5M7N9P0R1S2T3U4V" }}O cursor da próxima página está em pagination.next_cursor. Repasse-o em
?cursor= enquanto has_more for true.
Buscar por id
Seção intitulada “Buscar por id”curl "$LIGGA_BASE/v1/expenses/2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a?include=recurring" \ -H "Authorization: Bearer $LIGGA_API_KEY"{ "data": { "id": "2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a", "expense_number": "DESP-2026-0142", "description": "Aluguel da loja, julho de 2026", "final_amount": 3500, "status": "pending", "due_date": "2026-07-10", "is_recurring": true, "is_active": true, "recurring_expense_id": "b8c7d6e5-f4a3-4b2c-9d1e-0f9a8b7c6d5e", "recurring": { "id": "b8c7d6e5-f4a3-4b2c-9d1e-0f9a8b7c6d5e", "expense_template_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "recurrence_frequency": "monthly", "recurrence_interval": 1, "start_date": "2026-01-10", "end_date": null, "next_generation_date": "2026-08-10", "last_generated_expense_id": "2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a", "is_active": true, "created_at": "2026-01-05T09:12:00.000Z" } }, "meta": { "request_id": "req_01JZY7B2C3D4E5F6G7H8J9K0L1" }}O objeto acima está resumido para caber na página. A resposta real traz todos os campos da tabela de Campos.
POST é uma operação de escrita. Envie o cabeçalho
Idempotency-Key para poder fazer uma nova tentativa
sem duplicar o lançamento.
curl -X POST "$LIGGA_BASE/v1/expenses" \ -H "Authorization: Bearer $LIGGA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "expense_type": "variable", "description": "Manutenção do ar-condicionado", "original_amount": 450, "discount_amount": 50, "final_amount": 400, "supplier_id": "9a0f8e7d-6c5b-4a3f-2e1d-0c9b8a7f6e5d", "payment_method": "pix", "due_date": "2026-07-20", "status": "pending", "tags": ["manutencao"] }'A resposta é 201 Created. Como expense_number não foi enviado, a API gerou um:
{ "data": { "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f", "expense_number": "API-1752148800000-f3e2d1", "expense_type": "variable", "description": "Manutenção do ar-condicionado", "original_amount": 450, "discount_amount": 50, "final_amount": 400, "category_id": null, "subcategory_id": null, "cost_center_id": null, "supplier_id": "9a0f8e7d-6c5b-4a3f-2e1d-0c9b8a7f6e5d", "payment_method": "pix", "bank_account_id": null, "credit_card_id": null, "check_number": null, "installments": null, "status": "pending", "payment_date": null, "due_date": "2026-07-20", "notes": null, "tags": ["manutencao"], "is_recurring": null, "is_active": true, "payment_reference": null, "recurring_expense_id": null, "installment_number": null, "total_installments": null, "created_at": "2026-07-10T12:00:03.118Z", "updated_at": null }, "meta": { "request_id": "req_01JZY0A1B2C3D4E5F6G7H8J9K0" }}Atualizar
Seção intitulada “Atualizar”O PATCH legado é parcial: os campos que você não enviar continuam como estão.
Para despesas operacionais, use as operações e baixas descritas acima.
curl -X PATCH "$LIGGA_BASE/v1/expenses/2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a" \ -H "Authorization: Bearer $LIGGA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "notes": "Documento conferido em 08/07." }'A resposta é 200 OK com a despesa já atualizada, no envelope de item.
Desativar
Seção intitulada “Desativar”curl -X DELETE "$LIGGA_BASE/v1/expenses/2e6c1a9b-4d7f-4b3a-9c25-8f0e1d2c3b4a" \ -H "Authorization: Bearer $LIGGA_API_KEY"A resposta é 204 No Content, sem corpo. A despesa passa a ter
is_active: false.
Erros comuns
Seção intitulada “Erros comuns”| HTTP | type |
Quando acontece |
|---|---|---|
| 401 | invalid_token |
Cabeçalho Authorization ausente ou chave inexistente |
| 401 | token_revoked |
A chave foi revogada |
| 401 | token_expired |
A chave passou da data de expiração |
| 403 | module_not_enabled |
Módulo despesas fora do contrato |
| 403 | insufficient_scope |
Chave sem expenses:read na leitura ou expenses:write na escrita |
| 404 | not_found |
id inexistente ou pertencente a outra conta |
| 409 | idempotency_conflict |
Mesma Idempotency-Key reaproveitada com corpo diferente |
| 409 | resource_conflict |
Operação incompatível com pagamentos, origem ou estado da recorrência |
| 422 | validation |
Corpo inválido, como final_amount negativo ou data fora do formato YYYY-MM-DD; filtro, operador ou cursor não aceito |
| 429 | rate-limit-exceeded |
Limite de requisições do plano excedido |
| 503 | service_unavailable |
Conflito transitório de concorrência; repetir a operação com o mesmo UUID |
O corpo do erro traz type, title, status e request_id. O formato completo
está em Erros.
Veja também
Seção intitulada “Veja também”- Fornecedores (suppliers) — o destino de
supplier_id - Paginação, filtros e ordenação — cursor,
filter,sortefields - Idempotência — como repetir uma escrita com segurança
- Erros — catálogo completo e formato da resposta

