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:
202with an empty body and anX-Message-Idheader — the exact semantic@sendgrid/mailexpects.
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.
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>",
});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>",
});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)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
| Field | Support | Notes |
|---|---|---|
personalizations[] | Full | Each 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[] | Full | The text/html entry is preferred as the body, else text/plain. |
from / reply_to | Full | { email, name } objects. |
subject | Full | Top-level or per-personalization. |
attachments | Full | type maps to content type, content_id to the inline content ID. |
categories | Partial | Mapped to Sendly tags (sanitized). |
headers | Full |
Not supported (returns a clean SendGrid error)
| Field | Result |
|---|---|
template_id (d-... dynamic templates) | 400 — SendGrid template IDs can't be mapped to Sendly templates. |
send_at | 400 — 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.