FAQ

Questions, answered properly

The things people actually ask about Posthaste, with real answers rather than a sentence that avoids the question.

Is this the right thing for me?

The questions people ask before they read any documentation.

I just need an email service to send emails from my app. Is this it?

Yes, as long as the emails are ones your application sends to a person because that person did something — signing up, resetting a password, placing an order, being invited. That is what transactional email means, and it is most of what this does.

The one exception is operational announcements to your own users: an outage notice, a change to your terms, release notes. Those go to a contact list as a broadcast, on a separate stream from your transactional mail. Newsletters, promotions and mail to people who never signed up with you are marketing email, and we do not accept them. Everything else about sending is one HTTPS request.

How do I send emails from my website?

Your website’s server-side code makes the request; the browser never does. Verify the domain you want to send from, create an API key, and POST the message to /v1/emails.

Keep the key on the server. An API key in front-end JavaScript is readable by anyone who opens the page, and can then be used to send mail as you.

Is this an alternative to SendGrid, Mailgun, Resend or Amazon SES?

It does the same job: your application makes one HTTPS request and a transactional email — a password reset, a receipt, a verification code — arrives.

The difference worth comparing is who runs the mail servers. We run ours: the message is signed and delivered by our own infrastructure straight to the recipient, and we keep the entire SMTP conversation so you can read back exactly what the receiving server said. When a provider sends through a third party, the answer to "what happened to this message" can only be as detailed as what that third party passes back.

Why are my emails going to spam?

Usually one of four things: the sending domain is not authenticated, the sending IP has no reputation, the reverse DNS does not match the name the server introduces itself with, or the content looks like bulk mail.

We sign every message with DKIM from a domain you have verified, the sending IP’s reverse DNS and HELO name agree by design, and volume ramps gradually rather than starting at full speed — a brand-new IP sending thousands on day one is the fastest way to be filtered. What we cannot do is make a domain trusted overnight. Nobody can.

Why did my email bounce, and how do I find out?

Every message keeps the receiving server’s own words. Not a category, not a code we invented — the actual response its mail server gave, stored against the message.

That is the difference between "delivery failed" and knowing the recipient’s server said the mailbox is full, or that it does not exist, or that it wants you to slow down. The first tells you nothing you can act on.

My emails are not arriving and I do not know why. Where do I start?

Read the delivery record for the message. It holds the whole conversation with the recipient’s server, so the question becomes what the far end actually said rather than what we guessed it meant.

The common causes are a domain that is not verified, an address on your suppression list from an earlier bounce, or a message accepted and then deferred by the recipient. All three look identical from the outside and are told apart immediately by the record.

Do I need to run my own mail server?

No. Running the mail servers is the part we do. You make one HTTPS request.

The reason it is worth knowing we run them is that it determines what we can tell you afterwards, and who your mail’s reputation depends on.

Can I use SMTP instead of the API?

Yes. We run an SMTP relay: the host is smtp.posthastemail.dev, the username is literally posthaste, and the password is any API key with the emails:send scope. Ports 587 and 2587 are STARTTLS, 465 and 2465 are implicit TLS, and AUTH is only offered once TLS is up — a key is never accepted in the clear.

A message submitted over SMTP takes the same accept path as an API send: the same verified-domain check on the From header, the same suppression check, the same warmup ladder, the same attachment limits, and the same delivery record afterwards. An existing application that only speaks SMTP drops in by changing its mailer settings — /docs/smtp has the reply codes and worked configurations for nodemailer, Python, Django, Rails and WordPress.

The one thing that does not carry over is scheduled sending, which is API and SDK only. The mail server also listens on port 25, but that is for receiving bounces from the wider internet, not for accepting your mail.

How do I send email from a Node, Next.js or serverless app?

In TypeScript or JavaScript, install @posthaste/sdk and call posthaste.emails.send(). It has zero runtime dependencies — global fetch and node:crypto, nothing else — and it brings typed errors, retries that will not duplicate a send, pagination that terminates, and webhook signature verification.

