benchmarks
Put a campaign's reply, bounce, open and other rates beside its own workspace's over the same dates, as a value, a baseline and a ratio.
benchmarks() from outbound-sdk/benchmarks adds outbound.benchmarks.campaign(id), which returns a campaign’s rates beside the rates of the whole workspace it belongs to, over the same dates. A 6% reply rate means more next to the workspace’s 2.8%. No outside benchmark is used: every number comes from what this credential can read.
Sample
Runs on the shared seed: three campaigns, 800 sends and 22 replies in the two days.
import { createOutbound, OutboundError } from "outbound-sdk";
import { benchmarks } from "outbound-sdk/benchmarks";
import { memory } from "outbound-sdk/memory";
import { seed } from "../plugins-index/seed.ts";
const outbound = createOutbound({
adapter: memory({ data: seed, workspaceId: "agency" }),
plugins: [benchmarks()],
});
const percent = (rate: number | null): string =>
rate === null ? "not reported" : `${(rate * 100).toFixed(1)}%`;
const range = { from: "2026-11-12", to: "2026-11-13" };
const results = await Promise.all(
["cmp_founders", "cmp_ecom"].map((id) =>
outbound.benchmarks.campaign(id, range)
)
);
for (const { campaign, comparisons } of results) {
const { bounceRate, openRate, replyRate } = comparisons;
console.log(campaign);
console.log(
` reply rate ${percent(replyRate.value)}, workspace ${percent(replyRate.baseline)}, ratio ${replyRate.ratio?.toFixed(2)}`
);
console.log(
` bounce rate ${percent(bounceRate.value)}, workspace ${percent(bounceRate.baseline)}, ratio ${bounceRate.ratio?.toFixed(2)}`
);
// The seed has no opens, so there is no open rate and no ratio.
console.log(
` open rate ${percent(openRate.value)}, ratio ${openRate.ratio}`
);
}
// A campaign that does not exist.
try {
await outbound.benchmarks.campaign("cmp_missing", range);
} catch (error) {
if (OutboundError.is(error)) {
console.log(`${error.code}: ${error.message}`);
}
}
// An adapter with no workspace-wide stats cannot give the baseline.
const noWorkspace = createOutbound({
adapter: memory({ capabilities: { "stats.workspace": false }, data: seed }),
plugins: [benchmarks()],
});
try {
await noWorkspace.benchmarks.campaign("cmp_founders", range);
} catch (error) {
if (OutboundError.is(error)) {
console.log(`${error.code}: ${error.message}`);
}
}
memory:agency:cmp_founders
reply rate 6.0%, workspace 2.8%, ratio 2.18
bounce rate 1.0%, workspace 3.0%, ratio 0.33
open rate not reported, ratio null
memory:agency:cmp_ecom
reply rate 1.0%, workspace 2.8%, ratio 0.36
bounce rate 9.0%, workspace 3.0%, ratio 3.00
open rate not reported, ratio null
NotFound: Campaign cmp_missing was not found
Unsupported: Reading stats for the workspace scope is not available: the memory adapter does not support it (stats.workspace)
The workspace’s reply rate is 22 of 800, which is 2.75% and prints as 2.8%. The first campaign’s is 12 of 200, so its ratio is 6 divided by 2.75. The baseline includes the campaign itself.
Options
benchmarks() takes no options.
outbound.benchmarks.campaign(id, options?):
| Option | Type | Default | Meaning |
|---|---|---|---|
id |
string |
required | The campaign’s id or ref. On a combined instance, its ref. |
from, to |
string |
none | Inclusive YYYY-MM-DD. The same dates are used for both reads. Without them, both use the range a stats read uses when given no dates. |
signal, timeout, retries, deadline, maxRequests |
the instance’s | Passed to both reads. |
What it adds
outbound.benchmarks.campaign() returns a CampaignBenchmark:
| Field | Meaning |
|---|---|
campaign |
The campaign’s ref. |
comparisons |
One Comparison for each of replyRate, positiveReplyRate, bounceRate, openRate, unsubscribeRate and acceptanceRate. |
requests |
Vendor requests the two reads made. |
A Comparison is:
| Field | Meaning |
|---|---|
value |
The campaign’s rate, 0 to 1, or null when it cannot be computed. |
baseline |
The workspace’s rate over the same dates, or null. |
ratio |
value / baseline. 1.2 means 20% above the workspace. null when either side is null or the baseline is 0. |
The rates are the SDK’s own definitions, the same ones stats uses, computed from each row’s counts. A rate the vendor’s counts cannot support is null, never 0.
What it costs
Two stats reads, started together: the campaign’s totals and the workspace’s totals. On the in-memory adapter that is no request. On a vendor it is what those two reads cost there.
What it changes
It changes no capability, no record and nothing in meta. It has no layer of its own, so its position in plugins does not matter.
With other plugins
- Both reads go through the instance, so
cachecan serve them andbudgetcounts them. - On a combined instance, workspace stats come back one row per member. The baseline is the row of the campaign’s own provider and workspace.
- The result has no
meta. Iffailoverserved either read stale, the benchmark does not say so.
When it cannot answer
The last two cases in the sample:
| What happened | What you get | What to do |
|---|---|---|
| The campaign does not exist and the adapter says so, as the in-memory adapter does | NotFound from the stats read: “Campaign … was not found” |
Check the id. |
| The campaign’s stats read returns no row, or there is no workspace row for the campaign’s member | NotFound: “No stats were returned for campaign …” |
Check the id. The campaign may not have sent yet. |
| The adapter cannot read workspace-wide stats | Unsupported. The two reads start together, so the campaign’s read may already have run. |
Check outbound.capabilities["stats.workspace"] first. Without it there is no baseline. |
| The adapter cannot read campaign stats | Unsupported |
Use an adapter that declares stats.campaign. |
| A count is not reported | That rate’s value, baseline or both are null, and so is ratio |
Print “not reported”. Do not treat it as zero. |
Limits
- The comparison is a ratio of two rates. It carries no test of whether the difference is more than chance, and a campaign with a handful of sends can show a large ratio. For a tested comparison, see insights.
- The baseline is the whole workspace, including the campaign being compared and campaigns on other channels.