Pular para o conteúdo

Contatos, segmentos e verificação de endereços

Um contato é uma pessoa para quem você pode enviar e-mails, guardada uma vez por endereço. Comece adicionando um. Isso pede uma chave com operate.

import { Colding } from "colding";

const colding = new Colding("cd_your_key");
const contact = await colding.contacts.create({
  email: "[email protected]",
});
console.log(contact.id, contact.email);

Troque a chave e o endereço pelos seus. O endereço aqui é só um exemplo.

Exemplo de saída: o ID do novo contato e o endereço dele
6f1d2c3a-5b4e-4f70-9a8b-1c2d3e4f5a6b [email protected]

Se o endereço já existe, com maiúsculas ou minúsculas, a chamada responde 409 e indica o contato em details.contactId. Para adicionar ou completar sem se preocupar com isso, use o upsert.

Adicione ou atualize vários pelo endereço

Adicione ou atualize vários pelo endereço

contacts.upsert recebe até 500 contatos numa chamada. Um endereço novo é criado. Um que já existe é completado, ou fica como está se você passar mode: "skip".

const result = await colding.contacts.upsert({
  contacts: [
    { email: "[email protected]", firstName: "Ana", company: "Example Ltd" },
    { email: "[email protected]", firstName: "Ben" },
  ],
});
console.log(result.created, result.updated, result.failed);

created, updated e failed contam as linhas. results tem uma entrada por linha, na ordem em que você enviou.

  • Um valor em branco nunca apaga o que já está salvo. Para limpar um campo, use contacts.update, em que o valor em branco substitui o que havia.
  • subscribed: false registra que a pessoa pediu para não receber e-mails. Nada aqui desfaz isso, nem subscribed: true. Só tirar o endereço da lista de supressão desfaz.
  • Um endereço inválido faz falhar só a linha dele.
  • Se os contatos novos passarem do limite de contatos do seu plano, nada é salvo e a chamada responde 402.

Leia contatos e o histórico deles

contacts.get lê um contato: os campos, as etiquetas e o último resultado da verificação do endereço. Um contato de outro workspace responde 404. contacts.count aceita os mesmos filtros de contacts.list (q, labelId, subscribed, check e segmentId) e responde quantos contatos combinam, então use antes de uma ação em massa. As duas pedem read.

const subscribed = await colding.contacts.count({ subscribed: true });
console.log(subscribed.count);

const contactId = "66666666-6666-4666-8666-666666666666";
const contact = await colding.contacts.get({ contactId });
console.log(contact.email, contact.verification?.status);

const history = await colding.contacts.timeline({ contactId, limit: 20 });
for (const event of history.items) console.log(event.at, event.type);

const fields = await colding.contacts.listCustomFields();
console.log(fields.keys, fields.more);

contacts.timeline responde o que aconteceu com um contato, do mais novo para o mais antigo, uma página por vez. Passe o nextCursor como cursor para ir à página seguinte. Todo evento tem id, at e type, e os outros campos dependem do tipo. Um e-mail agendado aparece na hora em que vai sair, que pode estar no futuro.

Os tipos de evento

added
O contato foi criado. source diz como, e import nomeia a importação enquanto ainda a guardamos.
campaign_joined
O contato virou lead de uma campanha.
check
Uma das nossas verificações do endereço terminou. Vem com o status, o reason e o address que foi verificado.
email
Um e-mail de campanha ou direto para o contato: enviado, esperando ou barrado. Um que uma pessoa cancelou antes de sair não aparece.
reply
E-mail que o contato mandou para uma das suas caixas de e-mail. E-mails de warmup, relatórios de bounce e relatórios de spam não são respostas.
bounce
Um e-mail para o contato deu bounce permanente.
complaint
O contato marcou um e-mail como spam.
suppressed
O endereço, ou o domínio inteiro, entrou na lista de supressão.
unsuppressed
O endereço, ou o domínio, saiu da lista de supressão.
campaign_left
Uma pessoa ou uma chave tirou o lead da campanha. by diz quem.
opened
O contato abriu um e-mail pela primeira vez. Um evento por e-mail, e opens conta as aberturas.
clicked
O contato clicou num link de um e-mail. Um evento por link, no primeiro clique. url é a página para onde o link leva.
outside_email
E-mail que alguém da sua equipe escreveu ao contato no próprio app de e-mail. Vem com o assunto, a caixa de e-mail e a conversa, nunca o corpo.
import_updated
Uma importação alterou um contato que já estava aqui. import nomeia a importação enquanto ainda a guardamos. Um contato que a importação manteve como estava não tem evento.

