How a campaign runs
How a request-launched campaign runs
Step 1. Your system sends a single request. It contains the campaign name, the agent name, the dialing speed, the working hours, the purpose of the campaign, the consent attestation, and the list of numbers with the data for the conversation. Step 2. The platform parses the list. The same parsing as for a file in the dashboard: number normalization, duplicate detection, required-column checks, do-not-call registry matching, geography checks for the US. None of this is skipped or simplified on the API path. Step 3. The platform checks whether launching is allowed. Is the agent published, is there a line, is the window in the past, is the attestation signed, did all numbers end up in the do-not-call registry. Step 4. If everything is fine, the campaign starts. With the same scheduler as campaigns from the dashboard: at your speed, within your working hours, retrying no-answers by your rules, and under the per-number frequency caps in force in your organization. Step 5. You read the response. It tells you what happened: whether the campaign started, how many rows were accepted, and what is wrong with the rejected ones. There is no separate “button press” in this scheme: one request does all three things at once — creates the campaign, accepts the numbers, and launches it.How to read the response
A response always arrives, even when the campaign did not start. You read it from one field and two lists.status — the single answer to “did it start?”:
blockers — why it did not start. These are not “request errors” but the state of affairs: the agent was unpublished, numbers matched the do-not-call registry, the window is in the past, there is no signature under the consent attestation. Each reason has its own code — the same one the dashboard shows on the launch screen.
warnings — non-blocking notices. They block nothing and are present on practically every campaign: for example “no start was set, dialing begins right away” and “a frequency policy is in force” with the figures for how many times a day and a week one number may be called.
import — what happened to the list of numbers. How many rows were seen, accepted, accepted with warnings, rejected, how many duplicates, and for which reasons (grouped by reason code with the numbers of the first rows). dnc_screen states separately: how many readable rows were screened, how many were not, how many rows matched the registry and how many distinct numbers that is; every match carries the row, the original phone, the name, the reason and the registry record.
The campaign and the report are kept in every outcome. Even when the campaign did not start, it remains in the dashboard and the per-row report is available: this is the only way to understand what is wrong with the data, and deleting it along with the failure would destroy exactly what is needed to fix it.
One rule that removes the guesswork
An action that changed nothing is always an error. A successful response means the state in the response body is the new state. Therefore:- a create request answers with success even if the campaign did not start: the campaign and the report really were created, and
statushonestly saysdraft; - a separate “launch” request that launched nothing answers with an error — and names the reason inside the error, so you do not have to ask a second time;
- a repeated “pause” on an already paused campaign is not an error: the state is already what you asked for.
Retrying a request: why the idempotency key is mandatory
The network sometimes drops the response after the platform has already done everything. At that moment your system does not know whether the request arrived, and usually repeats it. For a single call that is annoying; for a list of ten thousand numbers it means calling everyone twice. That is why campaign creation and adding numbers to a campaign both require theIdempotency-Key header — an arbitrary string your system generates for every new campaign or batch (for example, the task identifier in your system). From there the platform sorts it out itself:
- same key, same body — the same response as the first time is returned; no second campaign appears;
- same key while the first request is still running —
409 idempotency_key_in_flight: wait for the response, do not retry again; - same key but a DIFFERENT body —
422 idempotency_key_reuse. This guards against the nastiest failure: without it you would receive the response to someone else’s request and conclude that the platform ignored your settings; - the request was rejected before the campaign was created — the key is released immediately, and a corrected request with the same key goes through normally. If the request was rejected after the campaign was created (the list could not be stored, say), the key stays taken and you find the campaign with
GET /v1/campaigns.