Back to your inbox

DEVELOPER TOOLS · API REFERENCE

An inbox in your code.
One less thing to build.

Private temporary inboxes for signup tests, verification codes and throwaway workflows. Every endpoint and SDK is free.

Create an inbox. Send it an email. Read the result.

The response tells you whether your inbox is live or sandbox. Live addresses receive email from your application or email provider; .test addresses use the sample endpoint. SDK files are available below, with no registry installation required.

Your first inbox, in a few lines.

SDKs handle private access for you.
// Download /sdk/inbini.mjs into this directory.
import { Inbini } from './inbini.mjs';

const mail = new Inbini({
  baseUrl: 'https://inbini.com/api/v1'
});
const inbox = await mail.inboxes.create({ expiresIn: '45m' });

try {
  console.log(inbox.address);
  if (inbox.data.mode === 'sandbox') await inbox.sample();
  // On a live domain, trigger your application's email now.
  const code = await inbox.waitForCode({ timeout: 60_000 });
  console.log(code);
} finally {
  await inbox.delete();
}

These examples run against a locally running or publicly accessible API. An owner-private Sites preview requires browser sign-in, so external SDK requests cannot access it directly.

PICK YOUR LANGUAGE

Small SDKs. Useful defaults.

OpenAPI specification

Prefer typed source? Download the TypeScript file. Go, PHP, Java, C# and Ruby are planned. They are not available yet.

THE RIGHT KEY FOR EACH REQUEST

No global API key to get started.

Creating an inbox on the default or a public domain needs no authentication. Each inbox is private: use its own token for every read or delete request.

CredentialHow you get itWhat it allows
INBOX_TOKEN · ib_…Returned when creating an inbox.Read/delete that inbox and its messages; deliver sandbox samples.
WORKSPACE_TOKEN · wk_…Returned when creating a workspace.Manage private domains and create inboxes on them. It cannot read inbox contents.
Receiver key · rx_…Generated after proving domain ownership.Deliver mail through a domain’s receiver endpoint. It cannot read inboxes or manage domains.

Authenticated HTTP requests use Authorization: Bearer TOKEN. JSON requests use Content-Type: application/json. SDKs set these headers for you.

REQUEST. RESPONSE. NEXT STEP.

Examples for every request.

Open an endpoint for its parameters, a complete request in your language, and the response to expect. Examples use https://inbini.com.

Before you copy

Use your real values for INBOX_ID, INBOX_TOKEN, MESSAGE_ID, DOMAIN_ID and WORKSPACE_TOKEN when a request needs them. Set them in your shell or secret manager; never put tokens in URLs or commit them.

JavaScript examples run as .mjs files in Node.js 22+ with inbini.mjs beside them. Python examples need Python 3.10+ and inbini.py beside the script. Download either SDK above. SDK examples resume existing inboxes with an initial GET, then perform the requested action.

Responses below are illustrative. Replace the sample IDs and abbreviated tokens with the real values returned by your API. HTTP successes wrap the result in data; the SDKs unwrap it. A 204 response has no body.

Inboxes & messages

Create a private address and choose when it expires. Save the returned inbox ID and token: the token is shown only on creation.

No authentication
ParameterWhereHow to use it
expiresInJSON · optionalPositive whole number + s, m, h or d. For example 45m, 2h or 7d. Defaults to 1h. Every inbox expires.
domainIdJSON · optionalOmit for the default domain. Use an ID from /api/config for a public domain, or from your workspace for a private domain.
Create an inbox · curl
curl --include --silent --show-error \
  --request POST "https://inbini.com/api/v1/inboxes" \
  --header 'Content-Type: application/json' \
  --data "{\"expiresIn\":\"45m\"}"
Example HTTP response
201 response
HTTP/1.1 201 Created
Content-Type: application/json

{
  "data": {
    "id": "11111111-1111-4111-8111-111111111111",
    "address": "[email protected]",
    "createdAt": "2026-09-17T12:00:00.000Z",
    "expiresAt": "2026-09-17T12:45:00.000Z",
    "domainId": null,
    "mode": "live",
    "token": "ib_EXAMPLE_TOKEN_SAVE_THE_REAL_VALUE"
  }
}
  • The SDK returns an inbox object. Read its address with inbox.address and save inbox.id and inbox.token privately to resume later.
  • For a public domain, no workspace key is needed. A private domain needs its owning workspace key and must be ready.

