Connect Plumail to your application
The REST API replaces a transactional sending service. The fields and response match Resend: a migration is just the base URL and the key.
Create an API key
In your Plumail workspace: Settings → API → New key. Name it, tick the permissions, save. The key is shown ONCE — copy it immediately. It starts with plm_live_ followed by thirty-two characters.
Set the key in the service environment
Like any secret: in environment variables, never in the repository.
PLUMAIL_API_KEY=plm_live_…If you're coming from Resend: copy the minimal client
One file, no dependency, the same signature as the SDK you're leaving. The rest of your sending code is unchanged: it still reads { data, error } and data.id.
curl -O https://plumail.fr/plumail-client.ts // then, in your code: - const resend = new Resend(process.env.RESEND_API_KEY); + const plumail = new Plumail(process.env.PLUMAIL_API_KEY); const { data, error } = await plumail.emails.send({ from, to, subject, html }); if (error) throw new Error(`Email delivery failed: ${error.message}`);Or send without copying anything
No library is needed: a plain HTTP call is enough. The response is a 202 with the message ID — the email is accepted, not yet delivered.
const res = await fetch("https://plumail.fr/api/v1/emails", { method: "POST", headers: { Authorization: `Bearer ${process.env.PLUMAIL_API_KEY}`, "Content-Type": "application/json", // A unique value per send: a retry will not create a duplicate. "Idempotency-Key": invoiceId, }, body: JSON.stringify({ from: "Your brand <[email protected]>", to: customer.email, subject: "Your invoice", html: "<p>Here it is.</p>", }), }); if (!res.ok) { // Always the same shape: { error: { code, message, details } }. // Write your code against "code", never against "message". const { error } = await res.json(); throw new Error(`${error.code} — ${error.message}`); } const { id } = await res.json();Check whether the message was delivered
The status evolves after sending: sent, then delivered, bounced or complained. A bouncing address is automatically added to the suppression list — your application does not need to handle it.
curl https://plumail.fr/api/v1/emails/ID \ -H "Authorization: Bearer $PLUMAIL_API_KEY"Check that it's connected
Ask for the account status. This is read-only: nothing is modified, nothing is sent. The response names your workspace and lists your verified sending domains — those are the addresses you can send from.
curl -s https://plumail.fr/api/v1/me \ -H "Authorization: Bearer $PLUMAIL_API_KEY"
Troubleshooting
Every API error carries a readable code. Here are the ones you encounter when connecting.
| 422 suppressed_recipient on a transactional email | The recipient unsubscribed from your campaigns, or their address has bounced. Plumail applies the suppression list to individual sends as well. If you want to separate transactional from newsletters, use two workspaces. |
|---|---|
| 401 invalid_api_key | The key is incomplete (a newline crept in during copy-paste) or has been replaced. Create a new one. |
| 401 revoked_api_key | The key was cut under Settings → API. Create another one. |
| 403 insufficient_scope | The key does not have the required permission. Permissions are chosen at creation and cannot be changed: create a key with the right scope. |
| 422 unverified_from_domain | The sending address is not on a verified domain in the workspace. The error message lists the ones that are. To add one: Domains → Add a domain, then set the DNS records. |
| 429 rate_limit_exceeded | More than 600 requests per minute for this key. The Retry-After header says how many seconds to wait. |