Matrix: M03M04
Open in panel — /app
Reference

API Reference

PostMTA exposes a RESTful HTTP API for all operations: sending email, managing domains and IP pools, querying analytics, configuring webhooks, and administering your workspace. All requests and responses use JSON.

Base URL

https://api.postmta.com

All API paths are relative to this base URL. The current API version is v1. Breaking changes are never introduced within v1; new optional fields and new endpoints are added periodically.

Authentication

API Key Header

Every request must include an Authorization header with a Bearer token:

Authorization: Bearer pmta_l_01HXABC123DEF456GHI789JKL012MNO345PQR

API keys are created from the dashboard under /app/access. Keys are scoped to a workspace and can be restricted to specific permission sets.

Key Scopes

ScopeDescription
keys:readList and retrieve API key metadata
domains:manageAdd, verify, and remove sending domains
domains:verifyInitiate and check domain verification (DKIM, SPF, DMARC)
messages:sendInject messages into the delivery pipeline
messages:readLook up message status and delivery events
analytics:readRead aggregate analytics and export data
webhooks:manageCreate, update, and delete webhook endpoints
webhooks:readView webhook configurations and delivery logs
ip-pools:manageCreate and manage IP pools and assignment
credits:readView credit balance and transaction history
credits:writeTop up credits and configure auto-reload
workspace:adminFull workspace administration (owners only)

Rate Limits

LimitValueScope
API requests1,000 req/minPer API key
Messages (inject)Varies by planPer workspace
Webhook deliveries200 delivery attempts/minPer endpoint
Suppression imports1 import job/hourPer workspace

Rate limit status is returned in every response header:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1750272030

Pagination

List endpoints return paginated results using cursor-based pagination. Pass cursor from the previous response to retrieve the next page:

GET /v1/keys?limit=25
GET /v1/keys?limit=25&after=cursor_01HXABC123DEF456

Response envelope:

{
  "data": [ ... ],
  "pagination": {
    "has_more": true,
    "next_cursor": "cursor_01HXABC123DEF456",
    "total": 142
  }
}

Error Response Schema

All errors follow a consistent envelope. See the Error Codes page for the full code reference.

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "You have exceeded the rate limit of 1000 requests per minute.",
    "status": 429,
    "details": {
      "limit": 1000,
      "window": "1m",
      "retry_after_seconds": 12
    },
    "request_id": "req_01HXABC123DEF456"
  }
}

Key Endpoints

API Keys

MethodPathDescription
GET/v1/keysList all API keys in the workspace
POST/v1/keysCreate a new API key
GET/v1/keys/:idRetrieve a specific key
DELETE/v1/keys/:idRevoke a key immediately

List Keys

curl https://api.postmta.com/v1/keys \
  -H "Authorization: Bearer pmta_l...xxxx"
{
  "data": [
    {
      "id": "key_01HXABC123DEF456",
      "name": "Production Send Key",
      "scopes": ["messages:send", "messages:read"],
      "workspace_id": "ws_01HXABC123DEF456",
      "created_at": "2026-01-15T09:23:41Z",
      "last_used_at": "2026-07-18T14:02:17Z",
      "expires_at": null
    }
  ],
  "pagination": { "has_more": false, "total": 1 }
}

Create Key

curl -X POST https://api.postmta.com/v1/keys \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Analytics Read-Only Key",
    "scopes": ["analytics:read"]
  }'
{
  "data": {
    "id": "key_01HXABC789GHI012",
    "name": "Analytics Read-Only Key",
    "scopes": ["analytics:read"],
    "key": "pmta_l_01HXABC789GHI012LMN345OPQ678RST901UVW",
    "workspace_id": "ws_01HXABC123DEF456",
    "created_at": "2026-07-18T15:00:00Z"
  }
}
Store the key immediately. The full key value is only returned once at creation time. There is no way to retrieve it again.

Sending Domains

MethodPathDescription
GET/v1/domainsList all sending domains
POST/v1/domainsAdd and verify a sending domain
GET/v1/domains/:idRetrieve domain status and DNS records
DELETE/v1/domains/:idRemove a sending domain

Add Domain

curl -X POST https://api.postmta.com/v1/domains \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "mail.example.com",
    "dkim_selector": "pmta"
  }'
{
  "data": {
    "id": "dom_01HXABC123DEF456",
    "domain": "mail.example.com",
    "dkim_selector": "pmta",
    "verification_status": "pending",
    "dns_records": {
      "dkim": {
        "name": "pmta._domainkey.mail.example.com",
        "type": "TXT",
        "value": "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEB...="
      },
      "spf": {
        "name": "mail.example.com",
        "type": "TXT",
        "value": "v=spf1 include:spf.postmta.com ~all"
      }
    },
    "created_at": "2026-07-18T16:00:00Z"
  }
}

Messages

MethodPathDescription
POST/v1/messagesInject a single message
POST/v1/messages/batchInject up to 1,000 messages in one request
GET/v1/messages/:idRetrieve message status and events

