Transactional email · invite-only

Transparent infrastructure under a simple API

Transactional email from your own domain, sent by our own mail stack in the EU. DKIM, SPF and DMARC under your control.

EUPostfix · opendkim · opendmarc · rspamd
API

POST /v1/emails

Bearer token + JSON. Same shape on staging and production — no SDK lock-in, no hidden middleware.

curl
api.postfly.app
# send an email, get a message_id back
curl -X POST "https://api.postfly.app/v1/emails" \
  -H "Authorization: Bearer pf_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "from":    "noreply@yourdomain.com",
    "to":      "user@example.com",
    "subject": "Welcome",
    "html":    "<p>Thanks for signing up.</p>",
    "tag":     "welcome"
  }'

# 202 Accepted
# { "id": "m_8h2qf09sx", "status": "queued", "createdAt": "..." }

Full API reference

Why postfly

What makes postfly different

Transactional email from your own domain, sent by our own mail stack in the EU. DKIM, SPF and DMARC under your control.

Our own mail stack

Postfix + opendkim + opendmarc + rspamd. No black box — every header you see is signed and logged by us.

DKIM with your domain

Sign outbound mail with d=yourdomain.com. The visible From: aligns with the cryptographic identity — no shared signing domain.

EU-hosted

Mail servers, database and queue run in the EU.

Webhooks

Every event (sent, delivered, bounced, complained) fires a webhook. Failed deliveries are retried with exponential backoff.

SMTP too

Apps that only speak SMTP send over port 587 with STARTTLS; the API key is the password.

Daily DNS health check

A cron re-verifies SPF/DKIM/DMARC about once a day. If something drifts we email you once — no spam, alert resets on recovery.

How it works

How it works

1

Add your domain

Paste DNS records from the dashboard. We verify SPF, DKIM and DMARC and surface what is missing.

2

Issue an API key

Scoped to the domains you added, and rotatable without a redeploy. Test keys carry a "pf_test_" prefix.

3

POST to /v1/emails

JSON in, JSON out. The response carries an id to query the state or match webhook events.

Invite-only

Invite-only