TempInbox docs

API reference

Base URL https://api.tempinbox.vn/api/v1. Everything is JSON; send Accept: application/json and, where a body is required, Content-Type: application/json.

58 operations across 14 groups, taken straight from the contract.

account

GET /me

Current account

Auth: userToken

Responses: 200 401

PATCH /me

Update the account

Auth: userToken

FieldTypeRules
localestringoptional, max 10

Responses: 200 401 422

GET /me/2fa

Two-factor status

Auth: userToken

Responses: 200 401

DELETE /me/2fa

Disable two-factor

Requires a current code, so a stolen session alone cannot remove the second factor.

Auth: userToken

FieldTypeRules
codestringrequired, max 16

Responses: 204 401 422

POST /me/2fa/setup

Begin two-factor enrolment

The secret is held server-side for a short window and is not active until `confirm` succeeds, so an abandoned enrolment cannot lock the account out.

Auth: userToken

Responses: 200 401 409

POST /me/2fa/confirm

Activate two-factor

Auth: userToken

FieldTypeRules
codestringrequired, max 16

Responses: 200 401 422

auth

POST /auth/register

Create an account

Auth: public

FieldTypeRules
emailstringrequired, max 255
passwordstringrequired, min 8, max 200
localestringoptional, max 10

Responses: 201 409 422 429

POST /auth/token

Sign in

With 2FA enabled the first call answers `401 auth.totp_required`; repeat it with `code`. Invalid credentials and a wrong code are the same failure to the caller by design.

Auth: public

FieldTypeRules
emailstringrequired, max 255
passwordstringrequired, max 200
codestringoptional, max 16

Responses: 200 401 422 429

billing

GET /plans

Plan and add-on catalog (prices in points)

Auth: public

Responses: 200

GET /payment-providers

Payment gateway registry (public)

Mirrors `settings['billing.providers']` so the frontend renders a switched-off gateway as a disabled button instead of hiding it or letting it be clicked until the next deploy.

Auth: public

Responses: 200

GET /me/subscription

Current subscription and effective entitlements

Auth: userToken

Responses: 200 401

POST /me/subscription

Subscribe or switch plan (debits points immediately)

Debit and activation happen in one transaction. Switching plans cancels the previous subscription and starts a fresh period; no proration.

Auth: userToken

FieldTypeRules
planCodestringrequired
intervalstringrequired, one of monthly, yearly

Responses: 201 401 402 422

DELETE /me/subscription

Turn off auto-renew (the paid period is never cut short)

Auth: userToken

Responses: 200 401

GET /me/wallet

Wallet balance

Auth: userToken

Responses: 200 401

GET /me/wallet/transactions

Points ledger (paginated)

Auth: userToken

ParameterInType

Responses: 200

POST /me/wallet/topups

Create a top-up order

`sepay` is active (VND bank transfer). `bnb` is `coming_soon` and returns 400 payment.provider_unavailable. The wallet is never credited from the bank webhook directly — reconciliation matches memo + amount.

Auth: userToken

FieldTypeRules
providerstringrequired, one of sepay, bnb
amountintegerrequired, ≥ 1
currencystringrequired, one of VND

Responses: 201 400 422

GET /me/wallet/topups/{orderId}

Poll top-up order status

Auth: userToken

ParameterInType
orderIdpathstring

Responses: 200 404

content

GET /content/notices

Notices to show over the product

Banners and dialogs published by staff, already filtered by schedule. The HTML is sanitised on write against a fixed allowlist, so it is safe to render as-is.

Auth: public

Responses: 200

GET /content/pages

Published pages

Titles and keys only — enough for a footer, without shipping every page body.

Auth: public

Responses: 200

GET /content/pages/{key}

One published page

Auth: public

ParameterInType
keypathstring

Responses: 200 404

domains

GET /me/domains

List private domains

Auth: userToken

Responses: 200 401

POST /me/domains

Add a private domain

Auth: userToken

