The adapter is a deploy tool, not an architecture decision
Vercel's Instagram adapter for Chat SDK is well-built. Webhook signature verification works. Event fan-out is clean. Message shape normalization handles the platform's quirks. The problem isn't the adapter—it's what teams assume when it works. Shipping the adapter in an afternoon does not mean your agent is ready for Instagram's runtime.
The adapter solves transport. It does not solve semantics. It normalizes inbound webhook payloads and shapes outbound sends to Meta's Graph API. What it does not handle: per-conversation state divergence when the same user bounces between web chat and Instagram DMs, Instagram's 24-hour standard messaging window (after which only specific message tags work), or the fact that rate limits are per-app and per-page—one noisy tenant burns everyone's budget. Your agent was built on HTTP request-response with stateless handlers. Instagram has buffering, expiration windows, and reaction webhooks that fire without conversational context. Adapters solve transport, not semantics.
Start by treating Instagram as a separate problem space. Your web chat error budget, latency SLOs, and retry logic were written for a different constraint model. Eve Chat SDK adapters make multi-surface deploys plug-and-play, but that ease is exactly where teams cut corners on state unification and per-platform observability.
Message buffering hides your real p95
Instagram's Messenger Platform batches inbound events. A user sends a message; Meta holds it for up to a few hundred milliseconds, then delivers it to your webhook. The timestamp in the payload tells you when the user sent it. The arrival time at your handler is when you actually see it. That gap is invisible in your latency dashboards, and it's where your p95 lives.
Consider the full path: user taps send (0ms), Meta buffers and delivers to your webhook (200-400ms), your agent runs a tool call (400ms), Meta sends the response back to the user (1200ms estimated), user's phone renders it (200ms). Total felt latency: ~2.4 seconds. But your handler logs show 400ms, and your SLO says you're fine. You're not.
The fix is mechanical but mandatory. Log delivery_timestamp - event_timestamp separately from handler duration. Set your agent turn budget to 1.5 seconds if you want sub-3-second felt latency on Instagram. Streaming tokens (SSE) are meaningless here—Instagram's API accepts finalized messages only. Every token-streaming optimization you built for web chat becomes dead weight. Wall-clock time between tool calls is where agent latency actually lives, and on Instagram, that wall-clock time includes platform buffering you don't control.
Quick replies mask context loss across platforms
Instagram's quick-reply button payloads look like structured intent. They're not. A quick_reply.payload is a string—your agent has to re-parse it. When the same user hits your web chat, then switches to Instagram, then goes back to web, you're now managing multiple thread IDs, session divergence, and state-store keys that don't unify.
The failure mode is subtle: the agent "forgets" because your adapter created a new conversation thread, not because your memory layer broke. A user on the website says "I want to book a flight." An hour later, they DM your bot on Instagram and say "what's the cheapest option?" Your agent has no memory of the flight search because the adapter keyed the conversation by channel + psid, not unified user_id.
Carousel and generic templates cap subtitles at 640 characters. Your RAG snippet won't fit. Quick replies max out at 20 items. Your ranking logic breaks. The fix is a canonical Message envelope before the agent sees anything. Normalize quick-reply payloads, carousel constraints, and media URLs to a channel-agnostic schema. Keep channel metadata in a sidecar so you can render back to Instagram's constraints, but don't let those constraints leak into your agent's reasoning.
Reactions and stories are new webhooks, not new features
Meta fires message_reactions and story_mention webhooks without a conversational turn. Your agent has no prompt-shaped context to reason about them. Reactions are not idempotent from Meta's side—you can receive the same react/unreact twice in seconds. Story mentions carry a media URL that expires in roughly 24 hours. If your ingestion queue backs up, the asset is gone.
Design separate dead-letter queues per event type so reaction storms don't poison message processing. Status codes from Meta that require immediate action are not 5xx errors: 10 (permission denied), 200 (blocked by user), 613 (rate limit). Your existing alert rules won't catch them.
For idempotency: messages use the message ID (mid). Reactions need a composite key: psid + mid + reaction_type + ts_bucket. Without it, a flurry of undo-redo taps replays the same reaction event multiple times, and your observability logs become noise. Log why each state transition happened, not just that it did—especially for reactions, which carry no intent signal.
Your error budget was written for HTTP, not for Meta
SLOs and retry logic built for web chat silently under-serve Instagram. Meta's rate limits are per-app and per-page. One noisy tenant burns everyone's budget. You need a token-bucket per page_id in Redis, not just exponential backoff with jitter.
The 24-hour messaging window is a hard deadline. After that, only message tags work: CONFIRMED_EVENT_UPDATE, ACCOUNT_UPDATE, etc. Use the wrong tag and Meta restricts your app. Retry budget: cap user-facing sends at 3 attempts over 90 seconds, then drop to DLQ. Anything longer feels like a broken bot. Log page_id, psid_hash, event_type, and Meta's trace_id on every outbound call. Per-connector observability like Vercel Connect shows token lifecycles, but it stops exactly where this problem starts—at the agent-to-platform boundary. You need agent-scoped logging to see how many retries each conversation burned.
When the adapter is the right call anyway
This is not a "don't use adapters" piece. There are cases where shipping Vercel's Instagram adapter as-is is genuinely correct.
FAQ-style bots with fewer than 5 intents and zero cross-channel identity: ship it. Internal ops tools where Instagram is a notification sink, not a conversation surface: ship it. MVPs where you're validating whether Instagram is worth the engineering investment before building a canonical message layer: ship it.
The moment to stop is the first time a user says "I already told you this on the website." That's your signal to invest in state unification. Watch for these four signals: users mentioning context from another channel, repeated questions that suggest memory loss, quick-reply misses where payload re-parsing failed silently, and felt latency complaints that don't match your handler logs. When two of those hit, it's time to talk to us about a unified conversation store and per-platform rate-limit budgets.
The one thing to measure before your next Instagram deploy
Before you ship the adapter to production, instrument delivery_timestamp - event_timestamp for one week. Run the same conversation flows you'd use in prod. If your felt-latency p95 is more than 2x your handler p95, that's the signal. At that point, the adapter is doing its job, but your agent's assumptions about latency are wrong. That's when you reach out—or you spend the next three months debugging why your bot feels slow on Instagram but not on web.