The wiring got easy. The decision got harder.
Eve's Chat SDK channel adapters for Slack, WhatsApp, Teams, SMS, and web widget used to require a sprint of plumbing. Webhook signature verification, token refresh loops, per-platform auth flows, message normalization — all of it. Then you'd ship it and spend three months learning that Slack's rate limits don't compose the same way WhatsApp's do.
That's done now. A channel adapter is a config file. Vercel Connect handles OAuth refresh and credential vaulting transparently. The boring work — the stuff that ate calendar time — is gone.
Which means the bottleneck moved. You no longer debate whether to build the integration. You debate whether you should. Not every chat surface deserves an agent. Some deserve a webhook. Some don't deserve anything.
Handler logic isn't portable: what breaks between Slack and WhatsApp
A single `on_message` handler that works on Slack will fail silent or loud on WhatsApp. Not because the code is wrong, but because the surfaces have incompatible constraints.
Slack Web API tier 2/3 methods have a ~20 requests per minute limit. WhatsApp Cloud API caps outbound message flow at 80 messages per second for business-initiated traffic, but it's also scoped to a 24-hour session window — after that, only template messages can be sent, and those require pre-approval. SMS has a 160-character-per-segment limit; Slack accepts 40k characters; WhatsApp tops out at 4096. A Slack user expects a 3-second interaction ack deadline for button clicks. WhatsApp webhooks retry until they receive a 200 OK, and they don't timeout the same way.
A handler that cheerfully calls an LLM and writes a 8k character response works fine on web and Slack. It silently truncates on WhatsApp and fails on SMS. If you don't instrument per-adapter, you discover this in production.
Each adapter needs its own retry policy, backoff curve, and idempotency logic. WhatsApp will redeliver inbound messages if you don't ack within a window. Slack will too on 5xx, but the retry semantics differ. SMS doesn't have read receipts or typing indicators, so your handler's assumption about "waiting for a response" breaks immediately.
The tooling made deployment frictionless. It didn't flatten the surfaces themselves.
State management diverges by thread model
Slack has `thread_ts` as a natural conversation key. A DM, a channel message, a thread reply — each is scoped differently, but the threading model is explicit. WhatsApp has no threads. Phone number is the only key. You own the windowing: is a message 3 hours old part of the same conversation or a new one? SMS has no concept of threads or sessions at all. Teams gives you `conversation.id` plus `activity.id`, with implicit tenant scoping.
A shared `conversation_id` abstraction that maps across all four surfaces is a trap. It silently merges a user's Slack DM with their WhatsApp thread if you're not careful about scoping. The canonical pattern we've seen work: durable transcript in Postgres, hot window in Redis keyed by per-surface identifiers (Slack's thread_ts, WhatsApp's phone_number + window_start, Teams' conversation.id), each with a surface-specific TTL. WhatsApp conversation window might be 30 minutes. A Slack thread lives longer. SMS has no native window, so you pick one (usually 5–15 minutes for transactional flows).
State divergence isn't a bug you catch in staging. It's a runtime invariant you have to enforce in code.
Not every surface deserves an agent
This is the hard conversation. SMS notifications and status pings — "Order #4521 shipped" or "Confirm: yes/no" — don't need an LLM. A templated webhook beats an agent by 100x on cost and latency. You're paying $0.002 per GPT-4o-mini call for a deterministic response that a 5ms regex handles. Slack `/command` triggers are often a single tool call, not a reasoning loop — a workflow wins.
WhatsApp customer support with document lookup is legitimate agent territory. Multi-turn, tool use, RAG — the agent earns its keep. A web chat widget on your pricing page can be an agent, but it needs heavy guardrails (token budget, output filtering, tool scope). Internal Slack notifications? Webhook. Sales qualification flow on WhatsApp? Agent.
The lens that works: if the interaction is one-shot or templated, use a webhook. If it requires multi-turn reasoning and tool use, use an agent. Apply this per channel, not per product. You might have an agent on WhatsApp and a webhook on SMS for the same business problem. That's correct.
Latency and cost math also matter. WhatsApp users tolerate 3–5 seconds. Slack interactive users want sub-second latency. If your latency budget is 500ms and the LLM takes 1.5s, the adapter is running on borrowed time. When you're turning on a new surface, the first question should be: does this interaction really need reasoning, or do I need to ship this fast?
Credential rotation is solved. Observability isn't.
Vercel Connect handles OAuth refresh, token vaulting, and per-tenant credential scoping — the plumbing that used to consume a week. What it doesn't give you is visibility into per-adapter health.
Instrument every adapter with four metrics: inbound webhook 2xx rate, handler p95 latency, tool-call error rate, and outbound send success. Don't share a dead-letter queue across surfaces. WhatsApp retries work differently than Slack's; merging them into one queue means you'll handle transient Slack failures with WhatsApp's exponential backoff curve, which is wrong.
Trace ID propagation from inbound webhook through the agent loop to outbound send matters more on multi-surface agents than it does on single-channel bots. When a message disappears on WhatsApp but succeeds on Slack, you need to follow it across adapters. Most observability platforms make this hard because they assume a single event source.
Vercel Connect fixed the auth headache. Don't let credential rotation be your excuse to skip per-channel telemetry. Agent observability in production is an operations problem, not a credential problem.
A decision checklist before you turn on a new adapter
Before you enable WhatsApp or Teams or SMS for your agent:
- Is the interaction multi-turn or one-shot? One-shot → webhook.
- What's the platform's rate limit and how does it compose with your LLM provider's TPM? (If WhatsApp's 80 msg/sec and your OpenAI TPM conflict, you have a problem.)
- Where does state live and what's the TTL? (Postgres + Redis per-surface keying, or you leak context.)
- What's the fallback when the model times out inside the platform's ack window?
- Who owns credential rotation alerts when Vercel Connect surfaces a refresh failure?
- Is there a template or approval process (WhatsApp, iMessage Business) that blocks free-form output?
This checklist looks straightforward. In practice, most teams learn these constraints by shipping and then firefighting. Knowing whether you need an agent or a workflow is half the battle. The other half is knowing which surfaces actually warrant the complexity.
If you're mid-rollout and the checklist above surfaced more questions than answers, that's the conversation we have most weeks. Reach out at /contact before you turn on the next adapter.