LiveAPI v1.2.015 endpointsRFC 5321 · 5322

Real addresses on s7ortlynk.com. One call to wait for the mail.

Create a mailbox, use it anywhere, then block on a single request until the message lands — with the verification code already pulled out for you.

https://mail.s7ortlynk.com/api/v1 Open webmail
# a fresh, real-looking address
$ curl -sX POST $BASE/mailboxes -d '{}' | jq -r .address
priya.nwosu31@s7ortlynk.com

# sign up somewhere with it, then wait
$ curl -s "$BASE/messages/wait?mailbox=priya.nwosu31" \
    | jq -r '.codes[0]'
731902

Overview

A self-hosted, receive-only mail service. Mail for any address you create is accepted on port 25 and kept until you delete it. You reach it three ways: this API, the webmail at mail.s7ortlynk.com (also at s7ortlynk.com), and the Telegram bot.

15
endpoints under /api/v1
TLS 1.2+
HTTPS and SMTP STARTTLS
1 000
messages kept per address
25 MiB
largest message accepted

Requests and responses are JSON unless noted. Every timestamp is ISO 8601 with an explicit +00:00 offset. An interactive console (Swagger UI) lives at /swagger and the machine-readable schema at /openapi.json.

These are permanent mailboxes. Nothing expires on a timer. An address keeps its newest 1 000 messages; past that, the oldest are dropped as new mail arrives.

Quick start

Three requests take you from nothing to a verification code. Paste your key below and every example on this page fills it in.

The key stays in this page's memory only. It is never saved or sent anywhere.

Create, then wait
BASE="https://mail.s7ortlynk.com/api/v1"
KEY="YOUR_KEY"

# 1. a random, realistic address (send a local_part to choose one)
curl -s -X POST "$BASE/mailboxes" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{}'

# 2. use the address to sign up somewhere

# 3. block until the mail lands; codes are already extracted
curl -s "$BASE/messages/wait?mailbox=priya.nwosu31&timeout=60" \
  -H "X-API-Key: $KEY" | jq '{subject, code: .codes[0], links}'

Give your HTTP client a timeout longer than the timeout you ask the server for. Otherwise the client gives up before the answer arrives.

Authentication

Every endpoint except /health needs your API key in a header.

Header
X-API-Key: YOUR_KEY

Get a key by messaging @AFK_MAIL_BOT on Telegram and sending /apikey. Sending /newkey issues a new one and the old key stops working at once.

A key sees only its own account. Asking for someone else's mailbox, message or attachment returns 404, exactly as if it did not exist, so ids cannot be probed.

Core ideas

Three ways to name a mailbox

Wherever a path or parameter takes a mailbox, you can pass its numeric id, its full address, or only the part before the @. These three point at the same mailbox:

/api/v1/mailboxes/7
/api/v1/mailboxes/priya.nwosu31@s7ortlynk.com
/api/v1/mailboxes/priya.nwosu31

Random addresses look like people

Creating a mailbox without a name gives it a realistic one built from common first and last names in six shapes: steven.gonzalez, priya.nwosu31, h.cook350, jisoo_wright, isabeldiaz13, jordanm6474. Some sign-up forms reject strings that look machine-made; these pass.

Message ids are a cursor

Ids only ever increase. Pass the last id you have seen as since_id to /messages or /messages/wait and you get only what is newer.

Codes and links come pre-extracted

Each full message carries codes and links. Codes are ranked by how close they sit to words like code, OTP or verify; years and order numbers are filtered out. Links that look like confirmation links sort first.

System

GET/api/v1/healthpublic

Liveness probe for uptime monitors. Also answers at /health.

200
{ "ok": true, "domain": "s7ortlynk.com", "version": "1.2.0" }
GET/api/v1/mekey

The account the key belongs to. The quickest way to check a key works.

200
{ "id": 1, "telegram_id": 935200729, "username": "admin",
  "is_admin": true, "mailbox_count": 3 }

Mailboxes

Only addresses that exist here receive mail. Anything else is turned away during the SMTP conversation with 550 No such mailbox here, before the sender transmits the message.

GET/api/v1/mailboxeskey

Every address you own, oldest first. Returns an array of Mailbox.

POST/api/v1/mailboxeskey

Create an address. Send {} for a random realistic name, or choose one.

FieldTypeDescription
local_partstring | null1–64 characters: lowercase letters, digits and . _ -, starting and ending with a letter or digit. Omit for a generated name.
Request
curl -s -X POST "$BASE/mailboxes" -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" -d '{"local_part": "orders"}'
201
{ "id": 7, "address": "orders@s7ortlynk.com", "local_part": "orders",
  "notify": true, "created_at": "2026-10-03T18:32:23.084585+00:00" }

400 when the name breaks the rules above, is reserved, is already taken, or you already have 10 addresses.