The SDK is a convenience, not a requirement. Underneath it is one POST to /v1/emails with an API key, and no connection to keep open, so the API works anywhere that can make an HTTPS request — a server, a container, a serverless function, an edge runtime, any language.

The quickstart in the documentation is a complete working example in the SDK, Node, curl and Python.

Is there a free tier?

Yes — 750 emails a month at $0, across up to 1 domains, with 30 days of delivery records.

Webhooks and delivery records are not charged for on any plan, including this one.

What is the difference between an email API and an SMTP relay?

An SMTP relay accepts mail from your application over the same protocol mail servers use between themselves, and forwards it. An email API accepts an HTTPS request describing the message and builds it for you.

In practice the API is easier to call from modern application code and from environments where holding an SMTP connection is awkward, and it makes per-message status straightforward to report back. We are the second kind.

Can I send newsletters or marketing campaigns?

No, and the refusal is deliberate. Bulk campaigns carry a different complaint profile from transactional mail, and on shared infrastructure one bad list damages inbox placement for every other sender on it.

Turning the category away is worth more to your password resets than the revenue would be.

Can I email all of my users about an outage or a change to my terms?

Yes. That is what contacts, lists and broadcasts are for: operational announcements to people who already use your product — outage and incident notices, terms and policy changes, release notes, account notices. Every contact records where permission to mail them came from, and there is no way to add one without saying.

A broadcast goes out on the built-in announcements stream, never the transactional one, so somebody who unsubscribes from announcements still receives their password resets. Every copy carries a one-click unsubscribe header and a visible unsubscribe link, goes through the same suppression list, daily cap and content checks as a single send, and the broadcast stops on its own if too many recipients bounce or complain.

It is not a newsletter tool. Promotions, re-engagement campaigns, and purchased, rented or scraped lists are not allowed, with or without a broadcast.

Getting started

What it takes to send your first message.

What do I actually need to set up?

One DNS record. You add a domain, we generate a DKIM key pair for it, and you publish the public half as a TXT record. Once we can see it, the domain is verified and you can send.

We deliberately do not ask you to change your SPF record. A domain may publish only one SPF record, and replacing yours with ours would break every other system that sends as you — your CRM, your helpdesk, your invoicing tool.

Why is SPF not required?

DMARC passes when either SPF or DKIM aligns with the visible sender domain. We sign every message with a key belonging to your domain, so DKIM alignment on its own satisfies DMARC.

The envelope sender on our mail is our own bounce domain, which is what lets us attribute a bounce to an exact send. SPF is evaluated against that envelope domain, which is ours — so an SPF record on your domain would not be doing the work people assume it does.

How long does domain verification take?

As long as your DNS takes to propagate — usually minutes, occasionally up to an hour. You can re-check on demand rather than waiting for a polling cycle.

If the record is wrong we tell you what we found instead of just saying "not verified", because the usual causes are a provider appending your domain to the record name twice, or splitting a long TXT value incorrectly.

Can I try it without a domain?

Not for real sending, no. Sending from an unverified domain is how a platform becomes a spoofing service, so it is refused outright rather than queued and quietly failed.

Sending and delivery

What happens between your API call and the recipient.

What happens when I call the API?

We run the cheap checks first — address shape, whether your domain is verified, whether the recipient is suppressed — and refuse immediately with a reason if any fail. Nothing is queued that we already know will not be sent.

If it passes, the message, its content, its delivery job and its first event are written in a single database transaction, then acknowledged. There is no window where a message is accepted but its job is missing, so a crash after acknowledgement cannot lose it.

What does "we run our own mail servers" actually mean?

We operate the mail transfer agent and the IP it sends from. When your message goes out, our software looks up the recipient domain’s MX records, opens an SMTP connection to their server, negotiates TLS, and conducts the conversation itself.

Most providers hand that step to Amazon SES or similar. That is the difference behind everything else on this site: you can only give somebody the receiving server’s own words if you were the one talking to it.

