LiquixPay API 1.0.0 — guia do integrador
Dólar digital a partir de Pix: operação 100% via API, com Low KYC / KYB
Visão geral
A LiquixPay API deixa o seu sistema vender dólar digital (USDC) a partir de Pix para os seus clientes, sem que você monte câmbio, tesouraria, conta em instituição de pagamento nem processo de cadastro. Você cota, executa, acompanha e recebe os avisos por chamadas HTTP; a Liquix cuida do resto e repassa ao cliente final todas as taxas — a cotação já vem fechada, com tudo incluído.
100% via API
Toda a operação (cotar, executar, acompanhar, receber avisos por webhook e encerrar um vínculo) acontece só por chamadas HTTP: sem tela para clicar, sem planilha, sem ligação, sem reunião, sem cadastro manual de operação. Os serviços são oferecidos com Low KYC / KYB: o cliente é verificado uma vez, de forma leve (pessoa física) ou por análise da empresa (pessoa jurídica), e depois você opera por cima desse cadastro. O grau de automação e os prazos da análise são definidos na contratação. Veja Verificação do cliente final (Low KYC / KYB).
Fica fora da API, de propósito e uma única vez por cliente: a emissão da sua chave e a criação do vínculo com cada cliente final, que a Liquix faz depois do consentimento dele (colhido fora da API). O cadastro do cliente por chamada de API (você iniciar o KYC/KYB pela API) não existe na v1.
O que o parceiro faz, em uma frase por etapa
- A Liquix emite a sua chave de API e vincula cada cliente final ao seu
cliente_ref(uma vez). - Você cota (
POST /cotacoes): recebe o preço prometido por 60 segundos, com o seu markup. - Você executa (
POST /operacoes): recebe o Pix que o cliente final paga. - O cliente paga o Pix; o dólar digital cai na carteira do próprio cliente.
- Você acompanha por webhook e por
GET /operacoes/{id}e registra a comissão quando a operação conclui.
O que a API garante
- Nunca move o dinheiro do cliente para você. O Pix só pode ser pago pelo CPF/CNPJ do cadastro do cliente final, e o dólar vai sempre para a carteira que o próprio cliente cadastrou. Destino, carteira, CPF e telefone nunca entram nem saem pela API.
- Preço prometido. Se na hora de executar o custo real passar do que a cotação prometeu, a operação é recusada antes de gerar o Pix (
preco_mudou): ninguém é cobrado a mais. - Isolamento total. Você só enxerga o que é seu. Recurso de outro parceiro e recurso inexistente respondem exatamente igual (
404 nao_encontrado). - Nada se repete sem querer. Todo
POSTaceitaIdempotency-Key; a operação nunca é executada duas vezes com a mesma chave. - Dinheiro sem arredondamento escondido. Valores em centavos inteiros ou texto decimal, nunca número de ponto flutuante.
Onde chamar
| Item | Detalhe |
|---|---|
| Endereço base | https://api.liquix.tech/parceiro/v1 (o endereço de produção é informado na contratação) |
| Formato | JSON em UTF-8. Content-Type: application/json em todo corpo. |
| Autenticação | Authorization: Bearer lqxp_... (veja Autenticação e escopos). |
| Versão | v1 no caminho. Detalhes em Convenções. |
| Datas | ISO 8601 em UTC, ex.: 2026-10-20T15:04:05Z. |
| Dinheiro | Centavos inteiros (valor_brl_centavos: 50000 = R$ 500,00) ou texto decimal ("500.00"). |
Chame a API do seu servidor. A chave é secreta: nunca a coloque em aplicativo, site ou qualquer código que o cliente final consiga ler.
Início rápido em 5 passos
Do zero à primeira operação. Troque $LIQUIXPAY_API_KEY pela chave que a Liquix emitiu para você (guarde-a numa variável de ambiente; nunca no código).
Passo 1 — Confira a chave
curl "https://api.liquix.tech/parceiro/v1/eu" -H "Authorization: Bearer $LIQUIXPAY_API_KEY"
Você vê o seu parceiro, os escopos da chave, os tetos, o limite_por_minuto, o seu markup_bps e os produtos liberados (dolar_compra). Se der 401 nao_autorizado, a chave está errada, expirada ou revogada.
Passo 2 — Peça a cotação
curl -X POST "https://api.liquix.tech/parceiro/v1/cotacoes" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"produto":"dolar_compra","cliente_ref":"cliente-1042","valor_brl":"500.00"}'
Guarde o cotacao.cotacao_id. Mostre ao cliente tarifa.rotulo e usdc_estimado_centavos. A cotação vale até expira_em (60 segundos, padrão) e só pode ser usada uma vez.
Passo 3 — Execute e mostre o Pix
curl -X POST "https://api.liquix.tech/parceiro/v1/operacoes" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1042-v1" \
-d '{"cotacao_id":"0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21"}'
Em 201, mostre operacao.pagamento.pix_copia_e_cola ao cliente (vale 5 minutos, padrão). O Idempotency-Key é obrigatório: gere um por intenção de compra e guarde-o com o pedido; se a conexão cair, repita exatamente o mesmo pedido com a mesma chave.
Passo 4 — Cadastre o seu webhook
curl -X POST "https://api.liquix.tech/parceiro/v1/webhooks/endpoints" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: webhook-producao-1" \
-d '{"url":"https://api.minhaloja.com.br/hooks/liquixpay"}'
A resposta traz o segredo (whsec_...) uma única vez. Guarde-o num cofre e use-o para verificar a assinatura de cada evento (veja Receber eventos (webhooks)). Sem a verificação, qualquer pessoa pode fingir um evento.
Passo 5 — Acompanhe até o fim e registre a comissão
Cada mudança de estado dispara um evento (operacao.aguardando_pagamento, operacao.processando, operacao.concluida...). O evento só avisa: confirme o estado de verdade com
curl "https://api.liquix.tech/parceiro/v1/operacoes/5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18" -H "Authorization: Bearer $LIQUIXPAY_API_KEY"
Quando estado = concluida, o dólar chegou à carteira do cliente e comissao_centavos é a sua comissão. recusada, falhou e expirada são finais e não geram comissão. Guarde o maior versao (= seq) que você já aplicou para ignorar avisos velhos.
E se algo der errado?
- Todo erro tem o mesmo formato (
error.code,error.message,error.request_id). Programe pelocode; veja o Catálogo de erros. 429,500 erro_interno, timeout ou queda de conexão: repita o mesmo pedido com a mesmaIdempotency-Key(com espera crescente e respeitandoRetry-After).- Exceção em
POST /operacoes: um503(indisponivel,tarifa_nao_configurada) significa que a operação foi registrada comorecusada(sem cobrança) e a cotação foi consumida; repetir com a mesma chave devolve essa operação. Para tentar de novo, cote de novo e use umaIdempotency-Keynova. - Dúvida de cobrança? Informe o
request_idà Liquix.
Autenticação e escopos
A chave de API
Toda chamada leva a chave no cabeçalho Authorization, no esquema Bearer:
Authorization: Bearer lqxp_<key_id>_<segredo>
- Formato:
lqxp_+key_id(12 caracteres, a parte pública que identifica a chave) +_+segredo(43 caracteres). A chave INTEIRA é mostrada uma única vez, quando a Liquix a emite. Guarde-a num cofre de segredos; a Liquix não consegue mostrá-la de novo. - Só do servidor. Nunca em aplicativo, site ou repositório. Se vazar, peça a revogação: ela vale na hora.
- Resposta única para chave ruim. Chave ausente, desconhecida, errada, expirada ou revogada recebem o MESMO
401 nao_autorizado, de propósito: a resposta não revela se a chave existe. - Freio contra tentativa de adivinhar. Muitas credenciais inválidas vindas do mesmo IP (30 por minuto) passam a receber
429 muitas_requisicoesno lugar do401. Uma chave válida nunca é barrada por isso. - Emissão, rotação e revogação são atos da Liquix, a pedido seu. Na rotação, a chave nova nasce com os mesmos escopos e tetos e a antiga continua valendo por uma carência (padrão 24 horas) para você trocar sem parar o serviço. A chave não se gerencia sozinha: não existe rota de chave na API.
Escopos
Cada chave tem um conjunto fechado de escopos. Uma operação sem o escopo exigido responde 403 escopo_insuficiente. Dê a cada sistema só o que ele precisa (por exemplo: o servidor que só consulta usa uma chave com operacoes:ler).
| Escopo | Libera |
|---|---|
eu:ler |
GET /eu — ver parceiro, chave, uso e produtos |
cotacoes:criar |
POST /cotacoes, GET /cotacoes/{id} |
operacoes:criar |
POST /operacoes — executar cotações |
operacoes:ler |
GET /operacoes, GET /operacoes/{id} |
vinculos:gerir |
GET /vinculos/{cliente_ref}, POST /vinculos/{cliente_ref}/revogar |
webhooks:gerir |
Todas as rotas /webhooks/* (endpoints e eventos) |
O escopo exigido de cada operação aparece na própria operação (campo x-required-scope e a linha Escopo exigido).
Tetos e limites da chave
| Limite | O que é | Estouro |
|---|---|---|
teto_operacao_usd_centavos |
Maior valor, em dólar, de UMA operação desta chave. | 422 acima_do_teto (já na cotação e de novo ao executar) |
teto_dia_usd_centavos |
Soma de dólar das operações desta chave nas últimas 24 horas. | 422 acima_do_teto ao executar |
| Teto do dia do parceiro | A mesma soma, com TODAS as chaves do parceiro. | 422 acima_do_teto ao executar |
limite_por_minuto |
Requisições por minuto da chave (de 1 a 600). | 429 muitas_requisicoes com Retry-After |
Teto 0 significa nada permitido (a API falha fechada). GET /eu mostra os tetos da sua chave e quanto já foi usado. Para aumentar um teto, fale com a Liquix.
Parceiro suspenso
Se o parceiro for suspenso ou encerrado, toda escrita responde 403 parceiro_suspenso, mas as leituras (GET) e a entrega de webhooks continuam: uma operação em andamento nunca fica sem acompanhamento.
Todo recurso é seu
O parceiro_id vem sempre da chave, nunca do pedido. Não existe forma de pedir dados de outro parceiro: um id alheio responde 404 nao_encontrado, idêntico ao de um id que não existe.
Convenções da API
Versionamento
- A versão vai no caminho:
/parceiro/v1. A versão do documento (info.version) segue o formatoMAIOR.MENOR.CORREÇÃO. - Mudanças compatíveis entram na mesma versão, sem aviso prévio: campos novos nas respostas, eventos novos, códigos de erro novos e novos valores de
estado. Seu código deve ignorar campos que não conhece e tratar com segurança umestado,typede evento ouerror.codeque ainda não existia (por exemplo, caindo num tratamento genérico). - Mudanças incompatíveis (remover ou mudar o significado de um campo, mudar um status) só chegam numa versão nova,
/parceiro/v2, com av1mantida em paralelo. O prazo mínimo de convivência entre versões é definido na contratação. - Rotas e produtos novos (por exemplo, novos produtos além de
dolar_compra) entram como adições compatíveis.
Formatos
| Tipo | Regra |
|---|---|
| Corpo | JSON em UTF-8 com Content-Type: application/json. Máximo de 16 KB (413 corpo_grande). |
| Campos desconhecidos | No pedido: recusados (422 corpo_invalido), a API não ignora campo extra. Na resposta: os esquemas marcam additionalProperties: false porque a v1 hoje não emite campos extras, mas campos novos podem aparecer em versões compatíveis. Configure o seu cliente (e o gerador de SDK) para IGNORAR campos desconhecidos nas respostas. |
| Ids | UUID em minúsculas. Ids de webhook: msg_ + 32 hexadecimais. |
| Datas | ISO 8601 em UTC. As operações usam o formato curto 2026-10-20T15:04:05Z; a gestão de webhooks usa microssegundos e +00:00. |
| Dinheiro em reais | Centavos inteiros (*_centavos) ou texto decimal (valor_brl: "500.00"). Nunca número com ponto flutuante. |
| Dólar | Centavos de dólar inteiros (usdc_centavos, *_usd_centavos). |
| Percentuais | Texto com 2 casas ("2.00") ou pontos-base inteiros (markup_bps: 50 = 0,50%). |
Cabeçalhos
| Cabeçalho | Sentido | Para que serve |
|---|---|---|
Authorization |
Requisição | Bearer lqxp_.... Obrigatório em toda rota. |
Idempotency-Key |
Requisição | Chave de idempotência dos POST. 8 a 128 caracteres entre letras, números, _, ., : e -. Veja abaixo. |
Content-Type |
Requisição | application/json quando há corpo (senão 415 tipo_nao_suportado). |
X-Request-Id |
Requisição (opcional) | Se você enviar 32 hexadecimais minúsculos, a API usa e devolve o seu id (ajuda a rastrear); senão ela cria um. |
X-Request-Id |
Resposta | O id da requisição, também em error.request_id. Informe-o ao falar com a Liquix. |
Idempotent-Replayed: true |
Resposta | A resposta é uma repetição guardada de um pedido anterior com a mesma Idempotency-Key. |
Retry-After |
Resposta | Em 429 e em 409 idempotencia_em_curso: segundos até tentar de novo. |
WWW-Authenticate: Bearer |
Resposta | No 401. |
Cache-Control: no-store |
Resposta | Quando a resposta traz um segredo (POST /webhooks/endpoints). |
A v1 não envia cabeçalhos X-RateLimit-*. O limite da sua chave está em GET /eu (chave.limite_por_minuto) e o estouro é sinalizado por 429 + Retry-After.
Idempotência
Todo POST que cria ou muda algo aceita (e a maioria exige) o cabeçalho Idempotency-Key. A regra:
- A chave vale 24 horas e é sua (não colide com a de outro parceiro).
- Ela fica amarrada ao pedido inteiro: método, caminho (com a consulta) e corpo. Mesma chave + MESMO pedido = a API devolve a resposta guardada, com o mesmo status, sem executar de novo, e marca
Idempotent-Replayed: true. - Mesma chave + pedido DIFERENTE (outro corpo, outro caminho, outro método) =
422 idempotencia_pedido_diferente. A API nunca devolve a resposta guardada nesse caso. - Mesmo pedido ainda em andamento =
409 idempotencia_em_cursocomRetry-After: espere e repita. - Respostas
5xxe falhas inesperadas não ficam guardadas: repetir é seguro e tenta de novo. EmPOST /cotacoesePOST /operacoes, as respostas4xxdo serviço (403,404,409e422) ficam guardadas com a chave por 24 horas (até um404 vinculo_nao_encontrado): para tentar de novo depois de uma recusa, cote de novo e use uma chave NOVA. O503dePOST /operacoesnão fica guardado, mas a operação já foi registrada comorecusada: repetir com a mesma chave devolve essa operação (200), não tenta de novo. Nas demais rotas, um erro (404,409,422) não fica guardado: repetir executa outra vez.
| Operação | Idempotency-Key |
|---|---|
POST /operacoes, POST /vinculos/{cliente_ref}/revogar, POST /webhooks/endpoints, POST /webhooks/endpoints/{id}/retomar, POST /webhooks/eventos/{id}/reenviar |
Obrigatório |
POST /cotacoes |
Opcional (recomendado) |
GET e DELETE /webhooks/endpoints/{id} |
Não se aplica |
Boa prática: gere a chave quando o usuário clica em "comprar" e guarde-a junto do pedido; use-a em toda nova tentativa desse mesmo pedido. Em POST /operacoes, a operação em si também é única por Idempotency-Key: mesmo passadas as 24 horas, a mesma chave não cria uma segunda operação.
Paginação
As listagens (GET /operacoes, GET /webhooks/eventos) usam cursor, não número de página, e ordenam da mais nova para a mais antiga.
limite: de 1 a 100 (padrão 20).- A resposta traz
data(a página) eproximo_cursor. Passe-o, exatamente como veio, emcursorpara pedir a página seguinte. proximo_cursor = nullquando não há mais itens. EmGET /operacoesele vem preenchido sempre que a página veio cheia (a seguinte pode vir vazia); emGET /webhooks/eventossó vem quando existe pelo menos mais um item.- O cursor é opaco: não o monte nem o interprete. Cursor inválido =
422 cursor_invalido. - O cursor é estável: operações novas não deslocam as páginas já lidas.
Formato de erro
Todo erro, de qualquer rota, tem o mesmo envelope:
{"error": {"code": "cotacao_expirada", "message": "a cotação expirou; faça uma nova cotação", "request_id": "9f1c2b3a4d5e4f6a8b7c0d1e2f3a4b5c"}}
- Programe por
error.code(estável). Amessageé fixa, em português, segura para log, e pode ganhar novas redações. - Erros de validação (
422 corpo_invalido,422 url_proibida) trazemerror.details:[{"field": "body.valor_brl", "code": "missing"}]. O valor recebido nunca é repetido na resposta. - A resposta de erro nunca traz dado interno, texto de exceção, nem nome de fornecedor.
- Veja o Catálogo de erros para a lista completa.
Limites de tamanho e de tempo
| Item | Valor padrão |
|---|---|
| Corpo da requisição | 16 KB |
| Validade da cotação | 60 segundos |
| Validade do Pix | 5 minutos |
Validade da Idempotency-Key |
24 horas |
| Requisições por minuto | limite_por_minuto da chave (de 1 a 600) |
| Endpoints de webhook | 5 ativos ou pausados |
Os valores padrão podem ser ajustados pela Liquix; a fonte da verdade é sempre a resposta (expira_em da cotação e do Pix, GET /eu para a chave).
Ciclo de vida da operação
Uma operação nasce em POST /operacoes e termina em um estado final. Estado final nunca volta atrás.
| Estado | O que significa e o que você faz | Evento (webhook) |
|---|---|---|
recebida |
Estado interno, no instante antes de abrir: na prática você não a vê (vira criando na mesma transação). |
— |
criando |
Aceita; a Liquix está abrindo a cobrança. Ainda não há Pix. Aguarde. | operacao.criada |
aguardando_pagamento |
O Pix foi gerado e espera o cliente pagar (padrão: 5 minutos). Mostre pagamento.pix_copia_e_cola ao cliente. |
operacao.aguardando_pagamento |
processando |
O pagamento chegou; o dólar está sendo entregue. Aguarde. | operacao.processando |
concluida (final) |
O dólar chegou à carteira do cliente. A comissão é devida: registre comissao_centavos. |
operacao.concluida |
recusada (final) |
Recusada ANTES de gerar o Pix (ex.: motivo = preco_mudou). Nada foi cobrado. Cote de novo, se fizer sentido. |
operacao.recusada |
falhou (final) |
Falhou depois do pagamento. Com motivo = devolvida, o valor voltou ao cliente. Avise o cliente; sem comissão. |
operacao.falhou |
expirada (final) |
O Pix venceu sem pagamento e a Liquix conciliou a cobrança. Nada foi cobrado. Cote de novo, se o cliente ainda quiser. | operacao.expirada |
em_hold |
Em análise: o desfecho precisa de conferência e nada será repetido nem reenviado. Aguarde; a Liquix resolve e a operação segue para um estado normal. | operacao.em_analise |
Caminhos possíveis
POST /operacoes
└─ criando ──┬─ aguardando_pagamento ──┬─ processando ──┬─ concluida
│ │ ├─ falhou
│ │ └─ em_hold
│ ├─ concluida
│ ├─ falhou
│ ├─ expirada
│ └─ em_hold ──┬─ aguardando_pagamento / processando / concluida / falhou
├─ recusada
└─ em_hold
Regras que valem sempre
- O GET decide. O estado verdadeiro é o de
GET /operacoes/{id}. O webhook só avisa que algo mudou; se um aviso se perder ou vier fora de ordem, o GET resolve. versao=seq. A cada mudança de estado o contadorversaosobe, e o evento traz o mesmo número emseq. Aplique só a mudança cujo número seja MAIOR que o que você já tem.em_holdnão é erro seu. Significa que o resultado não pôde ser confirmado com segurança (por exemplo, a resposta da cobrança não pôde ser confirmada). A Liquix nunca reexecuta nem adivinha: ela confere e a operação segue.motivopode serambiguo,sem_referencia,referencia_desconhecida,status_desconhecidoounull(análise do produto). Ao sair doem_hold, omotivoantigo pode permanecer no registro: decida peloestado, nunca pelomotivo. Evite criar outra operação para o mesmo cliente enquanto isso: pode haver uma cobrança em aberto (o409 operacao_em_andamentosó protege por cerca de 90 segundos).- Pix vencido.
expiradasó é registrada quando a Liquix concilia a cobrança, algum tempo (pelo menos 10 minutos) depois do vencimento do Pix. O tempo máximo até a conciliação é definido na contratação. Até lá oGETainda mostraaguardando_pagamentocompagamento.expira_emno passado: trate um Pix vencido como morto, mesmo que o estado ainda não tenha mudado. - Comissão. Só existe em
concluida(comissao_centavos);comissao_prevista_centavosé a previsão desde a cotação. Operaçãorecusada,falhououexpiradanão gera comissão. - Operação em andamento nunca fica sem acompanhamento: ela continua sendo atualizada mesmo com o seu parceiro suspenso, o vínculo revogado ou a chave sem escrita.
Preço, markup e comissão
Como o preço é montado
O preço que o cliente final paga é all-in: já inclui todas as taxas (as da Liquix, as da instituição de pagamento e o seu markup). Nada é cobrado por fora, e a Liquix nunca absorve custo: tudo é repassado ao cliente final, de forma transparente.
all-in (%) = tarifa da casa + seu markup
cliente recebe (US$) = valor em R$ ÷ câmbio de referência × (1 − all-in) − US$ 0,10
- Tarifa da casa: a da proposta comercial para a rota, igual para todo cliente. Ela vem pronta na cotação, em
tarifa.all_in. - Markup: a sua parte, em pontos-base (1 ponto-base = 0,01%). É definido pela Liquix para o seu parceiro (igual em todas as suas chaves) e sai na cotação (
markup_bps). Grade permitida:0, 25, 50, 100, 150(0%, 0,25%, 0,50%, 1,00% e 1,50%). O pedido nunca escolhe o markup. Markup0= só o preço da casa. - Valor mínimo: R$ 10,00 por operação.
- Fixo de US$ 0,10 por operação, que sai do dólar do cliente (já dentro de
usdc_estimado_centavos). - O dinheiro é calculado só com inteiros e decimais exatos: nenhum arredondamento escondido.
Exemplo numérico
R$ 500,00, câmbio de referência 5,50, all-in de exemplo de 2,00% e markup de 0,50% (markup_bps = 50):
| Passo | Conta | Resultado |
|---|---|---|
| all-in | tarifa da casa + markup | 2,00% |
| dólar bruto | 500,00 ÷ 5,50 | US$ 90,9090... |
| depois da tarifa | 90,9090... × (1 − 2,00%) | US$ 89,0909... |
| menos o fixo, arredondado para baixo | 89,0909... − 0,10 = 88,9909... | US$ 88,99 (usdc_estimado_centavos = 8899) |
| sua comissão | 500,00 × 0,50% | R$ 2,50 (comissao_prevista_centavos = 250) |
A cotação é uma promessa
POST /cotacoesdevolve o preço prometido por 60 segundos (padrão), de uso único.usdc_estimado_centavosé uma estimativa pelo câmbio de referência.- Na hora de executar, a Liquix confere o custo REAL contra o que foi prometido. Se passou, a operação é recusada antes de gerar o Pix (
409 preco_mudou): o cliente final nunca paga mais que a cotação. - O valor final que o cliente recebe está em
operacao.usdc_centavose pode diferir do estimado pelo câmbio efetivo da operação.
Sua comissão
- Comissão =
valor_brl_centavos × markup_bps ÷ 10000, arredondada para baixo (a Liquix nunca deve a mais). - Só nasce quando a operação chega a
concluida, no mesmo instante do estado final, e vem emcomissao_centavos.recusada,falhoueexpiradanão geram comissão. - A API não paga a comissão: ela registra o valor devido em cada operação. O acerto é feito pela Liquix fora da API, a partir das operações
concluida. Periodicidade e forma de pagamento da comissão: definidas na contratação. - Concilie com
GET /operacoes?estado=concluida: somecomissao_centavos.
Quem paga as taxas
O cliente final. Quem digita o valor digita um número redondo (por exemplo, R$ 500,00) e as taxas saem do dólar que ele recebe; nada é somado ao Pix. O Pix tem sempre exatamente o valor da cotação (valor_a_pagar_centavos = valor_brl_centavos).
Verificação do cliente final (Low KYC / KYB)
A Liquix verifica o cliente final uma vez, no cadastro, de forma leve (Low KYC, para pessoa física) ou por análise da empresa (KYB, para pessoa jurídica). Depois disso você opera por cima desse cadastro, sem pedir documento de novo a cada operação e sem nunca receber os dados pessoais dele: a API só conhece o seu cliente_ref.
Os itens marcados como "definido na contratação" são fixados no contrato com cada parceiro. O que não existe na v1 está dito como tal e não deve ser comunicado ao seu cliente como funcionalidade disponível.
Low KYC é o nome comercial do cadastro leve da pessoa física. Na API, o que muda o tratamento é só o tipo do cadastro (pessoa física ou jurídica); o grau de automação do cadastro e os prazos de análise são definidos na contratação.
Os níveis
PF — Low KYC (pessoa física, CPF)
- O que a Liquix verifica: nome, CPF válido, data de nascimento, e-mail, endereço, documento com foto (RG ou CNH, frente e verso), selfie e aceite dos termos. Sem comprovante de renda e sem reunião.
- Tempo de análise: definido na contratação.
- O que libera nesta API: operações
dolar_comprade R$ 10,00 em diante, tarifa da proposta, teto por operação de US$ 10.000 (padrão da Liquix) e o limite diário/mensal que restar no cadastro.
PJ — KYB (pessoa jurídica, CNPJ)
- O que a Liquix verifica: CNPJ válido, contrato social, sócios e beneficiários finais (acima de 25%, percentual definido na contratação), representante legal e aceite dos termos. A forma da análise (manual ou automática) é definida na contratação.
- Tempo de análise: definido na contratação.
- O que libera nesta API: operações
dolar_comprade R$ 10,00 em diante, tarifa da proposta, teto por operação de US$ 30.000 (padrão da Liquix) e o limite diário/mensal que restar no cadastro.
Reforçado (KYC Enhanced) — casos de maior risco ou de limites maiores
- Não existe fluxo próprio na v1: hoje o cadastro é sempre o da PF ou o da PJ.
Níveis por valor (cadastro simplificado até um teto menor)
- Não existem na v1: o limite vem do tipo do cadastro (PF ou PJ), não de níveis intermediários por valor.
Limites diário e mensal do cadastro
Além do teto por operação, cada cadastro tem um limite diário e mensal junto à instituição de pagamento. Os valores padrão do nível são, em dólar (o limite real é lido na hora de cada operação e pode ser diferente):
| Item | Por operação | Diário | Mensal |
|---|---|---|---|
| PF — Low KYC | US$ 10.000 | US$ 50.000 | US$ 100.000 |
| PJ — KYB | US$ 30.000 | US$ 100.000 | US$ 250.000 |
Quando o valor passa do que o cadastro ainda permite, a execução responde 422 acima_do_limite_do_cliente (nenhum Pix é gerado). A API não informa quanto resta ao cliente: reduza o valor ou espere o limite diário renovar. Pedido de aumento de limite do cliente: não há rota na v1; fale com a Liquix.
Como o cliente chega até a API
- O cliente final conclui o cadastro (KYC ou KYB) pelos canais da Liquix e aceita os termos. O consentimento para que você opere por ele é colhido fora da API.
- A Liquix cria o vínculo entre o
cliente_refque você escolheu e esse cliente verificado. - Daí em diante tudo é por API:
POST /cotacoes,POST /operacoes, webhooks. Você pode consultar e revogar o vínculo (GET /vinculos/{cliente_ref},POST /vinculos/{cliente_ref}/revogar).
Iniciar o cadastro (KYC/KYB) do cliente por chamada de API não existe na v1. Enquanto isso, o passo 1 é feito pela Liquix.
O que protege o seu cliente (e você)
- Só o titular paga. O Pix da operação só pode ser pago pelo CPF/CNPJ do cadastro verificado. Pix de terceiro é recusado.
- O dólar vai sempre para a carteira do próprio cliente, cadastrada por ele. Você nunca informa destino, e carteira nova passa por 24 horas de proteção (resfriamento) com aviso ao cliente, que pode congelá-la ("não fui eu").
- Nenhum dado pessoal passa por você nem pela API: CPF, CNPJ, telefone, documentos e carteira ficam do lado da Liquix.
- Cliente indisponível = recusa clara. Cadastro ausente, em análise ou ambíguo responde
409 cliente_indisponivel; carteira ausente,409 cliente_sem_carteira; carteira em proteção,409 carteira_em_resfriamento.
Resumo do que cada nível libera
| Item | PF — Low KYC | PJ — KYB |
|---|---|---|
Cotar e operar dolar_compra |
Sim | Sim |
| Valor mínimo | R$ 10,00 | R$ 10,00 |
| Tarifa | A da proposta | A da proposta |
| Teto por operação (padrão) | US$ 10.000 | US$ 30.000 |
| Limite diário e mensal | Do cadastro, lido na hora | Do cadastro, lido na hora |
| Webhooks e consulta de operações | Sim | Sim |
| Cadastro por API | não existe na v1 | não existe na v1 |
Receber eventos (webhooks)
Em vez de ficar perguntando "já pagou?", deixe a Liquix avisar o seu servidor a cada mudança de estado de uma operação. Os avisos são assinados no padrão aberto Standard Webhooks (HMAC-SHA256), então existem bibliotecas prontas para verificá-los em várias linguagens.
Regra de ouro: o aviso apenas aponta, o
GETdecide. O corpo do evento é uma fotografia do estado no instante em que ele ocorreu. Ao receber um evento, confirme o estado atual comGET /operacoes/{id}antes de agir sobre dinheiro (liberar pedido, creditar saldo...). Assim um aviso atrasado, repetido ou fora de ordem nunca causa erro.
Como funciona
- Você cadastra um ou mais endpoints HTTPS (
POST /webhooks/endpoints) e guarda o segredo de cada um (aparece uma única vez). - A cada mudança de estado de uma operação, a Liquix grava o evento e uma entrega para cada endpoint seu, na mesma transação da mudança (nenhum evento deixa de ser gravado e nenhum evento existe sem a mudança).
- Um trabalhador envia o
POSTao seu endpoint, assinado, com tentativas e espera crescente até o seu servidor responder2xx. - Você verifica a assinatura, deduplica pelo
webhook-id, responde2xxrápido e processa depois.
Os eventos continuam sendo gerados e entregues mesmo com o seu parceiro suspenso.
Eventos da v1
| Evento (estado da operação) | Quando acontece | O que fazer |
|---|---|---|
operacao.criada (criando) |
A operação foi aceita e a cobrança está sendo aberta. Ainda não há Pix. | Nada; é um aviso de acompanhamento. |
operacao.aguardando_pagamento (aguardando_pagamento) |
O Pix foi gerado e espera o pagamento. | Busque o Pix em GET /operacoes/{id} e mostre ao cliente. |
operacao.processando (processando) |
O pagamento chegou e o dólar está sendo entregue. | Aguarde o evento final. |
operacao.concluida (concluida) |
O dólar chegou à carteira do cliente final. Final. | Confirme com o GET, libere o pedido e registre data.comissao_centavos. |
operacao.recusada (recusada) |
Recusada antes do Pix, sem cobrança (data.motivo diz por quê). Final. |
Cote de novo, se fizer sentido. |
operacao.falhou (falhou) |
Falhou depois do pagamento; com motivo = devolvida o valor voltou ao cliente. Final. |
Avise o cliente. Sem comissão. |
operacao.expirada (expirada) |
O Pix venceu sem pagamento. Final. | Cote de novo, se o cliente ainda quiser. |
operacao.em_analise (em_hold) |
O desfecho precisa de conferência; nada será repetido nem reenviado. | Aguarde; a Liquix resolve e a operação segue. |
Todos têm o mesmo envelope e o mesmo data (o estado da operação). Veja a seção Webhooks da referência, com o corpo completo de cada um. Eventos de vínculo e de endpoint não existem. Eventos de checkout ainda não existem: assunto_tipo = checkout é reservado e não gera nada.
O que chega ao seu servidor
POST para a sua URL, Content-Type: application/json, corpo JSON compacto (sem espaços, até 64 KB) e três cabeçalhos:
| Cabeçalho | Valor | Observação |
|---|---|---|
webhook-id |
msg_ + 32 hexadecimais |
O mesmo em todas as tentativas e reenvios do evento. É a sua chave de deduplicação e é igual ao id do corpo. |
webhook-timestamp |
Segundos desde 1970 (inteiro) | Muda a cada tentativa. Use para rejeitar replay. Não é o timestamp do corpo. |
webhook-signature |
v1,<base64> |
Assinatura HMAC-SHA256. Pode trazer várias separadas por espaço; basta UMA conferir. |
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.concluida",
"timestamp": "2026-10-20T15:07:02Z",
"seq": 5,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "concluida",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 5,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": "2026-10-20T15:07:02Z",
"comissao_centavos": 250
}
}
id: owebhook-id.type: o tipo do evento.timestamp: quando o evento nasceu.seq: número de sequência da operação (=versao).data: o estado da operação, sem o Pix (para ele use oGET).- Em
operacao.concluida,data.comissao_centavostraz a sua comissão; nos outros eventos esse campo não existe.
Verificando a assinatura
O conteúdo assinado é webhook-id, ponto, webhook-timestamp, ponto e os bytes EXATOS do corpo:
conteudo = "<webhook-id>.<webhook-timestamp>." + corpo_bruto
chave = base64_decode( segredo sem o prefixo "whsec_" )
assinatura = "v1," + base64( HMAC_SHA256( chave, conteudo ) )
Passo a passo:
- Leia o corpo bruto (bytes) da requisição antes de qualquer parse de JSON. Reserializar muda os bytes e quebra a assinatura.
- Rejeite se
webhook-timestampestiver a mais de 5 minutos do seu relógio (proteção contra replay). - Calcule a assinatura esperada com o segredo do endpoint.
- Compare com cada item de
webhook-signatureque comece comv1,usando comparação em tempo constante. - Se nenhuma conferir, responda
400e não processe.
Com uma biblioteca pronta (recomendado)
# pip install standardwebhooks
from standardwebhooks import Webhook
webhook = Webhook(segredo) # "whsec_..." guardado no cofre
carga = webhook.verify(corpo_bruto, cabecalhos) # levanta exceção se a assinatura, o timestamp ou o id não conferirem
// npm install standardwebhooks
import { Webhook } from "standardwebhooks";
const webhook = new Webhook(segredo);
const carga = webhook.verify(corpoBruto, cabecalhos); // lança erro se não conferir
Sem biblioteca — Python
import base64, hashlib, hmac, time
def verificar(segredo: str, cabecalhos: dict, corpo_bruto: bytes, tolerancia_s: int = 300) -> bool:
cabecalhos = {nome.lower(): valor for nome, valor in cabecalhos.items()} # nomes de cabeçalho não diferenciam maiúsculas
msg_id = cabecalhos["webhook-id"]
momento = cabecalhos["webhook-timestamp"]
if abs(time.time() - int(momento)) > tolerancia_s:
return False # antigo ou do futuro: possível replay
chave = base64.b64decode(segredo.removeprefix("whsec_"))
conteudo = f"{msg_id}.{momento}.".encode() + corpo_bruto
esperado = base64.b64encode(hmac.new(chave, conteudo, hashlib.sha256).digest()).decode()
for item in cabecalhos["webhook-signature"].split(" "):
versao, _, valor = item.partition(",")
if versao == "v1" and hmac.compare_digest(valor, esperado):
return True
return False
Sem biblioteca — Node.js (Express)
import crypto from "node:crypto";
import express from "express";
function verificar(segredo, cabecalhos, corpoBruto, toleranciaS = 300) {
const id = cabecalhos["webhook-id"];
const momento = cabecalhos["webhook-timestamp"];
const assinaturas = cabecalhos["webhook-signature"];
if (!id || !momento || !assinaturas) return false;
if (Math.abs(Date.now() / 1000 - Number(momento)) > toleranciaS) return false; // possível replay
const chave = Buffer.from(segredo.replace(/^whsec_/, ""), "base64");
const esperado = crypto.createHmac("sha256", chave).update(`${id}.${momento}.`).update(corpoBruto).digest();
return assinaturas.split(" ").some((item) => {
const [versao, valor] = item.split(",");
if (versao !== "v1" || !valor) return false;
const recebido = Buffer.from(valor, "base64");
return recebido.length === esperado.length && crypto.timingSafeEqual(recebido, esperado);
});
}
const app = express();
// IMPORTANTE: express.raw entrega o corpo em bytes; express.json() reserializaria e quebraria a assinatura.
app.post("/hooks/liquixpay", express.raw({ type: "application/json" }), (req, res) => {
if (!verificar(process.env.LIQUIXPAY_WEBHOOK_SECRET, req.headers, req.body)) return res.sendStatus(400);
const evento = JSON.parse(req.body.toString("utf8"));
// 1) deduplique por evento.id 2) enfileire o processamento 3) responda 2xx já
res.sendStatus(200);
});
Sem biblioteca — linha de comando (curl + openssl)
# ENTRADAS: WEBHOOK_ID, WEBHOOK_TIMESTAMP, corpo.json (os bytes exatos do corpo) e o segredo em LIQUIXPAY_WEBHOOK_SECRET
CHAVE_HEX=$(printf '%s' "${LIQUIXPAY_WEBHOOK_SECRET#whsec_}" | base64 -d | xxd -p -c 256)
{ printf '%s.%s.' "$WEBHOOK_ID" "$WEBHOOK_TIMESTAMP"; cat corpo.json; } \
| openssl dgst -sha256 -mac HMAC -macopt "hexkey:$CHAVE_HEX" -binary | base64
# compare a saída com o que vem depois de "v1," em webhook-signature
Para testar o seu receptor localmente sem esperar um evento de verdade, dispare uma entrega simulada (o vetor de teste abaixo):
curl -X POST "http://localhost:8080/hooks/liquixpay" \
-H "Content-Type: application/json" \
-H "webhook-id: msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e" \
-H "webhook-timestamp: 1792508823" \
-H "webhook-signature: v1,Uaa/nBWUOTfCURienPw3n9FLGelTzghoR7EisxweLNk=" \
--data-binary @corpo.json
Vetor de teste (para conferir a sua implementação)
Use este vetor para ter certeza de que o seu código calcula a mesma assinatura. Ele usa um segredo de teste (não é de nenhum endpoint real): os 32 bytes em texto docs-liquixpay-vetor-de-teste-01. Para obter o formato whsec_, codifique esses bytes em base64 e ponha o prefixo: printf '%s' 'docs-liquixpay-vetor-de-teste-01' | base64. Como o vetor é antigo, desligue a checagem de 5 minutos só nesse teste.
| Item | Detalhe |
|---|---|
webhook-id |
msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e |
webhook-timestamp |
1792508823 |
| corpo (bytes exatos, uma linha) | {"id":"msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e","type":"operacao.concluida","timestamp":"2026-10-20T15:07:02Z","seq":5,"data":{"operacao_id":"5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18","cotacao_id":"0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21","produto":"dolar_compra","cliente_ref":"cliente-1042","estado":"concluida","motivo":null,"valor_brl_centavos":50000,"markup_bps":50,"tarifa":{"all_in":"2.00"},"usdc_centavos":8899,"comissao_prevista_centavos":250,"versao":5,"criada_em":"2026-10-20T15:04:05Z","atualizada_em":"2026-10-20T15:07:02Z","concluida_em":"2026-10-20T15:07:02Z","comissao_centavos":250}} |
webhook-signature esperada |
v1,Uaa/nBWUOTfCURienPw3n9FLGelTzghoR7EisxweLNk= |
Respondendo e tentando de novo
| Sua resposta | O que a Liquix faz |
|---|---|
2xx |
Entregue. Fim. |
410 Gone |
Desativa o endpoint de vez e cancela as entregas pendentes dele. |
3xx |
Falha. Redirecionamentos não são seguidos. |
4xx / 5xx / sem resposta em 15 s / erro de conexão ou TLS |
Falha: tenta de novo. Retry-After (em 429, 502, 503, 504) é respeitado. |
- Escada de tentativas (espera antes de cada tentativa, com variação de ±10%): imediata, 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 14 h, 20 h, 24 h. Até 10 tentativas; depois a entrega fica
esgotadae a Liquix é alertada. - Endpoint pausado: 20 entregas esgotadas seguidas pausam o endpoint (
estado = pausado,motivo_estado = muitas_falhas). Os eventos esperam na fila até você corrigir o servidor e chamarPOST /webhooks/endpoints/{endpoint_id}/retomar(uma entrega que passe de 80 horas sem nenhuma tentativa é cancelada commuito_antiga; reenvie-a comPOST /webhooks/eventos/{evento_id}/reenviar). - Evento velho demais: uma entrega que ficou 80 horas sem nenhuma tentativa é cancelada (
ultimo_codigo = muito_antiga); reenvie quando quiser. - Responda rápido. O prazo por tentativa é de 15 segundos (padrão). Responda
2xxassim que gravar o evento e processe em segundo plano. O corpo da sua resposta é ignorado (lido até 64 KB e descartado).
Ordem e deduplicação
- Eventos da mesma operação são entregues em ordem de
seq: um evento só sai depois que os deseqmenor, para o mesmo endpoint, foram entregues ou esgotados. Entre operações diferentes não há ordem. - A entrega é pelo menos uma vez: o mesmo evento pode chegar mais de uma vez (nova tentativa depois de uma resposta perdida, ou reenvio). Deduplique pelo
webhook-ide ignore evento cujoseqseja menor que o que você já aplicou à operação. - Idempotência é sua responsabilidade no receptor: processar duas vezes
operacao.concluidanão pode creditar duas vezes.
Segurança do seu receptor
- Use HTTPS na porta 443 com certificado válido. A Liquix só entrega em
https, porta 443, sem usuário/senha na URL. - A URL precisa ser pública: endereços internos, de loopback, de rede privada, de metadados de nuvem e IP literal são recusados no cadastro e de novo a cada tentativa (proteção contra SSRF e contra troca de DNS).
- Guarde o segredo num cofre. Nunca o registre em log.
- Verifique a assinatura sempre, mesmo atrás de firewall. Compare em tempo constante.
- Rejeite
webhook-timestampcom mais de 5 minutos de diferença.
Gestão dos endpoints e dos eventos
| Preciso... | Chame |
|---|---|
| Cadastrar um endpoint e receber o segredo | POST /webhooks/endpoints |
| Ver os endpoints e o estado de cada um | GET /webhooks/endpoints, GET /webhooks/endpoints/{endpoint_id} |
| Retomar um endpoint pausado | POST /webhooks/endpoints/{endpoint_id}/retomar |
| Remover (desativar) um endpoint | DELETE /webhooks/endpoints/{endpoint_id} |
| Conciliar: ver os eventos e as entregas | GET /webhooks/eventos, GET /webhooks/eventos/{evento_id} |
| Receber de novo um evento (depois de corrigir um defeito) | POST /webhooks/eventos/{evento_id}/reenviar |
Estados do endpoint: ativo (recebe) → pausado (espera na fila, por até 80 horas sem tentativa) → ativo (via retomar); desativado é definitivo (removido por você ou por 410 Gone). Cada parceiro tem no máximo 5 endpoints ativos ou pausados, e todos recebem todos os eventos (não há filtro por tipo nem rotação de segredo na v1: para trocar o segredo, remova o endpoint e cadastre outro).
Glossário
| Termo | O que é |
|---|---|
| Parceiro | Você: a empresa que integra a LiquixPay API para vender dólar digital aos seus clientes. |
| Cliente final | Quem compra o dólar digital: o cliente do parceiro, pessoa física ou jurídica, verificado pela Liquix (Low KYC / KYB). |
cliente_ref |
A referência do cliente final no SEU sistema (1 a 64 caracteres). É a única identificação do cliente que circula pela API; nunca CPF, telefone nem carteira. |
| Vínculo | A ligação entre o seu cliente_ref e um cliente verificado da Liquix. Nasce por ato da Liquix, depois do consentimento do cliente; você consulta e pode revogar. Sem vínculo ativo não há cotação nem operação. |
| Chave de API | O segredo lqxp_... que autentica cada chamada. Tem escopos, tetos e limite por minuto. |
| Escopo | Uma permissão da chave (cotacoes:criar, operacoes:ler...). Sem o escopo, 403 escopo_insuficiente. |
| Teto | O limite de valor, em dólar, de uma operação ou das últimas 24 horas (por chave e por parceiro). Estourou: 422 acima_do_teto. |
| Produto | O que se negocia. Na v1: dolar_compra (Pix em reais, dólar digital na carteira do cliente). |
| Cotação | O preço prometido por um valor em reais, válido por 60 segundos (padrão) e de uso único. Não move dinheiro. |
| Operação | A execução de uma cotação: nasce em POST /operacoes, gera o Pix e percorre os estados até concluida (ou outro estado final). |
| Pix copia e cola | O código que o cliente final cola no app do banco para pagar. Só o CPF/CNPJ do cadastro consegue pagá-lo. |
| Markup | A sua margem sobre o preço da casa, em pontos-base, somada à tarifa de toda cotação. Definido pela Liquix para o seu parceiro (igual em todas as suas chaves). |
| Comissão | O que você ganha em uma operação concluida: valor × markup, arredondado para baixo. A Liquix registra o valor; o acerto é feito por ela. |
| All-in | Preço com tudo incluído: tarifa da casa + taxas da instituição de pagamento + seu markup. Nada é cobrado por fora. |
| Pontos-base (bps) | 1 ponto-base = 0,01%. 50 bps = 0,50%. |
| Centavos | Todo dinheiro inteiro da API: reais em *_brl_centavos, dólar em *_usd_centavos e usdc_*_centavos. R$ 500,00 = 50000. |
em_hold / em análise |
Estado em que o desfecho não pôde ser confirmado com segurança; a Liquix confere e nada é repetido. |
| Idempotência | Garantia de que repetir o mesmo pedido (com a mesma Idempotency-Key) não executa duas vezes. |
| Webhook | O aviso que a Liquix envia ao seu servidor quando uma operação muda de estado. |
| Endpoint (de webhook) | O endereço HTTPS do seu servidor que recebe os avisos. Tem um segredo próprio. |
| Evento | Um aviso (operacao.concluida...). Tem id (= webhook-id), type, timestamp, seq e data. |
seq / versao |
O contador de mudanças de estado de uma operação. Serve para ignorar avisos velhos. |
| Segredo do webhook | O whsec_... de um endpoint, mostrado uma única vez, usado para verificar a assinatura. |
| Resfriamento | Proteção de 24 horas para carteira nova do cliente final, com aviso a ele ("não fui eu"). |
| Low KYC | Verificação leve da pessoa física (documento com foto e selfie), sem comprovante de renda nem reunião. |
| KYB | Verificação da pessoa jurídica (CNPJ, contrato social e sócios). |
request_id |
O identificador de cada requisição (também em X-Request-Id). Informe-o ao falar com a Liquix. |
| Cursor | O marcador opaco de paginação (proximo_cursor). |
Catálogo de erros
Todo erro vem no mesmo envelope {"error": {"code", "message", "request_id"}}. Programe pelo code. A mensagem é o texto fixo atual (pode ganhar nova redação). Cada operação da referência lista os códigos que ela pode devolver, com um exemplo de resposta.
Autenticação, limites e formato do pedido
nao_autorizado · HTTP 401 · credenciais inválidas
- Quando acontece: Faltou o cabeçalho
Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. - O que fazer: Envie
Authorization: Bearer lqxp_...com a chave inteira. Se a chave foi revogada ou expirou, peça uma nova à Liquix.
escopo_insuficiente · HTTP 403 · a chave não tem permissão para esta operação
- Quando acontece: A chave é válida, mas não tem o escopo que a operação exige (veja
Escopo exigidona operação echave.escoposemGET /eu). - O que fazer: Peça à Liquix uma chave com o escopo necessário. Chave de leitura não escreve.
parceiro_suspenso · HTTP 403 · parceiro suspenso: apenas leitura
- Quando acontece: O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada.
- O que fazer: Fale com a Liquix para reativar o parceiro. As operações já em andamento continuam sendo acompanhadas e os webhooks seguem chegando.
nao_encontrado · HTTP 404 · recurso não encontrado
- Quando acontece: O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente.
- O que fazer: Confira o identificador e a chave usada. Ids de recurso são UUID em minúsculas.
metodo_nao_permitido · HTTP 405 · método não permitido para este recurso
- Quando acontece: O caminho existe, mas não aceita o método HTTP usado (por exemplo,
PUT /operacoes). - O que fazer: Use o método descrito na referência da operação.
corpo_grande · HTTP 413 · corpo da requisição grande demais
- Quando acontece: O corpo passa do limite (padrão 16 KB) — pelo
Content-Lengthou pelo que chegou. - O que fazer: Envie só os campos do contrato; nenhum pedido da v1 precisa de mais que algumas centenas de bytes.
tipo_nao_suportado · HTTP 415 · use Content-Type: application/json
- Quando acontece: Há corpo, mas o
Content-Typenão éapplication/json. - O que fazer: Envie
Content-Type: application/json(charset opcional).
corpo_invalido · HTTP 422 · corpo da requisição inválido
- Quando acontece: O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato.
detailslista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. - O que fazer: Corrija os campos apontados em
details[].field. Não envie campos que não estão no contrato.
muitas_requisicoes · HTTP 429 · limite de requisições excedido; tente novamente
- Quando acontece: A chave passou do
limite_por_minuto(janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). - O que fazer: Espere
Retry-Aftersegundos e tente de novo, com espera crescente. O limite da sua chave aparece emGET /eu.
erro_interno · HTTP 500 · erro interno
- Quando acontece: Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o
request_idé o que a Liquix usa para achar o caso. - O que fazer: Tente de novo com a mesma
Idempotency-Key(resposta 5xx não fica guardada) e, se persistir, informe orequest_idà Liquix.
erro_http · HTTP 400 · requisição não atendida
- Quando acontece: Recusa genérica do servidor para um pedido malformado que não cai em nenhum erro acima (o status varia).
- O que fazer: Revise o pedido; se persistir, informe o
request_id.
Idempotência e paginação
idempotency_key_obrigatoria · HTTP 400 · informe o cabeçalho Idempotency-Key (8 a 128 caracteres: letras, números, _ . : -)
- Quando acontece: A operação exige
Idempotency-Key(ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números,_,.,:e-). - O que fazer: Gere um identificador único por intenção (por exemplo um UUID ou
pedido-1042-v1) e reenvie.
pedido_invalido · HTTP 400 · pedido fora do formato aceito
- Quando acontece: O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita.
- O que fazer: Encurte a consulta ou o caminho.
idempotencia_pedido_diferente · HTTP 422 · esta chave já foi usada em outro pedido
- Quando acontece: A mesma
Idempotency-Keyjá foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. Também aparece como: esta chave de idempotência já foi usada em outro pedido. - O que fazer: Use uma chave nova para cada pedido novo; repita a MESMA chave só para repetir exatamente o mesmo pedido.
idempotencia_em_curso · HTTP 409 · o pedido com esta chave ainda está em andamento
- Quando acontece: O mesmo pedido, com a mesma chave, ainda está sendo processado.
- O que fazer: Espere
Retry-Aftersegundos e repita o MESMO pedido com a MESMA chave: você recebe a resposta final.
cursor_invalido · HTTP 422 · cursor inválido
- Quando acontece: O
cursornão é umproximo_cursordevolvido pela API (adulterado, truncado ou fora do formato). - O que fazer: Use o
proximo_cursorexatamente como veio. Para recomeçar, omita ocursor.
Cotações e operações
produto_indisponivel · HTTP 422 · produto indisponível
- Quando acontece: O
produtonão existe ou não está liberado para o parceiro (a resposta é a mesma nos dois casos). - O que fazer: Use
dolar_compra(vejaprodutosemGET /eu) e confirme com a Liquix que o produto está liberado para você.
markup_invalido · HTTP 422 · o markup do parceiro está fora da grade permitida
- Quando acontece: O markup configurado para o seu parceiro não está na grade permitida (
0, 25, 50, 100, 150pontos-base por padrão). - O que fazer: Fale com a Liquix: o markup é definido por ela, nunca pelo pedido.
valor_invalido · HTTP 422 · o valor informado não está em um formato aceito
- Quando acontece: O
valor_brlnão é um número decimal em texto: só dígitos ASCII, no máximo 2 casas, sem separador de milhar, sem sinal, sem espaço. - O que fazer: Envie texto como
"500.00"ou"500,00"(não use número JSON).
abaixo_do_minimo · HTTP 422 · o valor está abaixo do mínimo da operação
- Quando acontece: O valor é menor que R$ 10,00 (ou pequeno demais para sobrar dólar depois das taxas).
- O que fazer: Cote um valor de R$ 10,00 ou mais.
acima_do_teto · HTTP 422 · o valor passa do teto permitido para esta chave
- Quando acontece: O valor, em dólares, passa do teto por operação da chave, do teto diário da chave ou do teto diário do parceiro. No teto diário contam as operações das últimas 24 horas que ainda seguram reserva (
recusada,falhoueexpiradaliberam a reserva; a cotação sozinha não reserva nada). - O que fazer: Reduza o valor ou espere o teto diário aliviar. Os tetos da chave aparecem em
GET /eu; para aumentá-los, fale com a Liquix.
acima_do_limite_do_cliente · HTTP 422 · o valor está acima do limite disponível do cliente final
- Quando acontece: O valor passa do máximo por operação do cadastro do cliente final ou do limite diário/mensal que ainda resta a ele.
- O que fazer: Reduza o valor. Veja a seção Verificação do cliente final (Low KYC/KYB) sobre os limites por nível.
vinculo_nao_encontrado · HTTP 404 · vínculo não encontrado
- Quando acontece: Não há vínculo ATIVO com este
cliente_ref(nunca vinculado, revogado ou de outro parceiro: a resposta é a mesma). - O que fazer: Peça à Liquix para criar o vínculo (o consentimento do cliente é colhido fora da API) ou confira o
cliente_ref.
cotacao_ja_usada · HTTP 409 · a cotação já foi usada
- Quando acontece: A cotação é de uso ÚNICO e já virou uma operação (com outra
Idempotency-Key). - O que fazer: Cote de novo. Para repetir o MESMO pedido use a mesma
Idempotency-Key: você recebe a operação existente.
cotacao_expirada · HTTP 409 · a cotação expirou; faça uma nova cotação
- Quando acontece: A cotação passou da validade (
expira_em, padrão 60 s). - O que fazer: Cote de novo e execute logo em seguida.
cliente_indisponivel · HTTP 409 · o cliente final não está disponível para esta operação
- Quando acontece: O cliente vinculado não tem cadastro aprovado utilizável (sem cadastro, em análise, ou identidade ambígua).
- O que fazer: Conclua o cadastro do cliente com a Liquix e tente de novo.
cliente_sem_carteira · HTTP 409 · o cliente final ainda não tem carteira cadastrada para esta operação
- Quando acontece: O cliente não cadastrou a carteira que recebe o dólar. O destino SEMPRE é a carteira do próprio cliente, cadastrada por ele.
- O que fazer: O cliente precisa cadastrar a carteira pelos canais da Liquix.
carteira_em_resfriamento · HTTP 409 · a carteira do cliente final ainda não foi liberada para receber
- Quando acontece: A carteira é nova e está no período de proteção de 24 horas, ou o aviso de segurança ao cliente ainda está pendente, ou o próprio cliente a congelou ("não fui eu").
- O que fazer: Tente depois que a carteira for liberada; se foi congelada, o cliente resolve pelos canais da Liquix.
operacao_em_andamento · HTTP 409 · já existe uma operação em andamento para este cliente final
- Quando acontece: Há outra compra em curso para o mesmo cliente final, ou um Pix foi gerado para ele há pouco: existe uma trava curta por cliente (cerca de 90 segundos desde a criação do Pix) contra cliques repetidos e operações simultâneas.
- O que fazer: Espere cerca de 90 segundos e tente de novo com uma cotação nova. Se o cliente ainda tem uma operação em
aguardando_pagamento, deixe-a terminar ou expirar antes de criar outra.
preco_mudou · HTTP 409 · o preço mudou desde a cotação; faça uma nova cotação
- Quando acontece: Na hora de executar, a cotação real não bate com a prometida (o custo passou do prometido ou a cobrança divergiu da cotação). Nenhum Pix foi entregue e nada foi cobrado.
- O que fazer: Cote de novo.
tarifa_nao_configurada · HTTP 503 · a tarifa para este valor não está disponível
- Quando acontece: Não há tarifa ativa para a combinação de valor e markup. A API recusa em vez de cobrar um preço errado. Em
POST /operacoes, a operação ficarecusada(sem cobrança). - O que fazer: Tente de novo mais tarde; se persistir, informe a Liquix.
indisponivel · HTTP 503 · a operação está indisponível no momento; tente novamente em instantes
- Quando acontece: Serviço de câmbio ou de pagamento fora do ar, ou resposta que a API não consegue garantir. Nenhum Pix foi entregue a você. Em
POST /operacoes, a operação fica registrada comorecusada(sem cobrança) e a cotação é consumida. Também aparece como: serviço temporariamente indisponível; tente novamente. - O que fazer: Em
POST /cotacoes: espere alguns segundos e repita. EmPOST /operacoes: repetir com a mesmaIdempotency-Keysó devolve a operaçãorecusada; cote de novo e use uma chave NOVA.
Webhooks
url_proibida · HTTP 422 · endereço do webhook não permitido
- Quando acontece: A URL do endpoint não passou na proteção contra SSRF: precisa ser
httpsna porta 443, sem usuário/senha, sem fragmento, sem IP literal, com host público que resolva só para endereços públicos.details[].codediz o motivo:esquema,porta,credenciais,fragmento,host,ip_literal,muito_longa,formato,dnsouip_proibido. A URL nunca é repetida na resposta. - O que fazer: Cadastre uma URL https pública (até 500 caracteres).
endpoint_ja_existe · HTTP 409 · já existe um endpoint ativo ou pausado com esta URL
- Quando acontece: Você já tem um endpoint ativo ou pausado com a mesma URL.
- O que fazer: Use o endpoint existente (
GET /webhooks/endpoints) ou remova-o antes de cadastrar de novo.
limite_de_endpoints · HTTP 409 · limite de 5 endpoints atingido; remova um antes de cadastrar outro
- Quando acontece: Cada parceiro pode ter no máximo 5 endpoints ativos ou pausados (os desativados não contam).
- O que fazer: Remova um endpoint com
DELETE /webhooks/endpoints/{endpoint_id}.
endpoint_nao_pausado · HTTP 409 · só um endpoint pausado pode ser retomado
- Quando acontece: O endpoint não está no estado
pausado(já está ativo ou foi desativado). - O que fazer: Só retome endpoints com
estado = pausado. Um endpoint desativado é definitivo: crie outro.
Referência das operações
Conta
Quem é o parceiro e a chave que está chamando, o consumo dos tetos nas últimas 24 horas e os produtos liberados. Comece por aqui.
GET /eu — Ver quem sou: parceiro, chave, uso e produtos
operationId: eu_obter
Devolve quem é o parceiro dono da chave, os dados da chave que fez a chamada (escopos, tetos, limite por minuto e validade), quanto do teto diário já foi usado nas últimas 24 horas, o seu markup e os produtos liberados. Use no começo da integração para conferir a chave e depois para acompanhar o consumo do teto sem esperar um acima_do_teto.
- Escopo exigido:
eu:ler - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- Uma chave de API válida com o escopo
eu:ler.
O que acontece
- Só leitura. A única coisa gravada é a marca de "último uso" da chave (no máximo uma vez por minuto).
Regras e limites
- O segredo da chave NUNCA é devolvido (só o
key_id, a parte pública). - Valores de teto e de uso estão em CENTAVOS de dólar (inteiros). Teto
0significa "nada permitido" (a API falha fechada). - Ao executar uma operação valem TRÊS tetos: por operação (da chave), do dia (da chave) e do dia (do parceiro, somando todas as chaves). Estourou qualquer um:
acima_do_teto. - Funciona com o parceiro suspenso.
Respostas de sucesso
- 200 — O parceiro, a chave, o uso das últimas 24 horas e os produtos liberados.
Exemplo: Parceiro ativo com chave completa
{
"parceiro": {
"slug": "loja-exemplo",
"nome": "Loja Exemplo",
"estado": "ativo"
},
"chave": {
"key_id": "docexemplo22",
"rotulo": "producao-backend",
"escopos": [
"eu:ler",
"vinculos:gerir",
"cotacoes:criar",
"operacoes:criar",
"operacoes:ler",
"webhooks:gerir"
],
"teto_operacao_usd_centavos": 500000,
"teto_dia_usd_centavos": 5000000,
"limite_por_minuto": 60,
"estado": "ativa",
"expira_em": null
},
"uso": {
"chave_24h_usd_centavos": 8899,
"parceiro_24h_usd_centavos": 17798
},
"markup_bps": 50,
"produtos": [
"dolar_compra"
]
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/eu" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
Cotações
O preço prometido por 60 segundos (padrão) para um valor em reais, já com o seu markup e todas as taxas repassadas ao cliente final. A cotação é de uso único e não move dinheiro.
POST /cotacoes — Cotar um valor em reais para um cliente final
operationId: cotacao_criar
Cota um valor em reais para um cliente que a Liquix já vinculou ao seu cliente_ref. A resposta é o preço prometido por 60 segundos (padrão), com o seu markup e todas as taxas já repassadas ao cliente final (a cotação é all-in), e a estimativa de quanto ele recebe em dólar digital. A cotação é de uso único e não move dinheiro.
- Escopo exigido:
cotacoes:criar - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Opcional, mas recomendado: com ele, repetir o mesmo pedido devolve a mesma resposta em vez de criar outra cotação.
Pré-condições
- Vínculo ATIVO para o
cliente_ref(senão404 vinculo_nao_encontrado). - Produto
dolar_compraliberado para você (senão422 produto_indisponivel). - Cliente final com cadastro aprovado (PF ativo ou PJ aprovado; senão
409 cliente_indisponivel). - Valor de pelo menos R$ 10,00 (senão
422 abaixo_do_minimo).
O que acontece
- Grava UMA cotação, de uso único, com validade em
expira_em. - NÃO cria operação, NÃO gera Pix, NÃO cobra nada e NÃO dispara webhook.
Regras e limites
- O markup é sempre o do SEU parceiro, igual em todas as suas chaves (
markup_bps, vejaGET /eu); o pedido nunca o escolhe. - A tarifa da casa é a da proposta e vem na cotação; o
tarifa.rotulojá vem pronto para mostrar ao cliente. usdc_estimado_centavosé uma estimativa pelo câmbio de referência; o valor final aparece emoperacao.usdc_centavos.- Se o dólar estimado passar do teto por operação da sua chave, a resposta é
422 acima_do_teto(o teto diário só é conferido ao executar). valor_brlé TEXTO ("500.00"ou"500,00"), com até 2 casas, sem milhar nem sinal; número JSON é recusado.- Campo fora do contrato (destino, carteira, CPF, telefone...) é recusado com
422 corpo_invalido: o destino do dólar é SEMPRE a carteira do próprio cliente.
Veja também: POST /operacoes, Preço, markup e comissão
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
Idempotency-Key |
header | não | Opcional, mas recomendado. Identificador único da sua intenção (8 a 128 caracteres: letras, números, _, ., :, -). Repetir o MESMO pedido com a mesma chave devolve a resposta guardada (24 h), com Idempotent-Replayed: true, sem executar de novo; a mesma chave em pedido DIFERENTE responde 422 idempotencia_pedido_diferente. |
pedido-1042-v1 |
Corpo da requisição
Cliente, produto e valor em reais.
{
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"valor_brl": "500.00"
}
Respostas de sucesso
- 201 — Cotação criada. Guarde o
cotacao_id: é tudo o quePOST /operacoesprecisa.
Exemplo: R$ 500,00 com markup de 0,50%
{
"cotacao": {
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00",
"rotulo": "2,00% + US$ 0,10"
},
"usdc_estimado_centavos": 8899,
"comissao_prevista_centavos": 250,
"expira_em": "2026-10-20T15:05:05Z"
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 400 | idempotency_key_obrigatoria |
A operação exige Idempotency-Key (ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números, _, ., : e -). |
| 400 | pedido_invalido |
O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita. |
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 404 | vinculo_nao_encontrado |
Não há vínculo ATIVO com este cliente_ref (nunca vinculado, revogado ou de outro parceiro: a resposta é a mesma). |
| 409 | cliente_indisponivel |
O cliente vinculado não tem cadastro aprovado utilizável (sem cadastro, em análise, ou identidade ambígua). |
| 409 | idempotencia_em_curso |
O mesmo pedido, com a mesma chave, ainda está sendo processado. |
| 413 | corpo_grande |
O corpo passa do limite (padrão 16 KB) — pelo Content-Length ou pelo que chegou. |
| 415 | tipo_nao_suportado |
Há corpo, mas o Content-Type não é application/json. |
| 422 | abaixo_do_minimo |
O valor é menor que R$ 10,00 (ou pequeno demais para sobrar dólar depois das taxas). |
| 422 | acima_do_teto |
O valor, em dólares, passa do teto por operação da chave, do teto diário da chave ou do teto diário do parceiro. No teto diário contam as operações das últimas 24 horas que ainda seguram reserva (recusada, falhou e expirada liberam a reserva; a cotação sozinha não reserva nada). |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | idempotencia_pedido_diferente |
A mesma Idempotency-Key já foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. |
| 422 | markup_invalido |
O markup configurado para o seu parceiro não está na grade permitida (0, 25, 50, 100, 150 pontos-base por padrão). |
| 422 | produto_indisponivel |
O produto não existe ou não está liberado para o parceiro (a resposta é a mesma nos dois casos). |
| 422 | valor_invalido |
O valor_brl não é um número decimal em texto: só dígitos ASCII, no máximo 2 casas, sem separador de milhar, sem sinal, sem espaço. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
| 503 | indisponivel |
Serviço de câmbio ou de pagamento fora do ar, ou resposta que a API não consegue garantir. Nenhum Pix foi entregue a você. Em POST /operacoes, a operação fica registrada como recusada (sem cobrança) e a cotação é consumida. |
| 503 | tarifa_nao_configurada |
Não há tarifa ativa para a combinação de valor e markup. A API recusa em vez de cobrar um preço errado. Em POST /operacoes, a operação fica recusada (sem cobrança). |
Exemplo (curl)
curl -X POST "https://api.liquix.tech/parceiro/v1/cotacoes" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{"produto":"dolar_compra","cliente_ref":"cliente-1042","valor_brl":"500.00"}'
GET /cotacoes/{id} — Consultar uma cotação
operationId: cotacao_obter
Devolve uma cotação SUA, vencida ou não. Serve para reler o preço prometido e a validade; não diz se a cotação já foi usada (para isso, consulte as operações).
- Escopo exigido:
cotacoes:criar - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- A cotação existe e é do seu parceiro. Cotação de outro parceiro, inexistente ou com id fora do formato: o MESMO
404 nao_encontrado.
O que acontece
- Só leitura.
Regras e limites
- Usa o mesmo escopo de criar cotação (
cotacoes:criar). - Uma cotação vencida continua legível; só não pode mais ser executada.
- O preço e a comissão prevista são os da hora em que a cotação foi criada: não mudam com o câmbio de agora.
- Serve para reconferir o que foi prometido ao cliente (por exemplo, depois de uma queda de conexão) sem gerar uma cotação nova.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
id |
path | sim | Identificador da cotação (UUID em minúsculas, no formato canônico). Outro formato responde 404, igual a uma cotação que não existe. | 0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21 |
Respostas de sucesso
- 200 — A cotação, com o preço prometido e a validade.
Exemplo: Cotação vigente
{
"cotacao": {
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00",
"rotulo": "2,00% + US$ 0,10"
},
"usdc_estimado_centavos": 8899,
"comissao_prevista_centavos": 250,
"expira_em": "2026-10-20T15:05:05Z"
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/cotacoes/0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
Operações
Executar uma cotação (gera o Pix que o cliente final paga), acompanhar o estado até o fim e conciliar. O GET é a fonte da verdade; o webhook só avisa.
POST /operacoes — Executar uma cotação e gerar o Pix
operationId: operacao_criar
Executa uma cotação: a Liquix cria a operação, reserva o valor nos seus tetos e gera o Pix que o cliente final paga. O pedido leva SÓ o cotacao_id: valor, cliente e destino já estão congelados na cotação. O dólar digital cai SEMPRE na carteira do próprio cliente final, cadastrada por ele, e o Pix só pode ser pago pelo CPF/CNPJ do cadastro dele.
- Escopo exigido:
operacoes:criar - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Obrigatório. Sem ele a resposta é
400 idempotency_key_obrigatoria.
Pré-condições
- Cotação SUA, dentro da validade e ainda não usada (
409 cotacao_expirada/409 cotacao_ja_usada). - Vínculo do cliente ainda ATIVO (
404 vinculo_nao_encontradose foi revogado depois da cotação). - Dentro dos tetos: por operação, diário da chave e diário do parceiro (
422 acima_do_teto). - Cliente final com cadastro aprovado, carteira de dólar cadastrada e liberada (resfriamento de 24 h para carteira nova) e sem outra compra em curso ou Pix recém-gerado para ele (trava de cerca de 90 segundos por cliente).
- Parceiro ativo (suspenso lê, mas não executa).
O que acontece
- Cria a operação e QUEIMA a cotação (uso único).
- Reserva o valor em dólar nos tetos (contagem das últimas 24 horas desde a criação):
recusada,falhoueexpiradaliberam a reserva;concluidacontinua contando até completar as 24 horas. - Grava a ata de auditoria ANTES de gerar o Pix; só depois o Pix é entregue.
- Dispara os webhooks
operacao.criadae depoisoperacao.aguardando_pagamento(ouoperacao.recusada/operacao.em_analise).
Regras e limites
Idempotency-KeyOBRIGATÓRIO e a operação NUNCA é executada duas vezes com a mesma chave. Em até 24 h, repetir o MESMO pedido devolve a resposta guardada (o mesmo status e o instantâneo da primeira vez, comIdempotent-Replayed: true); para o estado de agora useGET /operacoes/{id}.- As respostas 4xx do serviço (403, 404, 409 e 422) também ficam guardadas com a chave: para tentar de novo depois de uma recusa, cote de novo e use uma
Idempotency-KeyNOVA. O503não fica guardado, mas a operação JÁ foi registrada comorecusada(sem cobrança) e a cotação foi consumida: repetir com a mesma chave devolve essa operação (200), não tenta de novo. Falha inesperada (500) não fica guardada e repetir é seguro. - 201: operação criada,
estado = aguardando_pagamentoepagamentocom o Pix (padrão: vale 5 minutos). 200: a chave já tinha criado a operação mas não havia resposta guardada (a primeira tentativa foi interrompida, ou passaram mais de 24 h); devolve a operação que já existe, no estado de agora. - 202: desfecho pendente (
estado = em_hold, oucriandose o servidor não conseguiu gravar o desfecho). NÃO repita com chave nova: o Pix pode existir. Acompanhe porGET /operacoes/{id}e pelo webhookoperacao.em_analise; a Liquix resolve. - 409 / 422 / 503: recusa SEM cobrança e sem Pix. Veja o código em
error.code;preco_mudousignifica que a cotação real da execução não bate com a prometida (o custo passou do prometido ou a cobrança divergiu da cotação): cote de novo. - O valor, o cliente e o destino nunca vêm no pedido; um campo a mais responde
422 corpo_invalido. - A comissão só nasce quando a operação conclui (
concluida), no mesmo instante do estado final.
Veja também: GET /operacoes/{id}, Ciclo de vida da operação, Receber eventos (webhooks)
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
Idempotency-Key |
header | sim | Obrigatório. Identificador único da sua intenção (8 a 128 caracteres: letras, números, _, ., :, -). Repetir o MESMO pedido com a mesma chave devolve a resposta guardada (24 h), com Idempotent-Replayed: true, sem executar de novo; a mesma chave em pedido DIFERENTE responde 422 idempotencia_pedido_diferente. |
pedido-1042-v1 |
Corpo da requisição
SÓ o id da cotação.
{
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21"
}
Respostas de sucesso
- 200 — A
Idempotency-Keyjá tinha criado esta operação, mas não havia resposta guardada: devolve a operação que já existe, no estado de agora (o Pix só aparece se ainda estiveraguardando_pagamento). Nada é executado de novo.
Exemplo: Operação que a chave já tinha criado
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "processando",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 4,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:06:31Z",
"concluida_em": null
}
}
- 201 — Operação criada e pronta para o cliente pagar. Uma repetição da mesma chave em até 24 h devolve esta mesma resposta com
Idempotent-Replayed: true.
Exemplo: Pix gerado
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "aguardando_pagamento",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null,
"pagamento": {
"pix_copia_e_cola": "00020126580014br.gov.bcb.pix0136EXEMPLO-ILUSTRATIVO-NAO-PAGAVEL5204000053039865406500.005802BR5907LIQUIX6009SAO PAULO62070503***6304ABCD",
"expira_em": "2026-10-20T15:09:07Z",
"valor_a_pagar_centavos": 50000
}
}
}
- 202 — Desfecho pendente: a operação está em análise. Nada será repetido nem reenviado.
Exemplo: Em análise
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "em_hold",
"motivo": "ambiguo",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": null,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 400 | idempotency_key_obrigatoria |
A operação exige Idempotency-Key (ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números, _, ., : e -). |
| 400 | pedido_invalido |
O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita. |
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 404 | vinculo_nao_encontrado |
Não há vínculo ATIVO com este cliente_ref (nunca vinculado, revogado ou de outro parceiro: a resposta é a mesma). |
| 409 | carteira_em_resfriamento |
A carteira é nova e está no período de proteção de 24 horas, ou o aviso de segurança ao cliente ainda está pendente, ou o próprio cliente a congelou ("não fui eu"). |
| 409 | cliente_indisponivel |
O cliente vinculado não tem cadastro aprovado utilizável (sem cadastro, em análise, ou identidade ambígua). |
| 409 | cliente_sem_carteira |
O cliente não cadastrou a carteira que recebe o dólar. O destino SEMPRE é a carteira do próprio cliente, cadastrada por ele. |
| 409 | cotacao_expirada |
A cotação passou da validade (expira_em, padrão 60 s). |
| 409 | cotacao_ja_usada |
A cotação é de uso ÚNICO e já virou uma operação (com outra Idempotency-Key). |
| 409 | idempotencia_em_curso |
O mesmo pedido, com a mesma chave, ainda está sendo processado. |
| 409 | operacao_em_andamento |
Há outra compra em curso para o mesmo cliente final, ou um Pix foi gerado para ele há pouco: existe uma trava curta por cliente (cerca de 90 segundos desde a criação do Pix) contra cliques repetidos e operações simultâneas. |
| 409 | preco_mudou |
Na hora de executar, a cotação real não bate com a prometida (o custo passou do prometido ou a cobrança divergiu da cotação). Nenhum Pix foi entregue e nada foi cobrado. |
| 413 | corpo_grande |
O corpo passa do limite (padrão 16 KB) — pelo Content-Length ou pelo que chegou. |
| 415 | tipo_nao_suportado |
Há corpo, mas o Content-Type não é application/json. |
| 422 | abaixo_do_minimo |
O valor é menor que R$ 10,00 (ou pequeno demais para sobrar dólar depois das taxas). |
| 422 | acima_do_limite_do_cliente |
O valor passa do máximo por operação do cadastro do cliente final ou do limite diário/mensal que ainda resta a ele. |
| 422 | acima_do_teto |
O valor, em dólares, passa do teto por operação da chave, do teto diário da chave ou do teto diário do parceiro. No teto diário contam as operações das últimas 24 horas que ainda seguram reserva (recusada, falhou e expirada liberam a reserva; a cotação sozinha não reserva nada). |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | idempotencia_pedido_diferente |
A mesma Idempotency-Key já foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. |
| 422 | produto_indisponivel |
O produto não existe ou não está liberado para o parceiro (a resposta é a mesma nos dois casos). |
| 422 | valor_invalido |
O valor_brl não é um número decimal em texto: só dígitos ASCII, no máximo 2 casas, sem separador de milhar, sem sinal, sem espaço. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
| 503 | indisponivel |
Serviço de câmbio ou de pagamento fora do ar, ou resposta que a API não consegue garantir. Nenhum Pix foi entregue a você. Em POST /operacoes, a operação fica registrada como recusada (sem cobrança) e a cotação é consumida. |
| 503 | tarifa_nao_configurada |
Não há tarifa ativa para a combinação de valor e markup. A API recusa em vez de cobrar um preço errado. Em POST /operacoes, a operação fica recusada (sem cobrança). |
Exemplo (curl)
curl -X POST "https://api.liquix.tech/parceiro/v1/operacoes" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{"cotacao_id":"0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21"}'
GET /operacoes/{id} — Consultar uma operação
operationId: operacao_obter
Devolve uma operação SUA no estado de agora. O GET é a fonte da verdade: o webhook só avisa que algo mudou; se um aviso se perder ou chegar fora de ordem, esta consulta resolve. Enquanto a operação está em aguardando_pagamento, a resposta traz o Pix em pagamento.
- Escopo exigido:
operacoes:ler - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- A operação existe e é do seu parceiro (de outro parceiro, inexistente ou com id torto: o MESMO
404 nao_encontrado).
O que acontece
- Só leitura. Funciona também com o parceiro suspenso, a chave sem escrita e o vínculo revogado: operação em andamento sempre pode ser acompanhada.
Regras e limites
pagamentosó existe emaguardando_pagamento;comissao_centavossó emconcluida(nos outros estados o campo não existe).versaosobe a cada mudança de estado e é o mesmo número doseqdo webhook: guarde a maior que você já aplicou.- Estados finais (
concluida,recusada,falhou,expirada) nunca voltam atrás.
Veja também: Ciclo de vida da operação
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
id |
path | sim | Identificador da operação (UUID em minúsculas, no formato canônico). Outro formato responde 404, igual a uma operação que não existe. | 5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18 |
Respostas de sucesso
- 200 — A operação, no estado de agora.
Exemplo: Aguardando o Pix (traz pagamento)
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "aguardando_pagamento",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null,
"pagamento": {
"pix_copia_e_cola": "00020126580014br.gov.bcb.pix0136EXEMPLO-ILUSTRATIVO-NAO-PAGAVEL5204000053039865406500.005802BR5907LIQUIX6009SAO PAULO62070503***6304ABCD",
"expira_em": "2026-10-20T15:09:07Z",
"valor_a_pagar_centavos": 50000
}
}
}
Exemplo: Pagamento recebido, dólar a caminho
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "processando",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 4,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:06:31Z",
"concluida_em": null
}
}
Exemplo: Concluída (traz comissao_centavos)
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "concluida",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 5,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": "2026-10-20T15:07:02Z",
"comissao_centavos": 250
}
}
Exemplo: Recusada antes do Pix
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "recusada",
"motivo": "preco_mudou",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": null,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null
}
}
Exemplo: Pix venceu sem pagamento
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "expirada",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 4,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": null
}
}
Exemplo: Em análise
{
"operacao": {
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "em_hold",
"motivo": "ambiguo",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": null,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/operacoes/5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
GET /operacoes — Listar as operações do parceiro
operationId: operacao_listar
Lista as operações do seu parceiro, da mais nova à mais antiga, com paginação por cursor estável (novas operações não deslocam as páginas). Filtre por cliente ou por estado para conciliar.
- Escopo exigido:
operacoes:ler - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
O que acontece
- Só leitura.
Regras e limites
limitevai de 1 a 100 (padrão 20).proximo_cursorvem preenchido quando a página veio cheia; use-o exatamente como veio emcursor. A página seguinte de uma página cheia pode vir vazia.- Os filtros
cliente_refeestadocombinam (E). Valor de filtro fora do formato responde422 corpo_invalidocom o campo emdetails. - Cursor adulterado, truncado ou fora do formato responde
422 cursor_invalido.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
cliente_ref |
query | não | Só as operações deste cliente final (mesma regra de formato do vínculo). | cliente-1042 |
estado |
query | não | Só as operações neste estado: recebida, criando, aguardando_pagamento, processando, concluida, recusada, falhou, expirada ou em_hold. |
concluida |
cursor |
query | não | O proximo_cursor da página anterior (opaco; até 400 caracteres). Omita para a primeira página. |
MjAyNi0xMC0yMFQxNTowNDowNSswMDowMHw1YjlkMmY0MS04YzU1LTRlMGEtYjFkNy0yYTZmOWMzZTVkMTg |
limite |
query | não | Quantas operações por página, de 1 a 100. | 20 |
Respostas de sucesso
- 200 — Uma página de operações.
Exemplo: Página cheia (tem proximo_cursor)
{
"data": [
{
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "concluida",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 5,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": "2026-10-20T15:07:02Z",
"comissao_centavos": 250
}
],
"proximo_cursor": "MjAyNi0xMC0yMFQxNTowNDowNSswMDowMHw1YjlkMmY0MS04YzU1LTRlMGEtYjFkNy0yYTZmOWMzZTVkMTg"
}
Exemplo: Última página
{
"data": [
{
"id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "aguardando_pagamento",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null,
"pagamento": {
"pix_copia_e_cola": "00020126580014br.gov.bcb.pix0136EXEMPLO-ILUSTRATIVO-NAO-PAGAVEL5204000053039865406500.005802BR5907LIQUIX6009SAO PAULO62070503***6304ABCD",
"expira_em": "2026-10-20T15:09:07Z",
"valor_a_pagar_centavos": 50000
}
}
],
"proximo_cursor": null
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | cursor_invalido |
O cursor não é um proximo_cursor devolvido pela API (adulterado, truncado ou fora do formato). |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/operacoes" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
Vínculos
A ligação entre o seu cliente_ref e um cliente final verificado (Low KYC / KYB). Nasce por ato da Liquix, com o consentimento do cliente; aqui você consulta e revoga.
GET /vinculos/{cliente_ref} — Consultar o vínculo de um cliente final
operationId: vinculo_obter
Devolve o vínculo do cliente_ref neste parceiro, ativo ou revogado. O vínculo liga a SUA referência a um cliente verificado da Liquix; ele nasce por ato da Liquix, depois do consentimento do cliente (colhido fora da API) — não existe rota para criá-lo.
- Escopo exigido:
vinculos:gerir - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- Existe vínculo para o
cliente_refneste parceiro (de outro parceiro ou inexistente: o MESMO404 nao_encontrado).
O que acontece
- Só leitura.
Regras e limites
- Só
cliente_ref,estadoe as datas saem pela API: CPF, telefone, carteira e a referência do consentimento NUNCA saem. - Se houver um vínculo ativo e um antigo revogado com a mesma referência, vem o ativo.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
cliente_ref |
path | sim | Referência do cliente final (1 a 64 caracteres: letras, números, _, ., -). Fora desse formato responde 404. |
cliente-1042 |
Respostas de sucesso
- 200 — O vínculo, com o estado e as datas.
Exemplo: Vínculo ativo
{
"vinculo": {
"cliente_ref": "cliente-1042",
"estado": "ativo",
"criado_em": "2026-10-01T12:00:00Z",
"revogado_em": null
}
}
Exemplo: Vínculo revogado
{
"vinculo": {
"cliente_ref": "cliente-1042",
"estado": "revogado",
"criado_em": "2026-10-01T12:00:00Z",
"revogado_em": "2026-10-20T15:10:00Z"
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/vinculos/cliente-1042" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
POST /vinculos/{cliente_ref}/revogar — Revogar o vínculo de um cliente final
operationId: vinculo_revogar
Revoga o vínculo ATIVO do cliente_ref: a partir daí, cotação nova para esse cliente responde 404 vinculo_nao_encontrado. É o botão de "parar de operar com este cliente" (por exemplo, quando ele encerra a conta com você). Operações que já estavam em andamento seguem até o fim.
- Escopo exigido:
vinculos:gerir - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Obrigatório. Sem ele a resposta é
400 idempotency_key_obrigatoria.
Pré-condições
- Existe vínculo ATIVO para o
cliente_ref(sem vínculo ativo:404 nao_encontrado).
O que acontece
- O vínculo passa a
revogadoe ganharevogado_em. Não gera webhook. - Cotações ainda vigentes desse cliente deixam de poder ser executadas (
404 vinculo_nao_encontradoemPOST /operacoes).
Regras e limites
- Corpo vazio ou
{}; qualquer campo é recusado. - Repetir com a MESMA
Idempotency-Keydevolve a mesma resposta 200; com chave NOVA, o vínculo já está revogado e a resposta é404 nao_encontrado. - Revogar não é reversível por esta API: para voltar a operar com o cliente, a Liquix cria um vínculo novo.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
cliente_ref |
path | sim | Referência do cliente final (1 a 64 caracteres: letras, números, _, ., -). Fora desse formato responde 404. |
cliente-1042 |
Idempotency-Key |
header | sim | Obrigatório. Identificador único da sua intenção (8 a 128 caracteres: letras, números, _, ., :, -). Repetir o MESMO pedido com a mesma chave devolve a resposta guardada (24 h), com Idempotent-Replayed: true, sem executar de novo; a mesma chave em pedido DIFERENTE responde 422 idempotencia_pedido_diferente. |
pedido-1042-v1 |
Corpo da requisição
Sem campos: omita o corpo ou envie {}.
{}
Respostas de sucesso
- 200 — O vínculo, já revogado.
Exemplo: Vínculo revogado
{
"vinculo": {
"cliente_ref": "cliente-1042",
"estado": "revogado",
"criado_em": "2026-10-01T12:00:00Z",
"revogado_em": "2026-10-20T15:10:00Z"
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 400 | idempotency_key_obrigatoria |
A operação exige Idempotency-Key (ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números, _, ., : e -). |
| 400 | pedido_invalido |
O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita. |
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 409 | idempotencia_em_curso |
O mesmo pedido, com a mesma chave, ainda está sendo processado. |
| 413 | corpo_grande |
O corpo passa do limite (padrão 16 KB) — pelo Content-Length ou pelo que chegou. |
| 415 | tipo_nao_suportado |
Há corpo, mas o Content-Type não é application/json. |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | idempotencia_pedido_diferente |
A mesma Idempotency-Key já foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X POST "https://api.liquix.tech/parceiro/v1/vinculos/cliente-1042/revogar" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{}'
Webhooks · Endpoints
Cadastre, consulte, retome e remova os endereços HTTPS que recebem os eventos das suas operações. O segredo de assinatura aparece uma única vez, no cadastro.
POST /webhooks/endpoints — Cadastrar um endpoint de webhook
operationId: webhook_endpoint_criar
Cadastra o endereço HTTPS que vai receber os eventos das suas operações. A resposta traz o segredo de assinatura (whsec_...) uma única vez: guarde-o agora, porque ele é a única forma de conferir que o evento veio mesmo da Liquix (assinatura Standard Webhooks).
- Escopo exigido:
webhooks:gerir - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Obrigatório. Sem ele a resposta é
400 idempotency_key_obrigatoria.
Pré-condições
- URL
https, porta 443, sem usuário/senha, sem fragmento, sem IP literal, com host público que resolva só para endereços públicos (até 500 caracteres). - Menos de 5 endpoints ativos ou pausados (os desativados não contam).
- Nenhum endpoint ativo ou pausado com a mesma URL.
O que acontece
- Cria o endpoint em
ativoe gera um segredo novo e próprio dele. - A partir de agora, todo evento novo do seu parceiro gera uma entrega para este endpoint (eventos antigos não são reenviados sozinhos: use
reenviar).
Regras e limites
- A resposta tem
Cache-Control: no-store. O segredo NÃO é consultável depois; perdeu? remova o endpoint e cadastre outro. - Repetir a mesma
Idempotency-Keydevolve o MESMO endpoint (sem criar outro), mas SEM o segredo. - A URL é validada agora E de novo a cada tentativa de entrega (o DNS pode mudar); redirecionamentos não são seguidos.
- Erro de URL:
422 url_proibidacom o motivo emdetails[].code(esquema,porta,credenciais,fragmento,host,ip_literal,muito_longa,formato,dns,ip_proibido); a URL nunca é repetida na resposta. - Todos os endpoints ativos recebem TODOS os eventos: não há filtro por tipo de evento nem rotação de segredo na v1 (para trocar o segredo, remova e recadastre).
Veja também: Receber eventos (webhooks)
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
Idempotency-Key |
header | sim | Obrigatório. Identificador único da sua intenção (8 a 128 caracteres: letras, números, _, ., :, -). Repetir o MESMO pedido com a mesma chave devolve a resposta guardada (24 h), com Idempotent-Replayed: true, sem executar de novo; a mesma chave em pedido DIFERENTE responde 422 idempotencia_pedido_diferente. |
pedido-1042-v1 |
Corpo da requisição
A URL que recebe os eventos.
{
"url": "https://api.minhaloja.com.br/hooks/liquixpay"
}
Respostas de sucesso
- 201 — Endpoint criado. O
segredoaparece só aqui.
Exemplo: Primeira resposta, com o segredo
{
"id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"url": "https://api.minhaloja.com.br/hooks/liquixpay",
"estado": "ativo",
"segredo": "whsec_SEU_SEGREDO_EXIBIDO_UMA_VEZ"
}
Exemplo: Repetição da mesma Idempotency-Key (sem o segredo)
{
"id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"url": "https://api.minhaloja.com.br/hooks/liquixpay",
"estado": "ativo"
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 400 | idempotency_key_obrigatoria |
A operação exige Idempotency-Key (ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números, _, ., : e -). |
| 400 | pedido_invalido |
O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita. |
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 409 | endpoint_ja_existe |
Você já tem um endpoint ativo ou pausado com a mesma URL. |
| 409 | idempotencia_em_curso |
O mesmo pedido, com a mesma chave, ainda está sendo processado. |
| 409 | limite_de_endpoints |
Cada parceiro pode ter no máximo 5 endpoints ativos ou pausados (os desativados não contam). |
| 413 | corpo_grande |
O corpo passa do limite (padrão 16 KB) — pelo Content-Length ou pelo que chegou. |
| 415 | tipo_nao_suportado |
Há corpo, mas o Content-Type não é application/json. |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | idempotencia_pedido_diferente |
A mesma Idempotency-Key já foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. |
| 422 | url_proibida |
A URL do endpoint não passou na proteção contra SSRF: precisa ser https na porta 443, sem usuário/senha, sem fragmento, sem IP literal, com host público que resolva só para endereços públicos. details[].code diz o motivo: esquema, porta, credenciais, fragmento, host, ip_literal, muito_longa, formato, dns ou ip_proibido. A URL nunca é repetida na resposta. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X POST "https://api.liquix.tech/parceiro/v1/webhooks/endpoints" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{"url":"https://api.minhaloja.com.br/hooks/liquixpay"}'
GET /webhooks/endpoints — Listar os endpoints de webhook
operationId: webhook_endpoint_listar
Lista todos os seus endpoints, em qualquer estado (ativo, pausado ou desativado), do mais antigo ao mais novo. Nunca devolve o segredo.
- Escopo exigido:
webhooks:gerir - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- Uma chave com o escopo
webhooks:gerir.
O que acontece
- Só leitura.
Regras e limites
- Sem paginação: um parceiro tem no máximo 5 endpoints ativos ou pausados, mais os desativados.
- Cada item traz
estado,motivo_estado,esgotadas_seguidasecriado_em; ourlvolta como foi cadastrado (host em minúsculas). pausadocommotivo_estado = muitas_falhassignifica que o seu servidor falhou em 20 entregas seguidas: corrija e chame/retomar.
Respostas de sucesso
- 200 — Os endpoints do parceiro, em qualquer estado.
Exemplo: Um ativo e um pausado
{
"data": [
{
"id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"url": "https://api.minhaloja.com.br/hooks/liquixpay",
"estado": "ativo",
"motivo_estado": null,
"esgotadas_seguidas": 0,
"criado_em": "2026-10-20T15:00:00.123456+00:00"
},
{
"id": "7e1f0b52-93ac-4d1e-a0c8-5b6d7e8f9a0b",
"url": "https://api.minhaloja.com.br/hooks/reserva",
"estado": "pausado",
"motivo_estado": "muitas_falhas",
"esgotadas_seguidas": 20,
"criado_em": "2026-10-20T15:00:00.123456+00:00"
}
]
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/webhooks/endpoints" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
GET /webhooks/endpoints/{endpoint_id} — Consultar um endpoint de webhook
operationId: webhook_endpoint_obter
Devolve um endpoint SEU. Use para ver se ele continua ativo ou foi pausado por falhas. Nunca devolve o segredo.
- Escopo exigido:
webhooks:gerir - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- O endpoint existe e é do seu parceiro (de outro parceiro, inexistente ou com id torto: o MESMO
404 nao_encontrado).
O que acontece
- Só leitura.
Regras e limites
estado:ativo(recebe),pausado(os eventos esperam na fila, por até 80 horas sem tentativa) oudesativado(definitivo).esgotadas_seguidasconta as entregas seguidas que esgotaram todas as tentativas; ao chegar a 20 (padrão) o endpoint é pausado (motivo_estado = muitas_falhas).- Para ver o que aconteceu com cada evento neste endpoint, use
GET /webhooks/eventos(o estado de cada entrega aparece por endpoint).
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
endpoint_id |
path | sim | Identificador do endpoint (UUID em minúsculas, no formato canônico). Outro formato responde 404. | c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f |
Respostas de sucesso
- 200 — O endpoint, sem o segredo.
Exemplo: Endpoint ativo
{
"id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"url": "https://api.minhaloja.com.br/hooks/liquixpay",
"estado": "ativo",
"motivo_estado": null,
"esgotadas_seguidas": 0,
"criado_em": "2026-10-20T15:00:00.123456+00:00"
}
Exemplo: Endpoint pausado por falhas
{
"id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"url": "https://api.minhaloja.com.br/hooks/liquixpay",
"estado": "pausado",
"motivo_estado": "muitas_falhas",
"esgotadas_seguidas": 20,
"criado_em": "2026-10-20T15:00:00.123456+00:00"
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/webhooks/endpoints/c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
DELETE /webhooks/endpoints/{endpoint_id} — Remover (desativar) um endpoint
operationId: webhook_endpoint_remover
Desativa o endpoint de forma definitiva e cancela as entregas que estavam pendentes para ele. O endpoint continua aparecendo nas listagens, como desativado, para você ter o histórico.
- Escopo exigido:
webhooks:gerir - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- O endpoint existe e é do seu parceiro (senão
404 nao_encontrado).
O que acontece
estado = desativado,motivo_estado = removido_pelo_parceiro. As entregas pendentes viramcancelada(ultimo_codigo = endpoint_removido).- Deixa de contar no limite de 5 endpoints e a mesma URL pode ser cadastrada de novo (com segredo novo).
Regras e limites
- Repetir a chamada é inócuo: continua
200comestado = desativado; por isso não exigeIdempotency-Key. - Desativado é terminal: não existe "reativar". Para voltar a receber, cadastre outro endpoint.
- Para só interromper por um tempo, deixe o endpoint existir: se o seu servidor ficar fora, o endpoint é pausado sozinho e os eventos esperam na fila (por até 80 horas sem tentativa; depois disso use
reenviar).
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
endpoint_id |
path | sim | Identificador do endpoint (UUID em minúsculas, no formato canônico). Outro formato responde 404. | c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f |
Respostas de sucesso
- 200 — Endpoint desativado.
Exemplo: Desativado
{
"id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"estado": "desativado"
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X DELETE "https://api.liquix.tech/parceiro/v1/webhooks/endpoints/c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
POST /webhooks/endpoints/{endpoint_id}/retomar — Retomar um endpoint pausado
operationId: webhook_endpoint_retomar
Volta um endpoint pausado para ativo e zera o contador de falhas. Os eventos que esperaram durante a pausa não se perderam: são entregues agora, em ordem.
- Escopo exigido:
webhooks:gerir - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Obrigatório. Sem ele a resposta é
400 idempotency_key_obrigatoria.
Pré-condições
- O endpoint está
pausado(senão409 endpoint_nao_pausado). - O endpoint é do seu parceiro (senão
404 nao_encontrado).
O que acontece
estado = ativo,motivo_estado = null,esgotadas_seguidas = 0.- As entregas pendentes voltam a ser enviadas, respeitando a ordem por operação.
Regras e limites
- O endpoint é pausado sozinho quando 20 entregas seguidas esgotam as tentativas; corrija o seu servidor ANTES de retomar, senão ele pausa de novo.
- Corpo vazio ou
{}.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
endpoint_id |
path | sim | Identificador do endpoint (UUID em minúsculas, no formato canônico). Outro formato responde 404. | c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f |
Idempotency-Key |
header | sim | Obrigatório. Identificador único da sua intenção (8 a 128 caracteres: letras, números, _, ., :, -). Repetir o MESMO pedido com a mesma chave devolve a resposta guardada (24 h), com Idempotent-Replayed: true, sem executar de novo; a mesma chave em pedido DIFERENTE responde 422 idempotencia_pedido_diferente. |
pedido-1042-v1 |
Corpo da requisição
Sem campos: omita o corpo ou envie {}.
{}
Respostas de sucesso
- 200 — Endpoint ativo de novo.
Exemplo: Retomado
{
"estado": "ativo"
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 400 | idempotency_key_obrigatoria |
A operação exige Idempotency-Key (ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números, _, ., : e -). |
| 400 | pedido_invalido |
O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita. |
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 409 | endpoint_nao_pausado |
O endpoint não está no estado pausado (já está ativo ou foi desativado). |
| 409 | idempotencia_em_curso |
O mesmo pedido, com a mesma chave, ainda está sendo processado. |
| 413 | corpo_grande |
O corpo passa do limite (padrão 16 KB) — pelo Content-Length ou pelo que chegou. |
| 415 | tipo_nao_suportado |
Há corpo, mas o Content-Type não é application/json. |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | idempotencia_pedido_diferente |
A mesma Idempotency-Key já foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X POST "https://api.liquix.tech/parceiro/v1/webhooks/endpoints/c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f/retomar" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{}'
Webhooks · Eventos
Concilie: veja os eventos gerados, o corpo exato enviado e o estado de cada entrega, e peça o reenvio de um evento.
GET /webhooks/eventos — Listar os eventos e o estado das entregas
operationId: webhook_evento_listar
Lista os eventos do seu parceiro, do mais novo ao mais antigo, com o estado de cada entrega por endpoint. É a ferramenta de conciliação: se você suspeita que perdeu um aviso, ache o evento aqui e peça o reenvio.
- Escopo exigido:
webhooks:gerir - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
O que acontece
- Só leitura.
Regras e limites
- Paginação por cursor:
limitede 1 a 100 (padrão 20) eproximo_cursoropaco, que vemnullquando não há mais eventos. assunto_tipoaceitaoperacaoecheckout; na v1 só existem eventos deoperacao(checkouté reservado e volta vazio).assunto_idfiltra pelos eventos de UMA operação (passe o id da operação).
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
assunto_tipo |
query | não | Só os eventos deste tipo de assunto. Na v1: operacao (checkout é reservado). |
operacao |
assunto_id |
query | não | Só os eventos deste assunto (o id da operação). Até 128 caracteres: letras, números, _, ., :, -. |
5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18 |
cursor |
query | não | O proximo_cursor da página anterior (opaco; até 400 caracteres). |
MjAyNi0xMC0yMFQxNTowNDowNSswMDowMHw1YjlkMmY0MS04YzU1LTRlMGEtYjFkNy0yYTZmOWMzZTVkMTg |
limite |
query | não | Quantos eventos por página, de 1 a 100. | 20 |
Respostas de sucesso
- 200 — Uma página de eventos.
Exemplo: Evento entregue
{
"data": [
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"tipo": "operacao.concluida",
"assunto_tipo": "operacao",
"assunto_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"seq": 5,
"ocorrido_em": "2026-10-20T15:00:00.123456+00:00",
"entregas": [
{
"endpoint_id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"estado": "entregue",
"tentativas": 1,
"ultimo_status": 200,
"ultimo_codigo": null,
"proxima_tentativa_em": null
}
]
}
],
"proximo_cursor": null
}
Exemplo: Evento com entrega em nova tentativa
{
"data": [
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"tipo": "operacao.concluida",
"assunto_tipo": "operacao",
"assunto_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"seq": 5,
"ocorrido_em": "2026-10-20T15:00:00.123456+00:00",
"entregas": [
{
"endpoint_id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"estado": "pendente",
"tentativas": 2,
"ultimo_status": 503,
"ultimo_codigo": "http_503",
"proxima_tentativa_em": "2026-10-20T15:09:09.000000+00:00"
}
]
}
],
"proximo_cursor": null
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | cursor_invalido |
O cursor não é um proximo_cursor devolvido pela API (adulterado, truncado ou fora do formato). |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/webhooks/eventos" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
GET /webhooks/eventos/{evento_id} — Consultar um evento com o corpo enviado
operationId: webhook_evento_obter
Devolve um evento SEU, com o corpo JSON enviado ao seu endpoint (a assinatura vale sobre os bytes exatos da entrega, que são esta mesma estrutura em JSON compacto) e o estado de cada entrega.
- Escopo exigido:
webhooks:gerir - Escreve? Não: só leitura (funciona mesmo com o parceiro suspenso).
- Idempotency-Key: Não se aplica (leitura, ou operação naturalmente repetível).
Pré-condições
- O evento existe e é do seu parceiro (senão
404 nao_encontrado, igual para evento de outro parceiro).
O que acontece
- Só leitura.
Regras e limites
- O
iddo evento é owebhook-id; o caminho aceita omsg_...ou o UUID. entregastraz uma linha por endpoint que existia (ativo ou pausado) quando o evento nasceu: estado, tentativas, último status HTTP, último código de falha e próxima tentativa.- O
corpoé o que o seu endpoint recebe: use-o para reproduzir a verificação de assinatura ou para reprocessar um evento. - O prazo de retenção, ou seja, por quanto tempo os eventos continuam consultáveis, é definido na contratação.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
evento_id |
path | sim | Identificador do evento: msg_ + 32 hexadecimais (o mesmo valor do cabeçalho webhook-id) ou o UUID canônico. Outro formato responde 404. |
msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e |
Respostas de sucesso
- 200 — O evento com o corpo.
Exemplo: Evento operacao.concluida
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"tipo": "operacao.concluida",
"assunto_tipo": "operacao",
"assunto_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"seq": 5,
"ocorrido_em": "2026-10-20T15:00:00.123456+00:00",
"entregas": [
{
"endpoint_id": "c3a1e9d0-4b7f-4a52-8e6d-1f2b3c4d5e6f",
"estado": "entregue",
"tentativas": 1,
"ultimo_status": 200,
"ultimo_codigo": null,
"proxima_tentativa_em": null
}
],
"corpo": {
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.concluida",
"timestamp": "2026-10-20T15:07:02Z",
"seq": 5,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "concluida",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 5,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": "2026-10-20T15:07:02Z",
"comissao_centavos": 250
}
}
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X GET "https://api.liquix.tech/parceiro/v1/webhooks/eventos/msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY"
POST /webhooks/eventos/{evento_id}/reenviar — Reenviar um evento
operationId: webhook_evento_reenviar
Recoloca na fila as entregas já finalizadas do evento (entregue, esgotada ou cancelada) para os seus endpoints que não estão desativados. O reenvio usa o MESMO webhook-id e o mesmo corpo; só o webhook-timestamp e a assinatura são novos. Use depois de corrigir um defeito no seu servidor.
- Escopo exigido:
webhooks:gerir - Escreve? Sim: parceiro suspenso recebe
403 parceiro_suspenso. - Idempotency-Key: Obrigatório. Sem ele a resposta é
400 idempotency_key_obrigatoria.
Pré-condições
- O evento existe e é do seu parceiro (senão
404 nao_encontrado).
O que acontece
- As entregas finalizadas voltam a
pendentecom as tentativas zeradas, para envio imediato. - Entregas que já estão
pendenteouenviandonão mudam.reenviadasdiz quantas voltaram (pode ser0).
Regras e limites
- Como o
webhook-idé o mesmo, o seu receptor DEVE deduplicar por ele (um reenvio de evento que você já processou não pode repetir o efeito). - Responde
202: o envio é assíncrono; acompanhe pelo estado das entregas emGET /webhooks/eventos/{evento_id}. - Corpo vazio ou
{}.
Parâmetros
| Nome | Onde | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
evento_id |
path | sim | Identificador do evento: msg_ + 32 hexadecimais (o mesmo valor do cabeçalho webhook-id) ou o UUID canônico. Outro formato responde 404. |
msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e |
Idempotency-Key |
header | sim | Obrigatório. Identificador único da sua intenção (8 a 128 caracteres: letras, números, _, ., :, -). Repetir o MESMO pedido com a mesma chave devolve a resposta guardada (24 h), com Idempotent-Replayed: true, sem executar de novo; a mesma chave em pedido DIFERENTE responde 422 idempotencia_pedido_diferente. |
pedido-1042-v1 |
Corpo da requisição
Sem campos: omita o corpo ou envie {}.
{}
Respostas de sucesso
- 202 — Reenvio enfileirado.
Exemplo: Uma entrega voltou à fila
{
"reenviadas": 1
}
Exemplo: Nada a reenviar
{
"reenviadas": 0
}
Erros possíveis
| Status | Código | Quando |
|---|---|---|
| 400 | idempotency_key_obrigatoria |
A operação exige Idempotency-Key (ou você enviou uma, mas fora do formato: 8 a 128 caracteres entre letras, números, _, ., : e -). |
| 400 | pedido_invalido |
O caminho da requisição (com a consulta) passou do tamanho que a idempotência aceita. |
| 401 | nao_autorizado |
Faltou o cabeçalho Authorization, ou a chave é desconhecida, está errada, expirou ou foi revogada. A resposta é a MESMA nos cinco casos, de propósito: ela não revela se a chave existe. |
| 403 | escopo_insuficiente |
A chave é válida, mas não tem o escopo que a operação exige (veja Escopo exigido na operação e chave.escopos em GET /eu). |
| 403 | parceiro_suspenso |
O parceiro está suspenso ou encerrado: as leituras (GET) e a entrega de webhooks continuam, mas toda escrita é recusada. |
| 404 | nao_encontrado |
O recurso não existe, pertence a outro parceiro ou o identificador está fora do formato (a resposta é UNIFORME: nunca diz se o recurso existe). Também vale para rota inexistente. |
| 409 | idempotencia_em_curso |
O mesmo pedido, com a mesma chave, ainda está sendo processado. |
| 413 | corpo_grande |
O corpo passa do limite (padrão 16 KB) — pelo Content-Length ou pelo que chegou. |
| 415 | tipo_nao_suportado |
Há corpo, mas o Content-Type não é application/json. |
| 422 | corpo_invalido |
O corpo (ou um parâmetro de caminho/consulta) não segue o contrato: campo faltando, campo desconhecido (a API rejeita campos extras), tipo errado ou fora do formato. details lista QUAIS campos e o tipo do erro; nunca ecoa o valor recebido. |
| 422 | idempotencia_pedido_diferente |
A mesma Idempotency-Key já foi usada em um pedido DIFERENTE (outro corpo, outro caminho ou outro método). A API nunca devolve a resposta guardada nesse caso, para uma chave reutilizada por engano não repetir o efeito errado. |
| 429 | muitas_requisicoes |
A chave passou do limite_por_minuto (janela de 1 minuto), ou muitas chamadas com credencial inválida partiram do mesmo IP (nesse caso o 429 sai no lugar do 401). |
| 500 | erro_interno |
Falha inesperada do nosso lado. A resposta nunca traz o texto do erro; o request_id é o que a Liquix usa para achar o caso. |
Exemplo (curl)
curl -X POST "https://api.liquix.tech/parceiro/v1/webhooks/eventos/msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e/reenviar" \
-H "Authorization: Bearer $LIQUIXPAY_API_KEY" \
-H "Idempotency-Key: pedido-1042-v1" \
-H "Content-Type: application/json" \
-d '{}'
Referência dos eventos de webhook
operacao.criada
A operação foi aceita e está sendo aberta (estado = criando). Ainda não há Pix.
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.criada",
"timestamp": "2026-10-20T15:04:05Z",
"seq": 2,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "criando",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": null,
"comissao_prevista_centavos": 250,
"versao": 2,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:05Z",
"concluida_em": null
}
}
operacao.aguardando_pagamento
O Pix foi gerado (estado = aguardando_pagamento). Busque-o em GET /operacoes/{id} (pagamento.pix_copia_e_cola) e mostre ao cliente final.
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.aguardando_pagamento",
"timestamp": "2026-10-20T15:04:07Z",
"seq": 3,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "aguardando_pagamento",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null
}
}
operacao.processando
O pagamento chegou e o dólar está sendo entregue (estado = processando).
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.processando",
"timestamp": "2026-10-20T15:06:31Z",
"seq": 4,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "processando",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 4,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:06:31Z",
"concluida_em": null
}
}
operacao.concluida
O dólar chegou à carteira do cliente final (estado = concluida, final). data.comissao_centavos é a sua comissão devida.
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.concluida",
"timestamp": "2026-10-20T15:07:02Z",
"seq": 5,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "concluida",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 5,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": "2026-10-20T15:07:02Z",
"comissao_centavos": 250
}
}
operacao.recusada
A execução foi recusada ANTES de gerar o Pix (estado = recusada, final). Nada foi cobrado; data.motivo diz por quê (ex.: preco_mudou).
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.recusada",
"timestamp": "2026-10-20T15:04:07Z",
"seq": 3,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "recusada",
"motivo": "preco_mudou",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": null,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null
}
}
operacao.falhou
A operação falhou depois do pagamento (estado = falhou, final); data.motivo = devolvida quando o valor foi devolvido ao cliente.
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.falhou",
"timestamp": "2026-10-20T15:07:02Z",
"seq": 5,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "falhou",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 5,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": null
}
}
operacao.expirada
O Pix venceu sem pagamento (estado = expirada, final). Nada foi cobrado.
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.expirada",
"timestamp": "2026-10-20T15:07:02Z",
"seq": 4,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "expirada",
"motivo": null,
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": 8899,
"comissao_prevista_centavos": 250,
"versao": 4,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:07:02Z",
"concluida_em": null
}
}
operacao.em_analise
O desfecho precisa de conferência (estado = em_hold): a Liquix está analisando e NADA será repetido nem reenviado. data.motivo diz o porquê.
Como verificar: veja Receber eventos (webhooks). O corpo é um instantâneo: confirme o estado atual em GET /operacoes/{id} antes de agir.
Corpo enviado
{
"id": "msg_2f8a1c4e9b3d4a6f8c0e1d2b3a4c5d6e",
"type": "operacao.em_analise",
"timestamp": "2026-10-20T15:04:07Z",
"seq": 3,
"data": {
"operacao_id": "5b9d2f41-8c55-4e0a-b1d7-2a6f9c3e5d18",
"cotacao_id": "0f2c4d6e-8a1b-4c3d-9e5f-7a6b5c4d3e21",
"produto": "dolar_compra",
"cliente_ref": "cliente-1042",
"estado": "em_hold",
"motivo": "ambiguo",
"valor_brl_centavos": 50000,
"markup_bps": 50,
"tarifa": {
"all_in": "2.00"
},
"usdc_centavos": null,
"comissao_prevista_centavos": 250,
"versao": 3,
"criada_em": "2026-10-20T15:04:05Z",
"atualizada_em": "2026-10-20T15:04:07Z",
"concluida_em": null
}
}
Modelos de dados
CotacaoEnvelope
Resposta com uma cotação.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
cotacao |
CotacaoOut |
sim | A cotação. |
CotacaoOut
Uma cotação: o preço PROMETIDO por 60 segundos (padrão), de uso único. Não move dinheiro e não cria operação.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
cotacao_id |
string (uuid) |
sim | Identificador da cotação. É o único dado que POST /operacoes precisa para executar. |
produto |
string |
sim | Produto cotado (dolar_compra). |
cliente_ref |
string |
sim | Referência do cliente final cotado (a mesma do pedido). |
valor_brl_centavos |
integer |
sim | Valor que o cliente final pagará, em CENTAVOS de real (inteiro). R$ 500,00 = 50000. |
markup_bps |
integer |
sim | Markup do parceiro embutido no preço, em pontos-base (100 = 1,00%). É o do seu parceiro, nunca do pedido. |
tarifa |
TarifaOut |
sim | A tarifa prometida, com todas as taxas repassadas ao cliente final. |
usdc_estimado_centavos |
integer |
sim | Quanto o cliente final receberá, em CENTAVOS de dólar digital (USDC), estimado pelo câmbio de referência. É uma estimativa; o valor final sai em operacao.usdc_centavos. |
comissao_prevista_centavos |
integer |
sim | Sua comissão prevista se a operação concluir, em CENTAVOS de real: valor x markup, arredondado para baixo. Só é devida se a operação terminar em concluida. |
expira_em |
string (date-time) |
sim | Até quando a cotação vale (ISO 8601, UTC, Z). Depois disso POST /operacoes responde cotacao_expirada. |
DadosDaOperacao
O estado da operação no instante do evento: um SNAPSHOT. Não traz o Pix; para ele (e para a verdade atual) use GET /operacoes/{id}.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
operacao_id |
string |
sim | Id da operação. |
cotacao_id |
string |
sim | A cotação que originou a operação. |
produto |
string |
sim | Produto (dolar_compra). |
cliente_ref |
string |
sim | Referência do cliente final. |
estado |
um de recebida, criando, aguardando_pagamento, processando, concluida, recusada, falhou, expirada, em_hold |
sim | Estado da operação neste instante. |
motivo |
string ou null |
sim | Código de máquina que explica recusada, falhou ou em_hold (pode ser null em em_hold); null nos demais. |
valor_brl_centavos |
integer |
sim | Valor pago pelo cliente final, em CENTAVOS de real. |
markup_bps |
integer |
sim | Markup do parceiro, em pontos-base. |
tarifa |
TarifaDaOperacaoOut |
sim | A tarifa que valeu. |
usdc_centavos |
integer ou null |
sim | Dólar digital do cliente final, em CENTAVOS de dólar; null se ainda não definido. |
comissao_prevista_centavos |
integer |
sim | Comissão prevista, em CENTAVOS de real. |
comissao_centavos |
integer ou null |
não | Só no evento operacao.concluida: a comissão devida a você, em CENTAVOS de real. Nos outros eventos o campo não existe. |
versao |
integer |
sim | Igual ao seq do evento. |
criada_em |
string |
sim | Quando a operação foi criada (ISO 8601, UTC, Z). |
atualizada_em |
string |
sim | Última mudança de estado (ISO 8601, UTC, Z). |
concluida_em |
string ou null |
sim | Quando concluiu; null se ainda não. |
EndpointCriadoOut
Resposta do cadastro: o endpoint E o segredo de assinatura, mostrado UMA única vez.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Identificador do endpoint (UUID). |
url |
string |
sim | Endereço cadastrado, normalizado (host em minúsculas). |
estado |
"ativo" |
sim | Sempre ativo ao nascer. |
segredo |
string ou null |
não | Segredo de assinatura (whsec_ + base64 de 32 bytes), único deste endpoint. Guarde-o AGORA: não há como consultá-lo depois. A resposta repetida de uma Idempotency-Key já usada devolve o mesmo endpoint SEM este campo. Perdeu o segredo? Remova o endpoint e cadastre outro. |
EndpointOut
Um endpoint de webhook. O segredo nunca é devolvido depois da criação.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Identificador do endpoint (UUID). |
url |
string |
sim | Endereço que recebe os eventos. |
estado |
um de ativo, pausado, desativado |
sim | ativo: recebe. pausado: os eventos esperam na fila (por até 80 horas sem tentativa) até você chamar /retomar. desativado: definitivo (removido por você ou pelo seu servidor ter respondido 410 Gone). |
motivo_estado |
string ou null |
sim | Por que não está ativo: muitas_falhas (pausado), removido_pelo_parceiro ou gone (desativado); null se ativo. |
esgotadas_seguidas |
integer |
sim | Quantas entregas seguidas esgotaram todas as tentativas. Ao chegar a 20 (padrão) o endpoint é pausado. |
criado_em |
string |
sim | Quando foi cadastrado (ISO 8601, UTC, com microssegundos). |
EndpointRemovidoOut
Resposta da remoção.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | O endpoint removido. |
estado |
"desativado" |
sim | Sempre desativado (definitivo). As entregas pendentes dele foram canceladas. |
EndpointRetomadoOut
Resposta de retomar.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
estado |
"ativo" |
sim | O endpoint voltou a ativo; as entregas que esperaram escoam em ordem. |
EntregaOut
A entrega de UM evento para UM endpoint.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
endpoint_id |
string |
sim | O endpoint de destino. |
estado |
um de pendente, enviando, entregue, esgotada, cancelada |
sim | pendente: na fila (aguardando horário). enviando: tentativa em curso. entregue: seu servidor respondeu 2xx. esgotada: acabaram as tentativas. cancelada: endpoint removido/410, ou evento velho demais (muito_antiga). |
tentativas |
integer |
sim | Quantas tentativas já foram feitas. |
ultimo_status |
integer ou null |
sim | Último status HTTP que o seu servidor devolveu; null se nunca respondeu. |
ultimo_codigo |
string ou null |
sim | Motivo da última falha: http_<status>, timeout, conexao, tls, rede, redirecionamento, resposta_invalida, url_proibida, gone, endpoint_removido, muito_antiga...; null se a última tentativa deu certo. |
proxima_tentativa_em |
string ou null |
sim | Quando será a próxima tentativa (ISO 8601); null se não há. |
ErroCampo
Um campo recusado na validação.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
field |
string |
sim | Caminho do campo recusado (body.cotacao_id, query.limite...). Nunca o valor recebido. |
code |
string |
sim | Tipo do erro de validação (missing, extra_forbidden, string_too_short, uuid_parsing, esquema, ...). |
ErroDetalhe
O erro: código de máquina, mensagem fixa em português e o id da requisição.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
code |
string |
sim | Código de máquina, minúsculo e estável. Programe a sua lógica POR ESTE CAMPO, nunca pela message. |
message |
string |
sim | Mensagem fixa em português, segura para log. Pode ganhar novas redações; não dependa do texto. |
request_id |
string |
sim | Identificador da requisição (32 hex). Vem também no cabeçalho X-Request-Id; informe-o ao falar com a Liquix. |
details |
lista de ErroCampo ou null |
não | Só em corpo_invalido e url_proibida: os campos recusados e o tipo do erro. |
ErroOut
Envelope de erro único de toda a API.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
error |
ErroDetalhe |
sim | O erro. |
EuChaveOut
A chave que fez a chamada. O segredo da chave nunca é devolvido.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
key_id |
string |
sim | Parte pública da chave (12 caracteres), o trecho entre os dois _ de lqxp_<key_id>_<segredo>. Serve para identificar a chave em conversas. |
rotulo |
string |
sim | Nome que a Liquix deu à chave ao emiti-la. |
escopos |
lista de string |
sim | Escopos da chave: eu:ler, vinculos:gerir, cotacoes:criar, operacoes:criar, operacoes:ler, webhooks:gerir. |
teto_operacao_usd_centavos |
integer |
sim | Teto por operação desta chave, em CENTAVOS de dólar. 0 = nada permitido. |
teto_dia_usd_centavos |
integer |
sim | Teto das últimas 24 horas desta chave, em CENTAVOS de dólar (soma do dólar reservado pelas operações). 0 = nada permitido. |
limite_por_minuto |
integer |
sim | Quantas requisições a chave pode fazer por minuto (de 1 a 600). Passou disso: 429 muitas_requisicoes com Retry-After. |
estado |
string |
sim | Sempre ativa numa chamada autenticada: chave revogada ou expirada não autentica (responde nao_autorizado). |
expira_em |
string ou null |
sim | Quando a chave expira (ISO 8601 com fuso); null se não tem validade definida. Em rotação, a chave antiga expira no fim da carência. |
EuOut
Quem é o parceiro e a chave, o uso das últimas 24 horas e os produtos liberados.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
parceiro |
EuParceiroOut |
sim | O parceiro. |
chave |
EuChaveOut |
sim | A chave que fez a chamada. |
uso |
EuUsoOut |
sim | Uso das últimas 24 horas contra os tetos. |
markup_bps |
integer |
sim | Seu markup, em pontos-base (100 = 1,00%), somado ao preço da casa em toda cotação. |
produtos |
lista de string |
sim | Produtos liberados para você (dolar_compra). |
EuParceiroOut
O parceiro dono da chave.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
slug |
string |
sim | Identificador curto do parceiro (minúsculas e hífens). |
nome |
string |
sim | Nome do parceiro. |
estado |
string |
sim | ativo (lê e escreve) ou suspenso/encerrado (só lê; escrita responde parceiro_suspenso). |
EuUsoOut
O quanto já foi usado do teto diário nas últimas 24 horas.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
chave_24h_usd_centavos |
integer |
sim | Dólar reservado, nas últimas 24 h, pelas operações criadas por ESTA chave que ainda seguram reserva, em CENTAVOS de dólar. |
parceiro_24h_usd_centavos |
integer |
sim | O mesmo, somando TODAS as chaves do parceiro, em CENTAVOS de dólar. |
EventoDaOperacao
Envelope comum de todo evento. O corpo é JSON compacto (sem espaços) e tem no máximo 64 KB.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
string |
sim | Tipo do evento. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoDetalheOut
O evento com o corpo EXATAMENTE como é enviado.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex): o MESMO webhook-id do cabeçalho e o id do corpo. Use-o como chave de deduplicação. |
tipo |
string |
sim | Tipo do evento (operacao.concluida...). |
assunto_tipo |
um de operacao, checkout |
sim | A que o evento se refere. Na v1 só operacao (checkout é reservado). |
assunto_id |
string |
sim | Id do assunto (o id da operação). |
seq |
integer |
sim | Número de sequência do assunto (o versao da operação): sobe a cada mudança e define a ordem dos eventos do MESMO assunto. |
ocorrido_em |
string |
sim | Quando o evento foi gerado (ISO 8601, UTC, com microssegundos). |
entregas |
lista de EntregaOut |
sim | Uma entrega por endpoint que existia (ativo ou pausado) quando o evento nasceu. |
corpo |
object |
sim | O corpo JSON que o seu endpoint recebe (id, type, timestamp, seq, data), como objeto. Os bytes assinados são esta mesma estrutura em JSON compacto. |
EventoOperacaoAguardandoPagamento
O Pix foi gerado (estado = aguardando_pagamento). Busque-o em GET /operacoes/{id} (pagamento.pix_copia_e_cola) e mostre ao cliente final.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.aguardando_pagamento" |
sim | Sempre operacao.aguardando_pagamento. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoConcluida
O dólar chegou à carteira do cliente final (estado = concluida, final). data.comissao_centavos é a sua comissão devida.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.concluida" |
sim | Sempre operacao.concluida. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoCriada
A operação foi aceita e está sendo aberta (estado = criando). Ainda não há Pix.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.criada" |
sim | Sempre operacao.criada. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoEmAnalise
O desfecho precisa de conferência (estado = em_hold): a Liquix está analisando e NADA será repetido nem reenviado. data.motivo diz o porquê.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.em_analise" |
sim | Sempre operacao.em_analise. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoExpirada
O Pix venceu sem pagamento (estado = expirada, final). Nada foi cobrado.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.expirada" |
sim | Sempre operacao.expirada. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoFalhou
A operação falhou depois do pagamento (estado = falhou, final); data.motivo = devolvida quando o valor foi devolvido ao cliente.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.falhou" |
sim | Sempre operacao.falhou. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoProcessando
O pagamento chegou e o dólar está sendo entregue (estado = processando).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.processando" |
sim | Sempre operacao.processando. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOperacaoRecusada
A execução foi recusada ANTES de gerar o Pix (estado = recusada, final). Nada foi cobrado; data.motivo diz por quê (ex.: preco_mudou).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex), IGUAL ao cabeçalho webhook-id e estável em todas as tentativas e reenvios. Deduplique por ele. |
type |
"operacao.recusada" |
sim | Sempre operacao.recusada. |
timestamp |
string |
sim | Quando o evento nasceu (ISO 8601, UTC, Z). NÃO é o webhook-timestamp (esse é o da tentativa). |
seq |
integer |
sim | Número de sequência da operação: eventos da MESMA operação chegam em ordem de seq; ignore um evento cujo seq seja menor que o que você já aplicou. |
data |
DadosDaOperacao |
sim | O estado da operação neste instante. |
EventoOut
Um evento e o estado das suas entregas.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
sim | Id do evento (msg_ + 32 hex): o MESMO webhook-id do cabeçalho e o id do corpo. Use-o como chave de deduplicação. |
tipo |
string |
sim | Tipo do evento (operacao.concluida...). |
assunto_tipo |
um de operacao, checkout |
sim | A que o evento se refere. Na v1 só operacao (checkout é reservado). |
assunto_id |
string |
sim | Id do assunto (o id da operação). |
seq |
integer |
sim | Número de sequência do assunto (o versao da operação): sobe a cada mudança e define a ordem dos eventos do MESMO assunto. |
ocorrido_em |
string |
sim | Quando o evento foi gerado (ISO 8601, UTC, com microssegundos). |
entregas |
lista de EntregaOut |
sim | Uma entrega por endpoint que existia (ativo ou pausado) quando o evento nasceu. |
ListaEndpointsOut
Todos os seus endpoints, em qualquer estado, do mais antigo ao mais novo.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
data |
lista de EndpointOut |
sim | Os endpoints (no máximo 5 ativos ou pausados, mais os desativados). |
ListaEventosOut
Uma página de eventos, do mais novo ao mais antigo.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
data |
lista de EventoOut |
sim | Os eventos desta página (até limite). |
proximo_cursor |
string ou null |
sim | Cursor opaco da próxima página; null quando não há mais eventos (só vem preenchido se existe pelo menos mais um). |
ListaOperacoesOut
Uma página de operações, da mais nova à mais antiga.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
data |
lista de OperacaoOut |
sim | As operações desta página (até limite). |
proximo_cursor |
string ou null |
sim | Cursor opaco da próxima página; null quando a página não veio cheia (acabou). Uma página cheia pode ser a última: a seguinte virá vazia. |
OperacaoEnvelope
Resposta com uma operação.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
operacao |
OperacaoOut |
sim | A operação. |
OperacaoOut
Uma operação de câmbio do parceiro, com o estado atual. O GET é a fonte da verdade; o webhook só avisa que algo mudou.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string (uuid) |
sim | Identificador da operação (UUID). |
cotacao_id |
string (uuid) |
sim | A cotação que originou a operação. |
produto |
string |
sim | Produto da operação (dolar_compra). |
cliente_ref |
string |
sim | Referência do cliente final. |
estado |
um de recebida, criando, aguardando_pagamento, processando, concluida, recusada, falhou, expirada, em_hold |
sim | Estado da operação: recebida, criando, aguardando_pagamento, processando, concluida (final), recusada (final), falhou (final), expirada (final) ou em_hold (em análise). Estado final nunca volta atrás. |
motivo |
string ou null |
sim | Código de máquina que explica recusada, falhou ou em_hold (ex.: preco_mudou, indisponivel, ambiguo, sem_referencia, devolvida); pode ser null em em_hold e nos demais estados. Depois de sair do em_hold o motivo antigo pode permanecer: decida pelo estado. |
valor_brl_centavos |
integer |
sim | Valor que o cliente final paga, em CENTAVOS de real. |
markup_bps |
integer |
sim | Markup do parceiro desta operação, em pontos-base. |
tarifa |
TarifaDaOperacaoOut |
sim | |
usdc_centavos |
integer ou null |
sim | Dólar digital que o cliente final recebe, em CENTAVOS de dólar; null enquanto o valor não foi definido (criando, recusada, em_hold sem referência). |
pagamento |
PagamentoOut ou null |
não | Só em aguardando_pagamento; nos outros estados o campo não existe |
comissao_prevista_centavos |
integer |
sim | Comissão prevista, em CENTAVOS de real (valor x markup, para baixo). |
comissao_centavos |
integer ou null |
não | Sua comissão devida, em CENTAVOS de real. Só em concluida; nos outros estados o campo não existe |
versao |
integer |
sim | Contador que sobe a cada mudança de estado. O seq do webhook é o mesmo número: use-o para ignorar aviso velho. |
criada_em |
string (date-time) |
sim | Quando a operação foi criada (ISO 8601, UTC). |
atualizada_em |
string (date-time) |
sim | Última mudança de estado (ISO 8601, UTC). |
concluida_em |
string (date-time) ou null |
sim | Quando a operação concluiu (ISO 8601, UTC); null se ainda não concluiu. |
PagamentoOut
O Pix que o cliente final deve pagar. Só existe enquanto a operação está em aguardando_pagamento.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
pix_copia_e_cola |
string |
sim | Código Pix copia-e-cola (BR Code). SÓ o CPF/CNPJ do cadastro do cliente final consegue pagá-lo; o dólar vai sempre para a carteira do próprio cliente. |
expira_em |
string (date-time) |
sim | Até quando o Pix vale (ISO 8601, UTC). Padrão: 5 minutos depois da criação da operação. |
valor_a_pagar_centavos |
integer |
sim | Valor do Pix, em CENTAVOS de real (inteiro): o valor_brl_centavos da cotação. |
ReenvioOut
Resposta de reenviar.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
reenviadas |
integer |
sim | Quantas entregas voltaram à fila (as entregue, esgotada ou cancelada de endpoints não desativados; 0 se não havia nenhuma). |
TarifaDaOperacaoOut
A tarifa que valeu para a operação.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
all_in |
string ou null |
sim | Percentual all-in da cotação que originou a operação, com 2 casas, em texto; null se não estiver disponível. |
TarifaOut
A tarifa prometida na cotação, já com o seu markup e todas as taxas repassadas ao cliente final.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
all_in |
string |
sim | Percentual all-in (tudo incluído, exceto o valor fixo de US$ 0,10) com 2 casas, em texto. Ex.: "2.00" = 2,00%. |
rotulo |
string |
sim | A tarifa como o cliente final a lê, pronta para exibir: percentual mais o valor fixo. |
VinculoEnvelope
Resposta com um vínculo.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
vinculo |
VinculoOut |
sim | O vínculo. |
VinculoOut
O vínculo entre o seu cliente_ref e um cliente verificado da Liquix. Nasce por ato da Liquix e você pode revogá-lo.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
cliente_ref |
string |
sim | A referência do cliente final no seu sistema (1 a 64 caracteres: letras, números, _, ., -). Nunca é documento, dado de contato nem carteira. |
estado |
um de ativo, revogado |
sim | ativo: dá para cotar e operar para este cliente. revogado: cotação nova responde vinculo_nao_encontrado; as operações já em andamento seguem até o fim. |
criado_em |
string (date-time) |
sim | Quando o vínculo foi criado (ISO 8601, UTC). |
revogado_em |
string (date-time) ou null |
sim | Quando foi revogado (ISO 8601, UTC); null se está ativo. |
Outros produtos
Estes produtos usam o mesmo modelo de chave, cotação, operação e webhook. Os endpoints de cada um e as condições são definidos na contratação.
- Bitcoin Lightning: pagar ou receber por Lightning (fatura ou Lightning Address) a partir de um Pix, e pagar boleto com uma fatura Lightning.
- Bitcoin on-chain, nos dois sentidos: Pix para Bitcoin e Bitcoin para Pix ou boleto, com as confirmações da rede (cerca de 12 a 20 minutos).
- Qualquer token EVM: Pix para token (WETH, WBTC, USDT e outros da lista aprovada) e token para Pix ou boleto.
- Gift card de marcas globais: Pague em Pix, USDC ou DePix e receba o código do cartão.
- Cartão Global em dólar: Recarga do cartão internacional em dólar, paga em Pix.
- Pagamento a fornecedor no exterior: Envie em USDC e o fornecedor recebe por ACH, Wire ou SWIFT.
- Tesouraria e Folha: Caixa da empresa em dólar digital, que rende, e pagamento da equipe em USDC.
- Maquininha com QR fixo: Loja física recebe em Pix ou cripto com um QR fixo no balcão.
- Agentes de IA: Liquix Agents, gift card para agentes, agente que paga boleto e Pix, cotação por chamada, ferramentas para agentes, Liquix Reversa e preço e anúncio para lojistas de marketplace.
- Para parceiros: API personalizada, revenda com a sua margem, painel do parceiro e painel white-label.