GET/api/v1/mailboxes/{ref}key

One mailbox, by id, address or local part.

DELETE/api/v1/mailboxes/{ref}key

Deletes the address, all of its mail and its attachment files. Returns 204.

This cannot be undone, and the address becomes free for anyone to claim.

POST/api/v1/mailboxes/{ref}/mutekey

Stops Telegram notifications for this address. Mail is still received. Returns the updated Mailbox.

POST/api/v1/mailboxes/{ref}/unmutekey

Turns notifications back on.

Messages

GET/api/v1/messageskey

Summaries across your addresses, newest first.

QueryTypeDefaultDescription
mailboxstringallId, address or local part.
limitinteger501 to 200.
offsetinteger0For paging.
unread_onlybooleanfalseSkip messages already read.
since_idintegernoneOnly ids above this one.
GET/api/v1/messages/{id}key

The full message: headers, both bodies, attachments, codes and links. Opening it marks it read; add ?mark=false to look without changing that.

200 · MessageDetail
{
  "id": 42,  "mailbox_id": 7,
  "msg_from": "GitHub <noreply@github.com>",
  "msg_to": "priya.nwosu31@s7ortlynk.com",
  "subject": "[GitHub] Please verify your device",
  "body_text": "Hi,\n\nYour verification code is 731902\n…",
  "body_html": "<html>…</html>",
  "size_bytes": 5120,  "received_at": "2026-10-03T18:41:07.512+00:00",
  "is_read": true,  "attachment_count": 0,  "attachments": [],
  "codes": ["731902"],
  "links": ["https://github.com/sessions/verified-device?token=…"],
  "auth_verdict": "pass",
  "auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass", "verdict": "pass", "tls": "TLSv1.3" },
  "preview_url": "https://mail.s7ortlynk.com/p/2a.1.6aca8c94.YSpR4gEdWluxOwPrOobZqg"
}
GET/api/v1/messages/{id}/rawkey

The original message exactly as received, as message/rfc822: every Received hop, DKIM-Signature, Authentication-Results and so on. Save it as a .eml file to open in any mail client.

DELETE/api/v1/messages/{id}key

Deletes the message and its attachment files. Returns 204.

Wait for mail

The request automation is built around. It holds the connection open until a new message arrives, then returns it in full.

GET/api/v1/messages/waitkey

Returns the first message newer than since_id as a full MessageDetail, codes included. If nothing arrives before the timeout it returns null with status 200, so a client simply calls again.

QueryTypeDefaultDescription
mailboxstringallId, address or local part. Set it when several addresses are waiting at once.
since_idinteger0Return the first message with a higher id. Pass the last id you saw.
timeoutnumber30Seconds to hold the request, 1 to 120.
markbooleantrueMark the returned message read.
Wait up to a minute
curl -s "$BASE/messages/wait?mailbox=priya.nwosu31&timeout=60" \
  -H "X-API-Key: YOUR_KEY"

The server checks for new mail every half second, so a message is usually returned within a second of the sending server finishing its DATA command.

HTML & text views

Most mail arrives with both an HTML version and a plain-text version. This endpoint renders either one, safely.

GET/api/v1/messages/{id}/htmlkey
QueryTypeDefaultDescription
viewauto | html | textautoauto picks HTML when the message has it. text on HTML-only mail returns the HTML flattened to readable text, links kept.
imagesbooleanfalseAlso load remote images. Off by default so opening mail does not trigger tracking pixels.
200 · RenderedBody
{
  "id": 42,
  "html": "<p>Hi,</p><p>Your verification code is <b>731902</b></p>…",
  "format": "html",
  "available": ["html", "text"],
  "blocked_images": 1,
  "inlined_images": 0
}

format is the view actually rendered; available lists the versions the message really has. Show an HTML / Text switch only when both are present.

What the sanitizer removes: <script>, <style>, <iframe>, forms, every on… event handler, and javascript: and data: links. Images embedded in the message (cid:) are inlined. Every remaining link gets rel="noopener noreferrer nofollow".

The body_html field on a message is the raw HTML as sent. Never insert it into a page directly; use this endpoint.

Attachments

GET/api/v1/attachments/{id}key

Downloads one file with its original name and type. Ids come from a message's attachments list.

curl -OJ -H "X-API-Key: YOUR_KEY" "$BASE/attachments/9"

410 Gone means the record exists but the file is no longer stored.

Web previews

Every message can be opened as a web page that looks like it does in a mail client: subject, sender, date, the verification code highlighted, and the mail's own design. No login is needed to open one.

GET/p/{token}link is the key

Every message the API returns carries its link in preview_url, and the Telegram bot's View email button opens the same page. Links look like https://mail.s7ortlynk.com/p/4.1.6aca8c94.YSpR4g… and are made by the server; you cannot build one yourself.

