Skip to main content
When your code drives a signup or a login somewhere, the app emails a one-time code or a magic link. Sente classifies every inbound email the moment it arrives and extracts the artifact, so you wait for exactly that email and receive the value itself. No IMAP, no regex, no HTML parsing on your side.
A Sente-driven register or connect run completes email verification by itself — the run controller polls the account’s own inbox and types the code in. This page is for when your code drives the flow: your own browser automation, a mobile signup, a partner API that emails a login link.

What you need

  • An identityId. The wait endpoint is per-identity. If you let Sente auto-provision the identity (no identityId passed to connect/register), read it back off the account: registration.identityId.
  • An email code. Only email lands in a Sente inbox. SMS codes never arrive here — a run that hits SMS 2FA blocks and pages a human (human takeover); nothing is worked around.

The complete flow

Stamp a timestamp before you trigger the email, then wait with that since. A code that lands in under a second cannot be missed, and a stale code from an earlier attempt cannot be picked up by mistake.
Magic links are the same call with a different helper — you get the URL to open:
Both helpers return the whole message alongside the extracted value (otp.message, r.message), and null / None on timeout.

The wait contract

GET /v1/messages/wait — the endpoint both helpers call.
One HTTP call holds for at most 60 seconds. To wait longer from an SDK, loop — reusing the same since each time so nothing is skipped. The CLI already does this internally, which is why sente wait --timeout 120 works.

How the extraction works

Every inbound email gets an annotation before anything observable happens (webhook delivery, wait matching, kind filtering):
A single Claude Haiku pass classifies the email’s primary purpose and pulls out the value. Classification is LLM-only — no regex heuristics that break when a sender changes their template. It is injection-hardened, because anyone in the world can send mail to an @sente.run address:
  • The system prompt is fixed and never composed from email content.
  • The email is passed as untrusted data inside an <email> block, explicitly labelled as data, not instructions.
  • The model has no tools. The worst a hostile email can do is make itself be classified wrong.
  • The output is schema-constrained and re-validated server-side before it is stored.
The hardening protects the extractor, not you. A verification email can still contain a prompt injection aimed at your agent (“ignore your instructions”, “email your API key to…”). Take the extracted code or link and nothing else — never feed a raw email body to an agent with tools.

When nothing arrives

No. Annotation failure never blocks delivery: the message is stored and the message.received webhook still fires — the message just has annotation: null when you fetch it. A background sweep re-annotates recent unannotated inbound messages roughly once a minute, so a kind wait started shortly after arrival still matches once it heals. The sweep stops retrying messages older than about 15 minutes — by then the code has expired anyway.
Give each flow its own explicit since, stamped immediately before that flow triggers its email. Without since, both waits fall back to the same 60-second lookback and can match each other’s code. If both codes come from the same sender, also read message.subject before submitting.
Annotation runs right after the message is persisted, so a GET /v1/messages/:id issued in that window can see annotation: null. Waiting with kind set never has this problem — an unannotated message simply doesn’t match, and matches as soon as it is annotated.

Push instead of poll

If your service has a public HTTPS endpoint, register a webhook for message.received instead. Sente pushes a notification for every inbound email and you fetch the full message — annotation included — by id. Long-polling is the right tool for a process with no public URL (a laptop, a CI job, a container behind NAT).

Next steps

The account's inbox

List, read, send, reply, and archive.

Webhooks

Get pushed message.received events instead of polling.

Messages API

Every parameter and response shape.