Um e-mail que enviamos aparece uma vez, como email. Abertura ou clique de um scanner de e-mail ou de um proxy de e-mail nunca entra no histórico, e e-mail de warmup também não.

contacts.listCustomFields responde keys, os nomes dos campos personalizados que os seus contatos têm, de A a Z sem diferenciar maiúsculas, até 200. more é true quando há outros. O nome é o que um campo dinâmico lê num template. Os campos padrão (first_name, last_name, company, phone e email) não entram na lista.

Organize os contatos com etiquetas

Uma etiqueta é um nome sob o qual você guarda contatos e conversas da caixa de entrada. Os nomes são únicos, sem diferenciar maiúsculas. contacts.label adiciona e remove etiquetas de uma seleção numa só chamada.

const label = await colding.labels.create({ name: "Met at the fair" });
const touched = await colding.contacts.label({
  selection: {
    ids: ["77777777-7777-4777-8777-777777777777", "88888888-8888-4888-8888-888888888888"],
  },
  add: [label.id],
});
console.log(touched.count);

A chamada responde quantos contatos ela alterou.

Uma seleção é uma lista de até 1.000 IDs de contatos, ou todos os contatos que combinam com um filtro. No segundo caso, passe all: true, os filters e o expectedCount, o número que você conferiu. Se agora combinar uma quantidade diferente de contatos, nada muda e a chamada responde 409.

Os filtros são q (palavras no endereço, no nome ou na empresa), labelId, subscribed, check e segmentId. Excluir, exportar e conferir endereços usam a mesma seleção.

labels.update troca o nome de uma etiqueta e labels.remove exclui uma. As duas pedem operate. Os contatos e as conversas continuam com a etiqueta renomeada. Excluir uma etiqueta tira só a etiqueta: os contatos e as conversas que a tinham ficam.

const renamed = await colding.labels.update({
  labelId: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  name: "Met at the fair 2030",
});
console.log(renamed.name);