FieldTypeRules
domainstringrequired, max 253

Responses: 201 401 403 409 422

DELETE /me/domains/{id}

Remove a private domain

Auth: userToken

Responses: 204 401 404

POST /me/domains/{id}/verify

Re-check the DNS records

Both the TXT proof and the MX record must resolve. A domain that verified once is re-checked periodically, so removing the MX later suspends it again.

Auth: userToken

Responses: 200 401 404 422

filters

GET /me/filters

List custom extraction filters

Auth: userToken

Responses: 200 401

POST /me/filters

Create a filter (entitlement `filters.max`)

Auth: userToken

FieldTypeRules
namestringrequired, max 100
scopeTypestringoptional, one of all, domain
domainIdstring,nulloptional
fromPatternstring,nulloptional
subjectPatternstring,nulloptional
extractRegexstringrequired, max 512
priorityintegeroptional, ≥ 1, ≤ 9999
statusstringoptional, one of active, disabled

Responses: 201 401 422

POST /me/filters/test

Dry-run a filter against a sample without saving

Auth: userToken

FieldTypeRules
filterextractRegex, fromPattern, subjectPatternrequired
samplefrom, subject, textrequired

Responses: 200 401 422

PATCH /me/filters/{id}

Update a filter

Auth: userToken

FieldTypeRules
namestringoptional, max 100
extractRegexstringoptional, max 512
fromPatternstring,nulloptional
subjectPatternstring,nulloptional
priorityintegeroptional
statusstringoptional, one of active, disabled

Responses: 200 401 422

DELETE /me/filters/{id}

Delete a filter

Auth: userToken

Responses: 204 401

integrations

POST /integrations/sepay/webhook

SePay bank-transaction callback (append-only, idempotent)

Guarded by `Authorization: Apikey <shared-secret>` plus an ingress IP allowlist. The handler only appends a `sepay_transactions` row; crediting happens in the reconciliation job.

Auth: sepayApikey

Responses: 200 401

keys

GET /me/api-keys

List developer keys

Auth: userToken

Responses: 200 401

POST /me/api-keys

Issue a developer key

`key` is returned exactly once — only its hash is stored, so a database leak cannot be replayed. The tier follows the account plan and cannot be chosen.

Auth: userToken

FieldTypeRules
namestringrequired, max 100
scopesstring[]required
allowedCidrsstring[]optional
expiresAtstringoptional

Responses: 201 401 403 422

DELETE /me/api-keys/{prefix}

Revoke a developer key

Auth: userToken

Responses: 204 401 404

POST /keys/mailboxes

Create a mailbox with a developer key

Requires scope `mailboxes:write`. The mailbox belongs to the key's owner.

Auth: apiKey

FieldTypeRules
domainstringoptional, max 255
localPartstringoptional, max 64
ttlintegeroptional, ≥ 60

Responses: 201 401 403 422 429

GET /keys/mailboxes/{id}/messages

List a mailbox's messages with a developer key

Requires scope `messages:read`. A mailbox the key does not own answers 404 rather than 403, so the endpoint cannot be used to discover which mailboxes exist.

Auth: apiKey

ParameterInType

Responses: 200 401 403 404

GET /keys/mailboxes/{id}/otp/latest

Latest OTP for a mailbox with a developer key

Requires scope `otp:read`.

Auth: apiKey

Responses: 200 401 403 404

mailboxes

GET /domains

Active public domains

Auth: public

Responses: 200

GET /me/mailboxes

Mailboxes this account owns

Newest first, each with a bearer token. This is the only way to find a catch-all mailbox: it was opened by an incoming message, so the owner has never seen the address.

Auth: userToken

Responses: 200 401

POST /mailboxes

Create a mailbox

Auth: public

ParameterInType
FieldTypeRules
domainstringoptional
localPartstringoptional, min 1, max 64, pattern ^[a-z0-9][a-z0-9._-]{0,63}$
ttlintegeroptional, ≥ 60