Is mail encrypted in transit?

We negotiate STARTTLS with the receiving server whenever it offers it, which is the overwhelming majority of mail today.

Where a receiver offers no TLS at all, the alternative to sending in the clear is not delivering — so we deliver and record exactly what the connection was. The details are in that message’s record rather than hidden behind a summary.

What if I send the same request twice?

Pass an idempotency key and we deliver once. The second call returns the original message id rather than an error, so a client can retry blindly after a timeout without worrying about double-sending a receipt.

Do you support attachments, HTML, and custom headers?

HTML and plain text alternatives, custom headers, reply-to, List-Unsubscribe and attachments, yes. Up to 10 files per message and 10 MiB combined once decoded, including inline images referenced by cid. Executable file types are refused — the same set the big mailbox providers bounce at the door anyway — see the docs for the exact shape and limits.

Bounces and suppression

What happens when a message does not get through.

How do you know which message a bounce belongs to?

Every message goes out with a unique, signed return path. When a bounce comes back it addresses that return path, so attribution is exact rather than a guess based on the recipient address.

This matters more than it sounds: the same address may have received five messages from you this week, and "which one bounced" is the difference between a useful record and a shrug.

What is the difference between a bounce and a deferral?

A permanent refusal (a 5xx response — no such mailbox, domain does not exist) is a bounce. We stop, record it, and suppress the address immediately.

A temporary refusal (a 4xx — greylisting, rate limits, a full mailbox) is a deferral. We retry on a backoff and we never suppress, because suppressing on a transient error would make a perfectly good address unreachable forever.

Can I remove an address from the suppression list?

You can remove a suppression you created yourself, or one caused by a bounce you know has been fixed. You cannot remove a complaint suppression — somebody marked your mail as spam, and sending to them again is what gets an IP blocklisted.

Do you handle complaints from feedback loops?

Not yet, and we would rather say so than let the machinery imply otherwise. An ARF report is parsed, attributed to the exact message and the address suppressed — that code is written and tested — but we are enrolled with no provider’s feedback loop, so nothing is arriving for it to read.

What that means today: the only complaint signal we get is what a receiving server chooses to put in a bounce. Your complaint rate still feeds the warmup governor and still lowers your daily cap; it is simply measured from narrower evidence than it will be once the registrations are done.

Limits and deliverability

Why your cap is what it is, and how it moves.

Why can I only send 50 emails on my first day?

Because a new sending IP has no reputation, and volume that appears out of nothing looks exactly like a compromised host. Ramping gradually is what the receiving side expects from a legitimate sender.

The cap climbs one rung at a time — 50, 200, 500, 1,000, 2,500, 5,000, 10,000, 25,000, 50,000, 100,000 — after each day where you used at least 80% of it with complaints at or under 0.1% and bounces at or under 5%.

What happens if I hit the cap?

The API returns 429 with the limit, how much of it you have used, and a Retry-After header pointing at the reset, which is midnight UTC. Nothing is silently queued for tomorrow — a refused message was never accepted, so you decide what to do with it.

You can also check where you stand at any time without hitting a refusal: GET /v1/me reports your daily limit, what you have sent today and what remains.

Can I pay to skip the warmup?

No. The cap protects every other customer sharing the infrastructure, so it is not for sale on any plan. Capacity is earned by sending cleanly for a day, which is the only thing the receiving side actually responds to.

Will my email land in the inbox?

We cannot promise that, and neither can anyone else honestly. Placement is decided by the receiving provider based on your domain reputation, your content and our IP reputation.

What we can promise is that you will see exactly what happened, including the receiving server’s own diagnostic text when something went wrong — which is what lets you fix it rather than file a ticket.

Webhooks and records

Getting events out of Posthaste and into your systems.

How do I know a webhook really came from you?

Every delivery carries a timestamped HMAC signature over the raw request body. Verify it with a constant-time comparison and reject anything outside your tolerance window.

