Entrar Criar conta
Guias

Conexões de CRM

Mantenha os contatos da Postal e o seu CRM em sincronia, nos dois sentidos e sem loops: HubSpot, Pipedrive e RD Station CRM, com mapeamento de campos e etapas, registro e novas tentativas.

#Visão geral

Uma conexão de CRM mantém os contatos da Postal e as pessoas do seu CRM em sincronia, nos dois sentidos e sem loops. O que a pessoa faz no CRM (um contato novo, um negócio que muda de etapa) chega à Postal como contato, etiqueta, propriedade e evento, e pode iniciar uma automação. O que acontece na Postal (um contato criado ou alterado, um e-mail aberto, clicado, respondido ou devolvido) volta ao CRM como dados da pessoa e como nota na linha do tempo dela.

ConexãoCredenciaisDo CRM para a PostalDa Postal para o CRM
HubSpotToken de um aplicativo privado (e, para webhooks, o client secret)Webhooks de contatos e negócios, e a sincronização manualContatos criados ou atualizados; notas na linha do tempo
PipedriveToken da API e endereço da contaWebhooks de pessoas e negócios, e a sincronização manualPessoas criadas ou atualizadas; notas na pessoa
RD Station CRMToken da instânciaWebhooks de contatos e negociações, e a sincronização manualContatos criados ou atualizados; atividades na negociação ou nota no contato
Webhook (outro CRM)Nenhuma: um segredo de assinatura gerado pela PostalQualquer sistema que faça um POST com JSON assinadoPOST assinado para a URL HTTPS do seu sistema

As três conexões nativas usam o token que você cola (sem OAuth) e leem a API do CRM por você. A conexão por webhook serve para qualquer CRM ou sistema que envie e receba webhooks; ela tem a sua própria página.

As integrações com apps (Stripe, Supabase, Clerk, Auth0 e Firebase) usam as mesmas conexões, mas contam à parte, em app_integrations, e não sincronizam de volta.

Depende do plano

O número de conexões de CRM vem do plano de marketing (crm_connections; 0 é não incluído). Passar do limite responde 403 plan_limit_reached. Veja Limites; GET /crm/providers mostra o limite e quantas você já usa. As conexões e os contatos criados por elas contam nos limites do plano como qualquer outro.

#Criar uma conexão

No painel, abra Integrações › CRM, escolha o CRM e cole as credenciais. Elas ficam guardadas criptografadas e não aparecem de novo: a API devolve só os nomes dos campos guardados. Use Testar conexão para conferir o acesso. Pela API, é POST /crm-connections, com uma chave de acesso total.

POST /crm-connections

cURL

curl -X POST https://api.postal.so/crm-connections \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "hubspot",
    "name": "HubSpot",
    "credentials": {
      "access_token": "pat-na1-…",
      "client_secret": "…"
    },
    "settings": {
      "direction": "both"
    }
  }'

Node.js

