Pular para o conteúdo

Vendas (sales)

O recurso sales expõe as vendas e os pedidos da sua conta, isto é, as transações com transaction_type igual a sale ou order. É um recurso de leitura e escrita: você lista, busca por id, cria com itens, atualiza e cancela vendas pela API.

Módulo vendas — precisa estar habilitado no contrato
Escopos sales:read para ler; sales:write para criar, atualizar e cancelar
Operações leitura e escrita
Método Endpoint Escopo Descrição
GET /v1/sales sales:read Lista vendas e pedidos com paginação por cursor
GET /v1/sales/{id} sales:read Busca uma venda pelo id, com ?include=items,status_history
POST /v1/sales sales:write Cria uma venda com itens e responde 201
PATCH /v1/sales/{id} sales:write Atualiza status, pagamento, entrega e observações
DELETE /v1/sales/{id} sales:write Cancela a venda (status="cancelled") e responde 204

Fluxo operacional: catálogo, parcelas e recebimentos

Seção intitulada “Fluxo operacional: catálogo, parcelas e recebimentos”

Use estas rotas para uma venda com itens, parcelas e estoque confirmados juntos. Todas exigem o módulo vendas; escritas exigem sales:write e leituras, sales:read.

Método Endpoint Resultado
POST /v1/sales/operations Cria a venda operacional
PATCH /v1/sales/{id}/operations Edita dados ou substitui itens e pagamentos ainda sem histórico de recebimento
POST /v1/sales/{id}/cancel Cancela após estornar todos os recebimentos
GET /v1/sales/statuses Consulta status ativos e motivos de cancelamento da equipe
GET /v1/sales/{id}/payments Consulta pagamentos e suas parcelas
POST /v1/sales/installments/{id}/receipts Registra recebimento parcial ou total
GET /v1/sales/{id}/receipts Lista recebimentos e estornos
GET /v1/sales/receipts/{id} Consulta uma operação financeira
POST /v1/sales/receipts/{id}/reversals Estorna um recebimento, parcial ou integralmente

As três primeiras escritas respondem 200, com data e meta.request_id, meta.command_id, meta.action. Recebimentos e estornos respondem 201. Todas exigem Idempotency-Key com UUID: use o mesmo UUID e o mesmo corpo para recuperar uma resposta perdida. Alterar o conteúdo com o mesmo UUID resulta em 409 idempotency_conflict.

Corpo de POST /v1/sales/operations, para um item com preço cadastrado de R$ 100,00:

{
"sale_date": "2026-10-05",
"items": [
{
"catalog": {
"type": "item",
"id": "7e53a0d2-5265-4ddd-90ce-a456864b19a1"
},
"quantity": 1
}
],
"payments": [
{
"payment_method": "pix",
"amount_cents": 10000,
"destination_kind": "bank_account",
"destination_id": "84273f63-4ac0-435a-889c-ccde2ed8b9e8",
"due_dates": ["2026-10-05", "2026-11-05", "2026-12-05"]
}
],
"notes": "Pedido do cliente"
}

Cada referência de catalog usa type igual a item, product, service ou combo, com o identificador do catálogo correspondente. Os registros e componentes precisam estar ativos e pertencer à equipe autenticada. customer_id é opcional; quando informado, também deve pertencer à equipe.

A quantidade admite até três casas decimais. O servidor consulta o preço fixo do catálogo; se você enviar unit_price_cents, ele deve coincidir com esse preço. Para preço variável, unit_price_cents é obrigatório e deve respeitar a faixa cadastrada. Cada item pode ter discount_cents e notes.

Os campos monetários são centavos inteiros BRL, limitados a 9.999.999.999. O servidor arredonda cada quantidade × preço para centavos, aplica o desconto do item e soma os resultados em subtotal_cents. Depois aplica o discount_cents geral e acrescenta delivery_fee_cents e tax_cents. O total precisa ser positivo; descontos nunca podem exceder a base correspondente.

Informe de 1 a 100 itens e de 1 a 20 pagamentos. A soma de payments[].amount_cents deve ser exatamente o total calculado. Cada pagamento tem de 1 a 120 datas de vencimento em ordem; uma venda à vista usa uma única data. O servidor reparte o valor em centavos e deixa o resíduo na última parcela: 10000 em três datas resulta em 3333, 3333 e 3334. Todas nascem pendentes, com paid_amount_cents: 0. Não envie payment_status nem valores recebidos.

