Pular para o conteúdo principal

Envio de e-mail

Cada chamada envia um e-mail, a partir de um modelo já criado no Workfuse. O envio sai pela conta de envio da instituição e passa pelo limite do aquecimento de IP dela, o mesmo das campanhas.

A URL completa é https://api.workfuse.com/public/v1/email/send. Este endpoint mora em /email, não em /crm, mas usa a mesma chave do módulo CRM.

Antes de começar​

  • Um modelo salvo no editor. Crie o modelo em E-mails → Modelos e salve. Modelo que nunca foi salvo no editor responde 422.
  • O slug do modelo. Copie na tela do modelo. O slug não muda quando o modelo é renomeado, então a URL do seu webhook não quebra. O padrão é o nome em minúsculas, sem acento, com hífen: Boas-vindas Matrícula vira boas-vindas-matricula.
  • A conta de envio. A equipe Workfuse cadastra a conta de envio da instituição. Sem ela, a resposta é 422.
  • Uma API key com o módulo CRM no escopo. Veja Autenticação. A organização também precisa ter o CRM habilitado.

Enviar um e-mail​

POST /email/send · assíncrono

curl -X POST "https://api.workfuse.com/public/v1/email/send" \
-H "Authorization: Bearer wf_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{
"to": "ana.souza@exemplo.com",
"templateSlug": "boas-vindas-matricula",
"subject": "Bem-vinda, Ana",
"idempotencyKey": "matricula-88431",
"name": "Ana Souza",
"curso": "Engenharia Civil"
}'

O corpo é um JSON plano. As chaves reservadas são as da tabela; toda outra chave vira uma variável do modelo.

CampoTipoObrigatórioDescrição
tostringsimE-mail do destinatário. Tem que ser um e-mail válido. Espaços nas pontas são removidos e tudo vai para minúsculas.
templateSlugstringsim*O slug do modelo, até 160 caracteres. Também vale na URL, como ?templateSlug=; o do corpo vence.
templateIdstringsim*O id do modelo, até 200 caracteres. Alternativa ao slug.
subjectstringnãoO assunto, até 998 caracteres. Sem ele, vale o assunto do modelo. Sem nenhum dos dois, a resposta é 422.
idempotencyKeystringnãoAté 200 caracteres. Ver Idempotência.
variablesobjectnãoVariáveis do modelo. Aceita o mesmo que as chaves soltas.

* Informe templateSlug ou templateId: exatamente um. Os dois juntos, ou nenhum, respondem 422. Isso inclui ?templateSlug= na URL com templateId no corpo: também é 422.

O to não tem limite próprio de tamanho: a API não corta nem recusa pelo comprimento, só exige que seja um e-mail válido.

O slug é comparado exatamente como foi gravado e só procura na instituição da chave. Modelo excluído responde 404. Excluir um modelo libera o slug: um modelo novo criado depois com o mesmo slug passa a responder por ele.

Uma string vazia ("") em subject, idempotencyKey, templateSlug e templateId conta como não informado. É assim que o HubSpot envia uma propriedade vazia.

Variáveis no modelo​

No HTML do modelo, {{curso}} é trocado pelo valor enviado e {{curso|seu curso}} usa seu curso quando o valor vem vazio, null ou ausente.

  • Só valores simples. Cada variável é string, number, boolean ou null. Objeto ou lista, em qualquer chave, responde 400.
  • Todo valor é escapado como HTML. Não dá para mandar marcação pela variável.
  • variables vence a chave solta. Se curso vier solto e dentro de variables, vale o de variables.
  • name, email, phone, cpf, birth_date e gender também são lidos das variáveis. {{email}} sem variável usa o to.
  • O assunto não resolve variável. O texto de subject vai como veio.
  • Variável ausente sem texto padrão vira vazio. {{curso}} sem valor e sem |padrão some do e-mail, sem erro.
  • Sem limite de variáveis. A API não limita quantas variáveis vêm nem o tamanho de cada valor.

Todo e-mail sai com o descadastro, sem nada no modelo:

  • No HTML, um rodapé com o link Descadastrar entra antes do último </body> (ou no fim, se não houver </body>). Na versão em texto, o link vai na última linha.
  • O e-mail leva os cabeçalhos List-Unsubscribe e List-Unsubscribe-Post (descadastro em um clique nos clientes de e-mail).

