sphoroVOICEdocs
Markdown

Campaigns

A journey run over a list of contacts, paced against your own concurrency, pausable while it runs, and reportable while it is still going. This is the answer to "I have four thousand numbers".

The shape of it

create ──▶ upload entries ──▶ start ──┬──▶ pause ──▶ resume
                                       │
                                       ├──▶ cancel
                                       │
                                       └──▶ report   (readable throughout)

1 · Create it

POST/v1/journey_campaigns
Shell
curl -s -X POST https://voice.sphoro.com/v1/journey_campaigns \
  -H "Authorization: Bearer $SPHORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "journey_id": "'"$JOURNEY_ID"'",
    "name": "September collections"
  }'
Response201 Created
{
  "id": "cmp_5d81a3f70b2e94c6",
  "journey_id": "jny_2c4e…",
  "name": "September collections",
  "status": "draft",
  "entry_count": 0
}

A campaign is pinned to the journey's published version when it starts. Publishing a new version mid-campaign does not change what the running one does — which is what you want, and is why publishing during a campaign is safe.

2 · Load the contacts

POST/v1/journey_campaigns/{id}/entries
Shell
curl -s -X POST https://voice.sphoro.com/v1/journey_campaigns/$CAMPAIGN_ID/entries \
  -H "Authorization: Bearer $SPHORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {"contact": {"phone": "+919876543210", "email": "priya@example.com"},
       "variables": {"first_name": "Priya", "amount_due": 8400}},
      {"contact": {"phone": "+919812345678"},
       "variables": {"first_name": "Arun",  "amount_due": 2200}}
    ]
  }'

Every variable the journey's agents need must be here. A contact missing one is refused at upload rather than failing when it is dialled three days later — see variables.

Upload in batches and check the refusals. A four-thousand row load that comes back with sixty refusals is sixty people who will never be called, and the only moment you will be told is now.

3 · Start, and control it while it runs

POST/v1/journey_campaigns/{id}/start
POST/v1/journey_campaigns/{id}/pause
POST/v1/journey_campaigns/{id}/resume
POST/v1/journey_campaigns/{id}/cancel
Shell
curl -s -X POST https://voice.sphoro.com/v1/journey_campaigns/$CAMPAIGN_ID/start \
  -H "Authorization: Bearer $SPHORO_API_KEY"
ActionDoes
PauseStops starting new contacts, and holds every contact already walking the graph on the node they are standing on. Calls already connected finish normally.
ResumeStarts new contacts again, and releases the held ones within about thirty seconds, from where each left off.
CancelStops for good. Executions in flight are ended.
Pause is the button to know about before you start. The moment to discover the greeting says the wrong month is forty calls in, and pause is what turns that into forty rather than four thousand.

4 · Read the report while it runs

GET/v1/journey_campaigns/{id}/report
Shell
curl -s https://voice.sphoro.com/v1/journey_campaigns/$CAMPAIGN_ID/report \
  -H "Authorization: Bearer $SPHORO_API_KEY"
Response200 OK
{
  "campaign_id": "cmp_5d81a3f70b2e94c6",
  "status": "running",
  "entries": { "total": 4000, "started": 1240, "running": 86, "finished": 1154 },
  "outcomes": { "converted": 402, "unreachable": 611, "opted_out": 12, "stuck": 3, "expired": 0 },
  "nodes": {
    "remind":  { "entered": 1240, "left": 1154 },
    "hold":    { "entered": 700,  "left": 700 },
    "sms":     { "entered": 611,  "left": 611 },
    "arrange": { "entered": 402,  "left": 402 }
  }
}

The nodes block is the funnel: how many contacts entered each node and how many left. It is where a badly-drawn graph shows up — a node with far fewer leaving than entering has contacts standing on it, and stuck in the outcomes says some of them ran out of cases.

Pacing

A campaign paces its own dialling against your account's concurrency ceiling. It does not fire every contact at once, which is the mistake a hand-rolled loop makes — see concurrency for what that costs.

Two things still gate every individual call, and they are the reason a campaign's numbers never quite match its entry count:

  • Compliance. Suppressed numbers, missing consent and out-of-hours calls are refused before dialling. See compliance.
  • Calling windows. A wait node with a window holds the contact until the window opens where they are.

Listing them

GET/v1/journey_campaigns
GET/v1/journey_campaigns/{id}
Shell
curl -s https://voice.sphoro.com/v1/journey_campaigns \
  -H "Authorization: Bearer $SPHORO_API_KEY"

Before your first real campaign

  1. Run the journey on yourself. One execution, POST /v1/journey_executions, your own number. You will find something.
  2. Run it on ten colleagues. A campaign of ten, started and watched to completion.
  3. Check the suppression list is loaded, and that consent is where it needs to be. See compliance.
  4. Then load four thousand — and keep the pause endpoint to hand.