When it fails: 400 INVALID_LIFETIME for an invalid duration; 401 WORKSPACE_REQUIRED for a private domain without its key; 409 DOMAIN_NOT_READY until DNS and reception are verified.

Workspaces & domains

Service

FROM DNS TO YOUR FIRST DELIVERY

Connect a domain in order.

For an existing public domain, read GET /api/config and use its ID when creating an inbox. To connect a private domain, follow these steps or use Your domains.

  1. Create a workspace and save its token. Add your domain with POST /api/v1/domains.
  2. Publish the returned TXT proof and configure Cloudflare Email Routing for this domain or subdomain. Apply the MX records Cloudflare provides.
  3. Call the domain’s /verify endpoint. Check that ownershipVerified and mxVerified are both true.
  4. Generate a receiver key. Download the Cloudflare receiver and follow its README to deploy the Worker and set the domain’s catch-all routing rule. Use the returned inbound path, hostname and receiver key.
  5. Send a real email from another provider to testAddress. This tests the complete mail route; its contents are discarded.
  6. Read the domain until status is ready. Create an inbox with its domainId and your workspace key. Use the newly returned inbox token to read mail.

Keep the ownership TXT record published. DNS is rechecked on active use after ten minutes, and failed checks block new creation or delivery until verification succeeds. Reissuing a receiver key requires a new connection test.

HANDLE THE UNHAPPY PATH

Know what to retry.

HTTP errors use an error object instead of data. The SDKs raise InbiniError with the status and code. Network failures may raise a native error.

Example expired-inbox error
HTTP/1.1 410 Gone
Content-Type: application/json

{
  "error": {
    "code": "INBOX_EXPIRED",
    "message": "This inbox has expired. Create a new one."
  }
}
Status / codeWhat to do
400 · INVALID_LIFETIME / INVALID_FILTERCorrect the duration or filter. Repeating the same request will not fix it.
401 · UNAUTHORIZED / WORKSPACE_REQUIREDCheck the ID and correct token type. A deleted or cleaned-up inbox also returns 401.
403 · SANDBOX_ONLYSend a real email to a live inbox. The sample endpoint only works for .test addresses.
404 · MESSAGE_NOT_FOUND / DOMAIN_NOT_FOUNDCheck the resource ID and ownership. It may have been deleted.
409 · DOMAIN_NOT_READY / DOMAIN_NOT_VERIFIEDComplete DNS verification and the receiver connection test, then retry.
410 · INBOX_EXPIREDCreate a new inbox. Resuming an inbox cannot extend its expiry.
429 · RATE_LIMITEDWait the Retry-After header’s number of seconds. Setup and security operations can be throttled; inbox creation, polling and message count have no fixed quotas.
503 · DNS_UNAVAILABLE / DATABASE_UNAVAILABLERetry after the service or DNS lookup recovers. A failed lookup does not count as successful verification.
408 · WAIT_TIMEOUT (SDK only)No matching result arrived before the local polling deadline. Check filters and delivery; this is not an HTTP status returned by the inbox API.

Poll for the message you actually need.

Create an inbox, record your test start time, trigger your application’s email, and poll with that timestamp. The example below starts a new polling window; set after to the saved test start time if the email was already triggered.

JavaScript · filtered polling and errors
import { InbiniError } from './inbini.mjs';

// With an existing inbox object:
try {
  const code = await inbox.waitForCode({
    from: '[email protected]',
    subject: 'verification',
    after: new Date().toISOString(),
    timeout: 60_000, // milliseconds
  });
  console.log(code);
} catch (error) {
  if (error instanceof InbiniError) {
    console.error(error.status, error.code, error.message);
    // error.retryAfter is in seconds.
  } else {
    throw error;
  }
}

The wait helpers poll once per second and honor Retry-After when throttled. Ordinary one-shot SDK calls raise the error so your code can decide what to retry. Use waitForMessage / wait_for_message for the full message, or waitForLink / wait_for_link for its first HTTP(S) link.

Choose the expiry your workflow needs.

There are no fixed quotas for inbox creation, requests per inbox or messages per inbox. Inboxes still expire: use a positive duration such as 45m, 2h or 7d. The default is one hour, and resuming an inbox never extends it.

Message contents are plain text and are deleted on expiry or when you delete them. Operational receipt metadata is retained permanently, as described on the privacy page.

Inbini. Free in your favorite language.Back to your inbox