Work › Case study
Case study

Putting our own Instagram inbox agent live

Four days from an empty webhook to a real customer answered in 61 seconds — including the two bugs we caught, the message it refused, and every number from the ledger.

Our own test accountInstagram · interior-design niche · ~3,200 followers
Inbox AgentInstagramDogfooding

Before we let an AI agent anywhere near a client’s customers, we made it run our own. This is the unedited account of those four days: what we built, what broke, what the agent answered, and — more importantly — what it refused to answer. Everything below is reconstructed from the system’s own ledger, not from memory. Where a number appears, its source appears with it.

Challenge

Prove the entire loop on ourselves before any client: real DMs, real replies, real escalations — not a staged demo with planted questions.

Approach

An answer list in the owner’s words, escalation rules enforced in code, and a sender allow-list so no stranger could ever receive a test answer.

Result

Known questions answered in about a minute. The refund request was acknowledged, stopped, and handed to a person with the whole context. Two real bugs found — on us, not on a client.

The rule we set before writing a line of code

The test account is a real one — a small interior-design profile with about 3,200 followers and a steady trickle of genuine messages. That made the first decision easy and non-negotiable: the agent must not be able to speak to a real follower by accident.

So the agent was born paused. Even when we later armed it, it ran behind a sender allow-list: it would reply to exactly one account — our own second profile, the one we used to play the customer. Every message from anyone else was processed identically — understood, decided, drafted — but the draft was held, never sent. The owner could read what the agent would have said without a single follower ever seeing it.

That is not a testing convenience. It is how every client onboarding now works: the agent drafts silently until the owner has read its work and flips the switch. Going live is the removal of one line in a configuration file — after a human has judged the drafts, never before.

How it is built, in plain words

There is no inbox dashboard, no new app for anyone to learn. The customer stays in Instagram; the owner stays in Instagram and email. In between sits a pipeline with four stages, each deliberately boring:

Deliverythe platform notifies us of each new message, cryptographically signed
Queuethe event is verified, de-duplicated and written down before anything thinks
Decisionthe engine answers from approved content — or stops
Recordevery decision becomes a ledger row and, when needed, a mail to the owner

Two properties matter more than any feature. First, the connection runs through the platform’s official APIs, on an account the owner authorises through the platform’s own consent screen and can revoke at any time — nothing that risks the account. Second, the receiving end does no thinking at all: it verifies the signature, writes the event to disk, and acknowledges within milliseconds. Intelligence that runs inside a delivery window is intelligence that drops messages; ours runs after the message is safely on disk.

Worth knowing

Message delivery from the platform is “at least once” — the same event can arrive twice. Our queue claims each event id exclusively before any work happens, so a duplicate delivery is recognised and swallowed. This detail becomes the hero of a later section.

The answer list — the only thing the agent knows

The engine is not allowed to know things. It is allowed to know the answer list: a short document, written in the owner’s own voice, of the questions this business actually gets and how this business actually answers them. Ours had seven entries — how the free room plan works, what to send, whether it really is free, what a whole-house project means, and so on.

Around that list sit three fences, and they are enforced in code rather than requested in a prompt:

Always reaches a person
  • Refunds, payments, invoices — anything about money already paid or about to be
  • Complaints, and anything the customer is plainly unhappy about
  • Any question whose answer is not literally in the approved list — unknowns escalate, they are never improvised

The third fence is the one that surprises people. An unknown question is not an opportunity for the model to be helpful; it is a handover. In four days of testing the agent did not invent a single fact — not because the model is incapable of inventing, but because the path from “I’m not sure” to the customer runs through a person, by construction.

Day one: proving the pipe

Before any intelligence, we proved delivery. A message sent from the customer account at 21:16:12 was in our verified queue at 21:16:13 — about one second, signature checked, duplicate-safe.

Ingress proven

Signed webhook events verified, de-duplicated and queued. A deliberately unsigned request rejected; a replayed event swallowed.

Account connected

One tap on the platform’s own consent screen. Its existing message history replayed for context; its posts synced — all 117 of them, matching the profile count exactly.

The brain answers, on paper

Seven seeded test messages: the refund escalated, the unknown escalated, the safety question refused, the normal ones answered. All held — the agent was still paused.

Live, behind the allow-list

First real reply sent into the thread. First real escalation mailed.

Behaviour fixes from watching it live

Two design flaws found and corrected — details below, because they are the honest part.

The first real reply: 61 seconds

At 18:44 UTC on day three, the customer account asked: “do you do free room plans?” At 18:45:09 the answer was in the thread — from the approved list, in the account’s own lowercase voice.

61 secondsmessage arrival to answer sent — the engine’s own decision took about two of them1
the test account
do you do free room plans? yes, the first plan is free. just send a photo of the room, its rough measurements, and what you want it used for. the plan comes back here. replied in 61s
The actual first reply, reconstructed from the thread. Timing from the ledger.

Where did the minute go? Our worker polls its queue once a minute; the message simply waited for the next tick. We could collapse that to a couple of seconds with a push loop, and we have deliberately not done it yet: a beat of latency costs a customer nothing they can feel, while the polling design makes exactly-once behaviour trivially auditable. A duplicate reply embarrasses a business in public. A reply at 61 seconds instead of six is still, for a small business used to answering the next morning, a different universe.

The message it refused

Six minutes later we sent the message this entire product exists for: “i paid for a plan and want a refund.”

The agent did not answer it. It acknowledged the customer — a person from the team will get back to you here — and stopped. No apology that concedes fault, no policy recital that might be wrong, no goodwill gesture it has no authority to make. Toward the customer: calm silence held by a person-shaped promise. Toward the owner: everything.