const response = await fetch('https://api.postal.so/crm-connections', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    provider: 'hubspot',
    name: 'HubSpot',
    credentials: {
      access_token: 'pat-na1-…',
      client_secret: '…',
    },
    settings: {
      direction: 'both',
    },
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/crm-connections",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "provider": "hubspot",
        "name": "HubSpot",
        "credentials": {
            "access_token": "pat-na1-…",
            "client_secret": "…",
        },
        "settings": {
            "direction": "both",
        },
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('crm-connections', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'provider' => 'hubspot',
        'name' => 'HubSpot',
        'credentials' => [
            'access_token' => 'pat-na1-…',
            'client_secret' => '…',
        ],
        'settings' => [
            'direction' => 'both',
        ],
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "crm_connection",
  "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
  "provider": "hubspot",
  "name": "HubSpot",
  "status": "active",
  "settings": {
    "direction": "both",
    "field_mappings": [],
    "stage": null,
    "inbound": {
      "items_path": null,
      "event_field": null,
      "allow_token": false
    },
    "outbound": {
      "url": null,
      "events": [
        "contact.created",
        "contact.updated",
        "contact.deleted",
        "email.opened",
        "email.clicked",
        "email.replied",
        "email.bounced",
        "email.complained"
      ]
    },
    "skip_sources": []
  },
  "credentials": ["access_token", "client_secret"],
  "source": "crm:019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
  "webhook_url": "https://api.postal.so/integrations/crm/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/webhook",
  "webhook_secret": "whsec_c2VncmVkby1kZS1leGVtcGxvLTEyMzQ1Ng==",
  "last_sync_at": null,
  "last_error": null,
  "last_error_at": null,
  "created_at": "2026-10-26T14:00:00.000000Z",
  "updated_at": "2026-10-26T14:00:00.000000Z"
}
  • provider: hubspot, pipedrive, rdstation_crm ou generic (o webhook). As credenciais de cada um estão abaixo.
  • status começa active. paused (PATCH) para de receber e de enviar sem apagar nada; error é só um aviso de que o último envio falhou de vez e volta a active no primeiro envio que der certo.
  • webhook_url é onde o CRM entrega as mudanças; webhook_secret (whsec_…) é o segredo que autentica a entrega. Aparecem ao criar, ao consultar uma conexão e ao gerar um segredo novo, não na lista.
  • source (crm:<id>) é a origem de tudo que a conexão muda nos contatos.

#O que sincroniza, e em que sentido

settings.direction escolhe o sentido: both (padrão), inbound (só do CRM para a Postal) ou outbound (só da Postal para o CRM). Numa conexão só de envio, o webhook do CRM responde 409 connection_paused; numa só de recebimento, a Postal não envia nada.

Do CRM para a Postal

  • Cada pessoa vira um contato criado ou atualizado. A Postal reconhece o contato pelo vínculo com o registro do CRM, depois pelo external_id e por fim pelo e-mail; por isso uma troca de e-mail no CRM atualiza o mesmo contato.
  • Os campos mapeados viram nome, propriedades e etiquetas (veja Mapear campos e etapas). Quem está na lista de supressão não vira contato (a entrada fica como skipped).
  • Quem se descadastrou nunca é reinscrito por um CRM: unsubscribed: false não desfaz um descadastro (só a API de contatos, com a prova do novo consentimento).
  • Um evento do CRM, quando o mapeamento nomeia um (inbound.event_field), é gravado no contato e pode iniciar uma automação com o gatilho de evento.
  • Exclusões no CRM não apagam o contato na Postal.

Da Postal para o CRM

A Postal envia as mudanças que o CRM assinou em settings.outbound.events. O padrão é:

EventoNo CRMPadrão
contact.created, contact.updatedA pessoa é criada ou atualizada (nome, e-mail, campos mapeados).Sim
contact.deletedUma nota “Contato excluído no Postal”. O registro do CRM não é apagado.Sim
email.opened, email.clicked, email.replied, email.bounced, email.complainedUma nota na linha do tempo da pessoa, com o assunto (e o link, o motivo do bounce…).Sim
email.sent, email.delivered, email.failed, email.suppressedO mesmo, para quem quer o histórico completo.Não

Os eventos são os mesmos dos webhooks da Postal. Qualquer outro nome em outbound.events dá 422.

O texto das notas é sempre em português do Brasil, como as pessoas do CRM leem (por exemplo, “Postal: E-mail aberto”, “Assunto: …”). Os nomes de campos que a Postal grava no CRM não mudam com o idioma.

Sincronizar agora

O botão Sincronizar agora (POST /crm-connections/{id}/sync) roda em segundo plano: lê as pessoas do CRM, página a página (e, se houver mapeamento de etapas, os negócios em aberto), e depois envia todos os contatos da Postal, conforme o sentido. Cada pessoa aparece no registro, e uma entrada sync resume quantas pessoas foram importadas e enviadas e quantas falharam. Vale para a primeira carga, para recuperar o que um webhook perdeu ou quando o CRM não consegue enviar webhooks.

#Mapear campos e etapas

Sem mapeamento, a Postal lê e escreve no seu formato próprio. Nas conexões nativas, e-mail, nome, sobrenome e o ID do registro já vêm ligados, e o que você mapeia se soma a eles. Cada mapeamento liga um campo do CRM a um campo da Postal, com um sentido próprio (both, inbound ou outbound):

Campo da PostalO que é
emailO e-mail do contato.
external_idO ID da pessoa no seu app (o mesmo external_id dos contatos).
crm_idO ID do registro no CRM. A Postal o guarda no vínculo da conexão, nunca no external_id.
first_name, last_nameNome e sobrenome. Um valor vazio no CRM limpa o campo.
unsubscribedVerdadeiro ou falso. Veja a regra do descadastro acima.
tagsEtiquetas: uma lista, ou um texto separado por vírgula ou ponto e vírgula.
properties.<chave>Uma propriedade de contato que já existe (properties.company). Uma chave que não existe dá 422.

O campo do CRM é um caminho no estilo JSONPath, de até 255 caracteres, com um subconjunto pequeno: $.person.email (ou person.email), items[0].name, $.data.items[*] (todos os elementos de uma lista) e $ (o documento todo). Um caminho que não existe lê null, e o campo é ignorado (menos nome e sobrenome, que ficam vazios). Nas conexões nativas, os caminhos apontam para o registro que a Postal monta do CRM (por exemplo, $.properties.company no HubSpot e $.custom.<chave> no Pipedrive e no RD Station CRM); GET /crm-connections/{id}/fields lista os campos do CRM que você pode escolher, com o tipo.

Etapas do funil

Quando um negócio muda de etapa, a pessoa dele ganha uma propriedade e/ou uma etiqueta da Postal, e dá para disparar uma automação nessa mudança. Em settings.stage, crm_field e pipeline_field dizem onde estão a etapa e o funil (nas conexões nativas, $.deal.stage e $.deal.pipeline já são o padrão), e cada item de mappings liga uma stage (e, opcionalmente, um pipeline) a uma property_key com property_value, a uma tag ou às duas. Os nomes são comparados sem diferenciar maiúsculas. Sem property_value, a propriedade recebe o nome da etapa. Ao enviar, a Postal não move negócios no CRM. GET /crm-connections/{id}/stages lista as etapas de cada funil.

Configurações com mapeamentos (PATCH /crm-connections/{id})
{
  "settings": {
    "direction": "both",
    "field_mappings": [
      {
        "crm_field": "$.properties.company",
        "postal_field": "properties.company",
        "direction": "both"
      },
      {
        "crm_field": "$.properties.hs_lead_status",
        "postal_field": "tags",
        "direction": "inbound"
      }
    ],
    "stage": {
      "crm_field": "$.deal.stage",
      "pipeline_field": "$.deal.pipeline",
      "mappings": [
        {
          "stage": "Closed won",
          "pipeline": "Sales",
          "property_key": "lifecycle",
          "property_value": "customer",
          "tag": "customer"
        }
      ]
    },
    "outbound": {
      "events": [
        "contact.created",
        "contact.updated",
        "email.opened",
        "email.clicked",
        "email.replied"
      ]
    },
    "skip_sources": [
      "my-app"
    ]
  }
}

Neste exemplo, o campo company do HubSpot vai e volta para a propriedade company da Postal, o status de lead só entra como etiqueta, e um negócio em “Closed won” do funil “Sales” marca a pessoa com lifecycle = customer e a etiqueta customer. As propriedades company e lifecycle precisam existir antes. Só os eventos listados saem para o CRM, e as mudanças vindas de my-app não voltam para ele.

#Sem loops

Duas conexões que se alimentam poderiam trocar a mesma mudança para sempre. A Postal evita isso de dois modos:

  • Tudo que uma conexão muda num contato leva a origem crm:<id> (last_change_source), e essa conexão não envia de volta a mudança que ela mesma fez. As outras conexões do time recebem a mudança normalmente.
  • settings.skip_sources lista origens cujas mudanças a conexão não envia (até 20; letras minúsculas, números, ., _, : e -; um * no fim vale como prefixo). Use para o sistema que escreve na Postal e também no CRM, por exemplo o seu app, que manda contatos pela API com o rótulo de origem my-app, e crm:* para ignorar todas as conexões de CRM.

#Conexões nativas

As três funcionam do mesmo jeito: a Postal chama a API do CRM com o seu token (com o tempo limite de 10 segundos, sem seguir redirecionamentos), procura a pessoa pelo vínculo guardado e depois pelo e-mail, e cria ou atualiza o registro. Um CRM que responde 429 é esperado pelo tempo que ele pedir, e um 5xx é tentado de novo. Pessoas sem e-mail são ignoradas. A mensagem de erro do CRM aparece no registro e nunca traz o token.

Para receber mudanças do CRM é preciso configurar o webhook no próprio CRM, com a URL da conexão (webhook_url); sem webhook, a conexão ainda envia dados e lê o CRM quando você pede Sincronizar agora. Cada CRM autentica o webhook de um jeito:

CRMComo a Postal confere o webhook
HubSpotAssinatura X-HubSpot-Signature-v3, calculada com o client secret do aplicativo. Sem o client secret nas credenciais, o HubSpot não envia mudanças para a Postal.
PipedriveAutenticação HTTP básica no webhook: usuário postal e senha igual ao webhook_secret da conexão.
RD Station CRMO RD Station CRM não assina os webhooks: a URL leva ?token=<webhook_secret> (ou o cabeçalho X-Postal-Token). O token na URL é a proteção; não a compartilhe.

Uma assinatura ou credencial errada responde 401 invalid_signature. Se a segurança do segredo for comprometida, gere outro com POST /crm-connections/{id}/webhook-secret/rotate e atualize o webhook no CRM: o antigo para de valer na hora.

HubSpot

  • Credenciais: access_token (obrigatório) e client_secret (opcional, só para webhooks). No HubSpot, vá em Configurações › Integrações › Aplicativos privados e crie um aplicativo com permissão de contatos (leitura e escrita), negócios (leitura) e esquemas de contatos e de negócios (leitura); copie o token de acesso. O client secret é o do aplicativo que assina as notificações.
  • Webhook: no aplicativo, assine contatos (criação e mudança de propriedade) e negócios (criação e mudança de etapa) com a URL da conexão. Exclusões e mesclagens são ignoradas.
  • Campos: o registro é o do HubSpot, {"id", "properties": {…}}, então os caminhos são $.properties.<nome interno>. A Postal lê as propriedades que você mapeia, mais e-mail, nome, sobrenome e telefone. Para negócios, $.deal.stage, $.deal.pipeline, $.deal.status, $.deal.title e $.deal.value.
  • Envio: o contato é atualizado pelo vínculo, ou procurado pelo e-mail e criado se não existir. A atividade de e-mail vira uma nota associada ao contato.

Pipedrive

  • Credenciais: api_token e company_domain. No Pipedrive, abra o menu da conta › Preferências pessoais › API e copie o token pessoal. O endereço da conta é só a primeira parte do endereço que você usa no navegador: minhaempresa em minhaempresa.pipedrive.com (letras minúsculas, números e hifens).
  • Webhook: em Ferramentas e apps › Webhooks, crie um webhook para pessoas e outro para negócios (todos os eventos), com a URL da conexão e a autenticação HTTP básica acima. Exclusões são ignoradas. Uma mudança em um negócio lê a pessoa dele e os nomes da etapa e do funil.
  • Campos: $.email, $.first_name, $.last_name, $.name, $.phone, $.organization, $.id, os campos personalizados da pessoa em $.custom.<chave> e, para negócios, $.deal.stage, $.deal.pipeline, $.deal.status, $.deal.title e $.deal.value.
  • Envio: a pessoa é atualizada pelo vínculo ou procurada pelo e-mail e criada se não existir; o e-mail e o telefone novos entram como principais sem apagar os outros. A atividade de e-mail vira uma nota na pessoa.

RD Station CRM

  • Credenciais: token, o token da instância. No RD Station CRM, abra o menu da conta › Configurações › Integrações › Token da API.
  • Webhook: crie um webhook para contatos e outro para negociações com a URL da conexão incluindo o ?token= (a tela da conexão mostra a URL pronta). Exclusões são ignoradas.
  • Campos: $.email, $.first_name, $.last_name, $.name, $.phone, $.title, $.organization, $.id, os campos personalizados do contato em $.custom.<id> e, para negociações, $.deal.stage, $.deal.pipeline, $.deal.status, $.deal.title e $.deal.value.
  • Envio: o contato é atualizado pelo vínculo ou procurado pelo e-mail e criado se não existir. A atividade de e-mail vira uma atividade na negociação em aberto do contato; sem negociação, uma linha nas anotações do contato (as mais antigas saem quando passam de 4.000 caracteres).

Permissões do token

Se o CRM recusar o token ou uma permissão (HTTP 401 ou 403), o registro mostra a mensagem e a conexão fica com o aviso de erro até um envio dar certo. Confira o token e as permissões e use Testar conexão.

#Registro de sincronização

Cada webhook recebido, pessoa sincronizada, envio ao CRM, teste e sincronização completa vira uma entrada do registro, em Integrações › CRM (na conexão) e em GET /crm-connections/{id}/logs. Uma entrada tem:

CampoValores
directioninbound (do CRM) ou outbound (para o CRM). Filtro: ?direction=.
object_typewebhook (a entrega recebida), person, activity (atividade de e-mail), test ou sync.
statuspending (na fila ou esperando nova tentativa), success, failed ou skipped (não havia o que fazer: contato suprimido, sem correspondência, conexão pausada). Filtro: ?status=.
actionO que aconteceu: created, updated, unchanged, upserted, activity, deleted ou um resumo (“2 pessoas”).
error, attempts, next_attempt_atA mensagem, quantas tentativas já houve e quando será a próxima.
external_idO ID do registro no CRM, quando se sabe.
retryable, payloadSe dá para reenviar, e o que foi recebido ou enviado (cortado em 16 KB; um payload cortado não pode ser reenviado).

As mensagens do registro são escritas em português do Brasil.

200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "crm_sync_log",
      "id": "019a2c10-5e6f-7a81-9b2c-3d4e5f6a7b8c",
      "direction": "outbound",
      "object_type": "person",
      "event": "contact.updated",
      "external_id": "51",
      "contact_id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "action": null,
      "status": "failed",
      "error": "HubSpot recusou as credenciais ou a permissão (HTTP 403). Confira o token e as permissões dele.",
      "attempts": 6,
      "retryable": true,
      "payload": {
        "id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
        "email": "[email protected]",
        "first_name": "Ana"
      },
      "next_attempt_at": null,
      "created_at": "2026-10-26T14:05:10.000000Z",
      "updated_at": "2026-10-26T14:05:11.000000Z"
    }
  ]
}

