Pular para o conteúdo principal
Todo endpoint exige o cabeçalho Authorization: Bearer cliqo_… — veja Visão geral e autenticação.

GET /v1/me

Devolve a identidade e as capacidades da chave que chamou — e o relógio do servidor. Use como teste de conectividade. Funciona com qualquer escopo, inclusive send_only, que não pode ler mais nada. É de propósito: uma chave precisa conseguir se apresentar mesmo sem permissão de leitura. server_time é ISO-8601 em UTC. Ele existe para você notar uma diferença de relógio ou de fuso antes de concluir que um relatório está errado — o GET /v1/reports/campaigns recebe datas, e um dia de diferença é muito mais fácil de explicar quando dá para comparar os relógios.

POST /v1/contacts

Cria ou atualiza um contato (upsert por e-mail, dentro da marca da chave). Exige escopo que escreve contatos (full ou send_only).
Sobre os campos:
  • email — obrigatório; normalizado (espaços removidos, minúsculas).
  • opt_in / whatsapp_opt_in — booleanos que registram consentimento com origem e data. Só mande true quando o seu sistema de fato coletou aquele consentimento; a chamada de API vira o registro de proveniência dele.
  • whatsapp_opt_in: true sem telefone → 400 WHATSAPP_OPTIN_WITHOUT_PHONE.
  • source — texto livre de origem (até 50 caracteres), exibido no perfil do contato.
  • mode — o que fazer quando o contato já existe; padrão update. Veja a tabela abaixo.
  • custom_fields — gravado exatamente como veio (sem normalização neste caminho). Mande as chaves já em snake_case minúsculo e datas como AAAA-MM-DD, iguais às que o import e os formulários criam — senão você cria um campo paralelo. Exceção: chave que é tag do sistema (nome, primeiro_nome, email, link_descadastro, link_ver_online, company_name, endereco, data_do_envio, cupom_codigo, cupom_validade) grava em <chave>_1 (endereco → endereco_1), porque {{endereco}} no e-mail é o endereço da loja, e um valor em endereco nunca apareceria. Se você mandar as duas, a <chave>_1 explícita vence.
Erro que vale tratar explicitamente: 409 CONTACT_SUPPRESSED — o endereço descadastrou ou foi suprimido. Não contorne mudando o e-mail: a supressão protege o destinatário e a sua reputação de remetente, tenha vindo de um descadastro, de uma reclamação de spam, de um bounce forte ou da blocklist.

mode — as mesmas regras do import de CSV

São os quatro modos que a tela de importação já oferece, agora também aqui. Todos decidem uma coisa só: o que fazer quando o e-mail já existe naquela marca. Dois detalhes do replace, para não surpreender: tags só é gravado quando você o envia (nunca é apagado por omissão), e o source de um contato existente nunca é reescrito — ele registra por onde o contato entrou, não a última chamada. ⚠️ mode desconhecido é recusado com 400 MODE_INVALID, nunca aceito como update. Um mode: "only_new" virando silenciosamente “atualizar tudo” faria a plataforma executar o oposto do pedido, na base inteira. A resposta diz o que de fato aconteceu: created, updated e o mode aplicado. ① Suprimido continua suprimido. Um contato descadastrado existe. Se ignore (“só adicionar novos”) o tratasse como inexistente, re-inscreveríamos quem pediu para sair. A guarda de supressão roda antes do modo, então os quatro devolvem o mesmo 409. ② Contato existente nunca é re-inscrito. status e subscribed são definidos apenas quando o contato nasce. Em contato existente eles ficam intactos: um POST não faz voltar a receber e-mail quem tinha parado. ③ opt_in: true não ressuscita consentimento recusado. Se o contato já havia optado por não receber marketing, o consentimento não é regravado — e a resposta diz isso em email_consent: "skipped_previous_opt_out", para você não descobrir só na auditoria. Reativar é ato deliberado, feito no app por uma pessoa.

POST /v1/contacts/batch

A mesma escrita do POST /v1/contacts, para até 1.000 contatos numa chamada. Subir 5.000 contatos um a um eram 5.000 chamadas e, no limite de 600 por minuto da chave, no mínimo 8,5 minutos; aqui são 5 chamadas. ✅ Medido: 1.000 contatos em 4,6 s de ponta a ponta, contra um teto de duração de 30 s. A folga importa porque a checagem de domínio faz uma consulta de DNS por domínio distinto — uma base com muitos domínios diferentes custa mais que esta medição.
  • contacts — de 1 a 1.000 itens, cada um no formato do POST /v1/contacts (email, name, phone_whatsapp, tags, custom_fields, opt_in, whatsapp_opt_in). Mais de 1.000 → 413 BATCH_TOO_LARGE, com o teto em max_items.
  • mode — vale para o lote inteiro; são os mesmos quatro modos; um desconhecido é 400 MODE_INVALID, nunca silenciosamente update.
  • list — opcional; o número humanizado da lista ou o UUID dela, como no GET /v1/contacts. Todo mundo que entrou é vinculado a ela.
  • source — opcional, padrão api.