Get the link for the newest message
curl -s "$BASE/messages?limit=1" -H "X-API-Key: YOUR_KEY" | jq -r '.[0].preview_url'
QueryDefaultDescription
viewautohtml or text. The page has HTML / Text tabs that set this.
images01 shows remote images. The page offers a "Show images" link when some were hidden.

How a link is protected

PropertyDetail
SignedHMAC-SHA256 over the message id, owner and expiry with a 256-bit server key. Changing any character gives 404.
One messageA link opens exactly one message and nothing else.
Bound to the ownerIf the address is deleted and someone else claims it, old links stop working.
ExpiresAfter 7 days. Tap the button again for a fresh one.
No scriptsThe page ships no JavaScript and sends a policy that forbids scripts, frames, forms and every network request except, on request, images.
Not indexednoindex, no-store, no-referrer, and the token is redacted from server logs.

Anyone you forward a preview link to can read that one message until it expires. Treat it like the message itself.

Sender checks

Anyone can type any name into the From line of an email. When a message arrives, the server checks whether the sending domain actually vouches for it, the same three checks Gmail and Outlook run.

CheckQuestion it answers
SPFIs the server that delivered this allowed to send for that domain? (RFC 7208)
DKIMDoes the message carry a valid cryptographic signature from the domain? (RFC 6376)
DMARCDoes a passing SPF or DKIM result match the domain shown in From, and what does the domain ask receivers to do if not? (RFC 7489)

The results are on every message as auth, with a one-word summary in auth_verdict:

VerdictMeaningWhat clients do
passThe From domain is verified.Show a "Verified sender" mark.
failThe domain says this message is not theirs. Treat it as forged.Show a warning and do not highlight its code.
noneThe domain publishes nothing to check against.Show nothing.

Mail is never rejected or delayed because of these checks; they only label it. The same results are written into the stored source as an Authentication-Results header, and auth.tls records whether the message arrived over an encrypted connection.

If your automation acts on codes, check auth_verdict != "fail" first. codes is still filled for failed mail so that the decision is yours.

Schemas

Mailbox

FieldTypeNotes
idinteger
addressstringFull address.
local_partstringThe part before @.
notifybooleanWhether new mail triggers a Telegram message.
created_atstringISO 8601, UTC.
unreadintegerUnread messages in this mailbox.

MessageSummary

FieldTypeNotes
idintegerIncreasing. Use as since_id.
mailbox_idinteger
msg_fromstringThe From header as sent. Not verified.
subjectstringEmpty string when there was none.
size_bytesintegerSize of the whole raw message.
received_atstringWhen this server accepted it.
is_readboolean
attachment_countinteger
auth_verdictstringpass, fail, none or empty. See Sender checks.
preview_urlstringNo-login web view of this message. See Web previews.

MessageDetail

Everything in MessageSummary, plus:

FieldTypeNotes
msg_tostring
body_textstringEmpty for HTML-only mail.
body_htmlstringRaw, unsanitized. Render through /html.
attachmentsAttachment[]
codesstring[]Verification codes, most likely first.
linksstring[]Links, confirmation-looking ones first.
authobjectspf, dkim, dmarc (result words such as pass, fail, none), verdict, and tls (for example TLSv1.3, empty if unencrypted).

Attachment

FieldTypeNotes
idintegerFor /attachments/{id}.
filenamestringAs the sender named it.
content_typestringAs the sender declared it.
size_bytesinteger
is_inlinebooleanTrue for an image shown inside the HTML body.

RenderedBody

FieldTypeNotes
idinteger
htmlstringSanitized and safe to insert into a page.
formatstringhtml, text or empty.
availablestring[]Versions the message has.
blocked_imagesintegerRemote images held back.
inlined_imagesintegerEmbedded images shown.

Errors

Every error is {"detail": "…"}, written so you can show it to a person as it is.

StatusMeaningUsual cause
400Bad requestInvalid or reserved name, address taken, or the 10-address limit reached.
401UnauthorizedMissing or unknown X-API-Key, or a blocked account.
404Not foundNo such id, or it belongs to another account. The two look identical on purpose.
410GoneAn attachment's file is no longer stored.
422Validation errorA field is missing or the wrong type; the response names it.

SMTP replies

What another mail server hears when it delivers to mail.s7ortlynk.com on port 25. Useful when a sender reports a bounce.

ReplyWhen
220Greeting. EHLO then advertises STARTTLS, SIZE 26214400 and 8BITMIME.
250Recipient accepted, or message stored.
452More than 20 recipients in one transaction. The sender retries the rest in another.
451Temporary failure (for example, the database is busy). The sender retries later and nothing is lost. Internal errors are never reported as a permanent 5xx.
550The address was never created, or the domain is not s7ortlynk.com. This server does not relay.
552Message larger than 25 MiB.

