SendGrid API: how to send email with the v3 Mail Send API
A working tutorial to send email with the SendGrid API - curl and Node.js code, API key setup, sender verification, and fixes for the common 401 and 403 errors.
Dvir Atias
Founder
To send email with the SendGrid API, POST a JSON payload to https://api.sendgrid.com/v3/mail/send with an Authorization: Bearer header carrying your API key. The body needs a personalizations array (the recipients), a verified from address, a subject, and a content array. A successful call returns 202 Accepted with an empty body. This guide walks the full path - creating the key, verifying a sender, sending with curl and Node.js, and fixing the 401 and 403 errors almost everyone hits first.
How to send email with the SendGrid API
The SendGrid v3 Mail Send API is a single endpoint. Here is the minimal request that sends one plain-text email:
curl -X POST https://api.sendgrid.com/v3/mail/send \
-H "Authorization: Bearer $SENDGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"personalizations": [{ "to": [{ "email": "recipient@example.com" }] }],
"from": { "email": "you@yourdomain.com" },
"subject": "Hello from the SendGrid API",
"content": [{ "type": "text/plain", "value": "Sent with the SendGrid v3 Mail Send API." }]
}'The personalizations array is the part people trip over. Each object is one "envelope" - it holds the to, cc, and bcc recipients plus any per-recipient substitutions. To email three people separately, use three objects in the array. To email a group together, put three addresses in one object.
Below are the steps to get from a fresh account to a 202.
1. Create an API key
In the SendGrid dashboard, open Settings -> API Keys -> Create API Key. Give it Restricted Access and grant the Mail Send permission at minimum. Copy the key immediately - SendGrid shows it once. Store it in an environment variable rather than hardcoding it:
export SENDGRID_API_KEY="SG.xxxxxxxx..."A key with no Mail Send scope is the most common reason a syntactically perfect request still returns 403.
2. Verify a sender identity
SendGrid will not send from an address you have not proven you control. You have two options:
- Single Sender Verification - verify one specific
fromaddress by clicking a confirmation link. Fine for testing and low volume. - Domain Authentication - add CNAME records so you can send from any address at your domain, with SPF and DKIM configured for you. This is what you want for production deliverability.
The from.email in every request must match a verified sender or an authenticated domain, or the send fails.
3. Send with Node.js
For anything beyond a shell one-liner, use the official library. Install it:
npm install @sendgrid/mailThen send:
import sgMail from "@sendgrid/mail";
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
await sgMail.send({
to: "recipient@example.com",
from: "you@yourdomain.com", // a verified sender or authenticated domain
subject: "Hello from the SendGrid API",
text: "Sent with @sendgrid/mail.",
html: "<p>Sent with <strong>@sendgrid/mail</strong>.</p>",
});The @sendgrid/mail helper flattens the personalizations structure into plain to, from, subject, text, and html fields and builds the v3 payload for you. Under the hood it hits the same /v3/mail/send endpoint.
What do the SendGrid 401 and 403 errors mean?
Two status codes account for most failed first sends:
- 401 Unauthorized - the API key is missing, malformed, or wrong. Check that the
Authorizationheader readsBearer SG.xxxxand that the variable actually resolved. An empty$SENDGRID_API_KEYproduces this. - 403 Forbidden - the key is valid but not allowed to do this. Two usual causes: the key lacks the Mail Send scope, or the
fromaddress is not a verified sender. As of July 2026, SendGrid's own docs point at sender verification first for403on Mail Send.
A 400 Bad Request almost always means a malformed body - a missing content array, an empty personalizations, or a from that is a bare string instead of a { "email": "..." } object. A 202 with an empty body is success; SendGrid has accepted the message for delivery, which is not the same as it landing in the inbox.
Where SendGrid fits, and where it gets awkward for agents
SendGrid is a genuinely good product for what it was built for: high-volume transactional and marketing email from an application you control. Password resets, receipts, newsletters, shipping notifications - it sends those reliably at scale, with analytics and deliverability tooling that took years to build.
The friction shows up when the "sender" is an AI agent rather than an app. Agents do not just send - they hold conversations. They need to receive replies, and they often need their own address per customer or per task. SendGrid's model was not designed around those needs:
- Receiving is a separate, heavier setup. SendGrid can receive mail, but only through Inbound Parse, which means pointing an MX record at SendGrid and standing up a webhook to accept the parsed payloads. See our walkthrough of SendGrid Inbound Parse for what that involves.
- There is no per-agent inbox concept. You authenticate a domain, not a mailbox. Handing agent number 900 its own address is your problem to model, route, and clean up.
- Dynamic inbox creation is not a primitive. You cannot ask SendGrid to "create an inbox" at runtime and get back a live, receiving address. That is a thing you build.
None of this makes SendGrid wrong. It is a workload-fit question.
The agent-native alternative
AgenticEmail treats an inbox as a resource you create with an API call. Sending and receiving both live behind that one address. Create an inbox per agent at runtime:
curl -X POST https://api.agenticemail.dev/v1/inboxes \
-H "Authorization: Bearer $AGENTICEMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "username": "support-agent" }'That returns a live address like support-agent@inbox.agenticemail.dev. Send from it:
curl -X POST https://api.agenticemail.dev/v1/inboxes/{inbox_id}/messages/send \
-H "Authorization: Bearer $AGENTICEMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "to": "customer@example.com", "subject": "Order update", "text": "Your order shipped." }'Receiving is built in, with no MX changes on your side. Poll GET /v1/inboxes/{id}/messages for parsed JSON, register a webhook with POST /v1/webhooks, or subscribe to wss://api.agenticemail.dev/v1/events for a real-time stream. The full surface is in the docs.
SendGrid vs agent-native inboxes
| Capability | SendGrid | AgenticEmail |
|---|---|---|
| Sending | Strong, high volume | One POST per message |
| Receiving | Inbound Parse + MX record change | Built in - webhook, WebSocket, or poll |
| Per-agent identity | Model it yourself on one domain | One inbox per agent, created via API |
| Dynamic inbox creation | Not a concept | POST /v1/inboxes at runtime |
| Setup | API key + domain auth | API key, one call |
If you are choosing a provider for an agent that mostly blasts transactional mail, SendGrid is a fine pick, and our best email API comparison covers the field. If the agent needs to converse - send, receive, and own an address per customer - agent-native inboxes remove a layer of plumbing.
Frequently asked questions
Is the SendGrid API free?
Not permanently. As of July 2026, SendGrid no longer offers a standing free tier - new accounts get a 60-day trial capped at 100 emails per day, after which you move to a paid plan starting around $20 per month. AgenticEmail keeps a free tier for building and testing; see pricing.
Why is my SendGrid email not sending?
Work down the status code. A 401 means the API key is missing or wrong. A 403 means the key lacks the Mail Send scope or the from address is not verified. A 400 means the JSON body is malformed. And a 202 is success at the API layer - if the message never arrives despite a 202, the issue is deliverability (SPF, DKIM, spam filtering), not the API call.
Does SendGrid receive email?
Yes, through Inbound Parse. You point an MX record for a subdomain at SendGrid and it POSTs incoming mail to a webhook you host. It works, but it is a deliberate setup step rather than something on by default, which is a real difference from providers where receiving ships with the inbox.
What is the difference between the SendGrid API and SMTP?
Both send mail. The Web API (/v3/mail/send) is a single HTTPS request and is easier to debug, retry, and scope with a restricted key. SMTP relays a message over a mail protocol and suits legacy systems that already speak it. For new code, the API is usually the cleaner path.
Can SendGrid give each AI agent its own inbox?
Not natively. SendGrid authenticates domains, not individual mailboxes, so per-agent addresses are something you design and route yourself. If dynamic, per-agent inboxes are central to your product, an agent-native inbox API models that directly.