Responses: 201 409 422 429

GET /mailboxes/me

Mailbox info + TTL

Auth: mailboxToken

Responses: 200 401 410

DELETE /mailboxes/me

Destroy the mailbox now

Auth: mailboxToken

Responses: 204 401

POST /mailboxes/me/extend

Extend mailbox TTL (re-issues the mailbox token)

Auth: mailboxToken

FieldTypeRules
ttlintegeroptional, ≥ 60

Responses: 200 403 410

messages

GET /mailboxes/me/messages

List messages (cursor paginated, optional long-poll)

Auth: mailboxToken

ParameterInType

Responses: 200 401 410

GET /messages/{id}

Full message (sanitized HTML)

Auth: mailboxToken

Responses: 200 404

DELETE /messages/{id}

Delete a message

Auth: mailboxToken

Responses: 204 404

GET /messages/{id}/raw

Download the original message (.eml)

The bytes as delivered, before parsing — full `Received:` chain, `Authentication-Results`, the untouched MIME tree and the original HTML before sanitizing. Opens in any mail client. The original is the largest per-message item in redis-mail, which runs `noeviction`, so it is kept only briefly: **15 minutes** for free mailboxes and **1 hour** for paid ones. After that this answers `410 message.raw_expired` — gone, not missing. `Message.rawExpiresAt` says when, so a client can drop the button rather than offer a download that fails.

Auth: mailboxToken

Responses: 200 404 410

GET /messages/{id}/attachments/{attId}

Download an attachment

Always served as `application/octet-stream` with `Content-Disposition: attachment` and `nosniff`, whatever the sender declared: the file arrived from an unauthenticated stranger, so the browser must never be allowed to render it. Attachments above the inline size limit are not retained and answer 404.

Auth: mailboxToken

ParameterInType
attIdpathstring

Responses: 200 404

otp

GET /mailboxes/me/otp/latest

Latest extracted OTP

Auth: mailboxToken

ParameterInType

Responses: 200 404

session

GET /session

Mailboxes opened in this session

Newest first. Expired mailboxes are omitted rather than listed as dead addresses. Each mailbox carries a freshly minted bearer token capped at its own remaining TTL — the guest key is the only credential the visitor kept, so without tokens the list would name addresses it cannot read.

Auth: guestSession

Responses: 200 401

POST /session

Open a guest session

Redis-only, sliding 30 minutes, no account. Lets an anonymous visitor keep several mailboxes across reloads. It carries no plan and grants no extra quota — mailbox creation stays clamped per IP, so minting a new session buys nothing.

Auth: public

Responses: 201 429

DELETE /session

End the guest session

Drops the key and the session's mailbox index. The mailboxes themselves keep their own TTL — this is signing out, not deleting mail the visitor may still hold a token for.

Auth: guestSession

Responses: 204 401

system

GET /healthz

Liveness probe

Auth: public

Responses: 200

GET /readyz

Readiness probe (dependencies checked)

Auth: public

Responses: 200 503

webhooks

GET /me/webhooks

List webhook endpoints

Auth: userToken

Responses: 200 401

POST /me/webhooks

Register an endpoint (entitlement `webhooks.max`)

The URL must be https on port 443/8443 and resolve to a public address. The response is the only time the signing secret is returned.

Auth: userToken

FieldTypeRules
urlstringrequired, max 2048
eventsstring[]required
domainIdstring,nulloptional

Responses: 201 401 403 422

PATCH /me/webhooks/{id}

Update an endpoint (resuming clears the failure history)

Auth: userToken

FieldTypeRules
urlstringoptional
eventsstring[]optional
statusstringoptional, one of active, paused

Responses: 200 401 422

DELETE /me/webhooks/{id}

Delete an endpoint

Auth: userToken

Responses: 204 401

GET /me/webhooks/{id}/deliveries

Recent delivery attempts (ring buffer, last 100)

Auth: userToken

ParameterInType
idpathstring

Responses: 200 401