Your GTM belongs in gitRegister
Cookbooks
Engagement

Agentic engagement

Deploy an AI agent that holds email conversations with leads from a mailbox your workspace owns: a sending domain, a mailbox, native sendEmail and listEmailEvents on the agent, a native email trigger on reply and unsubscribe, and a heartbeat that checks thread status when nothing inbound happened.

Installation
Set up the agentic-engagement cookbook in this project. 1. If the Cargo CLI is not installed yet, follow every step in https://api.getcargo.io/INSTALL.md 2. From the Cargo project, run: cargo-ai cdk add cookbook/agentic-engagement 3. Then follow .claude/skills/agentic-engagement/SKILL.md. Stop for my approval before any paid call, and stop at "cargo-ai project plan" before deploying anything.
Paste it into Claude Code, Cursor, Codex, Gemini CLI, Devin or Replicas.

Say this to your agent

Give our jordan@ mailbox an agent that answers leads who reply to my first emails, and checks quiet threads once for a single follow-up.

Illustrative output #

Fictional records, to show the shape of what comes back.

text
From:    Priya Raman <priya@northwind.example>
Status:  replied  (native email trigger wakes the engager's chat)
Subject: Re: Routing inbound leads at Northwind
> Interesting. Does this work if our CRM is Salesforce, not HubSpot?

From:        Jordan <jordan@outreach.fabrikam.example>   (the bound mailbox)
Subject:     Re: Routing inbound leads at Northwind
In-Reply-To: <msg-2@northwind.example>
References:  <msg-1@outreach.fabrikam.example> <msg-2@northwind.example>
> Yes. Salesforce is a native connector, same as HubSpot, so routing writes
> straight to your lead owner field. Worth 20 minutes next week to see it on
> your own queue?

One threaded reply per wake, grounded in the workspace context; had Priya unsubscribed instead, the same wake would have ended with no send, and a thread left at sent gets at most one heartbeat follow-up.

What you will be asked #

InputWhy
sendingDomain (infra/domains/outreach.ts)Looked upA mailbox cannot be created on a domain the workspace does not own. Registering a second copy of a domain the workspace already has fails instead of binding.
mailboxIdentity (infra/mailboxes/rep.ts)AskedA fabricated sender is refused. Domain, username and type are create-only; getting the local part wrong means destroy plus a new inbox.
languageModel (infra/connectors/openai.ts)Looked upConversation quality and per-turn cost both live here.
mailboxChargeAskedA mailbox bills every month until destroy. Silence is not approval of a fleet.

Done when #

  • cargo-ai cdk plan reports the domain, the mailbox and the engager, and the operator approved the monthly mailbox line against a live pricing get
  • node --import tsx evals/contract.mjs passes: the agent trigger is native email with kinds: ["replied", "unsubscribed"], a heartbeat is declared whose prompt names listEmailEvents, the engager uses native sendEmail and listEmailEvents, and mailboxUuid is locked on the sendEmail use
  • the mailbox reaches active (cargo-ai mailboxManagement mailbox refresh-status) and warm-up has been started (mailbox start-warmup) so the send ramp can climb
  • a first send to yourself delivers from the declared address
  • a reply to that send wakes the engager without a human prompting it
  • the agent’s reply lands in the same thread in the mailbox (same subject, In-Reply-To set), not as a new conversation
  • a second reply continues that same thread; an unsubscribe or “not interested” produces no further send
  • an unanswered first send is still a live chat: the heartbeat is declared, and the agent treats that wake as a status check, not a sequence

What it costs #

A mailbox is a monthly, recurring charge for as long as it exists. Immediately before the plan, run cargo-ai mailboxManagement pricing get and read monthlyCredits for the chosen type. Say that number out loud as a per-month cost, not a one-off. destroy is the only way it stops.

If the plan also shows + create domain:…, that registration is a separate, non-refundable charge. Adopting an existing domain does not.

Each delivery is a sendEmail native action. Run cargo-ai connection native-integration get (or orchestration action list sendEmail) for the current per-send cost. The engager adds one LLM turn per wake, including heartbeats. The per-thread cap is the cost control: one send per wake, at most one unanswered follow-up, and a stop when the lead closes.

A mailbox that never starts warm-up stays at the floor of five real sends a day. Starting warm-up is what moves the ramp; it takes forty-five days to finish. Check mailbox get-send-allowance before promising volume.

To let an agent carry email conversations with leads, give it a mailbox your workspace owns and wake it on every reply, unsubscribe or silence. This cookbook declares the sending domain, the mailbox, native sendEmail and listEmailEvents on the agent, a native email trigger on reply and unsubscribe, and a heartbeat that checks thread status when nothing inbound happened.

shellscript
cargo-ai cdk add cookbook/agentic-engagement

What it does #

  • Owns the inbox. defineDomain plus defineMailbox provision (or adopt) the From address the lead sees. The mailbox is a recurring monthly charge.
  • Sends through native sendEmail. mailboxUuid is locked in the action’s config so every send is from that inbox, and so the native trigger knows which chats to wake.
  • Reads thread status. listEmailEvents is how a heartbeat (and any wake without a fresh event payload) sees replied, unsubscribed, bounced, or silence. Opens and clicks are tracking, not a reason to write.
  • Wakes on inbound status. agentNativeTrigger({ agentSlug: "email", config: { kinds: ["replied", "unsubscribed"] } }) continues the thread or stops it.
  • Checks silence. A heartbeat re-wakes the same chat so the agent can list events on a thread that stayed at sent. At most one unanswered follow-up.
  • Stops. Unsubscribe, “not interested”, or a request for a human ends the loop. A second unanswered send is a sequence; this agent is not one.

How it works #

flowchart TD
    domain["defineDomain"] --> mailbox["defineMailbox"]
    mailbox --> send["sendEmail<br/>mailboxUuid locked"]
    send --> agent["engager"]
    list["listEmailEvents"] --> agent
    agent -->|"first send, or a play calls it"| send
    send --> thread["thread"]
    thread -->|"status: replied or unsubscribed"| trigger["native email trigger"]
    trigger --> agent
    thread -->|"status still sent"| beat["heartbeat<br/>listEmailEvents"]
    beat --> agent
  1. First outbound. A human chats with the engager, or a play calls it with a lead. The agent calls sendEmail. That send belongs to this agent, which is what the trigger later keys off.
  2. Inbound status. IMAP picks up a reply; List-Unsubscribe records an opt-out. The native trigger wakes the same chat.
  3. Silence. If the thread stays at sent, the heartbeat re-wakes the chat so the agent can call listEmailEvents. One follow-up, then stop.
  4. The agent sends at most once per wake, passing inReplyTo and the full references chain so the thread stays a thread.

Adds 3 resource kinds plus the folders they file into.

FileResourceRole
infra/domains/outreach.tsdefineDomainsending domain the mailbox lives on
infra/mailboxes/rep.tsdefineMailboxthe From inbox; monthly charge
infra/agents/engager.tsdefineAgentconversation agent + native sendEmail / listEmailEvents + email trigger + heartbeat
infra/agents/engager.prompt.ts(not a resource)when to reply, how to thread, when to stop
infra/connectors/openai.tsdefineConnectorthe LLM the engager talks through
infra/folders/index.tsdefineFoldermailbox / agent folders named after the skill

Why sendEmail is not a tool #

A tool whose body is one native call is ceremony, and this repo refuses those. sendEmail is a native action the agent can call. The pin that used to live in a wrapper now lives on the use: config: { mailboxUuid: rep.uuid }. Take that pin out and the loop silently splits.

Placeholders (edit before deploy) #

  1. Sending domain — infra/domains/outreach.ts: the domain the workspace owns. Keep adopt: true if it was bought in the UI; drop it only to register a new one.
  2. Mailbox identity — infra/mailboxes/rep.ts: username, first name, last name of a real person. Type defaults to google.
  3. Language model — infra/agents/engager.ts.

What it does not do #

It does not blast a list, start warm-up (that is a CLI call after deploy), merge DNS, write to a CRM, or invent a first send from a reply trigger or a heartbeat. First outbound is a chat or a play that calls this agent; this skill is the loop after that.

Give your agents a runtime

Bring the agents you have.Start free, deploy in one command.