destination_kind aceita bank_account ou wallet. O destino deve ser ativo, em BRL e da mesma equipe. As formas de pagamento são pix, credit_card, credit_card_installments, debit_card, cash, bank_transfer, bank_slip, check, barter e wallet. Opcionalmente, card_machine_id vincula uma maquininha ativa à conta bancária de destino, para PIX ou cartão. A taxa vigente é capturada na criação e preservada nos recebimentos; o valor bruto da parcela e o líquido que entra no saldo ficam separados.

GET /v1/sales/statuses informa o pipeline da equipe. Sem configuração, os estados são created, in_progress, finalized e cancelled. A criação escolhe o estado marcado como inicial, a menos que você envie outro estado ativo permitido. Concluir em um estado final exige recebimento integral. Uma venda finalizada não pode ser editada.

PATCH /v1/sales/{id}/operations aceita notes, sale_date, customer_id e status. Para mudar itens ou qualquer valor, envie items e payments completos. Essa substituição só é permitida antes do primeiro recebimento; mesmo um recebimento totalmente estornado mantém seu histórico e impede apagar/recriar parcelas. As duas listas e os totais são substituídos em uma transação, incluindo a correção de estoque.

Essas escritas atendem às vendas criadas pelo fluxo operacional. Uma venda antiga precisa de tratamento explícito antes de migrar sua composição e seu estoque; não é convertida automaticamente. Nas vendas já adotadas, os PATCH e DELETE antigos retornam 409 resource_conflict.

Depois de consultar GET /v1/sales/{id}/payments, use o id da parcela em POST /v1/sales/installments/{id}/receipts:

{
"amount_cents": 2000,
"destination_kind": "bank_account",
"destination_id": "84273f63-4ac0-435a-889c-ccde2ed8b9e8",
"effective_date": "2026-10-05"
}

O valor recebido não pode ultrapassar o restante da parcela. O pagamento parcial preserva a parcela aberta; a quitação integral define paid. A resposta inclui o identificador do recebimento. Para estorná-lo, envie ao endpoint de reversão:

{
"amount_cents": 2000,
"effective_date": "2026-10-05",
"reason": "Cliente desistiu da compra"
}

Depois de zerar todos os recebimentos, cancele com POST /v1/sales/{id}/cancel:

{
"reason": "Cliente desistiu da compra",
"financial_effects": "require_reversed"
}

cancellation_reason_id é opcional e deve ser um motivo ativo da equipe. O cancelamento preserva parcelas e histórico, marca o estado de cancelamento configurado e restitui o estoque uma única vez. A restituição usa as quantidades capturadas na venda, mesmo se a composição do produto mudar depois. Não é permitido desativar o controle de estoque de um item enquanto essas quantidades ainda estiverem comprometidas.

Essas operações registram movimentos internos do LIGGA. Elas não executam transferências bancárias nem estornos na adquirente. A projeção de caixa depende da abertura explícita da conta ou carteira: consulte Operações financeiras.

As seções seguintes descrevem as rotas originais e seus campos em reais. Para o fluxo composto acima, use os campos com sufixo _cents.

Campo Tipo Descrição
id uuid Identificador da venda
customer_id uuid | null Cliente associado à venda
transaction_type string Tipo da transação: sale para venda ou order para pedido
transaction_number string | null Número sequencial da transação
status string | null Situação da venda, por exemplo pending, completed ou cancelled
sale_date string (ISO 8601) | null Data da venda
delivery_date string (YYYY-MM-DD) | null Data de entrega
subtotal number | null Soma dos subtotais dos itens, antes dos descontos
discount_amount number | null Desconto em valor absoluto
discount_percentage number | null Desconto percentual
tax_amount number | null Impostos
total_amount number | null Valor total: itens com descontos mais delivery_fee
notes string | null Observações
payment_method string | null Forma de pagamento, por exemplo pix, credit_card ou cash
payment_status string | null Situação do pagamento, por exemplo pending ou paid
delivery_fee number | null Taxa de entrega
order_code string | null Código do pedido, de uso livre na integração
is_active boolean | null Se o registro está ativo
created_at string (ISO 8601) Data de criação
updated_at string (ISO 8601) | null Data da última atualização
items TransactionItem[] Itens da venda, presentes apenas com ?include=items
status_history TransactionStatusHistory[] Histórico de status, presente apenas com ?include=status_history

