Primeiros passos

.md

Convenções e erros

As regras que valem para todas as rotas — leia uma vez e não precise reler em cada página.

URL base e formato

Toda rota mora sob https://linkzap.orion.moe/api/v1. Pedido e resposta são JSON em UTF-8; as rotas que recebem corpo esperam Content-Type: application/json.

O que volta fica sempre dentro de data. É uma casca aparentemente inútil, e ela existe para que acrescentar um campo ao lado (page, total, created) nunca quebre quem já lê o que está dentro.

Paginação

Três listas são paginadas — GET /contacts, GET /conversations e GET /messages. Elas aceitam page (a partir de 1) e limit (1 a 200, padrão 50), e devolvem total no corpo: o número de itens que casam com o filtro, não o da página.

Duas não são, porque não crescem: GET /channels e GET /flows devolvem tudo de uma vez, num { data: [...] } seco, sem page, limit nem total. Uma conta tem alguns números e algumas dezenas de fluxos — paginar ali seria cerimônia sem motivo.

Segunda página, de 20 em 20curl
curl "https://linkzap.orion.moe/api/v1/contacts?page=2&limit=20" \
  -H "Authorization: Bearer lz_SUA_CHAVE"

Datas e telefones

  • Datas saem em ISO-8601 com fuso UTC (2026-09-21T14:03:11.000Z). Converta para o fuso da sua operação na hora de mostrar — o painel faz isso com o fuso que está em Configurações → Geral.
  • Telefones entram como DDI + DDD + número, só dígitos: 5592999990000. Aceitamos com +, espaço, parêntese e traço e normalizamos; o que não vira um número brasileiro válido responde 422. Nas respostas o telefone volta sempre normalizado.
  • Identificadores são strings opacas. Não tente derivar nada delas nem presuma tamanho.

Erros

Todo erro é um JSON com error — uma frase em português, escrita para aparecer no seu log ou para a sua equipe ler. Quando o problema é de validação, vem junto issues, apontando campo por campo.

422 com o campo apontadoJSON
{
  "error": "Dados inválidos.",
  "issues": [
    { "path": "phone", "message": "String must contain at least 8 character(s)" }
  ]
}

Os status que você vai encontrar

  • 200Deu certo

    O corpo traz data, e nas listas também page, limit e total.

  • 201Criou

    Algo passou a existir: um contato novo, uma mensagem, um fluxo iniciado.

  • 400O corpo não é JSON

    Só em POST: o texto enviado não foi lido como JSON. Confira a vírgula sobrando e o Content-Type. Campo faltando ou fora do formato não é 400, é 422.

  • 401Chave ausente, inválida ou revogada

    Não tente de novo com a mesma chave — ela não vai passar a valer.

  • 403Fora do escopo

    Uma chave de canal pedindo outro canal. É 403 e não 404 de propósito: não dizemos se o outro canal existe.

  • 404Não existe

    Canal, conversa, contato, fluxo ou arquivo que não é desta organização.

  • 409Existe, mas não dá agora

    O caso comum é o número não estar pronto para enviar; o motivo vem em palavras.

  • 422Os dados não passam

    Telefone fora do formato, campo faltando, valor longo demais. Vem com issues[] apontando o campo.

  • 429Rápido demais

    Espere o que o Retry-After disser.

  • 500Foi culpa nossa

    Tente de novo com recuo. Se persistir, fale com o suporte com o horário da chamada.

Limite de chamadas

Cada chave pode fazer 120 chamadas por minuto, e a organização inteira, 600 somando todas as chaves. Passando disso, a resposta é 429 com Retry-After em segundos — espere o que ele disser em vez de tentar em laço.

Precisa de mais?

Os limites são generosos para integração de verdade e apertados para laço acidental. Se a sua operação passa disso de forma legítima, fale com o suporte antes de espalhar chaves para contornar — o teto da organização soma todas elas.

Repetir sem estragar

POST /contacts é idempotente pelo telefone: chamar duas vezes atualiza, não duplica (o created da resposta diz qual dos dois aconteceu). POST /messages não é — cada chamada manda uma mensagem. Se o seu código tem retentativa automática, guarde o messageId que voltou antes de tentar de novo.