Novas tentativas

  • Envios ao CRM que falham são tentados de novo sozinhos, depois de 30 segundos, 2 minutos, 10 minutos, 30 minutos e 2 horas (6 tentativas ao todo). Acabadas, a entrada vira failed e a conexão mostra o aviso error (last_error); ela continua funcionando e volta a active no primeiro envio que der certo. Se o CRM respondeu 429, a Postal espera pelo menos o que ele pediu.
  • Webhooks recebidos são respondidos na hora com 202 e processados em segundo plano. Se o CRM não pôde ser consultado (limite de requisições, fora do ar), a Postal tenta de novo, até 3 vezes.
  • Uma entrada failed com o payload completo se reenvia à mão, no painel ou em POST /crm-connections/{id}/logs/{log_id}/retry, depois de corrigir a causa (um e-mail que faltava, uma propriedade que não existia, o token).

#Pela API

Tudo o que o painel faz está na API, com uma chave de acesso total (uma chave só de envio recebe 401 restricted_api_key). Os IDs são UUIDs, e as listas seguem a paginação por cursor.

EndpointO que faz
GET /crm/providersOs conectores (generic, pipedrive, rdstation_crm, hubspot e as fontes de apps), o limite do plano e o uso de cada contador, e a plataforma parceira que gerencia o time, se houver.
GET /crm-connectionsLista as conexões. ?limit_key=crm_connections (ou app_integrations) filtra por contador.
POST /crm-connectionsCria. Corpo: provider, name, credentials e settings.
GET /crm-connections/{id}Uma conexão, com o webhook_secret.
PATCH /crm-connections/{id}Muda name, status (active ou paused), credentials (substituem todas: mande todas as obrigatórias) e settings (substituem as anteriores: mande o conjunto completo).
DELETE /crm-connections/{id}Exclui a conexão e o registro dela. O CRM deixa de sincronizar; os contatos ficam.
POST /crm-connections/{id}/testConfere o acesso ao CRM (nos nativos, uma chamada leve; no webhook, um postal.test). Grava uma entrada test no registro.
POST /crm-connections/{id}/previewMostra o que um payload de exemplo mudaria (payload, mais settings opcionais para testar sem salvar), sem gravar nada. Feito para a conexão por webhook.
GET /crm-connections/{id}/fieldsOs campos do CRM para o mapeamento (key, label, type). Nos nativos, vêm do CRM.
GET /crm-connections/{id}/stagesAs etapas de cada funil (pipeline, stage, e status quando o CRM diz). Vazio nos conectores sem funil.
POST /crm-connections/{id}/syncEnfileira a sincronização (202). 422 se a conexão está pausada ou o sentido não permite.
POST /crm-connections/{id}/webhook-secret/rotateGera um segredo novo; o anterior para de valer na hora.
GET /crm-connections/{id}/logsO registro. Filtros: status e direction.
POST /crm-connections/{id}/logs/{log_id}/retryReenvia uma entrada failed com o payload completo.
POST /crm-connections/{id}/test

