Install guide

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.

  1. 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.

  2. Set the key in the service environment

    Like any secret: in environment variables, never in the repository.

    PLUMAIL_API_KEY=plm_live_…
  3. 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}`);
  4. 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();
  5. 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"
  6. 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 emailThe 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_keyThe key is incomplete (a newline crept in during copy-paste) or has been replaced. Create a new one.
401 revoked_api_keyThe key was cut under Settings → API. Create another one.
403 insufficient_scopeThe 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_domainThe 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_exceededMore than 600 requests per minute for this key. The Retry-After header says how many seconds to wait.