Campos internos como team_id, contract_id, created_by e internal_notes nunca aparecem nas respostas.

Campo Tipo Descrição
id uuid Identificador do item
catalog_type string Tipo do item do catálogo: product, service ou package
catalog_item_id uuid Item do catálogo, veja Produtos (products)
quantity number Quantidade
unit_price number Preço unitário
discount_amount number | null Desconto em valor no item
discount_percentage number | null Desconto percentual no item
subtotal number quantity × unit_price, antes dos descontos
total_amount number Total do item depois dos descontos
notes string | null Observações do item
created_at string (ISO 8601) Data de criação
Campo Tipo Descrição
id uuid Identificador da entrada
old_status string | null Status anterior, null na criação
new_status string Novo status
change_reason string | null Motivo da mudança
created_at string (ISO 8601) Quando a mudança ocorreu
Campo Tipo Obrigatório Descrição
items array (1–200) sim Itens da venda, descritos abaixo
customer_id uuid | null não Cliente da venda
transaction_type 'sale' | 'order' não, padrão sale Venda ou pedido
sale_date string (ISO 8601) não, padrão agora Data da venda
delivery_date string (YYYY-MM-DD) | null não Data de entrega
status string não, padrão pending Status inicial
payment_method string | null não Forma de pagamento
payment_status string | null não Situação do pagamento
delivery_fee number ≥ 0 | null não Taxa de entrega
order_code string | null não Código do pedido
notes string | null não Observações

Cada entrada de items:

Campo Tipo Obrigatório Descrição
catalog_type 'product' | 'service' | 'package' sim Tipo do item do catálogo
catalog_item_id uuid sim Identificador do item do catálogo
quantity number > 0 sim Quantidade
unit_price number ≥ 0 sim Preço unitário
discount_amount number ≥ 0 não Desconto em valor
discount_percentage number 0–100 não Desconto percentual
notes string | null não Observações do item

Só estes campos podem ser alterados: status, payment_status, payment_method, notes, delivery_date e delivery_fee. Quando status muda, uma entrada é acrescentada automaticamente ao status_history.

Itens e valores não são editáveis por PATCH. Para corrigir itens, cancele a venda e crie outra.

O formato do filtro é ?filter[<campo>][<operador>]=<valor>. Os operadores disponíveis são eq, neq, gt, gte, lt, lte, in, nin, ilike e is_null.

Campo Uso típico Exemplo
customer_id vendas de um cliente filter[customer_id][eq]=9c8d7e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f
status por situação filter[status][in]=pending,completed
transaction_type só vendas ou só pedidos filter[transaction_type][eq]=order
transaction_number busca pelo número filter[transaction_number][eq]=000123
payment_status pagamentos pendentes filter[payment_status][eq]=pending
payment_method por forma de pagamento filter[payment_method][eq]=pix
sale_date por período filter[sale_date][gte]=2026-07-01
total_amount por faixa de valor filter[total_amount][gte]=100
is_active apenas ativos filter[is_active][eq]=true
created_at por data de criação filter[created_at][gte]=2026-07-01T00:00:00Z
updated_at sincronização incremental filter[updated_at][gt]=2026-07-09T12:00:00Z
  • Ordenação: sort=created_at, sort=sale_date ou sort=total_amount. O padrão é -created_at, das mais recentes para as mais antigas.
  • Paginação por cursor: ?limit=50&cursor=<next_cursor>. O limit vai de 1 a 100 e o padrão é 20.
  • Campos parciais: ?fields=id,transaction_number,status,total_amount.

Filtros combinados:

GET /v1/sales?filter[status][neq]=cancelled&sort=-sale_date
GET /v1/sales?filter[customer_id][eq]=9c8d7e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f&filter[payment_status][eq]=pending
GET /v1/sales?filter[sale_date][gte]=2026-07-01&filter[sale_date][lt]=2026-08-01&limit=50

Os detalhes de cada parâmetro estão em Paginação, filtros e ordenação.

