API Reference
Complete documentation for the MailerBot REST API.
Getting Started
MailerBot provides a RESTful API for automating direct mail. All requests and responses use JSON with camelCase field names.
To get started, create an account via POST /auth/register, then authenticate using either JWT tokens or API keys.
Authentication
Two authentication methods are supported:
1. JWT Bearer Token
Obtain via POST /auth/login with form-encoded body (username + password). Pass as Authorization: Bearer {token}. Access tokens expire in 30 minutes. Refresh via POST /auth/refresh with { "refreshToken": "..." }. Refresh tokens last 7 days.
2. API Key
Create in the dashboard under Settings → API Keys. Pass as X-API-Key: mb_live_... header. Keys are shown only once on creation.
Live vs Test Keys
API keys come in two modes, determined by their prefix:
mb_live_...— Live keys process payments and fulfill orders normally.mb_test_...— Test keys skip payment entirely and mark orders as test so they are never printed or mailed. Use these when building and testing integrations.
Test keys share the same data (contacts, documents, templates) as live keys — the only difference is that mailings created with a test key bypass payment and fulfillment. The mailing response will include isTest: true and paymentStatus: "test_skipped".
Resolution order: JWT checked first, then API Key.
Base URL
https://api.mailerbot.com/api/v1Pagination
All list endpoints return a paginated response:
{
"items": [...],
"total": 150,
"page": 1,
"pageSize": 20,
"pages": 8
}
Error Handling
Standard error format:
{
"detail": "Error description"
}
| Status Code | Description |
|---|---|
400 | Bad Request |
401 | Unauthorized |
404 | Not Found |
422 | Validation Error (includes loc, msg, type fields in detail array) |
Authentication
/auth/register
Public
Register a new user account.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| string | Required | Valid email address | |
| password | string | Required | Minimum 8 characters |
| fullName | string|null | Optional | User's full name |
Response 201
| Field | Type |
|---|---|
| id | string (UUID) |
| string | |
| fullName | string|null |
| isActive | boolean |
| createdAt | datetime |
curl -X POST https://api.mailerbot.com/api/v1/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "securepass123", "fullName": "Jane Doe" }'
const res = await fetch("https://api.mailerbot.com/api/v1/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "user@example.com", password: "securepass123", fullName: "Jane Doe" }) }); const user = await res.json();
import requests res = requests.post( "https://api.mailerbot.com/api/v1/auth/register", json={ "email": "user@example.com", "password": "securepass123", "fullName": "Jane Doe" } ) user = res.json()
/auth/login
Public
Authenticate with email and password. Uses application/x-www-form-urlencoded content type.
Request Body (form-encoded)
| Field | Type | Required | Description |
|---|---|---|---|
| username | string | Required | The user's email address |
| password | string | Required | Account password |
Response 200 — Standard
| Field | Type |
|---|---|
| accessToken | string |
| refreshToken | string |
| tokenType | string ("bearer") |
| expiresIn | integer (1800) |
MFA Challenge Response 200
When MFA is enabled on the account, the login response returns an MFA challenge instead of tokens directly. Complete authentication by calling POST /auth/mfa/verify with the returned mfa_token.
| Field | Type | Description |
|---|---|---|
| mfa_token | string | Temporary MFA session token |
| mfa_required | boolean (true) | Indicates MFA challenge is pending |
curl -X POST https://api.mailerbot.com/api/v1/auth/login \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=user@example.com&password=securepass123"
const res = await fetch("https://api.mailerbot.com/api/v1/auth/login", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ username: "user@example.com", password: "securepass123" }) }); const { accessToken, refreshToken } = await res.json();
import requests res = requests.post( "https://api.mailerbot.com/api/v1/auth/login", data={ "username": "user@example.com", "password": "securepass123" } ) tokens = res.json()
/auth/refresh
Public
Refresh an expired access token using a valid refresh token.
Request Body
| Field | Type | Required |
|---|---|---|
| refreshToken | string | Required |
Response 200
Same schema as login response (accessToken, refreshToken, tokenType, expiresIn).
curl -X POST https://api.mailerbot.com/api/v1/auth/refresh \ -H "Content-Type: application/json" \ -d '{ "refreshToken": "eyJhbGci..." }'
/auth/forgot-password
Public
Rate limited: 3/min
Request a password reset email. Always returns success to prevent email enumeration.
Request Body
| Field | Type | Required |
|---|---|---|
| string | Required |
Response 200
| Field | Type |
|---|---|
| message | string |
curl -X POST https://api.mailerbot.com/api/v1/auth/forgot-password \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com" }'
/auth/reset-password
Public
Rate limited: 5/min
Reset a user's password using the token from the forgot-password email.
Request Body
| Field | Type | Required |
|---|---|---|
| token | string | Required |
| new_password | string | Required |
Response 200
| Field | Type |
|---|---|
| message | string |
curl -X POST https://api.mailerbot.com/api/v1/auth/reset-password \ -H "Content-Type: application/json" \ -d '{ "token": "reset_abc123...", "new_password": "newsecurepass123" }'
/auth/verify-email
Public
Rate limited: 10/min
Verify a user's email address using the token from the verification email.
Request Body
| Field | Type | Required |
|---|---|---|
| token | string | Required |
Response 200
| Field | Type |
|---|---|
| message | string |
curl -X POST https://api.mailerbot.com/api/v1/auth/verify-email \ -H "Content-Type: application/json" \ -d '{ "token": "verify_abc123..." }'
/auth/resend-verification
Public
Rate limited: 3/min
Resend the email verification link to the given address.
Request Body
| Field | Type | Required |
|---|---|---|
| string | Required |
Response 200
| Field | Type |
|---|---|
| message | string |
curl -X POST https://api.mailerbot.com/api/v1/auth/resend-verification \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com" }'
/auth/check-passkey
Public
Check whether the given email address has a passkey registered, to determine if passwordless login is available.
Request Body
| Field | Type | Required |
|---|---|---|
| string | Required |
Response 200
| Field | Type |
|---|---|
| has_passkey | boolean |
curl -X POST https://api.mailerbot.com/api/v1/auth/check-passkey \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com" }'
/auth/passkey-login
Public
Start a passwordless passkey login flow. Returns WebAuthn authentication options that should be passed to the browser's WebAuthn API.
Request Body
| Field | Type | Required |
|---|---|---|
| string | Required |
Response 200
WebAuthn authentication options object (challenge, allowCredentials, timeout, etc.).
curl -X POST https://api.mailerbot.com/api/v1/auth/passkey-login \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com" }'
/auth/me
JWT Only
Get the current authenticated user's profile.
Response 200
Returns UserResponse (id, email, fullName, isActive, createdAt).
curl https://api.mailerbot.com/api/v1/auth/me \ -H "Authorization: Bearer eyJhbGci..."
/auth/me
JWT Only
Update current user profile.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| fullName | string|null | Optional | User's full name |
| returnAddress | object|null | Optional | Return address (name, company, addressLine1, addressLine2, city, state, zip) |
curl -X PUT https://api.mailerbot.com/api/v1/auth/me \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "fullName": "Jane Smith", "returnAddress": { "name": "Jane Smith", "company": "Acme Corp", "addressLine1": "123 Main St", "city": "Austin", "state": "TX", "zip": "78701" } }'
/auth/api-keys
JWT Only
Create a new API key. The full key value is returned only once in the response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Descriptive name for the key |
| scopes | string[] | Optional | Permission scopes |
| prefix | string | Optional | "mb_live" (default) or "mb_test". Test keys skip payment and mark mailings as test — they won't be fulfilled. |
Response 201
| Field | Type |
|---|---|
| id | string (UUID) |
| name | string |
| prefix | string |
| lastFour | string |
| scopes | string[] |
| isActive | boolean |
| createdAt | datetime |
| lastUsedAt | datetime|null |
| key | string |
# Create a live key curl -X POST https://api.mailerbot.com/api/v1/auth/api-keys \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Production Key", "prefix": "mb_live" }' # Create a test key (for integration testing) curl -X POST https://api.mailerbot.com/api/v1/auth/api-keys \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Test Key", "prefix": "mb_test" }'
/auth/api-keys
JWT Only
List all API keys for the current user. The full key value is not returned.
Response 200
Array of API key objects (id, name, prefix, lastFour, scopes, isActive, createdAt, lastUsedAt).
curl https://api.mailerbot.com/api/v1/auth/api-keys \ -H "Authorization: Bearer eyJhbGci..."
/auth/api-keys/{key_id}
JWT Only
Revoke an API key. This action cannot be undone.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| key_id | string (UUID) | The API key ID to revoke |
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/auth/api-keys/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer eyJhbGci..."
Documents
/documents
JWT / API Key
List documents with optional filtering.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| pageSize | integer | 20 | Items per page |
| type | string | -- | Filter by type |
Response 200
PaginatedResponse of DocumentResponse.
curl https://api.mailerbot.com/api/v1/documents?page=1&pageSize=20 \ -H "Authorization: Bearer eyJhbGci..."
/documents
JWT / API Key
Create a new document.
Request Body
| Field | Type | Default | Description |
|---|---|---|---|
| title | string | "Untitled Document" | Document title |
| content | string | "" | HTML content |
Response 201
| Field | Type |
|---|---|
| id | string (UUID) |
| title | string |
| content | string |
| createdAt | datetime |
| updatedAt | datetime |
| type | string |
| pdfUrl | string|null |
| thumbnailUrl | string|null |
| pageCount | integer|null |
| fileSize | integer|null |
curl -X POST https://api.mailerbot.com/api/v1/documents \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "title": "Q1 Welcome Letter", "content": "<p>Dear {{firstName}},</p><p>Welcome!</p>" }'
const res = await fetch("https://api.mailerbot.com/api/v1/documents", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ title: "Q1 Welcome Letter", content: "<p>Dear {{firstName}},</p>" }) }); const doc = await res.json();
import requests res = requests.post( "https://api.mailerbot.com/api/v1/documents", headers={"Authorization": f"Bearer {access_token}"}, json={ "title": "Q1 Welcome Letter", "content": "<p>Dear {{firstName}},</p>" } ) doc = res.json()
/documents/{document_id}
JWT / API Key
Get a single document by ID.
Path Parameters
| Parameter | Type |
|---|---|
| document_id | string (UUID) |
Response 200
DocumentResponse object.
curl https://api.mailerbot.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer eyJhbGci..."
/documents/{document_id}
JWT / API Key
Update a document.
Request Body
| Field | Type | Required |
|---|---|---|
| title | string|null | Optional |
| content | string|null | Optional |
curl -X PUT https://api.mailerbot.com/api/v1/documents/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "title": "Updated Title" }'
/documents/{document_id}
JWT / API Key
Delete a document.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/documents/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/documents/upload
JWT / API Key
Upload a PDF document. Max file size: 25 MB. Content type must be multipart/form-data.
Request Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
| file | file (PDF) | Required | PDF file to upload |
Response 201
DocumentResponse object.
curl -X POST https://api.mailerbot.com/api/v1/documents/upload \ -H "Authorization: Bearer eyJhbGci..." \ -F "file=@/path/to/document.pdf"
Contacts
/contacts
JWT / API Key
List contacts with pagination.
Query Parameters
| Parameter | Type | Default |
|---|---|---|
| page | integer | 1 |
| pageSize | integer | 20 |
| search | string | -- |
curl https://api.mailerbot.com/api/v1/contacts?page=1&pageSize=20 \ -H "Authorization: Bearer eyJhbGci..."
/contacts
JWT / API Key
Create a new contact.
Request Body
| Field | Type | Required |
|---|---|---|
| firstName | string | Required |
| lastName | string | Required |
| company | string|null | Optional |
| addressLine1 | string | Required |
| addressLine2 | string|null | Optional |
| city | string | Required |
| state | string | Required |
| zip | string | Required |
| countryCode | string | Optional |
| string|null | Optional | |
| phone | string|null | Optional |
Country code: ISO 3166-1 alpha-2 code (e.g. "US", "CA", "GB"). Defaults to "US" if omitted. Use List Countries to get valid codes.
Response 201
| Field | Type |
|---|---|
| id | string (UUID) |
| firstName | string |
| lastName | string |
| company | string|null |
| addressLine1 | string |
| addressLine2 | string|null |
| city | string |
| state | string |
| zip | string |
| countryCode | string |
| countryName | string |
| string|null | |
| phone | string|null |
| validationStatus | string |
| correctedAddress | object|null |
curl -X POST https://api.mailerbot.com/api/v1/contacts \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "firstName": "John", "lastName": "Smith", "addressLine1": "456 Oak Ave", "city": "Portland", "state": "OR", "zip": "97201", "countryCode": "US" }'
/contacts/{contact_id}
JWT / API Key
Get a single contact by ID.
curl https://api.mailerbot.com/api/v1/contacts/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/contacts/{contact_id}
JWT / API Key
Update a contact. All fields are optional.
curl -X PUT https://api.mailerbot.com/api/v1/contacts/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "company": "Acme Corp", "phone": "555-0123" }'
/contacts/{contact_id}
JWT / API Key
Delete a contact.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/contacts/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/contacts/import
JWT / API Key
Bulk import contacts. Optionally creates a contact list.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| contacts | ContactCreate[] | Required | Array of contact objects |
| listName | string|null | Optional | Creates a contact list with this name |
Response 201
| Field | Type |
|---|---|
| importedCount | integer |
| listId | string|null |
curl -X POST https://api.mailerbot.com/api/v1/contacts/import \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "contacts": [ { "firstName": "Alice", "lastName": "Johnson", "addressLine1": "789 Elm St", "city": "Denver", "state": "CO", "zip": "80201", "countryCode": "US" } ], "listName": "Q1 Prospects" }'
const res = await fetch("https://api.mailerbot.com/api/v1/contacts/import", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ contacts: [ { firstName: "Alice", lastName: "Johnson", addressLine1: "789 Elm St", city: "Denver", state: "CO", zip: "80201" } ], listName: "Q1 Prospects" }) }); const result = await res.json();
import requests res = requests.post( "https://api.mailerbot.com/api/v1/contacts/import", headers={"Authorization": f"Bearer {access_token}"}, json={ "contacts": [{ "firstName": "Alice", "lastName": "Johnson", "addressLine1": "789 Elm St", "city": "Denver", "state": "CO", "zip": "80201" }], "listName": "Q1 Prospects" } )
/contacts/validate
JWT / API Key
Validate addresses for a set of contacts.
Request Body
| Field | Type | Required |
|---|---|---|
| contactIds | string[] | Required |
Response 200
| Field | Type |
|---|---|
| contactId | string |
| status | string ("valid"|"invalid"|"corrected") |
| original | object |
| corrected | object|null |
curl -X POST https://api.mailerbot.com/api/v1/contacts/validate \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "contactIds": ["uuid-1", "uuid-2"] }'
Contact Lists
/contact-lists
JWT / API Key
List all contact lists.
Response 200
| Field | Type |
|---|---|
| id | string (UUID) |
| name | string |
| contactIds | string[] |
| createdAt | datetime |
| updatedAt | datetime |
curl https://api.mailerbot.com/api/v1/contact-lists \ -H "Authorization: Bearer eyJhbGci..."
/contact-lists
JWT / API Key
Create a new contact list.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string | Required |
| contactIds | string[] | Optional |
curl -X POST https://api.mailerbot.com/api/v1/contact-lists \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "VIP Customers", "contactIds": ["uuid-1", "uuid-2"] }'
/contact-lists/{list_id}
JWT / API Key
Get a contact list by ID.
curl https://api.mailerbot.com/api/v1/contact-lists/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/contact-lists/{list_id}
JWT / API Key
Update a contact list name.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string|null | Optional |
curl -X PUT https://api.mailerbot.com/api/v1/contact-lists/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Premium Customers" }'
/contact-lists/{list_id}
JWT / API Key
Delete a contact list.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/contact-lists/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/contact-lists/{list_id}/contacts
JWT / API Key
Add contacts to an existing list.
Request Body
| Field | Type | Required |
|---|---|---|
| contactIds | string[] | Required |
Response 204
No content.
curl -X POST https://api.mailerbot.com/api/v1/contact-lists/550e8400-.../contacts \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "contactIds": ["uuid-1", "uuid-2"] }'
/contact-lists/{list_id}/contacts
JWT / API Key
Remove contacts from a list.
Request Body
| Field | Type | Required |
|---|---|---|
| contactIds | string[] | Required |
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/contact-lists/550e8400-.../contacts \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "contactIds": ["uuid-1"] }'
Postcards
/postcards/templates
Public
List all available postcard templates.
Response 200
| Field | Type |
|---|---|
| id | string |
| name | string |
| description | string |
| category | string |
| thumbnailUrl | string |
| front | object |
| back | object |
curl https://api.mailerbot.com/api/v1/postcards/templates
/postcards/templates/{template_id}
Public
Get a single postcard template by ID.
curl https://api.mailerbot.com/api/v1/postcards/templates/tpl-001
/postcards
JWT / API Key
List user postcards with pagination.
Query Parameters
| Parameter | Type | Default |
|---|---|---|
| page | integer | 1 |
| pageSize | integer | 20 |
Response 200
| Field | Type |
|---|---|
| id | string (UUID) |
| title | string |
| front | PostcardSide |
| back | PostcardSide |
| width | integer |
| height | integer |
| createdAt | datetime |
| updatedAt | datetime |
| templateId | string|null |
curl https://api.mailerbot.com/api/v1/postcards?page=1&pageSize=10 \ -H "Authorization: Bearer eyJhbGci..."
/postcards
JWT / API Key
Create a new postcard.
Request Body
| Field | Type | Default | Description |
|---|---|---|---|
| title | string | -- | Postcard title |
| front | PostcardSide|null | null | Front side design |
| back | PostcardSide|null | null | Back side design |
| width | integer | 1800 | Width in pixels (300 DPI) |
| height | integer | 1275 | Height in pixels (300 DPI) |
PostcardSide: { elements: CanvasElement[], backgroundColor: string, backgroundImage: string|null }
curl -X POST https://api.mailerbot.com/api/v1/postcards \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "title": "Summer Sale Postcard", "width": 1800, "height": 1275, "front": { "elements": [], "backgroundColor": "#FFFFFF", "backgroundImage": null } }'
/postcards/{postcard_id}
JWT / API Key
Get a single postcard by ID.
curl https://api.mailerbot.com/api/v1/postcards/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/postcards/{postcard_id}
JWT / API Key
Update a postcard. All fields are optional.
Request Body
| Field | Type | Required |
|---|---|---|
| title | string|null | Optional |
| front | PostcardSide|null | Optional |
| back | PostcardSide|null | Optional |
curl -X PUT https://api.mailerbot.com/api/v1/postcards/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "title": "Updated Summer Sale" }'
/postcards/{postcard_id}
JWT / API Key
Delete a postcard.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/postcards/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
Mailings
/mailings
JWT / API Key
List mailings with pagination.
Response 200
| Field | Type |
|---|---|
| id | string (UUID) |
| name | string |
| type | string ("letter"|"postcard") |
| productTypeId | string|null |
| documentId | string|null |
| postcardId | string|null |
| contactListId | string |
| couponListId | string|null |
| status | string |
| recipientCount | integer |
| createdAt | datetime |
| scheduledDate | datetime|null |
| printColor | boolean |
| productCostPerPiece | number |
| totalCost | number |
| taxAmount | number |
| taxRate | number |
| paymentStatus | string |
| stripePaymentIntentId | string|null |
| isTest | boolean |
| postage | MailingPostage[] |
MailingPostage Object
| Field | Type |
|---|---|
| postageZoneId | string |
| postageZoneName | string |
| postageZoneSlug | string |
| postageRateId | string |
| postageRateLabel | string |
| postageRateSlug | string |
| recipientCount | integer |
| costPerPiece | number |
| subtotal | number |
curl https://api.mailerbot.com/api/v1/mailings?page=1&pageSize=20 \ -H "Authorization: Bearer eyJhbGci..."
/mailings
JWT / API Key
Create a new mailing.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Mailing name |
| type | string | Required | "letter" or "postcard" |
| documentId | string|null | Optional | Document ID (for letters) |
| postcardId | string|null | Optional | Postcard ID (for postcards) |
| contactListId | string | Required | Target contact list |
| couponListId | string|null | Optional | Coupon list to attach |
| scheduledDate | datetime|null | Optional | When to send |
| printColor | boolean | Optional | Color printing for letters (default false) |
| postageSelections | PostageSelection[] | Optional | One entry per postage zone. Zones left blank default to cheapest rate. |
Postage selections: Each entry in postageSelections specifies a postageZoneSlug and postageRateSlug. If omitted or empty, the cheapest available rate is automatically selected for each zone that has recipients. You can provide entries for specific zones to override the default, and omit the rest. Extra zones (with no recipients) are silently ignored. Use List Postage Zones and Get Pricing Catalog to discover available zones and rates.
PostageSelection Object
| Field | Type | Description |
|---|---|---|
| postageZoneSlug | string | Zone slug (e.g. "domestic", "canada", "international") |
| postageRateSlug | string | Rate slug (e.g. "standard-postage") |
curl -X POST https://api.mailerbot.com/api/v1/mailings \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Q1 Postcard Campaign", "type": "postcard", "postcardId": "pc_8f3a...", "contactListId": "cl_2b7c...", "scheduledDate": "2026-04-01T00:00:00Z", "postageSelections": [ { "postageZoneSlug": "domestic", "postageRateSlug": "standard-postage" }, { "postageZoneSlug": "canada", "postageRateSlug": "intl-standard" } ] }'
const res = await fetch("https://api.mailerbot.com/api/v1/mailings", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Q1 Postcard Campaign", type: "postcard", postcardId: "pc_8f3a...", contactListId: "cl_2b7c...", scheduledDate: "2026-04-01T00:00:00Z", postageSelections: [ { postageZoneSlug: "domestic", postageRateSlug: "standard-postage" }, { postageZoneSlug: "canada", postageRateSlug: "intl-standard" } ] }) }); const mailing = await res.json();
import requests res = requests.post( "https://api.mailerbot.com/api/v1/mailings", headers={"Authorization": f"Bearer {access_token}"}, json={ "name": "Q1 Postcard Campaign", "type": "postcard", "postcardId": "pc_8f3a...", "contactListId": "cl_2b7c...", "scheduledDate": "2026-04-01T00:00:00Z", "postageSelections": [ {"postageZoneSlug": "domestic", "postageRateSlug": "standard-postage"}, {"postageZoneSlug": "canada", "postageRateSlug": "intl-standard"} ] } ) mailing = res.json()
/mailings/{mailing_id}
JWT / API Key
Get a single mailing by ID.
curl https://api.mailerbot.com/api/v1/mailings/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/mailings/{mailing_id}
JWT / API Key
Update a mailing.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string|null | Optional |
| scheduledDate | datetime|null | Optional |
curl -X PUT https://api.mailerbot.com/api/v1/mailings/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Campaign Name" }'
/mailings/{mailing_id}
JWT / API Key
Delete a mailing. Only draft mailings can be deleted.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/mailings/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/mailings/{mailing_id}/validate-addresses
JWT / API Key
Validate all recipient addresses for a mailing.
Response 200
Array of AddressValidation objects.
curl -X POST https://api.mailerbot.com/api/v1/mailings/550e8400-.../validate-addresses \ -H "Authorization: Bearer eyJhbGci..."
/mailings/estimate-cost
JWT / API Key
Estimate cost before creating a mailing. Returns per-zone recipient counts so you know which postage zones to select. Use this to preview pricing and zone breakdown before committing to a mailing.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | Required | "letter" or "postcard" |
| contactListId | string|null | Optional | Existing contact list ID |
| contactIds | string[]|null | Optional | Specific contact IDs (alternative to contactListId) |
| documentId | string|null | Optional | Document ID (required for letters, used for page count) |
| printColor | boolean | Optional | Color printing (default false) |
Response 200
Same CostCalculation response as Calculate Cost. The totalCost will be null since no postage selections have been made yet — use zoneCounts to determine which zones need postage selections.
curl -X POST https://api.mailerbot.com/api/v1/mailings/estimate-cost \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "type": "postcard", "contactListId": "cl_2b7c..." }'
/mailings/{mailing_id}/calculate-cost
JWT / API Key
Calculate the cost of a mailing before sending. Returns per-zone recipient counts and pricing breakdown.
Response 200
| Field | Type | Description |
|---|---|---|
| type | string | "letter" or "postcard" |
| productTypeCode | string | Product type code |
| recipientCount | integer | Total across all zones |
| productCostPerPiece | number | Per-piece product cost (not including postage) |
| totalProductCost | number | recipientCount x productCostPerPiece |
| zoneCounts | ZoneRecipientCount[] | Per-zone recipient breakdown |
| unavailable | UnavailableRecipients[] | Recipients in disabled countries/zones |
| totalCost | number|null | Total including postage (only when postage_selections provided on the mailing) |
| pageCount | integer|null | Page count (letters only) |
| printColor | boolean | Color printing enabled |
| basePrice | number|null | Base price (letters only) |
| perPagePrice | number|null | Per-page price (letters only) |
| colorSurchargePerPage | number|null | Color surcharge per page (letters only) |
ZoneRecipientCount Object
| Field | Type |
|---|---|
| postageZoneId | string |
| postageZoneName | string |
| postageZoneSlug | string |
| recipientCount | integer |
curl -X POST https://api.mailerbot.com/api/v1/mailings/550e8400-.../calculate-cost \ -H "Authorization: Bearer eyJhbGci..."
/mailings/{mailing_id}/send
JWT / API Key
Submit a mailing for processing and delivery. If the mailing is unpaid, charges the specified card (or the user's default card) off-session. If already paid, transitions directly to processing.
API & Integration usage: Pass paymentMethodId to charge a specific card, or omit it to use your default card. Cards that require 3D Secure authentication will be rejected — use a card that does not require 3DS for headless API calls. Add cards via the Settings page in the web app, then use List Cards to get IDs and Set Default Card to choose one for automatic billing.
Test mode: When authenticated with a mb_test_ API key, payment is skipped entirely and the mailing transitions straight to processing with paymentStatus: "test_skipped". No card is required. The order will not be printed or mailed — use this for integration testing.
Request Body (optional)
| Field | Type | Required | Description |
|---|---|---|---|
| paymentMethodId | string|null | Optional | Stripe payment method ID to charge. Falls back to default card if omitted. |
Response 200
MailingResponse with status: "processing".
Error Responses 400
| Condition | Error Message |
|---|---|
| No card provided and no default set | No payment method provided and no default card on file. |
| Card requires 3D Secure | This payment requires 3D Secure authentication and cannot be completed without a browser. |
| Charge fails | Payment was not successful (status: ...). |
# Charge default card curl -X POST https://api.mailerbot.com/api/v1/mailings/550e8400-.../send \ -H "Authorization: Bearer eyJhbGci..." # Charge a specific card curl -X POST https://api.mailerbot.com/api/v1/mailings/550e8400-.../send \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "paymentMethodId": "pm_1Abc..." }' # Test mode — no payment required curl -X POST https://api.mailerbot.com/api/v1/mailings/550e8400-.../send \ -H "X-API-Key: mb_test_..."
// Charge default card const res = await fetch( `https://api.mailerbot.com/api/v1/mailings/${mailingId}/send`, { method: "POST", headers: { "Authorization": `Bearer ${accessToken}` } } ); // Charge a specific card const res = await fetch( `https://api.mailerbot.com/api/v1/mailings/${mailingId}/send`, { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ paymentMethodId: "pm_1Abc..." }) } ); const result = await res.json(); // result.status === "processing"
import requests # Charge default card res = requests.post( f"https://api.mailerbot.com/api/v1/mailings/{mailing_id}/send", headers={"Authorization": f"Bearer {access_token}"} ) # Charge a specific card res = requests.post( f"https://api.mailerbot.com/api/v1/mailings/{mailing_id}/send", headers={"Authorization": f"Bearer {access_token}"}, json={"paymentMethodId": "pm_1Abc..."} ) result = res.json() # result["status"] == "processing"
Campaigns
/campaigns
JWT / API Key
List campaigns with pagination.
Response 200
| Field | Type |
|---|---|
| id | string (UUID) |
| name | string |
| mailingIds | string[] |
| status | string |
| createdAt | datetime |
| updatedAt | datetime |
curl https://api.mailerbot.com/api/v1/campaigns?page=1&pageSize=20 \ -H "Authorization: Bearer eyJhbGci..."
/campaigns
JWT / API Key
Create a new campaign.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string | Required |
| mailingIds | string[] | Optional |
curl -X POST https://api.mailerbot.com/api/v1/campaigns \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Q1 2026 Outreach" }'
/campaigns/{campaign_id}
JWT / API Key
Get a single campaign by ID.
curl https://api.mailerbot.com/api/v1/campaigns/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/campaigns/{campaign_id}
JWT / API Key
Update a campaign.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string|null | Optional |
| status | string|null | Optional |
curl -X PUT https://api.mailerbot.com/api/v1/campaigns/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Campaign", "status": "active" }'
/campaigns/{campaign_id}
JWT / API Key
Delete a campaign.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/campaigns/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/campaigns/{campaign_id}/mailings
JWT / API Key
Add a mailing to a campaign.
Request Body
| Field | Type | Required |
|---|---|---|
| mailingId | string (UUID) | Required |
Response 200
CampaignResponse.
curl -X POST https://api.mailerbot.com/api/v1/campaigns/550e8400-.../mailings \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "mailingId": "ml_9d4e..." }'
/campaigns/{campaign_id}/mailings/{mailing_id}
JWT / API Key
Remove a mailing from a campaign.
Response 200
CampaignResponse.
curl -X DELETE https://api.mailerbot.com/api/v1/campaigns/550e8400-.../mailings/ml_9d4e... \ -H "Authorization: Bearer eyJhbGci..."
Dashboard
/dashboard/stats
JWT / API Key
Get aggregate dashboard statistics for the current user.
Response 200
| Field | Type |
|---|---|
| totalDocuments | integer |
| totalMailings | integer |
| lettersSent | integer |
| postcardsSent | integer |
| totalContacts | integer |
| totalSpent | number |
| totalQrScans | integer |
curl https://api.mailerbot.com/api/v1/dashboard/stats \ -H "Authorization: Bearer eyJhbGci..."
/dashboard/reporting
JWT / API Key
Get reporting data with optional date range filtering. Returns aggregate mailing stats and geographic distribution.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| startDate | string | Optional | Start date (YYYY-MM-DD) |
| endDate | string | Optional | End date (YYYY-MM-DD) |
Response 200
| Field | Type | Description |
|---|---|---|
| totalPieces | integer | Total mail pieces sent |
| totalSpent | number | Total amount spent |
| lettersCount | integer | Number of letter pieces |
| postcardsCount | integer | Number of postcard pieces |
| destinationsByState | array | Array of { state, count } objects |
curl https://api.mailerbot.com/api/v1/dashboard/reporting?startDate=2026-01-01&endDate=2026-03-31 \ -H "Authorization: Bearer eyJhbGci..."
Multi-Factor Authentication
/auth/mfa/totp/setup
JWT
Generate a TOTP secret and provisioning QR code URI. The user must confirm with a valid code via the enable endpoint to activate TOTP.
Response 200
| Field | Type | Description |
|---|---|---|
| secret | string | Base32-encoded TOTP secret |
| otpauthUrl | string | OTP auth URI for QR code generation |
curl -X POST https://api.mailerbot.com/api/v1/auth/mfa/totp/setup \ -H "Authorization: Bearer eyJhbGci..."
/auth/mfa/totp/enable
JWT
Verify a TOTP code against the secret from setup and permanently enable TOTP for the account.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| secret | string | Required | The TOTP secret from setup |
| code | string | Required | 6-digit TOTP code from authenticator app |
Response 200
| Field | Type |
|---|---|
| enabled | boolean (true) |
/auth/mfa/totp
JWT
Disable TOTP multi-factor authentication for the current user.
Response 200
| Field | Type |
|---|---|
| enabled | boolean (false) |
/auth/mfa/passkeys
JWT
List all registered passkeys (WebAuthn credentials) for the current user.
Response 200 (array)
| Field | Type | Description |
|---|---|---|
| id | string | Passkey identifier |
| name | string | User-assigned name for the passkey |
| createdAt | datetime | When the passkey was registered |
/auth/mfa/passkeys/{passkey_id}
JWT
Remove a registered passkey. Returns 204 No Content on success.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| passkey_id | string | The passkey identifier to delete |
/auth/mfa/passkeys/register/options
JWT
Get WebAuthn credential creation options to begin registering a new passkey. Pass the returned options to the browser's WebAuthn API (navigator.credentials.create()).
Response 200
WebAuthn credential creation options object (challenge, rp, user, pubKeyCredParams, timeout, etc.).
curl -X POST https://api.mailerbot.com/api/v1/auth/mfa/passkeys/register/options \ -H "Authorization: Bearer eyJhbGci..."
/auth/mfa/passkeys/register/verify
JWT
Verify and store a new passkey after the user has completed the WebAuthn registration ceremony.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| credential | object | Required | WebAuthn credential from navigator.credentials.create() |
| name | string | Required | User-assigned name for this passkey |
Response 200
| Field | Type |
|---|---|
| id | string |
| name | string |
| created_at | string (datetime) |
curl -X POST https://api.mailerbot.com/api/v1/auth/mfa/passkeys/register/verify \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "credential": { ... }, "name": "My MacBook" }'
/auth/mfa/passkeys/auth/options
Public
Get WebAuthn authentication options during the MFA login flow. Called after receiving an MFA challenge from POST /auth/login.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| mfa_token | string | Required | MFA token from login response |
Response 200
WebAuthn authentication options object (challenge, allowCredentials, timeout, etc.).
curl -X POST https://api.mailerbot.com/api/v1/auth/mfa/passkeys/auth/options \ -H "Content-Type: application/json" \ -d '{ "mfa_token": "mfa_abc123..." }'
/auth/mfa/verify
Public
Complete MFA verification during login. Submit the MFA token (received from POST /auth/login when MFA is enabled) along with a TOTP code or passkey response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| mfaToken | string | Required | MFA token from login response |
| code | string|null | Optional | 6-digit TOTP code (provide this or passkeyResponse) |
| passkeyResponse | object|null | Optional | WebAuthn assertion response |
Response 200
| Field | Type |
|---|---|
| accessToken | string |
| refreshToken | string |
| tokenType | string ("bearer") |
| expiresIn | integer (1800) |
curl -X POST https://api.mailerbot.com/api/v1/auth/mfa/verify \ -H "Content-Type: application/json" \ -d '{ "mfaToken": "mfa_abc123...", "code": "123456" }'
OAuth 2.1
/.well-known/oauth-authorization-server
Public
OAuth 2.0 Authorization Server Metadata (RFC 8414). Root-level endpoint — not under /api/v1. Returns standard OAuth server discovery metadata including supported grant types, scopes, and endpoint URLs.
Response 200
OAuth authorization server metadata JSON object.
curl https://api.mailerbot.com/.well-known/oauth-authorization-server
/.well-known/oauth-protected-resource
Public
Protected Resource Metadata (RFC 9728). Root-level endpoint — not under /api/v1. Returns metadata about the protected resource including supported token types and authorization server location.
Response 200
Protected resource metadata JSON object.
curl https://api.mailerbot.com/.well-known/oauth-protected-resource
/oauth/authorize
Public
OAuth 2.1 Authorization Endpoint. Root-level endpoint — not under /api/v1. Redirects the user to the consent page.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| client_id | string | Required | OAuth client ID |
| redirect_uri | string | Required | Callback URL |
| response_type | string | Required | "code" |
| scope | string | Optional | Space-separated scopes |
| state | string | Optional | CSRF state parameter |
| code_challenge | string | Required | PKCE code challenge |
| code_challenge_method | string | Required | "S256" |
Response
Redirect to consent page.
/oauth/authorize-info
Public
Get information about an OAuth client for display on the consent page.
Query Parameters
| Parameter | Type | Required |
|---|---|---|
| client_id | string | Required |
| scope | string | Optional |
Response 200
| Field | Type |
|---|---|
| client_name | string |
| scopes | string[] |
/oauth/authorize
JWT
Approve an OAuth authorization request from the consent page. Called by the frontend after the user approves the consent.
Request Body
| Field | Type | Required |
|---|---|---|
| client_id | string | Required |
| redirect_uri | string | Required |
| scope | string | Optional |
| response_type | string | Required |
| code_challenge | string | Required |
| code_challenge_method | string | Required |
| state | string | Optional |
Response 200
| Field | Type |
|---|---|
| redirect_url | string |
curl -X POST https://api.mailerbot.com/api/v1/oauth/authorize \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "client_id": "CLIENT_ID", "redirect_uri": "https://myapp.com/callback", "response_type": "code", "scope": "contacts:read mailings:read", "code_challenge": "CHALLENGE", "code_challenge_method": "S256", "state": "random_state" }'
/oauth/clients
JWT / API Key
Register a new OAuth 2.1 client application. The clientSecret is returned only once in the creation response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| clientName | string | Required | Display name for the application |
| redirectUris | string[] | Required | Allowed redirect URIs |
| allowedScopes | string[] | Optional | Requested scopes (e.g. "contacts:read", "mailings:write") |
| isConfidential | boolean | Optional | Confidential client (default: true) |
Available Scopes
| Scope | Description |
|---|---|
| contacts:read | View contacts |
| contacts:write | Create and update contacts |
| documents:read | View documents |
| documents:write | Create and update documents |
| postcards:read | View postcards |
| postcards:write | Create and update postcards |
| mailings:read | View mailings |
| mailings:write | Create and send mailings |
| campaigns:read | View campaigns |
| campaigns:write | Create and update campaigns |
| account:read | View account information |
Response 200
| Field | Type |
|---|---|
| id | string |
| clientName | string |
| clientId | string |
| clientSecret | string (shown once) |
| redirectUris | string[] |
| allowedScopes | string[] |
| grantTypes | string[] |
| isConfidential | boolean |
| isActive | boolean |
| createdAt | datetime |
curl -X POST https://api.mailerbot.com/api/v1/oauth/clients \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "clientName": "My Integration", "redirectUris": ["https://myapp.com/callback"], "allowedScopes": ["contacts:read", "mailings:write"] }'
/oauth/clients
JWT / API Key
List all OAuth clients created by the current user.
Response 200 (array)
| Field | Type |
|---|---|
| id | string |
| clientName | string |
| clientId | string |
| clientSecretLastFour | string|null |
| redirectUris | string[] |
| allowedScopes | string[] |
| grantTypes | string[] |
| isActive | boolean |
| createdAt | datetime |
/oauth/clients/{client_id}
JWT / API Key
Deactivate an OAuth client application. Returns 204 No Content on success.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| client_id | string | The client database ID |
/oauth/token
Public
OAuth 2.1 Token Endpoint. Exchange an authorization code for tokens or refresh an existing token. Uses application/x-www-form-urlencoded content type. Note: This endpoint is at /oauth/token (not under /api/v1).
Request Body (form-encoded)
| Field | Type | Required | Description |
|---|---|---|---|
| grant_type | string | Required | "authorization_code" or "refresh_token" |
| code | string | Conditional | Authorization code (required for authorization_code grant) |
| redirect_uri | string | Conditional | Must match the redirect_uri used during authorization |
| code_verifier | string | Conditional | PKCE code verifier (required for authorization_code grant) |
| client_id | string | Conditional | OAuth client ID |
| client_secret | string | Optional | Required for confidential clients |
| refresh_token | string | Conditional | Required for refresh_token grant |
Response 200
| Field | Type |
|---|---|
| access_token | string |
| token_type | string ("Bearer") |
| expires_in | integer |
| refresh_token | string|null |
| scope | string |
curl -X POST https://api.mailerbot.com/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=https://myapp.com/callback&code_verifier=VERIFIER&client_id=CLIENT_ID"
/oauth/revoke
Public
Revoke an OAuth token (RFC 7009). Always returns 200 per the spec. Note: This endpoint is at /oauth/revoke (not under /api/v1).
Request Body (form-encoded)
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | Required | The token to revoke |
| token_type_hint | string | Optional | "access_token" or "refresh_token" |
Payments
/payments/create-payment-intent
JWT / API Key
Create a Stripe PaymentIntent for a mailing. Optionally pass a saved payment method ID to charge immediately.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| mailingId | string | Required | The mailing to pay for |
| paymentMethodId | string|null | Optional | Saved card payment method ID for immediate charge |
Response 200
| Field | Type | Description |
|---|---|---|
| clientSecret | string | Stripe client secret for frontend confirmation |
| paymentIntentId | string | Stripe PaymentIntent ID |
| amount | integer | Total amount in cents (subtotal + tax) |
| subtotal | integer | Subtotal in cents |
| taxAmount | integer | Tax amount in cents |
| taxRate | number | Tax rate percentage (e.g. 8.25) |
| currency | string | Currency code (e.g. "usd") |
curl -X POST https://api.mailerbot.com/api/v1/payments/create-payment-intent \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "mailingId": "ml_9d4e..." }'
/payments/config
JWT / API Key
Get the Stripe publishable key for frontend payment initialization.
Response 200
| Field | Type |
|---|---|
| publishableKey | string |
/payments/cards
JWT / API Key
List saved payment methods (cards) for the current user. Each card includes an isDefault flag indicating the customer's default payment method.
Response 200 (array)
| Field | Type | Description |
|---|---|---|
| id | string | Stripe payment method ID |
| brand | string | Card brand (e.g. "visa", "mastercard") |
| last4 | string | Last 4 digits of card number |
| expMonth | integer | Expiration month |
| expYear | integer | Expiration year |
| isDefault | boolean | Whether this is the default payment method |
/payments/cards/setup
JWT / API Key
Create a Stripe SetupIntent for saving a new payment card. Returns a client secret for use with Stripe Elements on the frontend.
Note: This endpoint is designed for use with the MailerBot web app's Stripe Elements integration. The returned client secret requires our Stripe publishable key and properly configured CORS origins to complete the card setup flow.
Response 200
| Field | Type | Description |
|---|---|---|
| clientSecret | string | Stripe SetupIntent client secret |
curl -X POST https://api.mailerbot.com/api/v1/payments/cards/setup \ -H "Authorization: Bearer eyJhbGci..."
/payments/cards/default
JWT / API Key
Set a saved card as the default payment method. The default card is used automatically when sending mailings via API without specifying a payment method.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| paymentMethodId | string | Required | Stripe payment method ID to set as default |
Response 204
No content on success.
curl -X POST https://api.mailerbot.com/api/v1/payments/cards/default \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "paymentMethodId": "pm_1Abc..." }'
/payments/cards/{payment_method_id}
JWT / API Key
Remove a saved payment card. If the deleted card was the default, the default is cleared. Returns 204 No Content on success.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| payment_method_id | string | The Stripe payment method ID |
QR Tracking
/r/{slug}
Public
Redirect a QR short URL to its destination. This endpoint is at /r/{slug} (not under /api/v1). Records a scan event before redirecting.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| slug | string | The QR link slug |
Response 302
HTTP 302 redirect to the destination URL.
curl -L https://api.mailerbot.com/r/abc123
/qr/links
JWT / API Key
Create a trackable QR link. The generated short URL can be embedded as a QR code on postcards and letters to measure engagement.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| destinationUrl | string | Required | URL to redirect to when scanned |
| postcardId | string|null | Optional | Associated postcard ID |
| mailingId | string|null | Optional | Associated mailing ID |
| recipientId | string|null | Optional | Associated recipient/contact ID |
| qrElementId | string|null | Optional | Canvas element ID for the QR code |
Response 201
| Field | Type | Description |
|---|---|---|
| id | string | Link ID |
| slug | string | Short URL slug |
| shortUrl | string | Full short URL for QR code |
| destinationUrl | string | Destination URL |
| scanCount | integer | Number of times scanned |
| createdAt | datetime | Creation timestamp |
curl -X POST https://api.mailerbot.com/api/v1/qr/links \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "destinationUrl": "https://mysite.com/promo", "mailingId": "ml_9d4e..." }'
/qr/links
JWT / API Key
List all QR tracking links with scan counts. Supports pagination.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| pageSize | integer | 20 | Items per page (max 100) |
Response 200
Paginated response with QR link objects (same schema as Create Link response).
/qr/analytics
JWT / API Key
Get QR scan analytics including daily scan counts, top-performing links, and recent scans.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| days | integer | 30 | Number of days to look back (1-365) |
Response 200
| Field | Type | Description |
|---|---|---|
| totalScans | integer | Total scans in the period |
| uniqueLinks | integer | Number of unique links scanned |
| scansByDay | array | Array of { date, count } objects |
| topLinks | array | Top links by scan count with { slug, destinationUrl, scanCount, shortUrl } |
| recentScans | array | Recent scan events with geo/device info |
curl https://api.mailerbot.com/api/v1/qr/analytics?days=30 \ -H "Authorization: Bearer eyJhbGci..."
/qr/links/{link_id}
JWT / API Key
Delete a QR tracking link. Returns 204 No Content on success.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| link_id | string | The QR link ID to delete |
Pricing
/pricing
Public
Get the active pricing catalog. Returns all active pricing items including per-piece costs for letters and postcards.
Response 200
Array of pricing item objects.
curl https://api.mailerbot.com/api/v1/pricing
/pricing/countries
Public
List all supported countries. Use the code field as the countryCode when creating contacts.
Response 200
| Field | Type | Description |
|---|---|---|
| code | string | ISO 3166-1 alpha-2 code (e.g. "US", "CA") |
| name | string | Country name |
| isActive | boolean | Whether mailing to this country is enabled |
curl https://api.mailerbot.com/api/v1/pricing/countries
/pricing/postage-zones
Public
List active postage zones. Each zone groups countries by shipping rate. Use the slug value as postageZoneSlug in postage selections when creating mailings.
Response 200
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Zone ID |
| name | string | Zone display name |
| slug | string | Zone slug for API use |
| description | string|null | Zone description |
| sortOrder | integer | Display order |
| isActive | boolean | Whether zone is active |
curl https://api.mailerbot.com/api/v1/pricing/postage-zones
Coupons
/coupons
JWT / API Key
List all coupon lists for the current user.
Response 200
Array of CouponList objects.
curl https://api.mailerbot.com/api/v1/coupons \ -H "Authorization: Bearer eyJhbGci..."
/coupons
JWT / API Key
Create a new coupon list.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string | Required |
| description | string | Optional |
Response 201
CouponList object.
curl -X POST https://api.mailerbot.com/api/v1/coupons \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Summer Promo 2026", "description": "Summer discount codes" }'
/coupons/{list_id}
JWT / API Key
Get a coupon list by ID.
curl https://api.mailerbot.com/api/v1/coupons/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/coupons/{list_id}
JWT / API Key
Update a coupon list.
Request Body
| Field | Type | Required |
|---|---|---|
| name | string | Optional |
| description | string | Optional |
curl -X PUT https://api.mailerbot.com/api/v1/coupons/550e8400-... \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Promo Name" }'
/coupons/{list_id}
JWT / API Key
Delete a coupon list and all its codes.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/coupons/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."
/coupons/{list_id}/codes
JWT / API Key
List coupon codes in a list with optional filtering.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| page_size | integer | 20 | Items per page |
| used | boolean | -- | Filter by used/unused status |
curl https://api.mailerbot.com/api/v1/coupons/550e8400-.../codes?used=false \ -H "Authorization: Bearer eyJhbGci..."
/coupons/{list_id}/codes/import
JWT / API Key
Import an array of coupon codes into a list. Duplicate codes within the list are skipped.
Request Body
| Field | Type | Required |
|---|---|---|
| codes | string[] | Required |
Response 200
| Field | Type |
|---|---|
| imported | number |
| duplicates | number |
curl -X POST https://api.mailerbot.com/api/v1/coupons/550e8400-.../codes/import \ -H "Authorization: Bearer eyJhbGci..." \ -H "Content-Type: application/json" \ -d '{ "codes": ["SAVE10", "SAVE20", "PROMO25"] }'
/coupons/{list_id}/codes/{code_id}
JWT / API Key
Delete a specific coupon code from a list.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/coupons/550e8400-.../codes/code-uuid \ -H "Authorization: Bearer eyJhbGci..."
/coupons/{list_id}/availability
JWT / API Key
Check whether a coupon list has enough unused codes for a mailing.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| count | integer | Required | Number of codes needed |
Response 200
| Field | Type |
|---|---|
| available | number |
| sufficient | boolean |
curl https://api.mailerbot.com/api/v1/coupons/550e8400-.../availability?count=500 \ -H "Authorization: Bearer eyJhbGci..."
Assets
/assets
JWT / API Key
List uploaded image assets for the current user.
Query Parameters
| Parameter | Type | Default |
|---|---|---|
| page | integer | 1 |
| page_size | integer | 20 |
Response 200
PaginatedResponse of asset objects.
curl https://api.mailerbot.com/api/v1/assets?page=1&page_size=20 \ -H "Authorization: Bearer eyJhbGci..."
/assets
JWT / API Key
Upload an image asset. Accepted formats: JPEG, PNG, GIF, WebP, SVG. Maximum file size: 10 MB. Content type must be multipart/form-data.
Request Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
| file | file (image) | Required | JPEG, PNG, GIF, WebP, or SVG — max 10 MB |
Response 201
| Field | Type |
|---|---|
| id | string (UUID) |
| url | string |
| filename | string |
| size | number (bytes) |
| created_at | string (datetime) |
curl -X POST https://api.mailerbot.com/api/v1/assets \ -H "Authorization: Bearer eyJhbGci..." \ -F "file=@/path/to/image.png"
/assets/{asset_id}
JWT / API Key
Delete an uploaded asset.
Response 204
No content.
curl -X DELETE https://api.mailerbot.com/api/v1/assets/550e8400-... \ -H "Authorization: Bearer eyJhbGci..."