Matrix: M03M04M05
Open in panel — /app/access
Getting Started

SMTP Authentication

PostMTA supports two authentication pathways: a REST API accessed with Bearer tokens (or OAuth 2.0 client credentials), and traditional SMTP submission with AUTH PLAIN. Every API request also validates that the key's workspace is active.

API Key Management

API keys are long-lived credentials scoped to a single workspace. They are prefixed with pmk_live_ (production) or pmk_test_ (sandbox). Keys are stored as a bcrypt hash — only the plaintext value is returned once at creation time.

Create an API key

curl -X POST https://api.postmta.com/v1/keys \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "webapp-production",
    "permissions": [
      "messages:send",
      "messages:read",
      "domains:read",
      "webhooks:write"
    ]
  }'

Response:

{
  "id": "key_01HX5K9P7N3M4Q6R8T2V4W6Y8",
  "name": "webapp-production",
  "key": "pmk_live_abc123xyz789def456...",
  "permissions": ["messages:send", "messages:read", "domains:read", "webhooks:write"],
  "workspace_id": "ws_01HX5K9P7N3M4Q6R8T2V4W6Y0",
  "created_at": "2026-07-18T12:00:00Z",
  "last_used_at": null
}

List keys in a workspace

curl https://api.postmta.com/v1/keys \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "keys": [
    {
      "id": "key_01HX5K9P7N3M4Q6R8T2V4W6Y8",
      "name": "webapp-production",
      "permissions": ["messages:send"],
      "created_at": "2026-07-18T12:00:00Z",
      "last_used_at": "2026-07-18T14:22:00Z"
    }
  ],
  "total": 1
}

Revoke an API key

curl -X DELETE https://api.postmta.com/v1/keys/key_01HX5K9P7N3M4Q6R8T2V4W6Y8 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Returns 204 No Content on success. Revocation is immediate and irreversible.

Permission scopes

ScopeDescription
messages:sendInject outbound messages via REST or SMTP.
messages:readRead message status and event history.
domains:readList and inspect sending domains.
domains:writeAdd and verify new sending domains.
webhooks:writeRegister and manage webhook endpoints.
keys:writeCreate and revoke API keys (restricted).

SMTP AUTH PLAIN

AUTH PLAIN is the simplest SMTP authentication mechanism. The client sends a base64-encoded credential string containing both the username (your account ID) and password (your API key) in a single round-trip.

Lab / testing port: 2525 (unencrypted or STARTTLS)
Production port: 587 (STARTTLS required)
Legacy port: 25 (restricted — contact support to enable)

AUTH PLAIN command

The AUTH PLAIN credential format is:

\u0000USERNAME\u0000PASSWORD

For PostMTA, USERNAME is your account ID (e.g., acc_01HX5K9P7N3M4Q6R8T2V4W6Y0) and PASSWORD is your API key (e.g., pmk_live_abc123...). The entire string is base64-encoded before sending.

Manual encode example (what your SMTP library does):

# Plain credential string:
# \0acc_01HX5K9P7N3M4Q6R8T2V4W6Y0\0pmk_live_abc123xyz789def456...

# base64-encoded (Python):
import base64
cred = b'\0acc_01HX5K9P7N3M4Q6R8T2V4W6Y0\0pmk_live_abc123xyz789def456...'
print(base64.b64encode(cred).decode())
# Output: AGFjY18wMUg1UEs5UDdOM000UTZSOFQyVjRXNlY5MGhhYmMxMjN4eXo3OGRlZjQ1Ni4uLg==

Full SMTP session with AUTH PLAIN:

220 mail.postmta.com ESMTP PostMTA ready
EHLO my-mail-server
250-mail.postmta.com
250-8BITMIME
250-AUTH PLAIN
250 STARTTLS
AUTH PLAIN AGFjY18wMUg1UEs5UDdOM000UTZSOFQyVjRXNlY5MGhhYmMxMjN4eXo3OGRlZjQ1Ni4uLg==
235 Authentication successful

Full SMTP send flow

After successful authentication, send an email using the standard SMTP envelope commands. All content must be UTF-8 encoded.

MAIL FROM: <hello@mail.example.com>
250 OK
RCPT TO: <alice@example.com>
250 OK
DATA
354 Begin input; end with <CRLF>.<CRLF>
From: Example Inc <hello@mail.example.com>
To: Alice <alice@example.com>
Subject: Your order confirmation
Content-Type: text/html; charset="UTF-8"
List-Unsubscribe: <https://example.com/unsubscribe/alice@example.com>

<html>
<body>
<h1>Order Confirmed</h1>
<p>Hi Alice, your order #12345 has shipped.</p>
</body>
</html>
.
250 OK msg_id=msg_01HX5K9P7N3M4Q6R8T2V4W6Y8

The server returns a msg_id on acceptance.

The MAIL FROM address must belong to a verified sending domain on your account. Using an unverified domain results in a 550 5.7.1 Unverified sender domain rejection.

OAuth 2.0 — Client Credentials Flow

For service-to-service integrations, OAuth 2.0 client credentials are the recommended approach. You exchange a client ID and client secret for a short-lived JWT access token.

Request an access token

curl -X POST https://auth.postmta.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=CLIENT_ID_FROM_DASHBOARD" \
  -d "client_secret=CLIENT_SECRET_FROM_DASHBOARD"

Response:

{
  "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "workspace_id": "ws_01HX5K9P7N3M4Q6R8T2V4W6Y0"
}

Use the access token as a Bearer token on the REST API:

curl https://api.postmta.com/v1/messages/msg_01HX5K9P7N3M4Q6R8T2V4W6Y8 \
  -H "Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
Access tokens expire after 1 hour. Implement token refresh in your HTTP client to avoid disruptions. Store client ID and client secret securely — never in source code.

Workspace Membership

Every API key is bound to a single workspace. On every authenticated request, PostMTA verifies that:

  1. The API key is valid and has not been revoked.
  2. The key's workspace is active (not suspended or deleted).
  3. The key has the required permission scope for the requested operation.

If the workspace is suspended, the API returns 403 Forbidden with error code workspace_suspended. Contact support to resolve a suspended workspace.

{
  "error": "workspace_suspended",
  "message": "This workspace has been suspended. Contact hello@netwit.ca.",
  "workspace_id": "ws_01HX5K9P7N3M4Q6R8T2V4W6Y0"
}

Multi-workspace accounts can manage each workspace independently. Keys from one workspace cannot access another workspace's data, even if the account owns both.

Rate Limits

Rate limits protect the platform and ensure fair usage across all customers. Limits are enforced at two levels: per key and per workspace.

PlanPer-Key LimitPer-Workspace Limit
Starter60 req/min300 req/min
Growth300 req/min1,500 req/min
Business1,000 req/min5,000 req/min
EnterpriseCustomCustom

When a rate limit is exceeded, the API returns 429 Too Many Requestswith a Retry-After header indicating seconds to wait:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1752856860

{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Retry after 30 seconds.",
  "retry_after": 30
}

SMTP rate limits

SMTP connections are limited to 100 messages per connection and50 concurrent connections per key. For high-volume sending, use connection pooling and keep connections alive with periodicNOOP commands.

Next Steps