Skip to main content

Campaign orchestration

Sellable product

This capability is granted by an API key scoped to the `` product (product face). See the product reference below.

Outbound multi-channel campaign engine — define a campaign over an audience (a channel + a message template + a pacing/rate-limit + a schedule window), add contacts, and start it. An owner-elected pacer (single-owner Postgres advisory-lock election + FOR UPDATE SKIP LOCKED contact claim) selects due contacts respecting a per-minute rate budget derived from the persisted dispatch history (correct across ticks/restarts/failover) and dispatches each by POSTing the campaign's dispatch_endpoint (a channel capability) — it never sends bytes itself. Own logical Postgres store (I4). Dispatch/callback service tokens are resolved server-side (Infisical), never stored per-campaign or logged.

  • Group: Comms & workflow
  • Contract: contracts/campaign-orchestration/v1/openapi.yaml
  • Public base: https://api.vagarylabs.com (the consolidated API gateway — one host, per-brand sibling api.<zone>)
  • Auth: a product API key (vgk_…) issued from the console — Authorization: Bearer vgk_…
  • Product face (customer-keyed):
    • https://api.vagarylabs.com/product/v1/campaigns

Endpoints

MethodPathSummary
POST/v1/campaignsDefine (upsert) a campaign
GET/v1/campaignsList the caller's org campaigns
GET/v1/campaigns/&#123;campaign_id&#125;Get one campaign
PATCH/v1/campaigns/&#123;campaign_id&#125;Update pacing / schedule / template / retry (not status)
POST/v1/campaigns/&#123;campaign_id&#125;/startStart (or, for a future start_at, schedule) dispatching
POST/v1/campaigns/&#123;campaign_id&#125;/pausePause dispatching
POST/v1/campaigns/&#123;campaign_id&#125;/contactsAdd audience contacts (bulk, idempotent on contact_id)
GET/v1/campaigns/&#123;campaign_id&#125;/statsLive campaign stats (contacts by status + dispatch counts + trailing-minute rate)
POST/internal/campaigns/&#123;campaign_id&#125;/activateScheduler callback — flip a scheduled campaign to running
POST/internal/campaigns/&#123;campaign_id&#125;/paceRun exactly ONE pacing pass for a campaign. Cadence lives in the scheduler, not here; the budget cap holds regardless of call frequency because budget is derived from persisted dispatch history.
GET/healthliveness
GET/metricsPrometheus

Schemas

Pacing

dispatch-rate governance. rate is the channel-agnostic default; progressive/predictive/power are the restored voice pacing family generalized to available_capacity.

FieldTypeDescription
rate_limit_per_minuteintegerhard per-minute dispatch ceiling (always enforced)
modestring
lines_per_capacitynumber
power_lines_per_capacitynumber
max_failure_ratenumber

ScheduleWindow

FieldTypeDescription
start_hourinteger
end_hourinteger
daysarray0=Mon … 6=Sun
timezonestringIANA tz (e.g. America/New_York); default UTC

Schedule

FieldTypeDescription
start_atstringfuture start → durable delayed-start via the scheduler capability
end_atstring
windowobject

CampaignRequest

FieldTypeDescription
organization_idstringproduct-tier tenant (I6)
campaign_idstring
namestring
channelstring
dispatch_endpointstringhttp(s) URL of the channel capability that SENDS each contact
message_templateobject
pacingobject
scheduleobject
max_attemptsinteger
retry_delay_secondsinteger

CampaignPatch

FieldTypeDescription
namestring
message_templateobject
pacingobject
scheduleobject
max_attemptsinteger
retry_delay_secondsinteger
dispatch_endpointstring

Campaign

FieldTypeDescription
organization_idstring
campaign_idstring
namestring
channelstring
dispatch_endpointstring
message_templateobject
pacingobject
scheduleobject
max_attemptsinteger
retry_delay_secondsinteger
statusstring
created_atstring
updated_atstring

Contact

FieldTypeDescription
contact_idstring
addressstringchannel address — phone / email / webhook target
custom_fieldsobject

ContactsRequest

FieldTypeDescription
contactsarray

Generated by scripts/gen-capability-docs.py from contracts/campaign-orchestration/v1/openapi.yaml — the contract IS the source of truth; edit the contract, not this page.