A resposta conta o que aconteceu: received, created, updated, unchanged, linked, email_consents_granted, whatsapp_consents_granted, invalid_phones, e uma lista skipped com index (a posição no seu array), email e reason — e-mail ausente ou inválido, e-mail repetido dentro do lote (vale a última ocorrência), domínio que não recebe e-mail, contato suprimido, whatsapp_opt_in sem telefone, ou “não existe” sob updateonly. created + updated + unchanged + skipped.length é igual a received, com uma exceção: um contato criado por outra chamada no mesmo instante não aparece em contador nenhum. unchanged só conta sob ignore (os outros modos sempre reescrevem um contato existente). Item pulado não derruba o lote — os outros entram. Onde ele difere do POST /v1/contacts, de propósito:
  • opt_in: false não faz nada, exatamente como no endpoint de um só. Ele não é uma recusa; recusa é o contato se descadastrar. Um sistema que manda false para todo mundo que nunca respondeu não pode descadastrar a sua base inteira por acidente.
  • Contato suprimido não devolve 409 — ele aparece em skipped com o motivo e o resto do lote segue. Mesma regra (a guarda de supressão roda primeiro), contabilidade diferente.
  • Os valores de custom_fields são gravados como texto e as chaves são normalizadas como no import de CSV (snake_case minúsculo; Data de Nascimento vira data_de_nascimento). Mande as chaves já em snake_case minúsculo, iguais às que você usa no POST /v1/contacts, ou você cria um campo paralelo. Chave que é tag do sistema grava em <chave>_1, como no POST /v1/contacts.
  • O consentimento é por item e nunca é inventado: sem opt_in: true no item, nenhum consentimento de e-mail é registrado. whatsapp_opt_in: true exige um phone_whatsapp válido em E.164; um número inválido conta em invalid_phones e o contato é gravado sem o canal.
  • Uma falha do nosso lado no meio do lote devolve 503 BATCH_WRITE_FAILED, e o que já foi gravado permanece gravado. Repetir a chamada inteira é seguro — a escrita é idempotente por e-mail.
Exige uma chave que escreve contatos (full ou send_only).

GET /v1/contacts

Lê os contatos de uma lista — é por aqui que você tira os seus dados:
  • list é obrigatório — o número humanizado da lista ou o UUID dela. Não existe “exportar tudo” por omissão: devolver a base inteira porque alguém esqueceu um parâmetro não é um padrão seguro.
  • limit — 1 a 1000; padrão 100.
  • offset — padrão 0.
Cada contato traz id, email, name, phone_whatsapp, status, subscribed, tags, custom_fields, source, created_at e updated_at. Exige chave que lê — escopo “Só ler” ou “Tudo”. Uma chave “Só enviar” recebe 403 API_KEY_SCOPE_INSUFICIENTE: ela escreve contatos e, de propósito, não os lê de volta. Um número que surpreende: meta.total aqui conta destinatários elegíveis, enquanto contact_count em /v1/lists conta vínculos. A mesma lista legitimamente mostra dois números diferentes — descadastrados e suprimidos continuam na lista, mas não receberiam a campanha. Nenhum está errado; eles respondem perguntas diferentes.

GET /v1/lists

Todas as listas da marca a que a chave pertence:
  • limit — 1 a 1000; padrão 100.
  • offset — padrão 0.
Cada item traz o number humanizado da lista (o mesmo que aparece no app), o id, name, description, contact_count, created_at e updated_at. O objeto meta sempre traz count, total, limit, offset e has_more. has_more vem sempre, inclusive quando é false — assim você nunca precisa distinguir “ausente” de “false”, que é como uma integração para calada na primeira página.

GET /v1/lists/

Uma lista, por qualquer um dos dois identificadores:
Duas coisas que vale saber:
  • O objeto vem cru, sem envelope data — igual ao GET /v1/me, o outro endpoint de recurso único, e a convenção que se espera de um retrieve.
  • Ele é idêntico, byte a byte, a um item do data[] do plural, porque os dois saem do mesmo serializador. Código escrito contra um funciona contra o outro.
Lista que não existe nesta marca devolve 404 com código LIST_NOT_FOUND — a mesma resposta quer o id seja desconhecido, quer pertença a outra marca, então o endpoint nunca confirma a existência do dado de terceiro.

GET /v1/reports/campaigns

Métricas por campanha, filtráveis por período e canal:
  • from / to — datas em ISO-8601. A janela tem teto de 366 dias.
  • channel — email ou whatsapp; omita para os dois.

A janela, e o que acontece quando você a omite

Janela mais larga custa mais para calcular, e o custo cresce com a idade da conta — por isso a faixa é limitada: 🔑 A resposta sempre declara a janela que usou, em meta.from e meta.to, com o teto em meta.max_window_days. Leia de lá em vez de supor: faixa omitida é respondida com um padrão, não com “tudo”, e sem esses campos você não teria como saber a diferença.

O formato da resposta

É JSON — este endpoint não devolve arquivo, e não há variante CSV nem XLSX. data é um array com uma entrada por campanha; meta descreve a consulta que a produziu.
As taxas já vêm em porcentagem arredondada a duas casas (31.43 é 31,43%), não em fração. meta.truncated diz se a fonte cortou a lista. Como ler os números, por desenho:
  • Métrica que não existe naquele canal vem null, nunca 0 — por exemplo opens, open_rate e amp_rate no WhatsApp.
  • Taxa sem denominador vem null (0/0 não é 0%).
  • Campanha cujo disparo falhou pode vir com sent_at: null e ainda assim aparecer — disparo que falhou dentro da sua janela é informação, não ruído.

Eventos personalizados (site/pixel)

Além da superfície v1, a sua loja pode enviar eventos personalizados (para a dimensão “evento personalizado” dos segmentos) pelo endpoint do pixel, com o token de pixel da sua conta — detalhes na [referência do pixel] (a caminho desta central de ajuda), ou fale com a gente em [email protected].