Skip to content

Extend: Agents — sourcing and campaign agents

Lane 6 of Extend Tinbox — agents that source people and run outreach, built on agentic skills over the outreach seam.

Lane 6 is about agents that do outreach — finding the right people (sourcing) and running the campaigns that reach them. The building block is an agentic skill: a tenant-authored tool the assistant, the autopilot, MCP, and the CLI can all call. You give a skill a name, a typed input, and a run function; Tinbox handles the gating, the host allowlist, and exposing it to the model as a callable tool.

The complete example

Here is a real sourcing skill — find founders matching a query and hand them back as prospects:

import { agenticSkill } from "tinbox/cf/skills";
import { z } from "zod";

const sourceFounders = agenticSkill({
  id: "source-founders",
  label: "Source founders",
  description: "Find founders matching a query and return them as prospects.",
  requiresEnv: ["APOLLO_KEY"], // gated off until this secret is set
  requiresHosts: ["api.apollo.io"], // ctx.fetch host allowlist
  inputSchema: z.object({ query: z.string(), limit: z.number().optional() }),
  run: async ({ query, limit = 25 }, ctx) => {
    const res = await ctx.fetch(
      `https://api.apollo.io/v1/people/search?q=${encodeURIComponent(query)}`,
      {
        headers: { authorization: `Bearer ${ctx.env.APOLLO_KEY}` },
      },
    );
    return await res.json(); // returned data is model-facing
  },
});

Register it in config.skills (or, if you ship a plugin, in the plugin's skills / agentTools):

export default defineConfig({
  // …
  skills: [sourceFounders],
});

Once registered, the assistant, MCP, the CLI, and the autonomous loop can all call source-founders as a tool. Ask the assistant "find me 20 seed-stage founders in Berlin" and it fills the schema, calls run, and reasons over what comes back.

What an agentic skill is

An agentic skill is a tool you author. It has:

  • id — stable, kebab-case; how the model and the CLI refer to the tool.
  • label — a short human name for the UI.
  • description — shown to the model. This is the tool's prompt: say what it does and when to reach for it, plainly.
  • inputSchema — a Zod object. It types run's input and tells the model exactly what arguments to produce. No hand-written JSON Schema.
  • run(input, ctx) — your code. It returns model-facing data — whatever you return is what the model reads next, so return the fields that matter, not raw noise.

The preferred gate is a named Connection:

  • requires — names the Connections the skill needs. Until every one is configured, the skill is absent from chat, MCP, CLI, and autopilot. A configured client appears at ctx.connections[id], with scoped secrets and allowlisted fetch. See the shipped order-lookup example.

The legacy low-level scopes remain compatible:

  • requiresEnv — the skill stays gated off until every named secret is set. source-founders above is invisible until APOLLO_KEY exists. No key, no tool.
  • requiresHosts — pins ctx.fetch to an allowlist. A call to any host not listed is refused, so a skill can't be talked into exfiltrating to an arbitrary URL.

Inside run, use ctx.fetch (the allowlisted fetch) and ctx.env (your secrets) rather than reaching for globals — that is what keeps the gates real.

Reversibility — what the autopilot may do unattended

Every skill has a reversibility setting:

  • "reversible" (the default) — safe to undo, so the autopilot may run it without asking. Sourcing a list of people is reversible: it produces prospect rows you can drop.
  • "acting" — it takes an outside-visible action (sends mail, posts, charges). Mark it and the autopilot holds it for approval instead of firing it autonomously.
const sendOutreach = agenticSkill({
  id: "send-outreach",
  label: "Send outreach email",
  reversibility: "acting", // held for human approval under autopilot
  // …
});

Rule of thumb: if running it twice, or having it run while you're asleep, would embarrass you, it's "acting".

The agent itself

A skill is a tool; an agent is what wields tools toward a brief. Two shapes:

  • A per-inbox agent — declare a brief (its goal), the exact tools it may reach (a subset of your skills), and a human-in-the-loop policy (what it may do alone vs. what waits for you).
  • The autopilot — run the shared toolset on a schedule, with reversibility deciding what runs unattended.

The full agent model — briefs, tool scoping, approval policy, the four agent surfaces — lives in Agents. This lane only covers the outreach-flavored skills those agents call.

The outreach seam

Sourcing and campaigns ride Tinbox's outreach engine (cold prospects, campaigns), which has a deliberate ownership default:

A cold, BYO-scraped stranger stays campaign-only — a prospect row — and does not pollute your Contacts registry until a touch is earned: an approved send, a reply, a qualification, or a graduation.

So source-founders returning 200 people does not dump 200 rows into Contacts. They sit as prospects; only the ones you actually engage graduate into contacts. Sourcing also credits the real actor — the agent or conversation that did the sourcing — with a provenance event, so a contact page can later show "sourced via agent in YC W26" and you can audit where anyone came from.

Status — be honest: this is the design, and the sourcing seam is partially landed. It rides the coldOutreach feature flag and there is not yet a finished, polished campaign-agent UI. The skill mechanism and the ownership default are real; treat the end-to-end campaign-agent experience as coming, not done.

For the outreach side in depth, see Outreach agents, the one-shot Outreach in one message, and Onboarding cold outreach.

Same mechanism, different intent

The agenticSkill API also powers the agent tools that back dashboards in Lane 5 — Views. The code is identical; the difference is intent. Lane 5 tools answer inbox questions ("how many unreplied threads this week?"). Lane 6 tools reach outward — source people, run campaigns. Pick the lane by what the tool is for, not by how it's built.

Which lane instead?

Back to the hub: Extend Tinbox.