app.webhook() lets you receive messages through HTTP POST requests instead of the app.messages stream. It handles two webhook formats through the same method:
Detection is by payload shape (JSON vs protobuf), not headers. Your handler receives the same
(space, message) pair either way.
Configuring a webhook secret
Native Spectrum webhooks require a signing secret for HMAC verification. Pass it toSpectrum():
webhookSecret option can also be supplied via the SPECTRUM_WEBHOOK_SECRET environment variable (the explicit option takes precedence). A native delivery that arrives without a configured secret is answered 500.
Receiving deliveries
Callapp.webhook() from your HTTP server’s POST route. The method has two overloads:
- Web Request (Hono / Bun.serve / Workers)
- Raw (Express / Node)
message.id for exactly-once side effects.
Framework adapters
First-party adapters mount the endpoint for you and handle raw-body parsing correctly. Install the adapter package and its framework only when you use it.- Hono
- Express
- Elysia
SpectrumPluginOptions
SpectrumPluginOptions
What the SDK handles
- Signature verification. Native webhooks are verified with
HMAC-SHA256overv0:<timestamp>:<rawBody>, with a 5-minute replay window. Bad signature returns401, missing headers return400. - Payload deserialization. Native webhook JSON is deserialized into normal and objects, including reactions and grouped items.
- Attachment rehydration. Native webhooks carry attachment metadata only.
read()andstream()fetch the bytes lazily via the platform. - Format detection. Native vs Fusor is detected per request by payload shape — JSON for native, protobuf for Fusor.
Delivery semantics
app.webhook() is stateless and request-scoped — it does not feed app.messages, and it never opens the streaming connection. Both formats deliver at-least-once, so dedupe on message.id for exactly-once side effects.
For more on Spectrum’s webhook delivery model, see the Webhooks documentation.