API Letztech — Documentação para Parceiros
Referência de integração para marketplaces parceiros: criação de cobranças via Pix e Boleto, e consulta de status de uma transação. Cobre apenas os endpoints acessíveis via API key de marketplace (HTTP Basic Auth).
Base URL: https://api.letztech.com.br
Postman collection
Baixe a collection completa do Postman com todos os endpoints desta documentação já montados (autenticação HTTP Basic, headers de idempotência e exemplos de corpo prontos). Importe no Postman via Import → File e preencha as variáveis da collection.
Variáveis a preencher: apiKey, marketplaceId e sellerId (on_behalf_of). O baseUrl já vem preenchido com https://api.letztech.com.br, e o id da transação criada é salvo automaticamente em transactionId para reuso nas chamadas de consulta, refund e páginas públicas.
0. Antes de começar — API key e marketplaceId
Para autenticar, você precisa de dois valores, ambos obtidos dentro do painel Letztech (https://letztech.com.br/login), não por esta documentação:
- API key: um segredo gerado no painel, com confirmação por código enviado por e-mail (MFA). O valor em texto puro só é mostrado uma vez, no momento em que é gerado — a API guarda só um hash, sem como recuperá-lo depois. Se perder, gere outra: a chave anterior continua valendo por 24 horas, para você trocar sem parar a integração, e então deixa de funcionar. Gerar de novo em menos de 1 minuto é recusado (
409). marketplaceId: é o dono da chave, o mesmo identificador exibido/copiável no painel. Vai na URL de cada chamada (:marketplaceId), não é um dado secreto.
Há dois tipos de chave:
| Chave | Onde gerar | marketplaceId | Opera |
|---|---|---|---|
| De seller | Tela inicial do seller (/dashboard/seller) | id do seller | Só esse seller |
| De revenda | Painel da revenda, tela Integração (só o dono da revenda) | id da revenda | Todos os sellers da revenda, indicados em on_behalf_of |
A conta precisa estar active (aprovada) para gerar uma chave. As duas chaves convivem: o seller de uma revenda pode ter a própria chave, e a revenda a dela.
Chave de revenda
on_behalf_ofaceita qualquer seller da revenda. Um seller de outra revenda, ou que não existe, retorna404.- A revenda consulta e devolve qualquer transação dos sellers dela feita com o preço da revenda, inclusive as criadas pela chave do próprio seller. Fora disso,
404. - Com a revenda suspensa, a chave dela não cria cobranças (
403), mas continua consultando e devolvendo. - Os compradores (
customer) criados com a chave da revenda são da revenda e servem para todos os sellers dela; não aparecem para a chave do seller, e vice-versa.
1. Autenticação
Todas as chamadas usam HTTP Basic Auth, com a API key do marketplace como usuário e senha vazia:
Authorization: Basic base64("<API_KEY>:")A API key é vinculada a um marketplaceId específico (o dono da chave, um seller ou uma revenda). Toda chamada cujo :marketplaceId na URL não corresponda à API key usada retorna 403.
| Situação | Status |
|---|---|
| Header ausente ou malformado | 401 |
| API key inválida, revogada, inativa ou anterior já fora das 24h | 401 |
marketplaceId da URL não pertence à API key | 403 |
2. Idempotência
Envie o header X-Idempotency-Key (qualquer string única por tentativa lógica) em toda chamada de criação. Reenvios com a mesma chave e mesmo payload retornam o resultado original, sem duplicar a cobrança.
Além disso, o campo reference_id (obrigatório) funciona como chave de deduplicação por seller: reenviar o mesmo reference_id para o mesmo on_behalf_of devolve a transação já criada, sem criar outra cobrança. Vale para as duas chaves: se a revenda e o seller usarem o mesmo reference_id para o mesmo seller, recebem a mesma transação. O mesmo reference_id em sellers diferentes cria cobranças diferentes. Se a transação já criada não puder ser vista por quem reenviou (por exemplo, a revenda reenviando um reference_id que o seller usou antes de entrar nela), a resposta é 409 Conflict, sem dados da transação.
3. Rate limit
Por chave, a cada minuto:
| Chave | Limite |
|---|---|
| De seller | 300 requisições |
| De revenda | 1.200 requisições no total, e no máximo 300 criações de cobrança por seller (on_behalf_of) |
Acima disso: 429 Too Many Requests.
Limites transacionais do seller
Cada seller pode ter limites de valor para Pix e boleto somados: por cobrança, por dia e por mês (dia e mês de Brasília). Contam as cobranças criadas no período que não falharam nem expiraram; uma devolução não libera limite. A cobrança que passaria do limite é recusada com 422, sem ser emitida, e a message diz qual limite foi atingido e quanto resta, por exemplo Limite diário excedido: restam R$ 300,00 hoje (limite R$ 1.000,00). Os limites são definidos pela Letztech e pela revenda; o seller vê os dele em Configurações, no painel.
4. Criar cobrança Pix
Authorization: Basic base64("<API_KEY>:")
Content-Type: application/json
X-Idempotency-Key: <opcional, recomendado>Corpo da requisição
{
"amount": 15000,
"currency": "BRL",
"payment_type": "pix",
"on_behalf_of": "<seller_id>",
"reference_id": "order-1234",
"customer": "customer-abc-123",
"description": "Pagamento do pedido #1234",
"postback_url": "https://parceiro.com.br/webhooks/letztech",
"payment_method": {
"pix_expiration_date_time": "2025-12-31T23:59:59Z"
}
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
amount | integer | sim | Valor em centavos, inteiro positivo |
currency | string | sim | Código ISO 4217, ex: "BRL" |
payment_type | string | sim | Fixo: "pix" |
on_behalf_of | string | sim | ID do seller no Letztech, dono da cobrança |
reference_id | string | sim | Chave de deduplicação, única por seller |
customer | string | não | Identificador do comprador (referência livre) |
description | string | não | Descrição da cobrança |
postback_url | string (URL, HTTPS) | não | URL do parceiro que recebe webhooks de mudança de status (seção 7) |
split_rules | array | não | Divisão da venda entre sellers da mesma revenda (seção 7.1) |
payment_method.pix_expiration_date_time | string (ISO 8601) | não | Expiração do QR code (padrão: 1 hora). Sem pagamento até o prazo, a transação expira e o webhook transaction.expired é enviado |
Resposta — 201 Created
{
"id": "txn_...",
"resource": "transaction",
"amount": 15000,
"original_amount": 15000,
"currency": "BRL",
"description": "Pagamento do pedido #1234",
"transaction_number": "...",
"on_behalf_of": "<seller_id>",
"refunded": false,
"voided": false,
"captured": false,
"status": "pending",
"payment_type": "pix",
"payment_method": {
"id": "<txid Sicredi>",
"provider": "SICREDI",
"type": "DYNAMIC",
"reusable": false,
"qr_code": {
"emv": "<pix copia-e-cola>"
}
},
"fees": "150",
"metadata": {},
"webhook_secret": "<presente apenas se postback_url foi informado>",
"created_at": "2026-09-22T12:00:00.000Z",
"updated_at": "2026-09-22T12:00:00.000Z"
}payment_method.qr_code.emv, o "PIX copia e cola"). Gere a imagem do QR code a partir desse payload do seu lado (qualquer lib de QR code padrão).fees é a taxa cobrada do seller nesta cobrança, em centavos, definida no momento da criação pelo plano de taxas do seller. Se o plano mudar depois, as cobranças já criadas mantêm essa taxa.
Erros
| Status | Situação |
|---|---|
| 400 | Validação do payload, ou (chave de seller) on_behalf_of não corresponde a um seller conhecido |
| 401 | API key ausente/inválida |
| 400 | O valor da cobrança é menor ou igual à taxa do seller |
| 403 | marketplaceId não corresponde à API key; (chave de seller) on_behalf_of é outro seller; ou o seller, a revenda dele ou a revenda dona da chave não está ativo |
| 404 | (chave de revenda) on_behalf_of não é um seller da revenda |
| 409 | reference_id já usado para esse seller numa transação que quem chamou não pode ver |
| 422 | O seller ainda não tem plano de taxas configurado (fale com a Letztech) |
| 422 | Limite transacional do seller atingido (por cobrança, diário ou mensal; ver seção 3) |
| 429 | Rate limit excedido |
| 502 | Erro de comunicação com o provedor de pagamento |
5. Criar cobrança Boleto
Mesmo endpoint da criação de Pix — o tipo é definido por payment_type.
Corpo da requisição
{
"amount": 25000,
"currency": "BRL",
"payment_type": "boleto",
"on_behalf_of": "<seller_id>",
"reference_id": "invoice-2026-001",
"customer": "customer-abc-123",
"description": "Mensalidade Janeiro/2026",
"postback_url": "https://parceiro.com.br/webhooks/letztech",
"logo": "https://minha-empresa.com/logo.png",
"payment_method": {
"expiration_date": "2025-12-31",
"payment_limit_date": "2026-01-15",
"buyer": {
"first_name": "João",
"last_name": "Silva",
"email": "joao@email.com",
"phone_number": "5199999999",
"taxpayer_id": "12345678901",
"birthdate": "1990-05-15",
"address": {
"line1": "Rua das Flores",
"line2": "123",
"line3": "Sala 4",
"neighborhood": "Centro",
"city": "Porto Alegre",
"state": "RS",
"postal_code": "90000000"
}
},
"body_instructions": [
"Não receber após vencimento",
"Juros de 1% ao mês"
],
"billing_instructions": {
"late_fee": { "mode": "PERCENTAGE", "percentage": 2.0, "start_date": "2026-01-01" },
"interest": { "mode": "PERCENTAGE", "percentage": 1.0, "start_date": "2026-01-01" },
"discount": [
{ "mode": "FIXED", "amount": 500, "limit_date": "2025-12-20" }
]
}
}
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
amount | integer | sim | Valor em centavos, inteiro positivo |
currency | string | sim | Código ISO 4217, ex: "BRL" |
payment_type | string | sim | Fixo: "boleto" |
on_behalf_of | string | sim | ID do seller no Letztech, dono da cobrança |
reference_id | string | sim | Chave de deduplicação, única por seller |
customer | string | não | Ignorado em boleto — o pagador vem sempre de payment_method.buyer |
description | string | não | Descrição da cobrança |
postback_url | string (URL, HTTPS) | não | URL do parceiro que recebe webhooks de mudança de status (seção 7) |
split_rules | array | não | Divisão da venda entre sellers da mesma revenda (seção 7.1) |
logo | string (URL) | não | Logo impresso no boleto — ainda não aplicado (aceito e ignorado; ver aviso abaixo) |
payment_method.expiration_date | string (YYYY-MM-DD) | não | Data de vencimento |
payment_method.payment_limit_date | string (YYYY-MM-DD) | não | Data limite para pagamento após o vencimento — ainda não aplicado (aceito e ignorado; ver aviso abaixo) |
payment_method.buyer | objeto | sim | Dados do pagador/sacado — ver abaixo |
payment_method.body_instructions | string[] | não | Até 5 linhas de instrução impressas no boleto — ainda não aplicado (aceito e ignorado; ver aviso abaixo) |
payment_method.billing_instructions | objeto | não | Multa, juros e descontos — ainda não aplicado (aceito e ignorado; ver aviso abaixo) |
payment_method.buyer (obrigatório)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
first_name | string | sim | Nome do pagador |
last_name | string | sim | Sobrenome do pagador |
email | string | sim | E-mail do pagador |
phone_number | string | sim | Telefone com DDD, apenas dígitos |
taxpayer_id | string | sim | CPF ou CNPJ, apenas dígitos |
birthdate | string (YYYY-MM-DD) | não | Data de nascimento |
address.line1 | string | sim | Logradouro |
address.line2 | string | sim | Número |
address.line3 | string | não | Complemento |
address.neighborhood | string | sim | Bairro |
address.city | string | sim | Cidade |
address.state | string | sim | UF (2 letras) |
address.postal_code | string | sim | CEP, apenas dígitos |
payment_method.billing_instructions (opcional): late_fee e interest aceitam { mode, amount, percentage, start_date }; discount é uma lista de { mode: "FIXED" | "PERCENTAGE", amount, limit_date }.
billing_instructions (multa, juros e desconto), body_instructions, payment_limit_date e logo são aceitos sem erro, mas não são repassados ao banco: o boleto é emitido sem multa, sem juros, sem desconto e sem instruções personalizadas. Não dependa desses campos até esta nota ser removida.Resposta — 201 Created
Mesmo formato da criação de Pix (seção 4), com payment_type: "boleto". payment_method.barcode traz a linha digitável do boleto. payment_method.pix.qr_code.emv pode vir preenchido — veja o aviso abaixo antes de exibi-lo ao pagador.
"payment_method": {
"id": "<nosso número>",
"zoop_boleto_id": "<nosso número>",
"resource": "boleto",
"document_number": "<nosso número>",
"barcode": "<linha digitável>",
"status": "not_paid",
"url": "https://api.letztech.com.br/v1/pay/<id>/pdf",
"expiration_date": "2026-10-20",
"pix": { "qr_code": { "emv": "<pix copia-e-cola, se o BolePix foi emitido>" } }
}payment_method.pix são duas cobranças independentes no banco: um pagamento feito por esse Pix não confirma a transação (ela continua pending e nenhum webhook é enviado), e o pagador pode acabar pagando os dois. A página pública e o PDF da Letztech já não mostram esse Pix. Oriente o pagamento pela linha digitável. Esta nota será atualizada quando o boleto híbrido (boleto e Pix como o mesmo título) estiver ativo.payment_method.url é o link absoluto e público do PDF do boleto, e payment_method.expiration_date o vencimento (AAAA-MM-DD; null se desconhecido). Os dois vêm também na consulta e no replay. Os endpoints públicos por trás:
| Endpoint | Descrição |
|---|---|
GET/v1/pay/:id | Página pública (JSON) com os dados mínimos da cobrança, linha digitável e vencimento, sem autenticação |
GET/v1/pay/:id/pdf | PDF do boleto, também público |
Erros
Mesma tabela da seção 4.
Consultar o boleto
Devolve o objeto boleto no formato da Zoop. :id aceita o id do boleto (payment_method.id) ou o id da transação. Mesmo escopo da consulta de transação (seção 6): outro seller, outra revenda ou uma transação que não é boleto (Pix, por exemplo) retornam 404.
{
"id": "<nosso número>",
"zoop_boleto_id": "<nosso número>",
"resource": "boleto",
"status": "not_paid",
"reference_number": "<reference_id>",
"document_number": "<nosso número>",
"expiration_date": "2026-10-20",
"bank_code": "748",
"url": "https://api.letztech.com.br/v1/pay/<id da transação>/pdf",
"barcode": "<linha digitável>",
"paid_at": null,
"transaction_id": "<id da transação>",
"on_behalf_of": "<seller>",
"uri": "/v1/marketplaces/<marketplaceId>/boletos/<nosso número>",
"metadata": {},
"created_at": "2026-10-17T12:00:00.000",
"updated_at": "2026-10-17T12:00:00.000"
}status é paid ou not_paid. recipient, sequence e fingerprint vêm null; accepted, printed e downloaded vêm false (não são rastreados).
5.1 Cartão de crédito (por token)
O cartão é processado na Zoop: a Letztech recebe só o token do cartão e repassa a venda. Número, CVV e validade nunca passam pela Letztech: um corpo com esses campos é recusado (400).
Authorization: Basic base64("<API_KEY>:")
Content-Type: application/json{
"payment_type": "credit",
"on_behalf_of": "<id do seller>",
"amount": 9990,
"currency": "BRL",
"capture": true,
"reference_id": "pedido-5678",
"token": "<token do cartão gerado na Zoop>",
"installment_plan": { "mode": "interest_free", "number_installments": 3 },
"statement_descriptor": "LOJA X",
"postback_url": "https://parceiro.com.br/webhooks/letztech"
}amountem centavos;currencysóBRL. Parcelas: até 24.- O token vai em
tokenou emsource.card.id, nunca nos dois. capture: falsesó pré-autoriza; capture depois.reference_idé obrigatório e único por seller: repetir devolve a mesma venda, sem cobrar de novo.
Respostas
201: venda criada. O corpo é o objeto de transação da Zoop (idé o da Zoop), com o seureference_ide owebhook_secretse houverpostback_url.202: a Zoop não respondeu a tempo. A venda ficapendinge é conferida na Zoop em alguns minutos; consulte (seção 6) ou aguarde o webhook. Não reenvie com outroreference_id: repita com o mesmo, que nunca cobra duas vezes.- 4xx da Zoop (ex.: cartão negado): repassado com o mesmo status e corpo.
Capturar uma pré-autorização
{ "amount": 9990 }Sem amount, captura o total autorizado.
Cancelar ou estornar
X-Idempotency-Key: <obrigatório>{ "amount": 3000 }Total ou parcial (sem amount, o restante). :id aceita o id da Zoop ou o nosso. Uma operação por venda por vez: outra captura ou cancelamento em andamento dá 409. 202 significa que a Zoop não respondeu: a operação é conferida depois e nunca repetida sozinha.
6. Consultar status de uma transação
Authorization: Basic base64("<API_KEY>:")Retorna a transação no mesmo formato da criação (Pix ou Boleto), com o status atualizado. :id é o id retornado na resposta de criação. A chave de seller vê todas as transações dele, inclusive as criadas pela chave da revenda; a chave de revenda vê as dos sellers dela feitas com o preço da revenda. Qualquer outra retorna 404.
Use este endpoint para reconciliar o estado real de uma transação, principalmente como fallback caso uma entrega de webhook não chegue.
| Status | Situação |
|---|---|
| 200 | Transação encontrada |
| 401 | API key ausente/inválida |
| 403 | marketplaceId não corresponde à API key |
| 404 | Transação não encontrada, ou fora do que a chave enxerga |
Valores possíveis de status
| Valor | Significado |
|---|---|
pending | Aguardando pagamento |
succeeded | Pago e confirmado |
failed | Falhou |
refunded | Devolvido (total ou parcial) |
7. Devolver transação (Pix)
Authorization: Basic base64("<API_KEY>:")
Content-Type: application/json
X-Idempotency-Key: <opcional, recomendado>succeeded). Cartão se cancela ou estorna pelo /void (seção 5.1). Boleto não tem devolução via API.Corpo da requisição
{
"amount": 150.00,
"reason": "Produto não entregue"
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
amount | number (2 casas) | não | Valor a devolver em reais (ex: 150.00). Se omitido, devolve o valor restante integral |
reason | string | não | Motivo da devolução (máx 500 caracteres) |
amount é em centavos, ex: 15000), no refund o amount vai em reais com 2 casas decimais (ex: 150.00).Autenticada apenas pela API key (server-to-server), sem MFA — a própria API key é o fator de segurança. Aceita X-Idempotency-Key: retentativas com a mesma chave retornam a resposta original em vez de devolver duas vezes.
Com a chave de revenda, a devolução sai do saldo do seller, como pela chave do seller, e o dono do seller recebe um e-mail avisando. Vale também com a revenda suspensa.
A devolução pode ser confirmada na hora ou ficar em processamento até o Sicredi confirmar (alguns minutos). Nos dois casos a resposta é 200 e o valor já fica reservado. O webhook transaction.refunded só é enviado quando o Sicredi confirma. Se o Sicredi não realizar a devolução depois, o valor volta a ficar disponível para devolver e nenhum webhook transaction.refunded é enviado.
| Status | Situação |
|---|---|
| 200 | Devolução aceita (confirmada ou em processamento) |
| 400 | Transação não é Pix, não está liquidada, valor inválido ou saldo insuficiente do seller (ou de um destinatário do split, seção 7.1) |
| 401 | API key ausente/inválida |
| 403 | marketplaceId não corresponde à API key |
| 404 | Transação não encontrada, ou fora do que a chave enxerga (seção 6) |
| 422 | O Sicredi recusou a devolução |
7.1 Split entre sellers (split_rules)
Uma cobrança Pix ou boleto pode ser dividida entre o seller da transação (on_behalf_of, o principal) e até 10 outros sellers da mesma revenda, com o campo split_rules no corpo da criação (seções 4 e 5). As regras valem só na criação: não há como incluir ou mudar split depois.
"split_rules": [
{ "recipient": "<seller_id>", "amount": 3000, "charge_processing_fee": true },
{ "recipient": "<outro_seller_id>", "percentage": 10, "liable": false }
]| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
recipient | string | sim | Id do seller que recebe a parte (ou o id dele na Zoop). Precisa estar ativo, ser da mesma revenda e ser diferente do principal |
amount | integer | um dos dois | Valor fixo do destinatário, em centavos (como o amount da transação) |
percentage | number (2 casas) | um dos dois | Percentual do valor da transação (2.5 = 2,5%), arredondado para baixo em centavos |
liable | boolean | não (padrão true) | Se o destinatário responde pela devolução na proporção do que recebeu. Com false, a parte dele sai do principal |
charge_processing_fee | boolean | não (padrão false) | Se o destinatário paga parte da taxa, proporcional ao valor dele. Com false, recebe o valor inteiro e o principal paga a taxa toda |
A taxa é uma só, calculada pelo plano do seller principal. O principal fica com o valor da venda menos as partes dos destinatários e menos a taxa que eles não pagam. A resposta da criação e da consulta devolve split_rules com o amount de cada destinatário em centavos (já calculado, quando a regra foi enviada em percentual). Sem split, o campo não aparece.
Na devolução, cada destinatário com liable: true devolve a fração dele do valor devolvido, e o principal o resto. Se o principal ou algum destinatário não tiver saldo para a parte dele, a devolução inteira é recusada com 400, sem chamar o banco.
Diferenças em relação à Zoop
Recusadas com 400, com a mensagem dizendo o campo: mais de 10 destinatários, charge_recipient_processing_fee, is_gross_amount e POST /v1/marketplaces/:marketplaceId/transactions/:id/split_rules (split depois da criação). Nenhum campo de split é aceito e ignorado.
| Status | Situação |
|---|---|
| 400 | Destinatário inválido (inativo, de outra revenda, repetido ou o próprio principal), regra com amount e percentage juntos, soma acima do valor, principal sem valor para pagar a taxa, ou campo não suportado |
8. Notificações (webhooks)
Se postback_url foi informado na criação da transação (Pix, Boleto ou cartão), a Letztech faz POST nessa URL a cada mudança de status relevante: transaction.completed, transaction.failed, transaction.expired (Pix não pago dentro da validade, ou boleto não pago até 1 dia após o vencimento; se for pago depois, ainda é liquidado e gera transaction.completed), transaction.refunded (no cartão, a cada cancelamento ou estorno) e, só no cartão, transaction.charged_back (chargeback ou disputa aberta na venda).
X-Letztech-Signature (HMAC-SHA256 do corpo bruto, assinado com o webhook_secret retornado na criação/consulta) antes de confiar no conteúdo — qualquer um pode chamar sua postback_url sem essa verificação.O segredo é da chave que criou a transação: uma transação criada pela chave da revenda é assinada com o segredo da revenda, e uma criada pela chave do seller, com o do seller. Por isso o webhook_secret só vem na resposta para quem criou a transação — numa consulta pela outra chave, o campo não vem.
O webhook não é fonte de verdade: trate-o sempre como notificação de conveniência e reconcilie via consulta (seção 6) quando em dúvida.
9. Formato padrão de erro
{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed",
"timestamp": "2026-09-22T12:00:00.000Z",
"path": "/marketplaces/abc/transactions",
"requestId": "3f1c9e2a-..."
}message pode ser string ou array de strings (múltiplos erros de validação). requestId identifica a requisição nos logs internos da Letztech — inclua-o ao reportar um erro pra suporte. Também vem no header X-Request-Id em toda chamada (não só erros); se você mesmo enviar um X-Request-Id/X-Correlation-Id na requisição, a Letztech reaproveita esse valor em vez de gerar um novo.
