REST API¶
Use the MailJawn REST API to manage subscribers, track events, and work with assets programmatically. Every feature available in the dashboard is accessible through the API.
Base URL¶
All API endpoints are relative to:
Replace {project_id} with your app's UUID from the MailJawn dashboard.
Authentication¶
Authenticate every request with a Bearer token in the Authorization header:
API keys start with mj_ and are created in Dashboard > API Keys. See Account > API Keys for details.
Warning
API keys are shown only once at creation. Store them securely — MailJawn cannot recover a lost key.
Scopes¶
Each API key has a set of scopes that control what it can access. Choose the narrowest scope bundle that fits your use case.
| Scope | Permits |
|---|---|
subscribers:read |
List and view subscribers |
subscribers:write |
Create, update, delete, identify, and bulk import subscribers |
events:write |
Track subscriber events |
assets:read |
List and view assets |
assets:write |
Upload, update, and delete assets |
campaigns:read |
Read campaign data |
campaigns:write |
Create and manage campaigns |
templates:read |
Read email templates |
templates:write |
Create and manage templates |
sequences:read |
Read automation sequences |
sequences:write |
Manage automation sequences |
stats:read |
Read analytics and statistics |
email:send |
Send emails |
Scope Bundles¶
| Bundle | Includes | Best For |
|---|---|---|
| SDK | subscribers:write, events:write |
Mobile app integration |
| MCP | All read scopes + subscribers:write, events:write, campaigns:write, assets:write, email:send |
AI agent integration |
| Full Access | All scopes | Admin scripts |
Rate Limits¶
Requests are rate-limited per API key:
| Operation | Limit | Window |
|---|---|---|
| Read (GET, HEAD, OPTIONS) | 100 requests | 60 seconds |
| Write (POST, PATCH, DELETE) | 30 requests | 60 seconds |
| MCP | 60 requests | 60 seconds |
| Email send | 10 requests | 60 seconds |
Rate Limit Headers¶
Every response includes rate limit information:
| Header | Description |
|---|---|
RateLimit-Limit |
Maximum requests allowed in the current window |
RateLimit-Remaining |
Requests remaining in the current window |
RateLimit-Reset |
Unix timestamp when the window resets |
Retry-After |
Seconds to wait (only on 429 responses) |
Rate Limit Exceeded¶
When you hit the limit, you'll receive:
HTTP/1.1 429 Too Many Requests
Retry-After: 45
RateLimit-Limit: 30
RateLimit-Remaining: 0
RateLimit-Reset: 1740200000
Error Responses¶
All errors return JSON with a consistent format:
| Status Code | Meaning |
|---|---|
400 |
Bad request — invalid parameters or validation error |
401 |
Unauthorized — missing or invalid API key |
403 |
Forbidden — valid key, but missing required scope |
404 |
Not found — project, subscriber, or resource doesn't exist |
429 |
Too many requests — rate limit exceeded |
500 |
Server error — something went wrong on our end |
Endpoints¶
Identify Subscriber¶
Upsert a subscriber by email. Creates the subscriber if they don't exist, updates them if they do. This is the primary endpoint used by the Swift SDK.
Scope: subscribers:write
Request:
curl -X POST "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribers/identify/" \
-H "Authorization: Bearer mj_your_key" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"name": "Jane Doe",
"timezone": "America/New_York",
"custom_fields": {
"plan": "pro",
"signup_date": "2026-02-21"
},
"tags": ["vip", "early-adopter"],
"source": "api",
"trigger_automations": true
}'
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Subscriber's email address |
name |
string | No | Display name |
timezone |
string | No | IANA timezone (e.g., "America/New_York") |
custom_fields |
object | No | Arbitrary key-value pairs (merged with existing) |
tags |
array | No | Tag slugs to apply |
source |
string | No | How the subscriber was added (e.g., "api", "sdk") |
trigger_automations |
boolean | No | Whether to trigger welcome automations (default: true) |
device_type |
string | No | Device type (auto-captured by SDK) |
os_version |
string | No | OS version (auto-captured by SDK) |
app_version |
string | No | App version (auto-captured by SDK) |
locale |
string | No | Locale code (auto-captured by SDK) |
sdk_version |
string | No | SDK version (auto-captured by SDK) |
Response (200):
| Field | Type | Description |
|---|---|---|
subscriber_id |
UUID | The subscriber's unique identifier |
created |
boolean | true if new subscriber, false if updated |
List Subscribers¶
Scope: subscribers:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
status |
string | — | Filter by status: active, unsubscribed, bounced, complained |
tag |
string | — | Filter by tag slug |
search |
string | — | Search email and name |
limit |
integer | 50 | Results per page (1–100) |
offset |
integer | 0 | Pagination offset |
Request:
curl "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribers/?status=active&limit=20" \
-H "Authorization: Bearer mj_your_key"
Response (200):
{
"subscribers": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "Jane Doe",
"status": "active",
"timezone": "America/New_York",
"effective_timezone": "America/New_York",
"first_seen": "2026-02-01T10:00:00Z",
"last_seen": "2026-02-21T14:30:00Z",
"subscribed_at": "2026-02-01T10:00:00Z",
"device_type": "iPhone",
"os_version": "iOS 17.2",
"app_version": "2.3.1",
"locale": "en_US",
"sdk_version": "1.0.0",
"custom_fields": {"plan": "pro"},
"source": "api",
"tags": [
{"id": "uuid", "name": "VIP", "slug": "vip"}
],
"created": "2026-02-01T10:00:00Z",
"modified": "2026-02-21T14:30:00Z"
}
],
"total": 150,
"limit": 20,
"offset": 0
}
Bulk Import Subscribers¶
Import up to 1,000 subscribers in a single request.
Scope: subscribers:write
Request:
curl -X POST "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribers/" \
-H "Authorization: Bearer mj_your_key" \
-H "Content-Type: application/json" \
-d '{
"subscribers": [
{
"email": "user1@example.com",
"name": "User One",
"tags": ["imported"],
"trigger_automations": false
},
{
"email": "user2@example.com",
"name": "User Two"
}
]
}'
| Field | Type | Required | Description |
|---|---|---|---|
subscribers |
array | Yes | Array of subscriber objects (1–1,000) |
Each subscriber object supports the same fields as the identify endpoint.
Response (200):
Get Subscriber¶
Scope: subscribers:read
Request:
curl "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribers/{subscriber_id}/" \
-H "Authorization: Bearer mj_your_key"
Response (200): Same subscriber object as the list endpoint.
Update Subscriber¶
Scope: subscribers:write
Request:
curl -X PATCH "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribers/{subscriber_id}/" \
-H "Authorization: Bearer mj_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Name",
"timezone": "Europe/London",
"custom_fields": {"plan": "enterprise"},
"status": "unsubscribed"
}'
All fields are optional. Only included fields are updated.
| Field | Type | Description |
|---|---|---|
name |
string | Display name |
timezone |
string | IANA timezone |
custom_fields |
object | Merged with existing custom fields |
status |
string | active, unsubscribed, bounced, complained |
Response (200): Updated subscriber object.
Delete Subscriber¶
Scope: subscribers:write
Request:
curl -X DELETE "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribers/{subscriber_id}/" \
-H "Authorization: Bearer mj_your_key"
Response: 204 No Content
Subscribe (Public)¶
Public endpoint for signup forms. No API key required.
Scope: None
Rate limit: 10 requests per minute per IP address
See Subscribers > Subscribe Forms for full details, HTML form examples, and bot protection.
Request:
curl -X POST "https://api.mailjawn.com/api/v1/projects/{project_id}/subscribe/" \
-H "Content-Type: application/json" \
-d '{
"email": "visitor@example.com",
"name": "Site Visitor",
"tag": "newsletter"
}'
Response (200):
Track Event¶
Record a subscriber event for automation triggers and analytics.
Scope: events:write
Request:
curl -X POST "https://api.mailjawn.com/api/v1/projects/{project_id}/events/track/" \
-H "Authorization: Bearer mj_your_key" \
-H "Content-Type: application/json" \
-d '{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"event_name": "app_opened",
"properties": {
"screen": "home",
"duration_seconds": 120
},
"occurred_at": "2026-02-21T14:30:00Z",
"client_event_id": "550e8400-e29b-41d4-a716-446655440001"
}'
| Field | Type | Required | Description |
|---|---|---|---|
subscriber_id |
UUID | Yes | The subscriber who triggered the event |
event_name |
string | Yes | Event name (max 100 characters) |
properties |
object | No | Custom event data |
occurred_at |
ISO 8601 | No | When the event happened (defaults to server time) |
client_event_id |
UUID | No | Client-generated ID for idempotency/deduplication |
Response (201 Created — new event):
{
"event_id": "550e8400-e29b-41d4-a716-446655440002",
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"event_name": "app_opened",
"created": true,
"properties": {"screen": "home", "duration_seconds": 120},
"occurred_at": "2026-02-21T14:30:00Z"
}
Response (200 OK — duplicate): Same body with "created": false.
Tip
The app_opened event automatically updates the subscriber's last_seen timestamp. Other custom events do not.
List Assets¶
Scope: assets:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
search |
string | — | Search by filename |
limit |
integer | 50 | Results per page (1–100) |
offset |
integer | 0 | Pagination offset |
Request:
curl "https://api.mailjawn.com/api/v1/projects/{project_id}/assets/?limit=10" \
-H "Authorization: Bearer mj_your_key"
Response (200):
{
"assets": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"filename": "logo.png",
"description": "Company logo",
"url": "https://cdn.example.com/assets/logo.png",
"width": 200,
"height": 100,
"size_kb": 45,
"format": "PNG",
"created": "2026-02-01T10:00:00Z"
}
],
"total": 25,
"limit": 10,
"offset": 0
}
Upload Asset¶
Upload an image file. Accepted formats: JPEG, PNG, GIF, WebP. Maximum file size: 10 MB.
Scope: assets:write
Content-Type: multipart/form-data
Request:
curl -X POST "https://api.mailjawn.com/api/v1/projects/{project_id}/assets/" \
-H "Authorization: Bearer mj_your_key" \
-F "file=@hero.jpg" \
-F "description=Hero image for welcome email"
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | Image file (max 10 MB, JPEG/PNG/GIF/WebP) |
description |
string | No | Asset description (max 1,000 characters) |
Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"filename": "hero.jpg",
"description": "Hero image for welcome email",
"url": "https://cdn.example.com/assets/hero.jpg",
"width": 1920,
"height": 1080,
"size_kb": 350,
"format": "JPEG",
"created": "2026-02-21T14:30:00Z"
}
Get Asset¶
Scope: assets:read
Request:
curl "https://api.mailjawn.com/api/v1/projects/{project_id}/assets/{asset_id}/" \
-H "Authorization: Bearer mj_your_key"
Response (200): Same asset object as the upload response.
Update Asset¶
Update asset metadata.
Scope: assets:write
Request:
curl -X PATCH "https://api.mailjawn.com/api/v1/projects/{project_id}/assets/{asset_id}/" \
-H "Authorization: Bearer mj_your_key" \
-H "Content-Type: application/json" \
-d '{"description": "Updated description"}'
Response (200): Updated asset object.
Delete Asset¶
Scope: assets:write
Request:
curl -X DELETE "https://api.mailjawn.com/api/v1/projects/{project_id}/assets/{asset_id}/" \
-H "Authorization: Bearer mj_your_key"
Response: 204 No Content
Endpoint Summary¶
| Method | Endpoint | Scope | Description |
|---|---|---|---|
| POST | /subscribers/identify/ |
subscribers:write |
Upsert subscriber |
| GET | /subscribers/ |
subscribers:read |
List subscribers |
| POST | /subscribers/ |
subscribers:write |
Bulk import (up to 1,000) |
| GET | /subscribers/{id}/ |
subscribers:read |
Get subscriber |
| PATCH | /subscribers/{id}/ |
subscribers:write |
Update subscriber |
| DELETE | /subscribers/{id}/ |
subscribers:write |
Delete subscriber |
| POST | /subscribe/ |
None | Public form subscribe |
| POST | /events/track/ |
events:write |
Track event |
| GET | /assets/ |
assets:read |
List assets |
| POST | /assets/ |
assets:write |
Upload asset |
| GET | /assets/{id}/ |
assets:read |
Get asset |
| PATCH | /assets/{id}/ |
assets:write |
Update asset |
| DELETE | /assets/{id}/ |
assets:write |
Delete asset |
All endpoints are relative to /api/v1/projects/{project_id}/.
See also: Account > API Keys | Swift SDK Reference | MCP Integration