One trap worth naming: verify the raw bytes before parsing the JSON. A body that has been parsed and re-serialised will not match the signature, and the resulting bug looks like an intermittent signature failure.

What happens if my endpoint is down?

We retry with backoff — seconds, then minutes, then hours — and each retry carries the same idempotency key, so a redelivery can never be mistaken for a second event.

A 410 or 404 stops delivery immediately rather than retrying for twelve hours into an endpoint that has told us it no longer exists.

What is in the delivery record?

The actual conversation: the receiving server’s response codes, its diagnostic text, which MX we connected to, how many attempts were made and why each retry was scheduled.

It is append-only and hash-linked, so entries cannot be quietly rewritten after the fact. You can verify the chain yourself instead of taking our word for it.

Security and data

What we hold and how it is protected.

Who can see my message content?

Tenant isolation is enforced by PostgreSQL row-level security with FORCE enabled, and the application connects as a role that cannot bypass it. A query that forgets to filter by account returns nothing rather than somebody else’s data.

Message bodies are encrypted at rest with AES-256-GCM under a key held outside the database, so a database dump on its own does not read them. The subject line and the addresses are not encrypted — they are what the delivery record is indexed by — so if something must never be readable, keep it out of the subject.

Message bodies are stored separately from delivery records and deleted after 30 days, because the record of what happened needs to outlive the contents of the message.

Can your staff read my mail?

Only through one route, and never quietly. An operator opening a stored message body has to give a written reason of at least ten characters — enforced by the database, not by the screen — and the reason is written to an append-only audit log in the same transaction that reads the message.

Then you are emailed: which message, sent to whom, opened when, by whom, and the reason verbatim rather than summarised. It cannot be turned off. The promise is not "we never look", which would not survive the first abuse report; it is that we cannot look without telling you.

Where is my DKIM private key?

Encrypted at rest with AES-256-GCM, under a key held outside the database and mounted at runtime. A database dump on its own does not let anyone sign mail as your domain — which would be worse than leaking the data, since it would let them send DMARC-passing mail we vouch for.

Are you SOC 2 or ISO 27001 certified?

No — no certification of any kind, and no third-party audit of any kind. We are not going to imply otherwise with a badge or a phrase like "enterprise-grade", and we are not going to put a date on when that changes, because we have not decided. If what you need is an audited provider today, we are not it.

What we offer instead is specificity: the security page lists the controls that actually exist, described precisely enough to be checked, with the limits of each one stated next to it. Ask us about any of them.

Do you read or train on my email?

No. Your message content is processed to deliver it and for nothing else. It is not used to train anything, and it is not shared with any third party — there is no email provider in our sub-processor list, because we do the sending ourselves.

Pricing and the product

What it costs and what stage this is at.

How can you charge $19 a month for this?

Because we own the pipe. A provider built on top of another sending service pays for that service plus their own margin, and both come out of your bill. Running our own infrastructure means one cost instead of two.

Starter is $19 a month for 100,000 emails, and there is no charge for webhooks or delivery records.

Why will you not accept marketing email?

Bulk campaigns carry a different complaint profile from transactional mail, and on shared infrastructure one bad list damages placement for everyone. Refusing the category outright is worth more to your password resets than the revenue would be.

Announcements to your own users are the narrow exception, because the people receiving them already have a relationship with you and expect to hear about an outage or a terms change. They are held to tighter limits than a single send, not looser ones.

Is Posthaste production-ready?

The sending path, bounce handling, signed webhooks, the delivery record and the dashboard are built, tested and in production. The sending IP is still young, so inbox placement is still being established and the warmup ladder is deliberately conservative.

We would rather tell you that now than during your incident review. If you need years of IP reputation today, use Postmark and come back later.

What happens to my data if you shut down?

You can export your delivery records through the API at any time, and we would give notice rather than disappearing. It is a fair question to ask a young company, and the honest answer is that your records are exportable by design precisely so you are never locked in.