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

API Keys & SMTP Credentials

PostMTA exposes two credential systems: a REST API accessed via bearer tokens, and an SMTP endpoint accessed via SMTP AUTH PLAIN. Both credential types share the same RBAC role model and workspace membership.

API Key Lifecycle

Create a Key

POST to /v1/keys with a JSON body specifying the key name, scopes, and optional expiry. The response includes the full key value — shown only once — and a truncated preview for future reference.

curl -X POST https://api.postmta.com/v1/keys \
  -H "Authorization: Bearer pmta_live_admin_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production-send-key",
    "scopes": ["messages:send", "messages:read"],
    "workspace_id": "ws_01HXABC123DEF456",
    "expires_at": "2027-07-18T00:00:00Z",
    "description": "Used by the order confirmation service"
  }'

Response — the key field appears only here:

{
  "id": "key_01HXKEY001",
  "name": "production-send-key",
  "key": "pmta_live_sk_01HXabcdef123456789abcdef123456789abcdef12",
  "key_preview": "pmta_live_sk_01HXab...ef12",
  "scopes": ["messages:send", "messages:read"],
  "workspace_id": "ws_01HXABC123DEF456",
  "created_at": "2026-07-18T09:00:00Z",
  "expires_at": "2027-07-18T00:00:00Z",
  "last_used_at": null,
  "status": "active"
}
One-time display: The full key value is returned only in this response. PostMTA does not store the full key — only a secure hash. If you lose the key, you must revoke it and create a new one.

Revoke a Key

Revocation is immediate and permanent. Any in-flight request using the revoked key receives 401 Unauthorized.

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

Response:

{
  "id": "key_01HXKEY001",
  "status": "revoked",
  "revoked_at": "2026-07-18T14:30:00Z"
}

List Keys

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

Returns all keys for the workspace. The full key field is never returned for existing keys — only the key_preview.

SMTP AUTH PLAIN

For integrations that prefer SMTP submission, PostMTA exposes an SMTP submission endpoint. SMTP credentials are separate from API keys and have their own creation and revocation flow.

SMTP Ports

PortEncryptionUseNotes
2525STARTTLSLab / developmentUnencrypted until STARTTLS upgrade
587STARTTLS (required)ProductionConnection dropped if TLS not established
465Implicit TLSProduction (legacy)Deprecated but still supported

AUTH PLAIN Command

After connecting and completing STARTTLS, issue AUTH PLAIN. The credential format is:

auth_plain_id\0username\0password

Where username is the SMTP username and password is the SMTP password. Both are ASCII. The entire string is Base64-encoded before sending.

S: 220 mail.postmta.com ESMTP PostMTA
C: EHLO client.example.com
S: 250-mail.postmta.com
S: 250-STARTTLS
S: 250 AUTH PLAIN
C: AUTH PLAIN
S: 334
C: AG1haWwuZXhhbXBsZS5jb20AYWRtaW5f c3BfdGtleV9wYXNzXzE5MjM0NTY3OA==
S: 235 2.7.0 Authentication successful

Decoded auth string for the example above:

mail.example.com   <- username (SMTP credential name)
admin_smtp_pass_192345678   <- password

SMTP Send Example

After authentication, send a message using standard SMTP envelope commands:

C: MAIL FROM:<hello@mail.example.com> SIZE=2048
S: 250 2.1.0 OK
C: RCPT TO:<alice@example.com>
S: 250 2.1.5 OK
C: DATA
S: 354 Start mail input; end with <CRLF>.<CRLF>
C: From: Hello <hello@mail.example.com>
C: To: alice@example.com
C: Subject: Order Confirmation
C:
C: Hello Alice,
C: Your order #12345 has shipped.
C: .
S: 250 2.0.0 OK: queued as msg_01HX5K9P7N3M4Q6R8T2V4W6Y8

Role-Based Access Control

Every principal (API key or SMTP credential) is assigned a role at the workspace level. Roles are hierarchical — each role includes all permissions of the roles below it.

Role Permissions

PermissionViewerEditorOperatorOwner
Read analytics and delivery stats++++
Read sending domains and IP pools++++
Send email (messages:send)-+++
Manage suppression lists-+++
Manage webhooks-+++
Create and revoke API keys--++
Manage IP pools and warmup--++
Apply shaping policy--++
Manage workspace members and roles---+
Billing and credit purchase---+
Air-gap admin grants---+
Delete workspace---+

+ = has permission   - = no permission

Safe Credential View (No Hash Columns)

API keys and SMTP credentials are stored with their password hashes using Argon2id. When listing credentials via the API, hashes are never returned. If you need to query credentials directly from the database (strongly discouraged), use the safe read view that projects only non-sensitive columns:

SELECT
  id,
  name,
  key_preview,
  scopes,
  workspace_id,
  created_at,
  expires_at,
  last_used_at,
  status
FROM postmta.credential_safe_view
WHERE workspace_id = 'ws_01HXABC123DEF456';
Never query the raw credential store. The credential_safe_view is the only approved read path for credential metadata. The underlying credentials table is off-limits except to the PostMTA internal agent running under a separate security context.

Workspace Membership and RLS

PostMTA uses Row-Level Security (RLS) on all credential and workspace tables. Every API request is scoped to the workspace(s) the calling key has access to. Cross-workspace access requires explicit membership in each workspace.

To grant a key access to an additional workspace:

curl -X POST https://api.postmta.com/v1/workspaces/ws_01HXABC123DEF456/members \
  -H "Authorization: Bearer pmta_live_admin_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "key_id": "key_01HXKEY001",
    "role": "editor"
  }'

A key may have different roles in different workspaces. The role is evaluated at request time based on the workspace_id in the request path or payload.

SMTP Credential Creation

Create SMTP credentials separately from API keys. SMTP credentials use a username/password model and are used only for SMTP submission.

curl -X POST https://api.postmta.com/v1/smtp/credentials \
  -H "Authorization: Bearer pmta_live_admin_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "mail.example.com",
    "description": "Production SMTP submission for example.com",
    "workspace_id": "ws_01HXABC123DEF456",
    "allowed_sending_domain": "mail.example.com",
    "expires_at": "2027-07-18T00:00:00Z"
  }'

Response — the password field is shown only once:

{
  "id": "smtp_cred_01HXSMTCP001",
  "username": "mail.example.com",
  "password": "smtp_pass_19234567890abcdef",
  "password_preview": "smtp_pa...bcdef",
  "workspace_id": "ws_01HXABC123DEF456",
  "allowed_sending_domain": "mail.example.com",
  "created_at": "2026-07-18T09:00:00Z",
  "expires_at": "2027-07-18T00:00:00Z",
  "status": "active"
}

Credential Auditing

All credential lifecycle events are written to the immutable audit log:

View audit entries:

curl "https://api.postmta.com/v1/audit?resource_type=credential&limit=50" \
  -H "Authorization: Bearer pmta_live_admin_xxxxxxxxxxxx"