await colding.labels.remove({ labelId: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" });

Exclua contatos

contacts.remove exclui um contato. contacts.removeMany exclui uma seleção. As duas pedem operate e não têm volta.

await colding.contacts.remove({ contactId: "66666666-6666-4666-8666-666666666666" });

const filters = { check: "invalid" } as const;
const matching = await colding.contacts.count(filters);
const removed = await colding.contacts.removeMany({
  selection: { all: true, filters, expectedCount: matching.count },
});
console.log(removed.count);

A seleção funciona como nas etiquetas: até 1.000 IDs, ou all: true com filters, exclude (IDs para deixar de fora) e expectedCount. Rode contacts.count antes e passe o número dele, menos os IDs de exclude, em expectedCount. Se agora combinar uma quantidade diferente, nada é excluído e a chamada responde 409. Uma seleção com mais de 50.000 contatos é recusada. A chamada responde quantos contatos excluiu.

Um contato excluído sai das campanhas, e as verificações do endereço dele vão junto. A lista de supressão não é tocada: um endereço que pediu para não receber e-mails continua nela. As etiquetas dele continuam valendo para os outros contatos. Um lote de verificações sem mais nada a fazer termina.

Importe um arquivo CSV ou do Excel

No console, use Importar na página Contatos quando uma pessoa tem o arquivo. São quatro etapas: Arquivo, Colunas, Revisão e Concluído. Use a API quando um programa tem os dados. Para até 500 contatos por vez, contacts.upsert é mais simples.

Para enviar um arquivo inteiro, inicie uma importação e confirme. createImport ainda não muda nada. Ele responde as colunas que encontrou, os ajustes que sugere e o preview, um teste do que a confirmação faria. Mude um ajuste com contacts.updateImport, que aumenta a revision. commitImport informa a revisão que você leu.

const started = await colding.contacts.createImport({
  fileName: "leads.csv",
  csv: "email,first name\[email protected],Ana\[email protected],Ben\n",
});
console.log(started.preview?.blockers);

const done = await colding.contacts.commitImport({
  importId: started.id,
  revision: started.revision,
});
console.log(done.result);

Uma confirmação grava todas as linhas ou nenhuma. Repetir a mesma revisão devolve o mesmo resultado, então tentar de novo é seguro. Uma célula que marca alguém como Não enviar e-mails coloca o endereço na lista de supressão. Uma coluna de etiquetas dá a cada contato as etiquetas da célula dele, uma por linha, como a exportação escreve. Se você já tem uma etiqueta com esse nome, sem contar maiúsculas, o contato fica com ela; se não, ela é criada na confirmação. preview.newLabels mostra quais seriam criadas.

Um arquivo pode ter até 50.000 linhas e 10 MB. As células de um CSV são separadas por vírgula, ponto e vírgula ou tabulação, e você o envia como texto em csv ou como bytes em base64 em content. Um arquivo do Excel (.xlsx, .xlsm ou .xls) vai só em content. Lemos a primeira aba visível, como o texto que as células mostram: uma fórmula dá o último resultado salvo, e uma data no formato curto do Excel chega como ano-mês-dia. Um arquivo protegido por senha, com mais de 10 MB de texto nas células ou em outro formato (Numbers, OpenDocument ou Excel Binary) responde 400 e diz o que fazer: salve a lista como .xlsx ou CSV. Uma importação que você não confirma é excluída, com o arquivo, depois de 24 horas. O resumo de uma importação confirmada fica 7 dias. Os contatos ficam para sempre.

As linhas que falham aparecem em preview, as primeiras 1.000. Para ter todas, contacts.exportImportFailures({ importId, revision }) responde um link para um CSV com as linhas que falhariam naquela revisão: a linha de cabeçalho do próprio arquivo, cada célula como foi escrita e, na última coluna, o motivo. Corrija as linhas e importe esse arquivo. O link vale por alguns minutos e o arquivo é escrito quando você o abre. Depois que a importação é concluída, as células somem, então as linhas que falharam não voltam: envie o arquivo de novo.

Para achar uma importação de novo, contacts.listImports lista as importações do workspace, da mais nova para a mais antiga, uma página por vez. Cada uma vem com o estado e as contagens, nunca com as células do arquivo. Abra uma com contacts.getImport.

Salve um filtro como segmento

Um segmento é um filtro salvo com um nome. Ele não guarda uma lista de pessoas: quem combina com as condições na hora em que você o usa está nele. Um contato que você adicionar amanhã pode entrar sem você fazer nada.

const tried = await colding.segments.count({
  conditions: [{ field: "domain", op: "equals", value: "example.com" }],
});
console.log(tried.count);

const segment = await colding.segments.create({
  name: "People at example.com",
  conditions: [{ field: "domain", op: "equals", value: "example.com" }],
});
const page = await colding.contacts.list({ segmentId: segment.id });
console.log(page.items.map((contact) => contact.email));

segments.count testa condições sem salvar. segments.create salva. Um workspace guarda até 100 segmentos.

Passe o ID do segmento em segmentId para listar, contar, etiquetar, excluir, exportar ou conferir os contatos que combinam com ele. Se o segmento foi excluído depois, a chamada responde 404.

Uma condição tem um field, um op (o teste) e, nos testes que comparam, um value ou values. Os campos incluem search, email, domain, first_name, last_name, company, subscribed, suppressed, check, label, created_at e updated_at. Um campo custom é indicado por key. match é all (todas as condições) ou any (basta uma). Um grupo, de um nível só, tem condições próprias, e cada uma conta no limite. Um segmento tem até 20 condições. campaign_sent, campaign_replied, campaign_opened e campaign_clicked testam o que aconteceu com um e-mail de campanha. campaign_opened e campaign_clicked contam a abertura ou o clique de uma pessoa, nunca o de um scanner ou de um proxy de e-mail. Elas precisam do rastreamento de aberturas ou de cliques em um domínio de rastreamento verificado. Sem nenhum em uso, in e any não são cumpridos por ninguém, e not_in e none são cumpridos por todo mundo, inclusive quem a campanha nunca contatou. Para achar quem recebeu um e-mail e não fez nada com ele, adicione também campaign_sent.

Os testes que cada campo aceita estão no arquivo OpenAPI. Uma condição que não combina com o campo é recusada com 400 e o caminho dessa condição. Abrir o JSON da OpenAPI

segments.update recebe a revision que você leu. Se alguém mudou o segmento depois, nada é gravado e a chamada responde 409 com a revisão atual.

segments.list responde todos os segmentos, de A a Z, cada um com as suas condições. É uma página só, porque um workspace guarda até 100. segments.remove exclui um e pede operate. Os contatos dele ficam como estão, e uma chamada que ainda cita o segmento responde 404. Um segmento de onde uma campanha puxa leads responde 409 segmentLinked, com as campanhas em details: tire o vínculo de cada uma antes, com campaigns.unlinkSegment.

const segments = await colding.segments.list();
const old = segments.find((segment) => segment.name === "People at example.com");
if (old) await colding.segments.remove({ segmentId: old.id });

Confira os endereços antes de enviar

Uma verificação do endereço diz se ele recebe e-mails. Conferimos em etapas. Primeiro, a nossa análise do endereço e dos servidores de e-mail do domínio. Depois, nossa verificação pergunta ao servidor de e-mail do endereço se a caixa existe. Ela não consulta Microsoft 365, Outlook.com, Yahoo, AOL ou iCloud, porque esses servidores não dão essa resposta. Esses endereços, e os que não conseguimos conferir por conta própria, vão para um serviço de verificação pago. Iniciar uma verificação pede a permissão validate, porque o serviço cobra por cada endereço que responde.

const check = await colding.emailChecks.check({
  address: "[email protected]",
});
console.log(check.state, check.status, check.reason);

check espera um pouco pela resposta. Se o state ainda for queued, running ou dispatched, leia de novo com emailChecks.get. Quando o state é done, o status é o resultado e o reason diz o motivo.

O que cada resultado significa

valid
O endereço recebe e-mails.
risky
Não conseguimos confirmar, mas ele pode funcionar.
invalid
Ele não recebe e-mails.
unknown
Não conseguimos concluir. Isso não é inválido.

Uma verificação que não consegue terminar espera, em vez de adivinhar. Se o serviço de verificação estiver fora do ar ou o mês tiver acabado, o state é on_hold e o holdReason diz o motivo. Uma queda nunca vira resultado.

O que uma verificação consome

Cada resposta conta uma vez nas verificações do mês do seu plano, seja da nossa verificação ou do serviço pago. Um plano sem limite mensal nunca acaba. Quando não sobra nenhuma, a chamada responde 429 com details.resetAt e details.upgradeTo. Se o último pagamento falhou, ela responde 402 e não enfileira nada.

Um resultado vale por 30 dias, e um endereço com resultado válido não é conferido de novo. recheck: true pede uma nova conferência mesmo assim, mas não nas 24 horas seguintes à última resposta do serviço de verificação: essa resposta volta como está, sem nova conferência.

Leia a configuração antes de um lote grande. O month traz o que o workspace usou e o limite, que é null quando o plano não tem limite.

const setup = await colding.emailChecks.setup();
console.log(setup.state, setup.month);

Quando a sua chave tem validate e as verificações estão ligadas, um contato que uma gravação cria também é conferido: contacts.create, um endereço novo em contacts.upsert ou uma linha nova numa importação. Um contato que já existe mantém o resultado dele e não ganha uma nova conferência. Passe checkAddresses: false para pular. Um mês esgotado nunca recusa a gravação: o contato é salvo e a verificação dele fica em espera.

As verificações pedem que o dono aceite antes a política de uso aceitável. Até lá, emailChecks.check e emailChecks.start respondem 403 AUP_REQUIRED, e uma gravação salva o contato mas não inicia verificação (notStartedBecause: aup_required). Um agente pede ao dono com um handoff accept_aup.

Para conferir muitos endereços, inicie um lote com uma seleção, de até 50.000 contatos por lote. Passe um intentKey para que uma nova tentativa devolva o mesmo lote. Acompanhe com emailChecks.getRun, ou espere o evento email_check.run_completed. O evento email_check.run_held avisa quando o lote fica em espera.

const run = await colding.emailChecks.start({
  selection: {
    all: true,
    filters: { segmentId: "99999999-9999-4999-8999-999999999999" },
  },
  intentKey: "check-example-com-2030-01-15",
});
const now = await colding.emailChecks.getRun({ runId: run.id });
console.log(now.state, now.counts);

Encontre, pare e refaça lotes

emailChecks.list lista as verificações, da mais nova para a mais antiga, uma página por vez. Filtre por address (maiúsculas ignoradas), contactId, runId (um lote), state ou status, o resultado. emailChecks.listRuns lista os lotes do mesmo jeito, e cada um traz counts: quantas das suas verificações estão em cada ponto. Guardamos uma verificação por 30 dias depois que ela termina, e um lote por 30 dias depois que ele termina. O contato continua com o último resultado. As duas pedem read.

const checks = await colding.emailChecks.list({ address: "[email protected]" });
for (const check of checks.items) console.log(check.state, check.status);

const runs = await colding.emailChecks.listRuns({ limit: 10 });
for (const run of runs.items) console.log(run.id, run.state, run.counts);

emailChecks.cancelRun para um lote. As verificações dele que ainda não foram ao serviço de verificação param e não custam nada, a menos que outro lote também as queira ou que emailChecks.check as tenha pedido. Uma verificação que já foi ao serviço termina, porque pode já ter contado. Um lote que já foi cancelado ou terminou volta como está.

emailChecks.retryRun manda de novo ao serviço as verificações de um lote que ficaram sem resposta: as uncertain e as que ficaram em espera depois de esgotar as tentativas (noAnswer em counts). Cada uma pode contar de novo no mês, então valem as regras de uma verificação nova: 429 quando o mês acabou, 402 depois de um pagamento que falhou e 403 AUP_REQUIRED até o dono aceitar a política de uso aceitável. Um lote cancelado responde 409: inicie um lote novo. As duas chamadas pedem validate.

const stopped = await colding.emailChecks.cancelRun({
  runId: "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
});
console.log(stopped.state, stopped.counts.cancelled);

const again = await colding.emailChecks.retryRun({
  runId: "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
});
console.log(again.state, again.counts.uncertain, again.counts.noAnswer);

Um resultado nunca tira uma supressão

Uma verificação é indício, não permissão. Um resultado válido não devolve um endereço à lista de quem você pode contatar. A lista de supressão é lida primeiro, e só tirar o endereço dessa lista muda isso. Um endereço considerado inválido nunca recebe envio. Uma mensagem nova para um endereço arriscado ou sem conclusão pede recipientReviewed: true. Cada linha vale para um stream: cold (campanhas e mensagens) ou transactional (os e-mails do seu app). suppressions.list e suppressions.add aceitam stream. Um bounce impede o envio nos dois. No console, você pode tirar as duas linhas de uma vez. Se uma das linhas que você vai remover registra um pedido da pessoa para não receber e-mails, passe recipientAsked: true só se ela pediu para voltar a receber. Sem essa confirmação, nada é removido.

Uma mensagem nova precisa de um resultado recente antes de sair. O guia de envio mostra onde isso entra. Envie um e-mail e acompanhe

Exporte contatos para um arquivo

contacts.export recebe uma seleção e responde um link, não o arquivo. Baixe o arquivo com um GET simples, sem chave. O link é a permissão, então compartilhe só com quem deve ver esses contatos.

const file = await colding.contacts.export({
  selection: {
    ids: ["77777777-7777-4777-8777-777777777777", "88888888-8888-4888-8888-888888888888"],
  },
});
console.log(file.url, file.expiresAt);

O link vale por 10 minutos e abre no máximo 3 vezes. Cada chamada faz um link novo, então chame de novo depois do expiresAt. Um arquivo tem até 100.000 contatos.

O arquivo tem os campos de cada contato, os campos personalizados, as etiquetas (uma por linha na célula), o Não enviar e-mails e a última verificação do endereço. Os nomes das colunas vêm no idioma que você passa em locale, o do workspace por padrão, com vírgula entre as células em inglês e ponto e vírgula em português e espanhol, como o Excel espera. Ele traz os contatos que combinam na hora do download, então count é o número do momento em que você fez o link.

Uma célula que uma planilha poderia executar como fórmula começa com '.