
Your Convex Agent Can Text Now
Photon is now featured on Convex Components. Here's what that unlocks.
Things your Convex app couldn't do last week: send an iMessage.
Today we're releasing @spectrum-ts/convex, a Convex component that gives any Convex backend the ability to send and receive iMessage, RCS, and SMS through Photon — and it's now officially featured on Convex Components.
What the component does
The component handles the entire inbound and outbound messaging pipeline inside Convex's execution model:
Verified webhook ingestion. Inbound messages are HMAC-verified and written to a durable Convex table before the HTTP 200 is returned. All downstream processing is handed to Convex's scheduler.
Burst debouncing and in-flight cancellation. Rapid follow-up messages within a settling window are coalesced into a single handler call, and a new message cancels any reply that hasn't been sent yet — with all prior messages carried forward as context.
A paced gRPC outbox. Outbound sends are queued to an outbox and dispatched through Photon's gRPC transport, with deduplication on deterministic client GUIDs.
Automatic dedup on at-least-once delivery. Redeliveries of the same webhook never create duplicate rows or trigger duplicate handler calls.
No long-lived process anywhere. Your entire stack stays inside Convex.
Why this was hard
Building an iMessage agent has always meant hand-rolling a lot of infrastructure. Real users send messages in bursts, so you need debouncing. Replies mid-generation need to be cancelled when new context arrives. Webhooks redeliver, so you need dedup. Outbound sends need pacing and retry. None of this is your agent — it's all plumbing you have to build before your agent can say a word.
Serverless makes it harder still. Real-time messaging traditionally wants a long-lived process — a loop that holds a connection open and reacts to messages as they arrive. Convex isolates don't work that way: they're spun up per request and frozen the moment the HTTP response is returned. Any work after the 200 happens in an isolate that may already be torn down, which makes naive webhook handlers silently unreliable.
The component resolves this by making durability the boundary. The webhook route persists the verified delivery first, returns 200 second, and lets Convex's scheduler own everything after that. If your handler crashes, the message is still sitting in a table waiting to be processed. Nothing is lost.
That one design decision is what makes an iMessage agent on Convex possible at all — and it means the agent answers the user's final intent, not every intermediate message in a burst.
What this means for Convex developers
1. It unlocks something that simply wasn't possible before.
If you wanted to add iMessage to a Convex app, you were mostly out of luck: iMessage has no official API, and even with Photon's SDK, the app.messages loop needs a long-lived process that Convex isolates can't run. Now it's one npm install. No separate box on Railway or Render just to host a message loop. Zero extra infrastructure.
2. Message reliability you don't have to write yourself.
The classic webhook failure mode — handler crashes, isolate gets recycled, message vanishes — can't happen here, because persistence comes before the 200. At-least-once redeliveries are deduped on provider message IDs automatically. This is the kind of reliability engineering that usually costs a few hundred lines of easy-to-get-wrong code. Here it's the default behavior.
3. Human-feeling agent behavior, out of the box.
Real people don't send one perfectly formed message. They send "hey", then "so i was thinking", then "can you check X" — three texts in ten seconds. A naive implementation fires an LLM call for each one, which is expensive and makes your agent feel robotic. Collapse mode merges the burst into a single LLM call and cancels any in-flight reply when new context arrives. This is behavior we run in production at Photon; Convex developers don't have to reinvent it.
4. It's all TypeScript, and it feels native.
Type-safe end to end, built on Convex's own scheduler and table primitives, configured with three environment variables. There's no new mental model to learn — it doesn't feel like an external service bolted on, it feels like a capability Convex always had.
Put simply: your Convex app can now text like a human, and you don't write a single line of infrastructure code to get there.
Show me the code
Receiving messages is a route registration and a handler:
Sending is one call inside a Convex action:
That's the whole surface area. Webhook verification, durability, debouncing, cancellation, outbox pacing, and dedup are all happening underneath.
Why we built this
Photon's vision has always been simple: AI agents should have a native presence in the places people actually talk — starting with a phone number.
For that to happen, messaging can't be something developers bolt onto their stack with a sidecar server and a pile of glue code. It has to live where the application logic lives. Convex is one of the best backends of the AI era — reactive, durable, fully TypeScript — and we think backends like it deserve first-class messaging as a native capability, not an integration project.
This component is what that looks like in practice: the backend developers already love, now able to text. As agents become the primary interface between software and people, we want Convex — and every great backend — to be the most natural home for them. That's the AI agent backend, and messaging is how it talks.
Get started
Install:
npm install @spectrum-ts/convexComponent page: convex.dev/components/spectrum-ts/convex
Build an agent people can text. We can't wait to see what you ship on Convex.
Photon is messaging infrastructure for AI agents. One API gives developers access to the channels where people already communicate, including iMessage, SMS, RCS, WhatsApp, and more.
We’re building the communication layer that lets agents go everywhere.
P.S. Shoutout to our good friend Wayne at Convex for making this happen with us.


