SendlySendly
Migrate from

Migrate from SendGrid

Point the SendGrid SDK (JavaScript or Python) at Sendly by changing the base URL and API key

Sendly implements SendGrid's POST /v3/mail/send. Keep the official @sendgrid/mail (JavaScript) or sendgrid (Python) SDK — change the base URL / host and the API key.

  • Base URL / host: https://api.sendly.now/api/compat/sendgrid
  • Auth: Authorization: Bearer <sk_...> (the SDK sets this for you)
  • Success response: 202 with an empty body and an X-Message-Id header — the exact semantic @sendgrid/mail expects.

The two-variable swap

The transport's base URL is set on @sendgrid/client, which @sendgrid/mail then uses. setApiKey resets the base URL, so set the base URL after it.

Before (SendGrid)
import sgMail from "@sendgrid/mail";

sgMail.setApiKey("SG.your_sendgrid_key");

await sgMail.send({
  to: "customer@example.com",
  from: "you@yourdomain.com",
  subject: "Hello",
  html: "<p>Welcome aboard.</p>",
});
After (Sendly)
import sgMail from "@sendgrid/mail";
import sgClient from "@sendgrid/client";

sgClient.setApiKey("sk_your_sendly_key");
sgClient.setDefaultRequest("baseUrl", "https://api.sendly.now/api/compat/sendgrid");
sgMail.setClient(sgClient);

await sgMail.send({
  to: "customer@example.com",
  from: "you@yourdomain.com",
  subject: "Hello",
  html: "<p>Welcome aboard.</p>",
});
Before (SendGrid)
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

message = Mail(
    from_email="you@yourdomain.com",
    to_emails="customer@example.com",
    subject="Hello",
    html_content="<p>Welcome aboard.</p>",
)

SendGridAPIClient("SG.your_sendgrid_key").send(message)
After (Sendly)
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

message = Mail(
    from_email="you@yourdomain.com",
    to_emails="customer@example.com",
    subject="Hello",
    html_content="<p>Welcome aboard.</p>",
)

client = SendGridAPIClient(
    api_key="sk_your_sendly_key",
    host="https://api.sendly.now/api/compat/sendgrid",
)
client.send(message)

API reference

The one operation this dialect covers, with its full request and response schema:

Supported fields

FieldSupportNotes
personalizations[]FullEach personalization fans out into its own send (its to/cc/bcc + subject). Up to 100 per request; more is refused with 400 and nothing is sent.
content[]FullThe text/html entry is preferred as the body, else text/plain.
from / reply_toFull{ email, name } objects.
subjectFullTop-level or per-personalization.
attachmentsFulltype maps to content type, content_id to the inline content ID.
categoriesPartialMapped to Sendly tags (sanitized).
headersFull

Not supported (returns a clean SendGrid error)

FieldResult
template_id (d-... dynamic templates)400 — SendGrid template IDs can't be mapped to Sendly templates.
send_at400 — scheduled sends aren't supported in this phase.

Errors come back in SendGrid's { "errors": [{ "message", "field", "help" }] } shape with the matching status, so the SDK's own error handling — a thrown ResponseError in JavaScript, an HTTPError in Python — fires exactly as it does today.

On this page