E-mails transacionais
Um e-mail transacional é o que o seu sistema dispara para uma pessoa por causa de algo que ela fez: a troca de senha, o código de acesso, a abertura de um ticket. O Workfuse recebe o pedido com o mesmo corpo e a mesma resposta do envio único transacional do HubSpot (versão 2026-09), monta o e-mail com o modelo da instituição e o envia pela conta de envio dela, no pool transacional, sem link de descadastro.
O sufixo dos caminhos é o do HubSpot, sob API_BASE/public/v1/crm. Quem já integra com o
HubSpot troca três coisas: a base da URL, a chave e o emailId de cada modelo.
POST /crm/marketing/transactional/2026-09/single-email/send → 200 PENDING
o Workfuse confere bounce e spam, monta o modelo e envia
GET /crm/marketing/v3/email/send-statuses/{statusId} → 200 COMPLETE · SENT
Antes de enviar
- A instituição precisa estar ligada. O módulo
crm.transactionalEmailprecisa estar ativo na organização; sem ele, o pedido recebe403. A equipe Workfuse liga o envio transacional na conta de envio da instituição (pool transacional e domínios de remetente permitidos); sem isso, o pedido recebe422. - O modelo e o código dele. A instituição cria o modelo em CRM › Comunicação ›
E-mail › Modelos, com assunto, corpo e, se quiser, o remetente. Na edição do modelo
aparece o Código para a API: é o número que vai no
emailId. - A credencial. Um token de conta de serviço (
wfs_...) com a permissãointegration.public-crm.usee a superfície de API pública, ou uma API key com o módulocrm. Ela vai no cabeçalhoAuthorization: Bearer, como você já manda ao HubSpot, e define a instituição.
Enviar
POST /marketing/transactional/2026-09/single-email/send
curl -X POST "API_BASE/public/v1/crm/marketing/transactional/2026-09/single-email/send" \
-H "Authorization: Bearer wfs_seu_token_aqui" \
-H "Content-Type: application/json" \
-d '{
"emailId": 1042,
"message": { "to": "ana@exemplo.com", "sendId": "reset-123" },
"customProperties": { "reset_link": "https://exemplo.com/reset?t=abc" }
}'
| Campo | O que é |
|---|---|
emailId | O código do modelo, inteiro (texto só de dígitos também vale) |
message.to | O destinatário: "Nome <e-mail>" ou o e-mail. Só o endereço é usado; o nome não vai no e-mail |
message.from | Opcional. "Nome <e-mail>" ou o e-mail; o domínio precisa estar entre os permitidos da conta. Sem ele, vale o remetente do modelo e, por fim, o padrão da conta |
message.sendId | Opcional. O mesmo identificador gera um e-mail só |
message.replyTo, message.cc, message.bcc | Opcionais, até 10 endereços cada |
contactProperties | Valores de contato que o modelo usa (firstname, ...) |
customProperties | Os valores do pedido (reset_link, número do ticket, ...) |
A resposta é a EmailSendStatusView do HubSpot:
{ "statusId": "cmt9envio", "status": "PENDING", "requestedAt": "2026-10-16T12:00:00.000Z" }
Recusa sem erro HTTP. Como no HubSpot, um modelo inexistente, um destinatário ou
remetente inválido, um valor faltando ou um identificador reaproveitado respondem 200
com o envio já concluído e o resultado:
{ "statusId": "cmt9outro", "status": "COMPLETE", "sendResult": "MISSING_TEMPLATE_PROPERTIES",
"message": "Faltam valores de campos que o modelo usa e que não têm padrão.",
"requestedAt": "2026-10-16T12:01:00.000Z", "completedAt": "2026-10-16T12:01:00.000Z" }
sendResult | Quando |
|---|---|
SENT | O provedor aceitou o e-mail |
MISSING_CONTENT | O código não é de um modelo desta instituição, ou o modelo está sem conteúdo ou assunto |
MISSING_TEMPLATE_PROPERTIES | Um campo do pedido (custom.*) sem valor e sem padrão |
INVALID_TO_ADDRESS | Destinatário inválido |
INVALID_FROM_ADDRESS | Remetente inválido ou fora dos domínios permitidos |
IDEMPOTENT_FAIL | O sendId já foi usado com outro modelo ou destinatário; nenhum e-mail sai |
PREVIOUSLY_BOUNCED, PREVIOUS_SPAM | O endereço teve bounce permanente ou denunciou spam antes |
PORTAL_SUSPENDED | O envio transacional foi desligado depois do pedido |
UNDELIVERABLE | O provedor descartou o e-mail |
O schema declara os 51 valores do HubSpot; os de cima são os que o Workfuse produz.
O mesmo identificador. O mesmo sendId com o mesmo modelo e destinatário devolve o
envio original, sem segundo e-mail.
Consultar
GET /marketing/v3/email/send-statuses/{statusId} devolve a mesma EmailSendStatusView,
com eventId quando o e-mail saiu. Só a credencial da própria instituição enxerga o envio;
outra recebe 404.
Erros
Toda recusa HTTP vem no corpo Error do HubSpot: message, correlationId, category e,
quando há, subCategory e errors[] com o campo em in.
| HTTP | category | Quando |
|---|---|---|
400 | VALIDATION_ERROR | Corpo fora da forma: emailId ou to faltando, tipo ou tamanho errado |
401 | INVALID_AUTHENTICATION | Credencial ausente ou inválida |
403 | MISSING_SCOPES | Sem o módulo ou a permissão, ou crm.transactionalEmail desligado |
404 | OBJECT_NOT_FOUND | Envio de outra instituição ou inexistente (consulta) |
422 | VALIDATION_ERROR | Envio transacional desligado na instituição (TRANSACTIONAL_EMAIL_NOT_ENABLED) |
429 | RATE_LIMITS | Mais de 120 pedidos por minuto no token; respeite o Retry-After |
Os campos do modelo
O modelo aceita a sintaxe do HubSpot:
{{ custom.reset_link }}— o valor decustomProperties.{{ contact.firstname }}— decontactProperties, senão do contato da instituição com o mesmo e-mail (firstnameé a primeira palavra do nome).{{ personalization_token('contact.firstname','Aluno') }}— com padrão.
Listas e objetos em customProperties são aceitos, mas o modelo não os lê, como no
HubSpot. Os valores do pedido são apagados quando o envio termina.
Se você vem do HubSpot
- O Workfuse não cria nem altera contato. O envio se liga ao contato que já existe
com o mesmo e-mail na instituição;
contactPropertiessó alimenta o modelo. - O descadastro de marketing não bloqueia o e-mail transacional; bounce permanente e denúncia de spam bloqueiam.
- Troque a base, a chave e o
emailId. O resto do corpo e da resposta é o mesmo.