Usage
Construct an Outbound instance with an adapter, list and read records, follow a campaign down to its sequence, stats and leads, and handle the one error class.
Create one instance per adapter, then call the same methods on it whichever vendor is behind it.
Construct an instance
import { createOutbound } from "outbound-sdk";
import { instantly } from "outbound-sdk/instantly";
// Reads INSTANTLY_API_KEY. Or pass it: instantly({ apiKey }).
const outbound = createOutbound({ adapter: instantly() });
console.log(outbound.provider, outbound.maturity);
console.log(outbound.capabilities["replies.category"]);
instantly beta
vendor
Constructing an instance makes no request. maturity says how far the adapter has been checked against the live API (see what is verified), and capabilities says what it can do before you ask.
To try the SDK without a key, use the in-memory adapter. The rest of this page does.
Read
import { createOutbound } from "outbound-sdk";
import { demoData, memory } from "outbound-sdk/memory";
const outbound = createOutbound({ adapter: memory({ data: demoData() }) });
// One page, with a cursor for the next.
const page = await outbound.campaigns.list({ limit: 1, status: "active" });
console.log(
page.items.length,
"campaign on this page; complete:",
page.complete
);
// A handle is one record and everything under it.
const [first] = page.items;
const campaign = outbound.campaign(first?.ref ?? "");
const sequence = await campaign.sequence();
console.log("steps:", sequence.steps.length);
const stats = await campaign.stats({ from: "2026-10-01", to: "2026-10-31" });
console.log("sent:", stats.rows[0]?.sent);
for await (const lead of campaign.leads.listAll()) {
console.log(lead.email, lead.status);
}
// Every result says how it was obtained.
console.log(stats.meta.evidence, stats.meta.provider, stats.meta.complete);
1 campaign on this page; complete: true
steps: 2
sent: 90
person1@example.com replied
person2@example.com active
exact memory true
- Collections (
campaigns,senders,leads,messages,replies,lists,tags,blocklist,webhooks,workspaces) havelistfor one page andlistAllfor every page, fetched under the vendor’s rate limit as you iterate. See collections. - Handles (
campaign(id),sender(id),lead(id),list(id),thread(id)) are one record and what hangs off it. See handles. - Every result carries
meta: how it was obtained (exact,sampledorderived), when, how many vendor requests it cost and whether it is complete. See results.
An id can be the vendor’s own id or a ref from any record. On an instance that combines several adapters, use the ref.
Handle errors
There is one error class, OutboundError, with a code.
import { createOutbound, OutboundError } from "outbound-sdk";
import { demoData, memory } from "outbound-sdk/memory";
const outbound = createOutbound({
// An adapter that, like HeyReach, cannot read campaign settings.
adapter: memory({
capabilities: { "campaigns.settings": false },
data: demoData(),
}),
});
try {
await outbound.campaigns.get("no-such-campaign");
} catch (error) {
if (OutboundError.is(error)) {
console.log(error.code, error.permanent);
}
}
try {
await outbound.campaign("cmp_founders").settings();
} catch (error) {
if (OutboundError.is(error)) {
console.log(error.code, "-", error.message);
}
}
console.log(await outbound.campaigns.exists("no-such-campaign"));
NotFound true
Unsupported - Reading a campaign's settings is not available: the memory adapter does not support it (campaigns.settings)
false
A missing record is NotFound. A read the adapter cannot serve is Unsupported, thrown before any request. exists never throws for a missing record. The full list of codes is on the errors page.
Next
- Capabilities: branch on what an adapter can do.
- Insights: computed answers on top of these reads.
- Rate limits and retries: what the SDK does so you do not have to.
- Guides: complete tasks, end to end.