inboxsink API

Scriptable disposable inboxes, so you can test signup flows without depending on a real mailbox. Five routes, one base at https://inboxsink.com, JSON in and out.

The route that changes anything is wait: it blocks until the message arrives. Without it, an end-to-end test guesses with sleep and turns flaky the moment the sending queue lags.

JavaScript client

A dependency-free package for Node 18 and above. Source on GitHub.

npm i inboxsink
import { InboxSink } from 'inboxsink';
const sink = new InboxSink(process.env.INBOXSINK_API_KEY);

const inbox = await sink.createInbox();
const code = await sink.waitForOtp(inbox.id);   // '204815'

waitForOtp throws rather than returning null when no code arrives: a test should fail loudly, not quietly submit an empty field.

Authentication

One Authorization header per call. Keys are created from the dashboard and start with ibsk_.

curl -H "Authorization: Bearer ibsk_…" \
  https://inboxsink.com/v1/domains

The plain key is shown only once, at creation. After that only its prefix stays visible — we store nothing but its hash.

Create an inbox

POST /v1/inboxes

Every parameter is optional: domain (a random pool domain by default), prefix (random by default), ttl_seconds (60 seconds to 30 days).

curl -X POST https://inboxsink.com/v1/inboxes \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"ttl_seconds": 900}'

→ 201
{ "id": "42", "address": "9f2c1a@mailhusk.com", "expires_at": "2026-09-01T15:12:00Z" }

An inbox created through the API is private: only the key that created it can read it.

Wait for a message

GET /v1/inboxes/:id/wait

Blocks until a message arrives, then answers immediately. timeout in milliseconds (30 000 by default, 120 000 maximum), since to only get messages after a given id.

Answers 204 with no body if the delay runs out — that is not an error, it is the normal answer to "nothing arrived".

curl -H "Authorization: Bearer $KEY" \
  "https://inboxsink.com/v1/inboxes/42/wait?timeout=45000"

→ 200
{ "message": { "id": "913", "from": "no-reply@acme.com", "subject": "…",
               "otp": "204815", "link": "https://acme.com/confirm/9f3a" } }

otp and link are extracted for you. A code is only reported when the message actually announces one ("code", "verification", "sign in"…) — an invoice total or a year is never mistaken for one.

List messages

GET /v1/inboxes/:id/messages

Takes since and limit (50 by default, 200 maximum). Returns summaries, without message bodies.

Read a message

GET /v1/messages/:id

The whole message: text, html, headers and the attachment list.

Delete an inbox

DELETE /v1/inboxes/:id

Erases the inbox and its messages immediately. Answers 204.

Errors

204No content — for wait, the delay ran out with no message
400Invalid parameter
401Key missing, unknown or revoked
403That inbox belongs to another key
404Inbox or message gone, or already expired
429Too many calls

Limits and lifetime

Public pool inboxes live one hour; inboxes created through the API follow their ttl_seconds, up to thirty days. After that the inbox and its messages are deleted — there is no archive to recover afterwards.

Messages over ten megabytes are refused on arrival. Addresses on an unknown domain are rejected during the SMTP session, without generating a bounce.

These inboxes cannot send mail, and that is deliberate: a message sent from a disposable domain would not arrive anywhere.