If you’re building an agent that needs to react to incoming email, you’ve probably had this conversation with yourself:
“I’ll just poll the API every thirty seconds.”
Then later, when it matters: “I should really make this faster — but I can’t host a webhook from where my agent runs.”
That’s the corner most B2A delivery patterns paint customers into. Webhooks require a public endpoint. Polling is the fallback. Sandboxed agents — running inside Claude Code, inside CoWork, inside corporate networks with outbound-only policy, inside laptops behind NAT — can’t accept inbound webhooks. So they poll. Slowly, because faster polling is expensive. And they wait.
We just shipped a third option.
What’s actually shipped
AgenticBoxes now offers three delivery channels for account events, all backed by one durable feed:
- Webhook push — if you can host a public endpoint, we’ll POST to it.
- IoT/MQTT push (new) — your subscriber connects outbound over mutual TLS to AWS IoT, subscribes to its own per-account topic, and receives events as they happen.
- Pull —
GET /events?since=<cursor>returns the same events, in order, whenever you ask.
You can use any combination. The durable, ordered event feed is the guarantee. IoT is the fast lane.
The MQTT path is what lets sandboxed agents stop polling. There’s no inbound port to open, no public URL to host, no firewall rule to request. The subscriber initiates the connection outbound, authenticates with a client certificate, and stays open. When something happens on your account — an email arrives, a domain verifies, a beta toggle flips — the event is published to your topic on a connection you opened.
How a subscriber connects
POST /account/iot/provision (admin scope)
That call mints an X.509 client certificate, attaches a per-account IoT policy, and returns everything you need to connect:
iot_endpointandport(8883)client_id(your account’suserid, required by the policy)topic(agenticboxes/accounts/{userid}/events)certificate_pemandprivate_key— the private key is shown onceroot_ca_url(Amazon Root CA 1)
The cert is bound to a Thing named for your account. The IoT policy authorizes that one Thing to connect with its own client ID and subscribe to its own topic — nothing else. Per-account isolation is enforced by AWS IoT, not by us.
Once connected, your subscriber receives JSON payloads of the form:
{ "type": "mail.received", "payload": { … }, "ts": "2026-05-25T17:42:01Z" }
One topic per account; every event type travels the same topic. Subscribe once, parse on type.
The provisioning endpoint, the response shape, and the IoT (beta) reference live at docs.agenticboxes.email/reference. Raw spec at docs.agenticboxes.email/openapi.yaml.
What the IoT channel does and doesn’t guarantee
This is the part most B2A “we have webhooks” claims hand-wave through. The honest version is two paragraphs.
Delivery is QoS 1 — at-least-once — to a connected subscriber. If your subscriber is offline, briefly disconnected, or rejecting messages when the event is published, that push is best-effort. A failed publish is logged on our side and not retried on the IoT channel. Be idempotent — dedupe on the event cursor when you process.
The durable feed is the safety net. Every event is written to the ordered, hash-chained agenticevents store before the IoT push is even attempted. If you miss a push, nothing is lost. You catch up with GET /events?since=<cursor> whenever you reconnect — and the cursor walks forward, so a missed push isn’t a missing fact, just a deferred read.
Guarantee = the feed. Speed = IoT. The events you receive over MQTT are the same hash-chained records behind our audit trail — same data, pushed live.
What it does for end-to-end latency
We’ve watched mail.received events land on a subscriber in about 5 seconds end-to-end against a baseline of ~3 minutes when the same subscriber was polling. We’re not publishing that 5-second number as an IoT-transport metric — most of it is upstream email-arrival and SES inbound processing time, not the MQTT hop. IoT is the fast last hop, not the whole pipeline.
We’ll publish measured IoT-transport numbers when we have them. Until then, the honest framing is near-real-time, with the feed underneath guaranteeing nothing’s lost when the push doesn’t land.
Who this is for
If your agent runs anywhere it can’t host a webhook — Claude Code’s sandboxed execution, CoWork’s restricted environment, a Mac on a coffee shop network, a developer laptop behind corporate NAT — IoT is the channel that lets you react to events instead of polling for them.
If you can host a webhook, use webhooks. They’re cheaper for you to operate and our delivery story is the same.
If you don’t need real-time at all, the pull feed is always there, with a cursor you control. The poll interval is your call, not a platform default — there is no “minimum polling interval” forced on you.
The receipts
Every claim in this post maps to live code:
- The endpoint:
POST /account/iot/provisionat docs.agenticboxes.email/reference (IoT (beta) section) - The feed:
GET /events?since=<cursor>— same cursor model asGET /messages?since= - The topic structure: one topic per account, all event types
- The chain: same hash-chained
agenticeventsstore as the public audit trail — read that score, then read this article. Both anchor on the same feed.
The IoT channel is beta during this window. The provisioning endpoint is live; the documented contract is real; the feed underneath is production-grade. If you want a cert and a topic to point a subscriber at, the call returns one today.
If you want a working subscriber example, a topic-structure diagram, or measured IoT-transport latency numbers — we’ll build those next. We don’t reference them as live until they are.
Receipts for this post
- Written by: Brian (with Marketing Claude OAuth assist)
- Edited by: Aunt Caroline (Anthropic Sonnet 4.6, API)
- Posted by: Neo (Anthropic Opus 4.7, API), AgenticBrian Holdings CTO
- Directed by: Brian (human)
- Images: AgenticBen (left-brain: Haiku API, right-brain NanoBanana 2 using OpenRouter API)