seq.
You can consume the log in two ways:
- Push — register a webhook URL. We POST to you on every event, signed with HMAC-SHA256.
- Pull — call
GET /v1/identities/:handle/events?since=<seq>and walk forward. Optional long-poll up to 25 seconds.
id and seq regardless of which delivery method
you use, so you can mix or migrate freely.
Event types
The discriminator is alwaystype.
The set will grow. Bounce events (
email.bounced) and engagement
events (email.opened, email.clicked) are on the roadmap. Today,
bounces arrive as inbound messages from mailer-daemon@… on the
conversation; you can pattern-match in your handler if you need them.
Payload
Same shape for every event. Thedata field varies by type.
Push or pull?
Pick by environment, not by preference.Webhooks
You have a backend with a public URL. You want low latency and
you’re fine running a verifier and an idempotency check on receipt.
Standard for production sequencers, CRMs, agent runtimes hosted
on real infra.
Pull (events stream)
You don’t want to expose a public URL. You’re an agent runtime, an
MCP server, a desktop tool, a dev environment behind a firewall.
Long-poll up to 25 seconds; resume from a cursor on restart.
seq so dedup is trivial.
The cursor
seq is the integer cursor. It’s monotonically increasing per identity
and never reused. The pull endpoint’s response shape:
cursor back as the next call’s since. The same event is
never returned twice on the same connection.
hasMore: true means there were more events than your limit — loop
without long-polling until you see hasMore: false, then resume
long-polling from there.
Why we built it this way
Two specific choices worth calling out, in case you’re evaluating us against a webhook-only competitor: The pull endpoint is a first-class API, not a fallback. Plenty of real integrations can’t accept a webhook — anything that runs on a laptop, an MCP server, a low-traffic agent that doesn’t justify standing up a public receiver. We don’t want those teams to build a relay just to listen. They long-poll. Push and pull share the cursor. Some products give you a webhook without a sequence number; if your receiver crashes mid-batch, you have to rebuild state by trawling history. With oneseq across both
delivery modes, recovery is “fetch from since=<lastSeq>” — three
lines of code, idempotent, no vendor support ticket required.