Repeating the same RCPT TO in one transaction is accepted once; the message is stored a single time.

Limits

10
addresses per account
1 000
messages kept per address
25 MiB
largest message
120 s
longest wait

Reserved names

These can never be created, so abuse and bounce reports always reach the operator:

postmaster  abuse  admin  root  hostmaster  webmaster
noreply  no-reply  mailer-daemon  support

Rate limits

The API itself has none. Outbound mail is limited in practice by the receiving provider, which may slow down a burst from a new sending address.

Security

AreaWhat happens
TransportHTTPS only, with Let's Encrypt certificates for both hostnames; plain HTTP redirects. The API process listens on localhost and is reachable only through that proxy. A Content-Security-Policy blocks any script that is not part of the site. Inbound SMTP is encrypted with STARTTLS (TLS 1.2+).
AccountsEvery lookup is scoped to the key's account. Other accounts' ids return 404.
Mail contentHTML is sanitized before display; remote images are blocked unless asked for.
RelayingPort 25 accepts mail only for addresses created here. It is not an open relay.
SendingTurned off. The service cannot be used to send mail at all, so it cannot be abused as a spam source.
AttachmentsAlways downloaded as files in the webmail, never opened as pages, so a hostile HTML or SVG attachment cannot run.
Preview linksSigned, expiring, one message each, no scripts. See Web previews.
FloodingAt most 20 recipients per SMTP transaction, duplicates stored once.
FilesSender-chosen attachment names are cleaned and prefixed, so they cannot escape the storage folder.

Domain & DNS

What it takes for Gmail, Outlook, iCloud and everyone else to deliver here, and to stop anyone faking mail from @s7ortlynk.com.

Done
MX and A recordss7ortlynk.com → mail.s7ortlynk.com → 136.175.82.99. Every provider delivers here.
Done
Encrypted deliverySTARTTLS on port 25 with a Let's Encrypt certificate (RFC 3207). Gmail shows the grey lock, not the red one.
Done
Website on both namesmail.s7ortlynk.com and s7ortlynk.com, each with its own certificate.
Done
postmaster@ and abuse@Required by RFC 5321; owned by the operator account.
Check
SPF: "this domain sends nothing"TXT at s7ortlynk.com: v=spf1 -all
Check
DMARC: reject fakesTXT at _dmarc.s7ortlynk.com: v=DMARC1; p=reject; rua=mailto:postmaster@s7ortlynk.com

Because the service only receives, DKIM keys, reverse DNS and IP blocklists do not affect it. They only matter if sending is ever turned on.

Webmail

A full inbox at mail.s7ortlynk.com. Sign in with your API key.

FeatureHow
HTML · Text · SourceEvery open message has a switch. HTML is the designed version, Text the plain version, Source the original message with all headers. The page remembers whether you prefer HTML or Text.
Keyboardh HTML, t Text, s Source.
CodesA detected code is shown above the message with a copy button.
ImagesRemote images stay blocked until you choose Load images.
Links to a messagehttps://mail.s7ortlynk.com/#m42 opens message 42 after sign-in.
AddressesCreate (typed or random), mute, and delete with a confirmation step.

Telegram bot

Message @AFK_MAIL_BOT and send /start. New mail arrives as a notification with the code on top and buttons underneath.

CommandWhat it does
/startMain menu with buttons for every action.
/new [name]Create an address; without a name you get a realistic random one.
/listYour addresses, each with Inbox, notification and Delete buttons.
/inboxRecent mail, eight per page, tap a number to open.
/read idOpen a message.
/codeThe newest verification code across all addresses.
/apikey · /newkeyShow or replace your API key.
/webLink to the webmail.

When mail arrives

The notification shows who it is from, the subject, the code if there is one, and a short preview. It has two buttons:

ButtonWhat it does
Copy codeCopies the verification code. Shown only when one was found and the sender was not flagged as forged.
View emailOpens the message as a web page, the way it looks in a mail client, with no login. See Web previews.

Opening a message in the bot (/read or from /inbox) adds Attachments when there are any, and Delete.

Changelog

VersionChanges
1.2.0Receive-only: sending removed. HTTPS at mail.s7ortlynk.com and s7ortlynk.com. Web previews (/p/{token}) behind the bot's HTML version button. Mail keeps its own styling, safely. view and available on /html; unread on mailboxes. Realistic random addresses. STARTTLS on receive. Webmail HTML / Text / Source switch. Redesigned bot with buttons. /messages/wait returns messages in arrival order and ids are never reused. Fixes from a full security audit.
1.1.0/api/v1 prefix. /messages/wait. Codes and links extraction. Mailbox lookup by address or name. Mute and unmute. Security fixes for recipient handling, header injection and recipient flooding.
1.0.0First release: mailboxes, messages, attachments, sending.