Send a Message

curl -X POST https://api.postmta.com/v1/messages \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "sender@example.com",
    "to": "recipient@example.com",
    "subject": "Your Monthly Statement",
    "html": "<html><body>...</body></html>",
    "text": "Plain text version",
    "tags": ["statement", "july"],
    "custom_args": { "user_id": "12345" },
    "send_at": "2026-07-20T09:00:00Z"
  }'
{
  "data": {
    "id": "msg_01HXABC123DEF456",
    "from": "sender@example.com",
    "to": "recipient@example.com",
    "status": "accepted",
    "created_at": "2026-07-18T16:30:00Z",
    "send_at": "2026-07-20T09:00:00Z"
  }
}

Batch Send

curl -X POST https://api.postmta.com/v1/messages/batch \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "from": "sender@example.com",
        "to": "recipient1@example.com",
        "subject": "Campaign One",
        "html": "<html>...</html>"
      },
      {
        "from": "sender@example.com",
        "to": "recipient2@example.com",
        "subject": "Campaign One",
        "html": "<html>...</html>"
      }
    ]
  }'
{
  "data": {
    "accepted": 2,
    "rejected": 0,
    "messages": [
      { "id": "msg_01HXABC123DEF456", "to": "recipient1@example.com", "status": "accepted" },
      { "id": "msg_01HXABC789GHI012", "to": "recipient2@example.com", "status": "accepted" }
    ]
  }
}

Webhooks

MethodPathDescription
GET/v1/webhooksList all webhook endpoints
POST/v1/webhooksRegister a new webhook endpoint
PUT/v1/webhooks/:idUpdate an existing endpoint
DELETE/v1/webhooks/:idDelete a webhook endpoint

Create Webhook

curl -X POST https://api.postmta.com/v1/webhooks \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/postmta",
    "events": ["message.delivered", "message.bounced", "message.complained"],
    "secret": "whsec_your_signing_secret"
  }'
{
  "data": {
    "id": "whk_01HXABC123DEF456",
    "url": "https://yourapp.com/webhooks/postmta",
    "events": ["message.delivered", "message.bounced", "message.complained"],
    "status": "active",
    "created_at": "2026-07-18T17:00:00Z"
  }
}

Templates

MethodPathDescription
GET/v1/templatesList all templates
POST/v1/templatesCreate a new template
GET/v1/templates/:idRetrieve a template
PUT/v1/templates/:idUpdate a template
DELETE/v1/templates/:idDelete a template

Create Template

curl -X POST https://api.postmta.com/v1/templates \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "July Newsletter",
    "subject": "Your July Update from {{company_name}}",
    "html": "<html><body><h1>Hello {{first_name}}</h1><p>...</p></body></html>",
    "text": "Hello {{first_name}}, ...",
    "tags": ["newsletter", "july"]
  }'
{
  "data": {
    "id": "tmpl_01HXABC123DEF456",
    "name": "July Newsletter",
    "subject": "Your July Update from {{company_name}}",
    "version": 1,
    "tags": ["newsletter", "july"],
    "created_at": "2026-07-18T17:30:00Z",
    "updated_at": "2026-07-18T17:30:00Z"
  }
}

Analytics

MethodPathDescription
GET/v1/analytics/summaryAggregate metrics for a date range
GET/v1/analytics/timeseriesTime-series metrics at hourly/daily/weekly resolution
GET/v1/analytics/exportExport analytics data as CSV or JSON

Summary

curl "https://api.postmta.com/v1/analytics/summary?start=2026-07-01&end=2026-07-18" \
  -H "Authorization: Bearer pmta_l...xxxx"
{
  "data": {
    "period": { "start": "2026-07-01", "end": "2026-07-18" },
    "metrics": {
      "sent": 145293,
      "delivered": 143418,
      "bounced": 362,
      "complained": 28,
      "suppressed": 1485,
      "unique_opens": 87204,
      "unique_clicks": 21801
    },
    "rates": {
      "deliver_rate": 98.71,
      "bounce_rate": 0.25,
      "complaint_rate": 0.02,
      "open_rate": 60.80,
      "click_rate": 15.20
    }
  }
}

OpenAPI Specification

The full OpenAPI 3.1 specification is available at:

https://api.postmta.com/openapi.json

You can import this into Postman, Insomnia, or any OpenAPI-compatible tooling to get a fully-typed, interactive API client.

SDK Availability

Official SDKs wrap the REST API in idiomatic language bindings. All SDKs are generated from the OpenAPI spec and support the full API surface.

LanguagePackageRepository
Node.js / TypeScript@postmta/nodegithub.com/postmta/postmta-node
Pythonpostmta-pythongithub.com/postmta/postmta-python
Gogithub.com/postmta/postmta-gogithub.com/postmta/postmta-go
Rubypostmtagithub.com/postmta/postmta-ruby

See the SDKs & Libraries page for installation and usage examples for each language.