curl Quickstart
No SDK. No language runtime. Just curl and a Bearer token. Useful for shell scripts, cron jobs, CI tasks, language-agnostic integrations.
1. Get an API key
Generate one at /api-keys. Two types: ms_test_* (sandbox) and ms_live_* (production). Store in your shell:
terminal
export MAILSTORM_API_KEY="ms_test_..."2. Send a single email
terminal
curl -X POST https://mailstorm.dev/v1/emails \ -H "Authorization: Bearer $MAILSTORM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "onboarding@yourdomain.com", "to": "user@example.com", "subject": "Hello", "html": "<p>It works.</p>" }'
Response (success):
response
{"id":"39b79255069f04bcb5f9c94643c74e8f","status":"queued"}3. Get an email by id
terminal
curl https://mailstorm.dev/v1/emails/$EMAIL_ID \ -H "Authorization: Bearer $MAILSTORM_API_KEY"
Response includes both Resend-shape fields (object, last_event, to as array) and Mailstorm-native fields (status, ses_id, error, sent_at). Pick whichever shape your code expects.
4. Send a batch (Resend wire shape)
terminal
curl -X POST https://mailstorm.dev/v1/emails/batch \ -H "Authorization: Bearer $MAILSTORM_API_KEY" \ -H "Content-Type: application/json" \ -d '[ {"from":"a@yourdomain.com","to":["x@b.c"],"subject":"1","html":"<p>1</p>"}, {"from":"a@yourdomain.com","to":["y@b.c"],"subject":"2","html":"<p>2</p>"} ]'
5. Send a batch (Mailstorm-native wire shape)
terminal
curl -X POST https://mailstorm.dev/v1/batch \ -H "Authorization: Bearer $MAILSTORM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "emails": [ {"from":"a@yourdomain.com","to":"x@b.c","subject":"1","html":"<p>1</p>"}, {"from":"a@yourdomain.com","to":"y@b.c","subject":"2","html":"<p>2</p>"} ] }'
6. Idempotency
terminal
curl -X POST https://mailstorm.dev/v1/emails \ -H "Authorization: Bearer $MAILSTORM_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-12345" \ -d '{...}'
Same key + same body within 24h returns the original response without re-sending.
7. Common errors
| Code | Body | Meaning |
|---|---|---|
| 401 | {"error":"invalid api key"} | Key doesn't exist or was deleted |
| 401 | {"error":"missing api key"} | No Authorization header |
| 401 | {"error":"account disabled — contact support"} | Account paused (abuse trigger or manual) |
| 403 | {"error":"domain X is not registered..."} | FROM domain not verified for this account (live mode) |
| 429 | {"error":"daily send cap reached..."} | Tier or probation cap exceeded |
What's next
- Webhooks — POST handler patterns for delivered/bounced/inbound events
- API Reference — every endpoint with try-it-out