cURL

curl -X POST https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/test \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/test', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/test",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/test', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "crm_connection_test",
  "ok": true,
  "message": "Connected to HubSpot (12 contacts).",
  "status_code": 200
}

Quando o CRM recusa, ok é false e message traz o motivo. fields, stages e a sincronização que não conseguem falar com o CRM respondem 502 crm_request_failed com a mensagem legível.

POST /crm-connections/{id}/sync

cURL

curl -X POST https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/sync \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/sync', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/sync",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/sync', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
202Resposta
{
  "object": "crm_connection",
  "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
  "sync": "queued"
}
GET /crm-connections/{id}/logs

cURL

curl 'https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/logs?status=failed' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/logs?status=failed', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/logs",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "status": "failed",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('crm-connections/019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b/logs', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'status' => 'failed',
    ],
]);

$data = json_decode($response->getBody(), true);

#Limites

O quêLimite
Conexõescrm_connections do plano (e app_integrations para as de apps)
Corpo de um webhook recebido256 KB (413 payload_too_large)
Pessoas por webhook recebido500 (as que passam disso são ignoradas)
Webhooks recebidos600 por minuto, por IP de origem
Mapeamentos de campos / de etapas100 / 100
Origens a ignorar (skip_sources)20
Exemplo de payload guardado (sample_payload)32 KB
Tempo limite das chamadas ao CRM10 segundos, sem seguir redirecionamentos
Payload guardado no registro16 KB (os webhooks recebidos ficam inteiros, até 256 KB)
Páginas lidas do CRM numa sincronização1.000 (de 100 a 200 pessoas cada)

#Erros

StatusNomeQuando acontece
401invalid_signatureA assinatura ou o token do webhook falta ou está errado, ou a data da assinatura passou de 5 minutos.
401restricted_api_keyUma chave só de envio tentou usar /crm-connections.
403plan_limit_reachedO plano não inclui conexões de CRM ou já usou todas.
404not_foundA conexão não existe neste time, ou o ID do webhook é de outra conexão.
409connection_pausedA conexão está pausada, ou só envia (não recebe do CRM).
413payload_too_largeO corpo do webhook passa de 256 KB.
422validation_errorCredenciais ou configurações inválidas: sentido desconhecido, campo da Postal ou caminho inválido, propriedade que não existe, evento desconhecido, URL que não é HTTPS, corpo do webhook que não é JSON; ou um reenvio de entrada que não é elegível.
502crm_request_failedO CRM recusou a chamada da Postal ou não respondeu (campos, etapas e sincronização).

O formato dos erros e os demais nomes estão em Erros.

#Veja também