GET /v1/sales/{id} aceita o parâmetro include, com valores separados por vírgula:

  • items — itens da venda, ordenados por created_at crescente;
  • status_history — mudanças de status, em ordem cronológica.
GET /v1/sales/3f9a1b7e-2c4d-4e8f-b6a1-5d0c9e8f7a2b?include=items,status_history

Valores desconhecidos em include são ignorados. A listagem GET /v1/sales não aceita include, e a resposta do POST já vem com items.

Janela do terminal
curl "$LIGGA_BASE/v1/sales?filter[status][eq]=pending&limit=2&sort=-sale_date" \
-H "Authorization: Bearer $LIGGA_API_KEY"
{
"data": [
{
"id": "3f9a1b7e-2c4d-4e8f-b6a1-5d0c9e8f7a2b",
"customer_id": "9c8d7e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"transaction_type": "sale",
"transaction_number": "000231",
"status": "pending",
"sale_date": "2026-07-09T18:40:00.000Z",
"delivery_date": null,
"subtotal": 189.8,
"discount_amount": null,
"discount_percentage": null,
"tax_amount": null,
"total_amount": 179.8,
"notes": null,
"payment_method": "pix",
"payment_status": "pending",
"delivery_fee": null,
"order_code": null,
"is_active": true,
"created_at": "2026-07-09T18:40:02.000Z",
"updated_at": null
},
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"customer_id": null,
"transaction_type": "order",
"transaction_number": "000230",
"status": "pending",
"sale_date": "2026-07-08T10:12:00.000Z",
"delivery_date": "2026-07-15",
"subtotal": 250,
"discount_amount": null,
"discount_percentage": null,
"tax_amount": null,
"total_amount": 265,
"notes": "Entregar no período da manhã",
"payment_method": null,
"payment_status": null,
"delivery_fee": 15,
"order_code": "PED-2026-0230",
"is_active": true,
"created_at": "2026-07-08T10:12:05.000Z",
"updated_at": "2026-07-08T10:20:44.000Z"
}
],
"pagination": {
"next_cursor": "eyJpZCI6ImExYjJjM2Q0LWU1ZjYtNGE3Yi04YzlkLTBlMWYyYTNiNGM1ZCIsInYiOiIyMDI2LTA3LTA4VDEwOjEyOjAwLjAwMFoifQ",
"has_more": true,
"limit": 2
},
"meta": { "request_id": "req_01J9ZXAMPLE" }
}

O cursor da próxima página está em pagination.next_cursor. Enquanto pagination.has_more for true, repita a chamada com cursor igual a esse valor.

Janela do terminal
curl "$LIGGA_BASE/v1/sales/3f9a1b7e-2c4d-4e8f-b6a1-5d0c9e8f7a2b?include=items,status_history" \
-H "Authorization: Bearer $LIGGA_API_KEY"
{
"data": {
"id": "3f9a1b7e-2c4d-4e8f-b6a1-5d0c9e8f7a2b",
"customer_id": "9c8d7e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"transaction_type": "sale",
"transaction_number": "000231",
"status": "pending",
"sale_date": "2026-07-09T18:40:00.000Z",
"delivery_date": null,
"subtotal": 189.8,
"discount_amount": null,
"discount_percentage": null,
"tax_amount": null,
"total_amount": 179.8,
"notes": null,
"payment_method": "pix",
"payment_status": "pending",
"delivery_fee": null,
"order_code": null,
"is_active": true,
"created_at": "2026-07-09T18:40:02.000Z",
"updated_at": null,
"items": [
{
"id": "6c7d8e9f-0a1b-4c2d-8e3f-4a5b6c7d8e9f",
"catalog_type": "product",
"catalog_item_id": "b7e1f3a2-8c4d-4e5f-9a6b-1c2d3e4f5a6b",
"quantity": 2,
"unit_price": 49.9,
"discount_amount": null,
"discount_percentage": null,
"subtotal": 99.8,
"total_amount": 99.8,
"notes": null,
"created_at": "2026-07-09T18:40:02.000Z"
},
{
"id": "7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a",
"catalog_type": "service",
"catalog_item_id": "0d9e8f7a-6b5c-4d3e-2f1a-9b8c7d6e5f4a",
"quantity": 1,
"unit_price": 90,
"discount_amount": 10,
"discount_percentage": null,
"subtotal": 90,
"total_amount": 80,
"notes": null,
"created_at": "2026-07-09T18:40:02.000Z"
}
],
"status_history": [
{
"id": "8e9f0a1b-2c3d-4e4f-8a5b-6c7d8e9f0a1b",
"old_status": null,
"new_status": "pending",
"change_reason": null,
"created_at": "2026-07-09T18:40:02.000Z"
}
]
},
"meta": { "request_id": "req_01J9ZXAMPLE" }
}