Quem se descadastra passa a ser suprimido: os próximos envios para o endereço recebem 202 e não saem.

Resposta 202​

{
"externalMessageId": "clzd8p3r40004s6vp6c2n5t7b"
}

O 202 quer dizer aceito e na fila, não enviado.

  • Endereço suprimido (descadastro, bounce ou denúncia de spam) também recebe 202, e o e-mail não sai.
  • Depois do 202, o envio ainda pode falhar sem aviso: conta de envio removida, modelo excluído, recusa do SendGrid ou retentativas esgotadas.
  • O modelo é lido na hora do envio. Editar o modelo entre o 202 e o envio muda o que sai, e um modelo excluído nesse meio não é enviado.
  • O resultado (entregue, bounce, spam) não volta por esta API. A instituição acompanha na aba Entregabilidade. Não há consulta por externalMessageId nem webhook de status para quem chama.

Retenção dos dados​

Os dados pessoais de um envio encerrado (enviado, falho ou suprimido) são apagados 90 dias depois da data prevista de envio: o endereço, o assunto, as variáveis e a mensagem de erro. O status, as datas e os eventos do provedor ficam, então relatórios e o limite do aquecimento não mudam.

Idempotência — idempotencyKey​

Repetir o mesmo idempotencyKey devolve 202 com o mesmo externalMessageId e não enfileira outro e-mail, mesmo que o modelo tenha mudado ou sido excluído depois. O escopo é a instituição da chave: a mesma chave em outra instituição é outro envio.

  • Chave reaproveitada com outro pedido é 409. A mesma chave com to, templateId, templateSlug, subject ou variáveis diferentes responde 409 EMAIL_SEND_IDEMPOTENCY_KEY_REUSED e nada é enfileirado. Maiúsculas no to e a ordem das variáveis não contam como diferença. Envios gravados antes desta regra continuam respondendo 202 com o id antigo.
  • A repetição pula as validações. Modelo, assunto e conta de envio não são checados: a resposta é 202 mesmo que um pedido novo igual falhasse com 404 ou 422.
  • Pedidos simultâneos com a mesma chave gravam um envio só e devolvem o mesmo externalMessageId.

Gere uma chave por ocorrência de envio: o mesmo valor só nas retentativas desse envio. Record ID, nome do workflow e slug do modelo sozinhos se repetem quando o mesmo contato recebe o modelo de novo (reinscrição, outro evento), e o segundo envio é tratado como repetição e não sai. Inclua algo que muda a cada ocorrência, como o id do evento ou a data da inscrição.

Sem a chave, não há proteção contra duplicata depois de um timeout.

Quando o limite do aquecimento é atingido​

informação

A API nunca recusa um envio pelo limite do aquecimento. Ela responde 202 e o e-mail espera. O único limite que recusa é o de pedidos por minuto, com 429 (ver Erros).

  • Acima do limite da hora, o e-mail sai na hora seguinte. Acima do limite do dia, sai no dia seguinte. O relógio é o de America/Sao_Paulo.
  • Com a rampa pausada por denúncia de spam, o e-mail é reavaliado a cada hora.
  • Só conta no limite o e-mail que foi enviado.
  • A ordem de saída dos e-mails que esperam não é garantida.
  • Depois de 30 dias esperando, o envio desiste e não sai.
  • Campanhas e API da mesma instituição dividem o mesmo limite.

Erros​

O formato de cada envelope está em Erros.

HTTPerrorCodeQuando
400—Corpo malformado, to inválido, ou variável que não é valor simples.
401API_KEY_MISSING, API_KEY_INVALIDChave ausente ou inválida. Com token de conta de serviço, SERVICE_ACCOUNT_TOKEN_INVALID.
403MODULE_NOT_IN_KEY_SCOPE, CAPABILITY_NOT_ENABLEDA chave não tem o módulo CRM, ou a organização não tem o CRM habilitado. Com conta de serviço, PERMISSION_DENIED.
404EMAIL_TEMPLATE_NOT_FOUNDSlug ou id inexistente, excluído ou de outra instituição.
409EMAIL_SEND_IDEMPOTENCY_KEY_REUSEDidempotencyKey já usada nesta instituição com outro pedido. Não retente: gere outra chave.
422EMAIL_SEND_TEMPLATE_SELECTOR_INVALIDtemplateSlug e templateId juntos, ou nenhum.
422EMAIL_TEMPLATE_NOT_RENDEREDO modelo nunca foi salvo no editor.
422EMAIL_SEND_SUBJECT_MISSINGSem subject no pedido e no modelo.
422EMAIL_SENDER_ACCOUNT_NOT_CONFIGUREDA instituição ainda não tem conta de envio.
429SERVICE_ACCOUNT_RATE_LIMITEDSó com token de conta de serviço: 120 pedidos por minuto. Respeite o Retry-After.
429EMAIL_SEND_RATE_LIMITEDSó com API key wf_: 120 envios por minuto por chave (padrão). Respeite o Retry-After.
5xx—Falha nossa e transitória.

