Skip to content

The wait API

Tests should not sleep and poll. The wait API holds the request open until the email you are waiting for arrives, then returns it with its text, its links, any one-time codes and any sign-in links.

All three endpoints need the viewer role or higher. Waits hold the connection open, so give your HTTP client a timeout a little longer than the wait’s.

GET /api/wait returns the next email to an address that matches.

ParameterDefaultMeaning
torequiredThe receiving address.
fromPart of the sender’s address, any case.
subjectWords in the subject, any case.
containsWords in the body (text or HTML), any case.
folderINBOXThe folder to watch, such as Junk or Sent.
existingtrue also matches messages already there. Otherwise only mail that arrives after the request starts counts.
timeout30sHow long to wait, as 45s, 2m and so on. At most 5m.
Terminal window
curl -s -G https://c1.simmail.dev/api/wait \
-H "Authorization: Bearer smk_your_key" \
--data-urlencode "[email protected]" \
--data-urlencode "subject=verify" \
--data-urlencode "timeout=20s"
{
"address": "[email protected]",
"folder": "INBOX",
"uid": 1,
"message_id": "<[email protected]>",
"from": "[email protected]",
"from_name": "Acme",
"to": ["[email protected]"],
"subject": "Verify your email",
"date": "2026-10-10T14:30:00Z",
"text": "Your verification code is 482913.\n\nOr click https://app.yourcompany.test/verify?token=abc123",
"links": ["https://app.yourcompany.test/verify?token=abc123"],
"codes": ["482913"],
"magic_links": ["https://app.yourcompany.test/verify?token=abc123"]
}

GET /api/wait/reply returns the next email a persona sends.

ParameterDefaultMeaning
fromrequiredThe persona’s address.
in_reply_toOnly a reply in this thread: the Message-ID of your email, with its angle brackets. It matches the reply’s In-Reply-To or References.
fasttrue has the person write their due reply now, instead of on the simulated clock.
timeout30sAs above, at most 5m.
Terminal window
curl -s -G https://c1.simmail.dev/api/wait/reply \
-H "Authorization: Bearer smk_your_key" \
--data-urlencode "[email protected]" \
--data-urlencode "in_reply_to=<[email protected]>" \
--data-urlencode "fast=true"

The answer has the same fields as above, read from the person’s Sent folder, with by_persona: true.

With fast=true, simmail runs the person’s next planned job now, again and again, until a matching email appears. Without it, the person replies when the simulated clock reaches their reply. You can move the clock yourself with POST /api/clock/advance.

StatusMeaning
200The reply.
404Nobody plays that address.
408No reply before the timeout.
409With fast=true: the person sat down to write and decided not to reply (or their AI is off), and nothing else is planned. Waiting longer would not help.

A person who chose not to answer at all (their reply rate) plans nothing, so the wait runs to its timeout and ends with 408. The persona.ignored event says why.

POST /api/extract runs the same extraction on any text or HTML you send.

Terminal window
curl -s -X POST https://c1.simmail.dev/api/extract \
-H "Authorization: Bearer smk_your_key" \
-H "Content-Type: application/json" \
-d '{"text": "Your code is 482913. Reset your password: https://app.yourcompany.test/reset?token=xyz"}'
{
"codes": ["482913"],
"links": ["https://app.yourcompany.test/reset?token=xyz"],
"magic_links": ["https://app.yourcompany.test/reset?token=xyz"]
}
  • codes: 4 to 8 digits, or 6 to 8 letters and digits with at least one digit, shortly after a word such as code, OTP, PIN, passcode, verification, verify, one-time or security. A line holding only 4 to 8 digits also counts. When the email has only HTML, its text is used.
  • links: every link in the text and HTML.
  • magic_links: links whose address contains verify, verification, confirm, magic, sign-in, signin, login, log-in, reset, activate, auth or token=. Unsubscribe links are never magic links.

All three are always lists, empty when nothing is found.