Extend: Replies — reply rules, auto-responses, and drip follow-ups
Lane 2 of Extend Tinbox — the three scopes of reply logic, from org-wide Handbook policy to per-send thread directives, on one autonomy dial.
Lane 2 of the six-lane Extend Tinbox series is about what happens after a message lands on a thread — how Tinbox drafts an answer, follows up when the room goes quiet, or just taps you on the shoulder. The headline feature here is thread directives: reply logic you attach to a single send. They ship today.
Attach reply logic to a send
A thread directive rides along with emails.send. When the recipient replies — or when they don't — Tinbox acts on the plan you handed it. Here is the whole thing, runnable:
import { Tinbox } from "tinbox/sdk";
const tb = new Tinbox({ host: "https://acme.dev", apiKey: process.env.TINBOX_API_KEY });
await tb.emails.send({
from: "sales@acme.dev",
to: "lead@corp.com",
subject: "Your quote",
text: "Here's the quote you asked for …",
// When they reply on this thread, draft an answer in your voice (lands in Review).
onReply: { mode: "draft", tone: "warm, concise", instructions: "Answer pricing questions; offer a call." },
// If no reply arrives, follow up on a schedule. Each step fires only if still silent.
drip: [
{ after: "3d", ifNoReply: true, mode: "draft", instructions: "Gentle bump — restate the offer." },
{ after: "7d", ifNoReply: true, mode: "notify" },
],
});
That's it — no separate rule, no dashboard config. The directive lives on the thread the send opens.
The autonomy dial: mode
Every directive — onReply and every drip step — carries a mode that decides how far Tinbox is allowed to go on its own:
draft(default) — the agent writes the reply in your voice and drops it in the Review queue. Nothing is sent. This is the safe default at every scope.send— auto-sends the drafted reply. This works only if the deployment opts in withdirectives: { allowAutoSend: true }intinbox.config.ts. Otherwise the send is rejected with a clear error — it is never silently downgraded todrafton the SDK path, so you always know your intent didn't take.ask— parks a check-in question in Review ("They asked for a discount — approve 10%?") instead of a full draft.notify— notification only. No draft, no send; just a heads-up that the event fired.
The same draft → ask → send progression is the dial at every scope below. Start at draft, earn trust, turn the dial up.
Drip follow-ups
drip is an ordered list of steps that fire while the thread stays silent:
after— a duration string ("3d","7d","2h","30m","1w") or a raw millisecond number. Time is measured from the send.ifNoReply(defaulttrue) — the step fires only if the thread is still silent. A reply cancels the remaining schedule.- Each step takes its own
modeandinstructions, so a drip can nudge softly, then just notify you, then stop.
Two guardrails keep a thread from looping forever: every directive has a hard expiry (default 14 days) and a maxFires cap (default 1). Past either limit, the directive goes dormant.
Managing directives after the send
onReply also accepts its snake-case alias on_reply — the same dual-acceptance you get with reply_to/replyTo — so payloads coming from other languages or JSON tooling just work.
Once a send is out, use the typed client to inspect or pull directives off a thread:
const active = await tb.api.directives.list.query({ threadId });
await tb.api.directives.cancel.mutate({ id: active[0].id });
The types DirectiveAction, DripStep, and ThreadDirective are exported from tinbox/sdk if you want to build the payload with full type-checking.
If you'd rather not write code at all, the set_thread_directive manifest tool lets the assistant attach the exact same directive by conversation — see the last section.
Three scopes, one dial
Reply logic lives at three widening scopes. They share the same draft/ask/send autonomy dial, so what you learn at the narrow end carries all the way out.
-
Thread directive (per-send / per-thread) — the narrowest scope, the SDK example above. Logic scoped to exactly one conversation, expiring on its own. Reach for this when the plan is specific to this lead, this quote, this thread.
-
Agents (per-inbox) — a standing agent bound to an inbox that drafts or sends replies across every thread that lands there. Same dial, wider reach: instead of decorating one send, you give an inbox a durable personality and policy. Use this when the behavior should apply to all mail for a mailbox, not just one send.
-
Handbook (org-wide policy) — the broadest scope: plain-language rules every agent surface honors ("never quote a discount over 15% without asking", "always offer a call to enterprise leads"). The Handbook is the constitution the per-inbox agents and per-thread directives operate inside. It sets defaults and boundaries; the narrower scopes specialize within them.
Author at the narrowest scope that does the job, and let the wider scopes set the guardrails.
Same feature, three front doors
Everything above works identically no matter how you author it:
- The SDK — the
onReply/drippayload shown at the top. - The composer's "After sending…" menu — pick the mode, tone, and drip schedule in the UI when you send by hand.
- The assistant — just ask: "if they reply, draft an answer in my voice" or "bump them in three days if it's quiet." The assistant attaches the directive via the
set_thread_directivemanifest tool.
They all write the same ThreadDirective. There is no "advanced" path and "easy" path — one feature, three surfaces.
Which lane instead?
- To classify inbound mail rather than reply to it — route, tag, or extract structured data from what arrives → Parsers, lane 1.
- To send ad-hoc mail from another app or script (no reply logic attached) → Scripts, lane 3.
For the full six-lane map, see the Extend Tinbox hub. For the complete send payload — attachments, scheduling, idempotency — see the Sending API guide.