Enviar pela API
A chamada de envio, campo a campo, com o que cada resposta quer dizer.
Um pedido POST, um JSON, e uma resposta com o identificador da mensagem. A
referência completa está em Referência da API; esta página é a
explicação.
Autenticação
Uma chave de API em cada pedido, no cabeçalho.
Authorization: Bearer cconnect_xxxxxxxxxxxxxxxxxxxxxxxx
A chave decide de que conta é o pedido, por isso não há nenhum identificador de conta no URL nem no corpo. Se a chave estiver limitada a uma stream, só pode enviar por domínios dessa stream.
O pedido mínimo
curl -X POST https://app.c-connect.pt/v1/messages \
-H "Authorization: Bearer $CCONNECT_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "recibos@mail.empresa.pt",
"to": "alguem@example.org",
"subject": "O teu recibo",
"text": "Obrigado pela tua compra."
}'
Obrigatórios são o from, o to, e um corpo: text, html, ou os dois.
Os campos
| Campo | O que é |
|---|---|
from |
Qualquer caixa num domínio teu verificado. Verificamos o domínio, nunca a parte antes do @. |
from_name |
O nome que aparece antes do endereço. |
to |
Um destinatário. Uma mensagem, uma pessoa. |
subject |
O assunto. |
text |
A versão em texto simples. |
html |
A versão em HTML. |
headers |
Cabeçalhos teus, como um objecto. |
unsubscribe_url |
Obrigatório na stream de marketing. |
raw |
Uma mensagem MIME completa que construíste. Ver Enviar MIME em bruto. |
Envia sempre text além de html. Um cliente de email que não renderize HTML
mostra a alternativa, e uma mensagem só com HTML pontua pior em alguns filtros.
Um destinatário de cada vez
Não há campo para vários destinatários, nem cc, nem bcc. É deliberado:
- cada mensagem tem o seu estado, os seus eventos e o seu identificador, e uma entrega que falha para um endereço não diz nada sobre os outros;
- uma supressão é por endereço, e uma mensagem para cinco pessoas das quais uma está suprimida não tem resposta certa;
ccnuma mensagem automática mostra a cada destinatário o endereço dos outros, o que é uma fuga de dados que ninguém quis.
Para enviar a mil pessoas, faz mil pedidos. A API aguenta.
A resposta
{
"id": "01JB2XK7Q9R3N8Z6T4V0M5W1YC",
"message_id_header": "<01JB2XK7Q9R3N8Z6T4V0M5W1YC@c-connect.pt>",
"status": "queued",
"stream": "transactional",
"from": "recibos@mail.empresa.pt",
"to": "alguem@example.org",
"subject": "O teu recibo",
"created_at": "2026-09-18T10:41:02+01:00"
}
O código é 202 Accepted, e não 200. Aceite não é entregue: a mensagem
foi assinada e posta em fila, e o que o destino fez com ela chega depois, como
eventos. Uma integração que trate 202 como "entregue" vai mentir ao utilizador
mais cedo ou mais tarde.
Guarda o id. É por ele que consultas a mensagem, é ele que aparece nos
webhooks, e é ele que o suporte pede.
Quando corre mal
| Código | O que aconteceu | O que fazer |
|---|---|---|
401 |
Chave em falta, desconhecida ou revogada. | Verifica o cabeçalho e o painel. |
403 |
A chave está limitada a outra stream. | Usa a chave certa para este domínio. |
422 |
O corpo não passou a validação, ou o domínio não é teu, ou não está verificado, ou o endereço está suprimido. | Lê o message e os errors. |
429 |
Acima do limite de pedidos. | Espera e repete. |
5xx |
Nosso. | Repete com espera crescente. |
Um 422 por endereço suprimido não é um erro teu: é o sistema a impedir um
envio que ia devolver outra vez. Trata-o como um resultado e não como uma
falha.
Limites e repetições
Há dois tectos e não um. O limite de pedidos é por chave, 600 por minuto. A quota é da conta inteira, por hora e por dia, e é a que interessa a quem envia em lote: todas as chaves da conta gastam a mesma.
Cada 202 traz o que sobra dela, por isso não precisas de perguntar nem de a
descobrir a bater com a cabeça:
X-CConnect-Quota-Window: hour
X-CConnect-Quota-Limit: 2000
X-CConnect-Quota-Remaining: 1873
X-CConnect-Quota-Reset: 1758186000
A janela indicada é a que está mais perto de esgotar, a hora ou o dia, que é a
que decide quando podes mandar a próxima. O Reset são segundos desde a época.
Um lote bem escrito lê o Remaining da última resposta e pára quando chega a
zero, em vez de continuar a bater no 429. Se quiseres saber o tamanho do lote
antes de o começar, GET /v1/me devolve as duas janelas.
A resposta certa a um 429 é esperar o que o Retry-After diz, não abrir mais
ligações: mais ligações gastam a mesma quota mais depressa.
Repete um 5xx e um 429. Não repitas um 4xx: o pedido é o mesmo e a
resposta vai ser a mesma.
Repetir sem enviar duas vezes
Um 202 que já recebeste e repetes envia uma segunda mensagem. O problema real
não é esse, é o outro: um pedido que rebentou a meio, ou uma ligação que caiu
depois de nós termos aceitado e antes de tu teres lido a resposta. Nesse caso
não sabes se saiu.
Põe uma chave tua no pedido e o problema desaparece:
curl -X POST https://app.c-connect.pt/v1/messages \
-H "Authorization: Bearer $CCONNECT_KEY" \
-H "Idempotency-Key: fatura-2026-001" \
-H "Content-Type: application/json" \
-d '{ "from": "faturas@mail.empresa.pt", "to": "cliente@exemplo.pt", ... }'
Uma chave por mensagem, escolhida por ti. O identificador que já tens do teu lado costuma ser a melhor: o número da factura, o id da encomenda, o do utilizador mais a acção. Uma chave aleatória por pedido não serve de nada, porque uma repetição gerava outra.
O que acontece a seguir:
| Segundo pedido | Resposta |
|---|---|
| Mesma chave, mesmo corpo | O mesmo 202 de antes, com Idempotent-Replay: true. Não sai nada. |
| Mesma chave, corpo diferente | 409. A chave já é de outra mensagem. |
| Mesma chave, o primeiro ainda a correr | 409 com Retry-After: 1. Tenta outra vez daqui a um instante. |
| Sem chave | Sai uma segunda mensagem, como sempre saiu. |
As chaves são da conta, não da chave de API, e duram 24 horas. Um pedido
recusado liberta a chave: se te devolvermos 422 por o endereço estar
suprimido, podes corrigir o destinatário e repetir com a mesma.