Limite de pedidos por minuto​

Com API key wf_, o limite padrão é 120 envios por minuto por chave, numa janela de 1 minuto. A equipe Workfuse ajusta esse valor no servidor pela variável de ambiente EMAIL_SEND_API_KEY_RATE_LIMIT_PER_MINUTE (um número maior que zero, arredondado para baixo; ausente, zero, negativo ou não numérico volta para 120).

As respostas desse limite trazem os cabeçalhos RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset (segundos até a janela virar). O 429 traz também o Retry-After.

Retentativa​

Retente 5xx e 429, com o mesmo idempotencyKey. Não retente 4xx: é erro de configuração, e o mesmo pedido volta a falhar.

Se o envio já tinha sido gravado antes do 5xx, ele não se perde, e o reenvio com a mesma chave devolve 202 com o mesmo externalMessageId, sem duplicar:

  • Com retentativa. Se a falha foi ao colocar o envio na fila, o reenvio com a mesma chave o coloca na fila na hora.
  • Sem retentativa. Uma rotina a cada 5 minutos recoloca na fila o envio parado há mais de 2 horas. Se ele continuar parado, a rotina tenta de novo com espera crescente: por volta de 2 h, 6 h, 14 h e 30 h depois do pedido, e daí uma vez por dia. Passado o prazo máximo de espera do aquecimento mais 1 dia, o envio desiste e não sai.

Configurar no HubSpot​

Para disparar o e-mail de um workflow, use a ação Send a webhook. Ela exige o Data Hub Professional ou Enterprise.

  1. Opcional: acima da ação, um random split, para soltar só um percentual dos registros enquanto a rampa do aquecimento cresce.
  2. Adicione a ação Data ops → Send a webhook e escolha o método POST.
  3. Na URL, fixe o modelo pelo slug: https://api.workfuse.com/public/v1/email/send?templateSlug=boas-vindas-matricula. Uma ação envia um modelo só.
  4. Em Authentication type, escolha API key: nome Authorization, local Request header, e o segredo com o valor Bearer wf_sua_chave_aqui, com a palavra Bearer e o espaço antes da chave.
  5. Em Customize request body, monte o corpo:
    • to = a propriedade Email;
    • idempotencyKey = a propriedade que você montou com Format data (concat(...) grava num campo de texto que pode ir no corpo), juntando o Record ID a algo que muda a cada ocorrência, como explicado em Idempotência;
    • uma chave por variável do modelo, com a propriedade do contato;
    • subject com um valor estático, se quiser trocar o assunto do modelo.
  6. Ligue Configure rate limit. Ele segura a ação e as seguintes do workflow, e a fila não acumula além do limite do dia.
  7. Rode Test action. Um 202 com externalMessageId é o esperado.

O HubSpot dispara uma requisição por registro: cada contato que entra no workflow gera um POST.

aviso

Nunca use "Include all properties". O corpo que o HubSpot monta traz listas e objetos aninhados, e a API responde 400 a valor que não é simples. O HubSpot não retenta 4xx, então o envio se perde.

Propriedade vazia chega como "" e cai no texto padrão da variável, se o modelo tiver um.

Retentativa do HubSpot​

O HubSpot retenta por até 3 dias, a primeira vez 1 minuto depois, com intervalo máximo de 8 horas. Não retenta 4xx, exceto 429, e nesse caso respeita o Retry-After. Isso casa com a regra da seção de erros: 5xx e 429 voltam, e o idempotencyKey evita o envio em duplicata.

Comportamento medido em knowledge.hubspot.com em 16/09/2026.