timedecisiontiersentintent
18:45:09answer3trueinquire about free plan
18:50:07escalate1heldrequest for refund
A customer needs you TIER 1
“i paid for a plan and want a refund”
Your agent already said: “i understand. i will pass this to a person on the team and they will get back to you here about your refund.”

The handover mail carries the intent, the customer’s exact words, the agent’s exact words — quoted so the owner can never accidentally contradict what was already promised — and the fields the agent captured along the way: a name, an item, a date. The owner opens the thread already knowing everything that happened in it.

An agent that never answers money questions is safer than one that always answers.

operating rule — enforced in the send path, not requested in a prompt

The bug we caught before any client could

Here is the section a marketing team would cut, and the reason this case study exists.

During day-two testing, our worker processed the same batch of messages twice. Seven test messages produced fourteen decisions and five duplicate escalation mails. The cause was mundane, which is exactly what makes it dangerous: a directory permission was wrong, the “move this event to done” step failed silently, and every polling cycle happily re-read the same queue. In production, that is the same customer greeted again and again by an increasingly deranged-looking business.

The fix was not to repair the permission — that too, but permissions break again. The fix was to make correctness not depend on it: before any work happens, the worker now claims the event’s id in an exclusive, write-once record. If the claim exists, the event is already handled — whatever happened to any file move, whatever the permissions say. We then proved it: one event injected, the worker run twice, the second run reporting an empty queue.

The lesson we filed

Never let “the work is done” be recorded by the same mechanism that can fail silently. The claim is the record; the file move is just housekeeping.

Two design flaws only live traffic could show

The customer heard silence

Our first escalation design held everything back from the customer — including the acknowledgment. Watching it live made the flaw obvious: the customer who asked for a refund was left on read, which is precisely the feeling this product exists to end. The fix: acknowledgments for escalations and refusals are now sent — they carry no facts and no promises, only “a person will come back to you here.” The facts still wait for the human.

The urgent mail got rate-limited

To stop a chatty customer generating five mails in five minutes, we had throttled handover mails to one per conversation per hour. Then a real sequence showed the flaw: a casual message used the hour’s slot, and the refund request six minutes later was silently suppressed. A money complaint must never lose a race to small talk. Now the most urgent tier always mails, and the throttle guards only the routine tiers.

Both fixes were one line each. Both were invisible until real messages flowed. That is the argument for running your own product before selling it — made better by the fact that we had no choice but to learn it.

What it would not touch

In the same period the account held nine recent conversations. The agent drafted replies for three. The other six were photos and shares with no words — nothing to ground an answer on. It refused them and marked them for a person, and the report says so in plain language rather than burying the number.

61sfirst real reply, arrival to sent1
~2smedian engine decision2
3 of 9threads drafted; 6 were photo-only3
100%money questions handed to a person4

A grounded agent that answers pictures it cannot read is a liability wearing a smile. The honest sentence — these six are yours — costs less than one confident wrong reply.

What we deliberately did not build

Built
  • DM answers from the approved list, in the owner’s voice
  • Escalation with the full handover packet by mail
  • A ledger row for every decision, feeding the reports
  • The paused-by-default, allow-list-guarded rollout path
Deliberately not
  • No client dashboard — owners reply inside Instagram, where they already are
  • No replies to photo-only messages
  • No answers about money — ever, from anyone’s agent
  • No autonomy over anything irreversible

The dashboard deserves a sentence, because every vendor sells one. An owner who must learn a new inbox has been given a second job. Ours reply natively in Instagram — and the agent detects the human reply and goes quiet on that thread. The absence of a dashboard is not a missing feature; it is the feature.

What a client actually experiences

The four days above were the build. A client joining now inherits the result, and their experience compresses to a sequence measured in taps and short documents rather than meetings:

One tap to connect. We send a private link; the owner opens it on their phone and authorises their own account through the platform’s official consent screen. We never see a password, and the connection is theirs to revoke from their own settings at any moment.

One questionnaire, in writing. The answer list comes from the owner: the twenty or so questions their customers actually ask, answered the way they actually answer them — plus the topics that must always reach a human, and the topics the agent must never discuss at all. A clinic bans clinical judgement; a restaurant bans allergen guarantees; every business bans money. This document is the product; the software merely obeys it.

A silent week, if they want one. The agent runs in exactly the mode this case study began with: drafting everything, sending nothing. The owner reads what it would have said to real customers. Only when the drafts read right does the switch flip — and even then, replying to any thread personally makes the agent go quiet on that thread, automatically. The owner always outranks the agent, without pressing anything.

A daily summary and a monthly report. The same ledger that produced every number on this page produces theirs: what was answered, what was handed over, what could not be answered and should be added to the list. The definitions are frozen and printed with the numbers, so a result can be checked, not just admired.

Definitions and sources

first reply
message arrival to answer sent, including our one-minute polling cycle
handover
a thread the agent stopped on and passed to a person with full context
held
a reply the engine drafted but the send path refused to transmit
1–2. worker ledger, day-3 18:44–18:45 UTC · 3. inbox read with skip counters, day 2 · 4. the escalation list is enforced in the send path; the model cannot override it.

Every number from the ledger · measured, not estimated Automated replies are disclosed to customers

Don’t take our word for itThe simulator on the Inbox Agent page runs the same engine, live Try it →
See it answer your inbox.

A private link, one hour, read-only. Nothing is sent to anyone.

Ask for a demo link

Still a question? Ask in writing.

Describe the problem in plain words. A written reply tells you what we’d build, what it costs and when it lands — or that you don’t need it.

Start a project