Um id que não existe, que pertence a outra conta ou que não é sale nem order responde 404 not_found.

Janela do terminal
curl -X POST "$LIGGA_BASE/v1/sales" \
-H "Authorization: Bearer $LIGGA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-loja-8842" \
-d '{
"customer_id": "9c8d7e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"transaction_type": "sale",
"payment_method": "pix",
"payment_status": "pending",
"items": [
{
"catalog_type": "product",
"catalog_item_id": "b7e1f3a2-8c4d-4e5f-9a6b-1c2d3e4f5a6b",
"quantity": 2,
"unit_price": 49.9
},
{
"catalog_type": "service",
"catalog_item_id": "0d9e8f7a-6b5c-4d3e-2f1a-9b8c7d6e5f4a",
"quantity": 1,
"unit_price": 90,
"discount_amount": 10
}
]
}'

Resposta 201 Created, já com items:

{
"data": {
"id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"customer_id": "9c8d7e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"transaction_type": "sale",
"transaction_number": "000232",
"status": "pending",
"sale_date": "2026-07-10T14:05:00.000Z",
"delivery_date": null,
"subtotal": 189.8,
"discount_amount": null,
"discount_percentage": null,
"tax_amount": null,
"total_amount": 179.8,
"notes": null,
"payment_method": "pix",
"payment_status": "pending",
"delivery_fee": null,
"order_code": null,
"is_active": true,
"created_at": "2026-07-10T14:05:01.000Z",
"updated_at": null,
"items": [
{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"catalog_type": "product",
"catalog_item_id": "b7e1f3a2-8c4d-4e5f-9a6b-1c2d3e4f5a6b",
"quantity": 2,
"unit_price": 49.9,
"discount_amount": null,
"discount_percentage": null,
"subtotal": 99.8,
"total_amount": 99.8,
"notes": null,
"created_at": "2026-07-10T14:05:01.000Z"
},
{
"id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
"catalog_type": "service",
"catalog_item_id": "0d9e8f7a-6b5c-4d3e-2f1a-9b8c7d6e5f4a",
"quantity": 1,
"unit_price": 90,
"discount_amount": 10,
"discount_percentage": null,
"subtotal": 90,
"total_amount": 80,
"notes": null,
"created_at": "2026-07-10T14:05:01.000Z"
}
]
},
"meta": { "request_id": "req_01J9ZXAMPLE" }
}
Janela do terminal
curl -X PATCH "$LIGGA_BASE/v1/sales/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e" \
-H "Authorization: Bearer $LIGGA_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "completed", "payment_status": "paid" }'

A mudança de status acrescenta uma entrada ao status_history, visível em GET /v1/sales/{id}?include=status_history.

Janela do terminal
curl -X DELETE "$LIGGA_BASE/v1/sales/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e" \
-H "Authorization: Bearer $LIGGA_API_KEY"

A resposta é 204 No Content, sem corpo.

As respostas de erro usam application/problem+json, com type, title, status e request_id. Os detalhes estão em Erros.

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 insufficient_scope A chave não tem sales:read ou sales:write
403 module_not_enabled Módulo vendas não habilitado no contrato
404 not_found id inexistente, de outra conta, ou que não é sale nem order
409 idempotency_conflict Mesma Idempotency-Key reenviada com outro corpo da requisição
409 resource_conflict Venda finalizada, parcela com histórico, recebimentos pendentes de estorno ou uso de escrita antiga em uma venda operacional
503 service_unavailable Disputa concorrente; repita com o mesmo UUID e corpo
422 validation Corpo ou filtro inválido, por exemplo items vazio, quantity menor ou igual a zero, ou operador não permitido
429 rate-limit-exceeded Limite de requisições do plano excedido