01 · Quickstart
Do painel ao primeiro POST
Você precisa de uma conta, uma chave de API e um domínio verificado. O envio é feito por HTTP; nenhum SDK é necessário.
- 1
Crie uma chave
No painel, abra API Keys. Copie a chave quando ela aparecer.
- 2
Verifique o domínio
Cadastre o domínio remetente e publique os registros DNS mostrados pelo painel.
- 3
Envie por HTTP
Troque a chave, o remetente e o destinatário no exemplo abaixo.
curl -X POST https://usecorreo.com/api/v1/emails \
-H "Authorization: Bearer uc_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"from": "Sua Loja <oi@sualoja.com.br>",
"to": ["cliente@exemplo.com.br"],
"subject": "Pedido confirmado",
"html": "<p>Seu pedido saiu para entrega.</p>"
}'{
"id": "eml_seu_id"
}02 · Segurança
Autenticação Bearer
Toda chamada da API v1 exige uma chave no header Authorization.
Authorization: Bearer uc_live_sua_chaveA chave em claro aparece uma única vez quando é criada. Guarde-a em uma variável de ambiente ou secret manager; não envie para o navegador nem grave no repositório.
03 · Endpoint principal
Enviar um email
O contrato aceita texto, HTML, cópias, headers e anexos. O remetente precisa pertencer a um domínio verificado na conta.
/api/v1/emailsconst response = await fetch("https://usecorreo.com/api/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CORREO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "Sua Loja <oi@sualoja.com.br>",
to: ["cliente@exemplo.com.br"],
subject: "Pedido confirmado",
html: "<p>Seu pedido saiu para entrega.</p>",
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(`${data.error.code}: ${data.error.message}`);
}
console.log(data.id); // eml_...Quando algum destinatário já está suprimido, a resposta também pode incluir suppressed_recipients. Se todos estiverem suprimidos, o email é registrado com status suppressed e não entra na fila.
04 · Retentativas seguras
Idempotência
Use uma chave estável por operação para impedir que o mesmo retry registre dois emails.
/api/v1/emails · Idempotency-Keycurl -X POST https://usecorreo.com/api/v1/emails \
-H "Authorization: Bearer uc_live_sua_chave" \
-H "Idempotency-Key: pedido-8123-confirmacao" \
-H "Content-Type: application/json" \
-d '{
"from": "Sua Loja <oi@sualoja.com.br>",
"to": ["cliente@exemplo.com.br"],
"subject": "Pedido confirmado",
"text": "Seu pedido saiu para entrega."
}'05 · Observabilidade
Consultar um envio
Busque o estado atual e a sequência de eventos armazenados para um email da sua conta.
/api/v1/emails/{id}curl https://usecorreo.com/api/v1/emails/eml_seu_id \
-H "Authorization: Bearer uc_live_sua_chave"type EmailResponse = {
object: "email";
id: string;
from: string;
to: string[];
cc: string[];
bcc: string[];
reply_to: string[];
subject: string;
status:
| "queued"
| "sending"
| "sent"
| "delivered"
| "bounced"
| "complained"
| "failed"
| "suppressed";
created_at: string;
sent_at: string | null;
last_error: string | null;
events: Array<{ type: string; created_at: string; payload: unknown }>;
};O endpoint aplica ownership pela API key: um id de outra conta responde como recurso não encontrado. Eventos só aparecem quando são registrados pelo ciclo de envio.
06 · Identidade de envio
Domínios
Cadastre o domínio, publique os registros retornados e solicite uma nova verificação depois da propagação DNS.
/api/v1/domains/api/v1/domains/{id}/verify/api/v1/domains/{id}/api/v1/domains/{id}curl -X POST https://usecorreo.com/api/v1/domains \
-H "Authorization: Bearer uc_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{ "name": "sualoja.com.br" }'A criação retorna o domínio e os registros DNS em records. Depois de publicá-los, use o id retornado para verificar:
curl -X POST https://usecorreo.com/api/v1/domains/dom_seu_id/verify \
-H "Authorization: Bearer uc_live_sua_chave"07 · Conteúdo
Anexos em base64
Envie até 20 anexos. A soma estimada do conteúdo decodificado não pode ultrapassar 10 MB.
{
"from": "Sua Loja <oi@sualoja.com.br>",
"to": ["cliente@exemplo.com.br"],
"subject": "Seu comprovante",
"text": "O comprovante está anexado.",
"attachments": [
{
"filename": "comprovante.pdf",
"content": "JVBERi0xLjQK...",
"content_type": "application/pdf"
}
]
}Cada item exige filename e content. O campo content_type é opcional.
08 · Eventos
Webhooks assinados
Cadastre uma URL no painel e selecione os eventos que sua aplicação precisa receber.
Eventos disponíveis: email.sent, email.delivered, email.bounced, email.complained, email.opened, email.clicked, email.failed e email.suppressed.
O header correo-signature usa o formato t=timestamp,v1=assinatura. A assinatura é HMAC SHA-256 de timestamp.rawBody.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyCorreoWebhook(
signatureHeader: string,
rawBody: string,
secret: string,
) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => part.split("=", 2)),
);
const timestamp = parts.t;
const signature = parts.v1;
if (!timestamp || !signature) return false;
const timestampNumber = Number(timestamp);
if (!Number.isFinite(timestampNumber)) return false;
if (Math.abs(Date.now() / 1000 - timestampNumber) > 300) return false;
const expectedHex = createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const expected = Buffer.from(expectedHex, "hex");
const received = Buffer.from(signature, "hex");
if (expected.length !== received.length) return false;
return timingSafeEqual(expected, received);
}A existência de um tipo na lista não garante que todo envio produzirá esse evento; o webhook é criado quando o evento correspondente é registrado.
09 · Proteção de reputação
Supressões e descadastro
Consulte, adicione ou remova endereços da lista da sua conta pela API.
/api/v1/suppressions/api/v1/suppressions/api/v1/suppressions/{id}# Listar — use ?email= para filtrar um endereço
curl "https://usecorreo.com/api/v1/suppressions?email=cliente@exemplo.com.br" \
-H "Authorization: Bearer uc_live_sua_chave"
# Adicionar manualmente
curl -X POST https://usecorreo.com/api/v1/suppressions \
-H "Authorization: Bearer uc_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{ "email": "cliente@exemplo.com.br" }'
# Remover pelo id
curl -X DELETE https://usecorreo.com/api/v1/suppressions/sup_seu_id \
-H "Authorization: Bearer uc_live_sua_chave"A listagem retorna até 100 registros e aceita o filtro ?email=. Bounces permanentes, reclamações e descadastros processados pelo Correo também podem adicionar endereços à lista.
List-Unsubscribe
Contas novas habilitam os cabeçalhos de descadastro por padrão. Para desativar o comportamento em uma mensagem específica, envie unsubscribe: false:
{
"from": "Sua Loja <oi@sualoja.com.br>",
"to": ["cliente@exemplo.com.br"],
"subject": "Seu código de acesso",
"text": "Código: 123456",
"unsubscribe": false
}unsubscribe: true força a injeção. Se você já enviar um header List-Unsubscribe, o Correo preserva o valor informado.
10 · Referência
Erros estáveis em JSON
Respostas de erro usam sempre o objeto error com code e message.
{
"error": {
"code": "validation_error",
"message": "to: Invalid email address"
}
}11 · Compatibilidade
Migrar por HTTP
A API aceita os campos principais do payload usado pelo Resend. Faça a migração no nível HTTP: endpoint, chave e tratamento de resposta.
type EmailPayload = {
from: string;
to: string | string[];
subject: string;
html?: string;
text?: string;
};
export async function sendWithCorreo(payload: EmailPayload) {
const response = await fetch("https://usecorreo.com/api/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CORREO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const data = await response.json();
if (!response.ok) {
throw new Error(`${data.error.code}: ${data.error.message}`);
}
return data as { id: string; suppressed_recipients?: string[] };
}Revise também o retorno de erros e adicione Idempotency-Key nos fluxos que podem repetir a mesma operação.