PosthasteDocsGet an API key

Documentation

Send email, and see exactly what happened to it.

Posthaste delivers your mail directly to the recipient’s server from infrastructure we run ourselves, and keeps the entire SMTP conversation so you can read it back. This reference covers every endpoint, every error type and every event we will send you.

Quickstart

Three things stand between you and a delivered email: a key, one DNS record, and one request.

  1. Create an API key. Keys are created in the dashboard by a signed-in owner or admin whose email address has been confirmed. The key is shown once. How to get one →
  2. Verify a sending domain. Add the domain, publish the single DKIM record it returns, then run verification. Sending from an unverified domain is refused outright. Domain setup →
  3. Send. One request, one recipient. There is an official SDK for TypeScript, Python and Go; anywhere else, it is one HTTPS request and there is nothing to install.
// npm i @posthaste/sdk
import { Posthaste } from '@posthaste/sdk'

const posthaste = new Posthaste({ apiKey: process.env.POSTHASTE_KEY })

const { id, status } = await posthaste.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Reset your password',
  text: 'Click the link to reset your password.',
})
// msg_AZLm3kQ8T2Sf9pXbNc7HrQ  'queued'

202 Accepted means the message is durably stored and queued — not that it has been delivered. The message row, its content, its delivery job and its first event are committed in one transaction, so a crash cannot lose a message you were told we had.

To find out what happened next, read the message record or subscribe to webhooks.

The reference

The base URL is https://api.posthastemail.dev and every request carries an API key as a bearer token. Start with Conventions if you would rather see the shape of the API before any one endpoint.

Getting started

Sending

Sending domainsPublish one DKIM record, verify it, and read the per-record check results.Verify a domain, step by stepAdd the DKIM record at your DNS provider — Cloudflare, Route 53, GoDaddy and thirteen more, plus the two mistakes that get people stuck.Send an emailEvery field on POST /v1/emails, including replyTo and List-Unsubscribe.SMTP relaySend from any existing app by SMTP — host, ports, AUTH and the full reply-code table.Nodemailer transportKeep every sendMail() call you already have and change one config line. What maps, what changes, and what is refused rather than dropped.Django email backendChange EMAIL_BACKEND, add a key, and every send_mail() call you already have goes over Posthaste. What maps, what is refused rather than dropped, and how fail_silently behaves.Rails and ActionMailerKeep every mailer you already have and change two lines of configuration. What maps, what changes, and what is refused rather than dropped.Laravel mail driverChange MAIL_MAILER and add a key. Every Mailable, notification and queued job you already have keeps working — with an exception class per refusal instead of a three-digit SMTP code.TemplatesStore content and send it by name. Versions are immutable, a send pins the one it used, and a missing variable is refused rather than rendered blank.Batch sendUp to 100 messages in one request, each reported by index. One refused item never takes the rest down.Message streamsSeparate transactional mail from everything else, so a complaint about an announcement never withholds a password reset.Contacts and listsAn address book of your own users, for operational announcements. Every contact records where consent came from, and whether it can be mailed is the suppression list’s answer.BroadcastsAn outage notice or a terms change to every contact on a list — each copy checked like a single send, never on the transactional stream, and stopped automatically if bounces or complaints climb.Tags and metadataLabel a message with your own vocabulary, get it back on the log and every webhook, and filter by it.VerifyEmail a one-time code and check it. The attempt cap, expiry, resend throttle and the rule that keeps the code out of the subject line are already built.Address validationCheck an address before you send: syntax, MX with the RFC 5321 fallback, null MX, disposable and role accounts — and whether you have already bounced off it.Messages and the waybillRead back a message, its status, its events and the SMTP conversation.Live tailWatch mail move as it moves — a filterable event stream that resumes from where it dropped, so a lost connection never costs an event.Delivery analyticsDelivery rate and time to inbox, p50 and p95, for every receiving provider and for each of your own tags — with nothing invented where there is nothing to measure.SuppressionsWhy an address stops receiving mail, and which entries can never be removed.Sending limitsThe monthly allowance, your account’s daily cap, and how the cap moves.

Receiving

Reference

Limits, at a glance

Four different limits can return 429, and they mean different things. If you arrived here from an error message, this is the one you want.

  • Your account’s daily sending cap starts at 50 messages and rises as your sending history builds — good days move it up a step, bad ones move it down. Error type daily_limit_reached. How the cap moves →
  • Your plan’s monthly allowance is a fixed number of accepted messages per calendar month in UTC. Error type monthly_limit_reached, with a Retry-After pointing at the start of next month. Moving to a larger plan lifts it immediately.
  • The platform’s own daily ceiling is ours rather than yours: a cap on what everyone on the shared sending IP sends in a day, because that total is the number receivers judge. Error type platform_paused, with a short Retry-After. Nothing is wrong with your account. Why it exists →
  • The request rate limit is 1,200 requests per 60 seconds, counted per API key — unrelated to how much mail you may send. Error type rate_limited. Rate limits →

You never have to wait for a 429 to find out where you stand. GET /v1/me returns the cap, what you have sent today and what is left.

NextAuthentication →How to get an API key, what the scopes allow, and why there is no test environment.