SendGrid Compatibility
ScaiSend's /v3/ surface is a SendGrid v3 API clone. If you have existing code written against api.sendgrid.com/v3/mail/send, you can point it at https://scaisend.scailabs.ai/v3/mail/send and most of it will work without changes. This page covers what's compatible, what's not, and how to migrate.
What's compatible#
The core sending surface and the features that matter for most integrations:
| SendGrid feature | ScaiSend support | Notes |
|---|---|---|
POST /v3/mail/send (full schema) |
Yes | Matches request body exactly |
| Personalizations (up to 1000) | Yes | Same structure, same limits |
Dynamic templates (template_id: d-...) |
Yes | Handlebars syntax |
| Attachments (base64, up to 10) | Yes | Same limits |
Inline attachments (disposition: inline, content_id) |
Yes | Same CID model |
send_at (scheduled delivery) |
Yes | Requires mail.schedule scope |
batch_id / batch status |
Yes | Includes POST /v3/mail/batch, GET /v3/mail/batch/{id} |
asm (Advanced Suppression Manager) |
Yes | Groups and per-group opt-outs |
mail_settings.sandbox_mode |
Yes | Plus test-key-level sandbox enforcement |
mail_settings.bypass_* |
Yes | All four flags |
tracking_settings.click_tracking |
Yes | Including enable_text |
tracking_settings.open_tracking |
Yes | Including substitution_tag |
tracking_settings.subscription_tracking |
Yes | Including substitution_tag and custom HTML/text |
tracking_settings.ganalytics |
Yes | UTM params |
categories |
Yes | Max 10 per message |
custom_args |
Yes | Preserved on message and events |
headers |
Yes | Custom headers passed through |
Templates — /v3/templates + versions |
Yes | Legacy and dynamic generations |
Messages — /v3/messages |
Yes | Listing and detail |
API keys — /v3/api_keys |
Yes | Scopes match |
Stats — /v3/stats, /v3/stats/categories |
Yes | Daily aggregates |
Bounces — /v3/suppression/bounces |
Yes | List, get, delete, bulk import |
Spam reports — /v3/suppression/spam_reports |
Yes | List, get, delete |
Global unsubscribes — /v3/asm/suppressions/global |
Yes | List, check, add, delete |
Suppression groups — /v3/asm/groups |
Yes | CRUD + group suppressions |
Webhook endpoints — /v3/user/webhooks |
Yes | Multiple URLs |
Event Webhook settings — /v3/user/webhooks/event/settings |
Yes | SendGrid's single-URL model |
| Signed webhook requests | Yes | HMAC-SHA256; see note below on headers |
What's different#
A few places where ScaiSend diverges from SendGrid. Most are additive (ScaiSend has more) or under-the-hood (different but invisible).
API key prefixes#
SendGrid keys start with SG.. ScaiSend keys start with sg_live_ or sg_test_:
1 2 3 | |
Test keys in ScaiSend force sandbox mode — you can't accidentally send real mail with a test key. SendGrid doesn't have this distinction.
Webhook signature headers#
SendGrid signs webhooks with X-Twilio-Email-Event-Webhook-Signature and X-Twilio-Email-Event-Webhook-Timestamp, using ECDSA with a base64 PEM public key.
ScaiSend signs with X-ScaiSend-Signature and X-ScaiSend-Timestamp, using HMAC-SHA256 with a per-endpoint signing secret. Simpler, symmetric, and the secret rotates with a single API call.
The event payload format is the same (same event names, same structure); only the transport-level signing is different.
Sender domain verification#
SendGrid has "Domain Authentication" — create a domain in the UI, it gives you CNAMEs. ScaiSend uses TXT records directly for DKIM, SPF, and DMARC. Simpler DNS (no CNAME dance; fewer records), but you can't do the CNAME-indirect-proxy trick that SendGrid documents as its default.
See Sender Domains for the ScaiSend flow.
Multi-tenant model#
SendGrid has "Subusers" — sub-accounts nested under a main account with their own credentials. ScaiSend has partner/tenant/user, managed via ScaiKey. If you were using Subusers to serve multiple customers, map each customer to a ScaiSend tenant and don't use Subusers.
What's not implemented#
Intentionally omitted from ScaiSend:
- Marketing Campaigns (
/v3/marketing/campaigns). ScaiSend is transactional-first. If you need campaign tooling, run a separate marketing product that calls/v3/mail/sendper recipient. - Contacts / lists (
/v3/marketing/contacts). No first-class contact management; ScaiSend tracks suppressions but not an address book. - Email validation API (
/v3/validations/email). No upfront email-address validator; use a third-party tool before sending. - IP access management UI (admin IP allowlists). Not implemented.
- Inbound Parse Webhook. ScaiSend processes DSN bounces and FBL reports on the inbound side; it does not accept arbitrary inbound mail for parsing into webhooks. Use a separate inbound-mail tool if that's a requirement.
If a SendGrid feature you depend on isn't listed above, check the API Reference — the list of implemented endpoints is authoritative.
Migration steps#
1. Provision ScaiSend#
- Create your partner and tenant in ScaiKey (or run
scaisend sync --fullif you're bringing an existing ScaiKey). - Add your sender domain (
POST /api/admin/domains) and publish the DKIM/SPF/DMARC records. - Verify the domain (
POST /api/admin/domains/{id}/verify).
2. Create an equivalent API key#
1 2 3 4 | |
Store the returned key.
3. Import your suppression lists#
SendGrid export → ScaiSend import.
1 2 3 4 5 6 7 8 9 10 11 | |
For unsubscribes, call POST /v3/asm/suppressions/global (or POST /v3/asm/groups/{id}/suppressions if you're moving group-scoped unsubscribes).
4. Mirror your templates#
SendGrid dynamic templates and ScaiSend dynamic templates use the same Handlebars syntax. Create the template, create a version, copy the HTML/subject/preheader verbatim. The IDs will be different (ScaiSend generates its own d-* IDs), so update wherever your code references them.
1 2 3 4 5 6 7 8 9 10 11 | |
5. Re-subscribe webhooks#
Register a new webhook endpoint on ScaiSend, save the signing secret, update your webhook handler:
1 2 3 4 5 6 7 | |
Your webhook handler needs to switch from SendGrid's ECDSA signature verification to ScaiSend's HMAC-SHA256. See Webhooks for the recipe.
6. Flip the base URL#
The change is usually a single constant:
1 2 | |
Deploy. Watch your message stream for the first few sends. Once you're confident, retire the SendGrid integration.
Sending from the SendGrid SDK#
The official SendGrid SDKs let you set a custom base URL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
The Python SDK is cleaner on this. For TypeScript, a plain fetch often ends up simpler than configuring the official SDK to point at ScaiSend.
What doesn't work#
If you see these, don't try to work around them — they're intentional differences:
- SendGrid's SDK "sandbox validation" tools. The SDK has local validators; ScaiSend has its own validator on the server side. It catches different things; trust the 400 response over the SDK's local check.
- Subuser impersonation. Don't try; use tenants.
- Marketing campaign endpoints. They don't exist.
- API key with
SG.prefix. Won't validate. Get a ScaiSend key.
What's next#
- Sending Mail — the full
/v3/mail/sendreference. - Authentication — creating and using keys.
- Suppressions — CSV bulk import endpoints for migration.