Create a campaign
Create a campaign — and, with contacts, start dialling in the same call.
Create a campaign — and, with contacts, start dialling in the same call.
This is the single endpoint to drive from an external system. One request
creates the campaign, loads the contacts, applies transliteration and starts
dialling. Nothing else has to be called: the response carries batch_id and
total_contacts alongside the campaign.
Contacts arrive one of two ways, dispatched on Content-Type:
application/json — send agent_id, name and a contacts array. Each
contact is one flat object: phone_number (to_number is accepted too) and,
beside it, that recipient's prompt variables —
{"phone_number": "+919812345678", "callee_name": "Raju"} fills {callee_name}.
Every key other than the number becomes a variable under its own name, so a nested
variables object arrives as ONE variable called variables; put the variables
at the top level instead. The number is read from those two keys only: a contact
keyed mobile, as a CSV header may be, is refused with a 422 that names it.
multipart/form-data — send name, agent_id and file, a UTF-8 CSV with
one header row. The phone column is the first header named to_number,
phone_number, phone, mobile, number or msisdn
(case-insensitive). Every other column becomes a per-contact variable under its
header name, and a column named name is also mirrored to callee_name — the
key the agent's prompt and the dashboard read. The structured settings
(working_hours, retry_policy, follow_up_policy, transliteration,
default_call_variables, metadata) travel as JSON object strings in their own
form fields, because multipart has no types.
Transliteration rewrites uploaded columns into the script the agent speaks — a list of Latin-script names read by a Hindi agent, say. It is set once here and every later upload, retry and follow-up on this campaign inherits it.
Limits. At most 50,000 contacts per request and 20MB per CSV. Every number must
contain 7 to 15 digits; bad rows come back together in one 422 naming up to 20 of
them — CSV line numbers (the header is line 1) or positions in contacts — so a
list is fixed in one pass.
Starting language. A contact's agent_language (a column of that name in a CSV)
starts its call in one of the agent's languages, by code; empty is the agent's default
language. On an agent with two or more languages, a contact naming a code the agent
doesn't have is left out and the rest are dialled: skipped_count counts them and
skipped_rows lists them, each with its line or position and why. A list in which
every contact names one is a 422, and no campaign is left behind. An organisation may have at most 250,000 contacts waiting
to be dialled across all of its campaigns: a list that would pass that is a 409,
which clears as the queue drains. An account that cannot fund a call is a 402.
Neither leaves a campaign behind.
Omit contacts entirely and nothing dials: you get the policy on its own. Send another request with the same settings when the next list is ready.
A campaign holds the agent, the caller IDs, the timezone, the working hours, the retry policy and the follow-up policy.
Two settings are worth getting right first time:
timezoneandworking_hoursdecide when dialling is legal. Withworking_hours_enforced: true, a day you leave out is a day nothing dials, and a retry that lands outside the window is pushed to the next open one rather than placed at 4am.retry_policy.max_retriescounts attempts AFTER the first, per contact.
The agent is verified before anything is written: a mistyped agent_id
is a 404 here rather than a campaign that fails all 3,000 of its contacts at
dispatch time.
Rate limit: 1200 requests per minute per organization, shared by the endpoints that place, stop and transfer calls and create campaigns. Exceeding it returns 429 with Retry-After; the X-RateLimit-* response headers report your remaining allowance on every call.
v2/campaignsAuthorization
AuthorizationRequiredBearer <token>
Your Graine API key. Create one in the dashboard under Developers, or via POST /v2/api-keys. Send it as Authorization: Bearer <key>.
In: header
Request Body
application/jsonRequirednameRequiredName
Human label, shown wherever the campaign appears.
1Maximum length: 200contactsContacts
Contacts to start dialling immediately. Supply these and this ONE call creates the campaign, creates its batch and begins dialling — nothing else needs calling afterwards. Omit them and you get the policy on its own, with nothing to dial: the published API has no call that adds contacts to an existing campaign, so send the next list as a new POST /v2/campaigns.
Each entry needs phone_number (or to_number) in E.164; every other key becomes one of that contact's call variables under its own name, so {callee_name} in the agent's prompt is filled per person. Keys are not unpacked: a nested variables object becomes ONE variable named variables.
agent_idRequiredAgent Id
Agent that places every call. Must exist in your organisation.
1phone_numbersPhone Numbers
Caller IDs to dial from. Each must be registered to your organisation. OMIT to use every number registered to your organisation — which is what an external integration usually wants, since it would otherwise have to know your number inventory to place a single call.
phone_number_strategyPhone Number Strategy
round_robin | random | least_loaded.
"round_robin"timezoneTimezone
IANA timezone the working hours and the campaign window are evaluated in.
"UTC"working_hours_enforcedWorking Hours Enforced
Set true to confine dialling to working_hours. False dials whenever the window is open.
falseworking_hoursWorking Hours
Keyed by lowercase day name: monday … sunday. Days you omit are closed when enforcement is on.
max_concurrent_callsMax Concurrent Calls
Ceiling on this campaign's SIMULTANEOUS calls. Omit for no campaign ceiling. It can only LOWER what the organisation's limit and the fair share across active campaigns already allow — raising it past the org limit has no effect.
1Maximum: 10000retry_policyRetry Policy
Defaults to 3 retries, fixed 15-minute delay, working hours respected.
follow_up_policyFollow Up Policy
Defaults to disabled.
transliterationTransliteration
Defaults to disabled.
start_timeStart Time
ISO-8601 with a timezone offset. Nothing dials before it. Omit to allow dialling immediately.
end_timeEnd Time
ISO-8601 with a timezone offset. Nothing dials after it and the campaign expires.
default_call_variablesDefault Call Variables
Prompt variables for every call, overridable per contact.
default_call_contextDefault Call Context
Free-form context for every call.
metadataMetadata
Anything you want stored alongside.
Query Parameters
organization_idOrganization Id
Must match the organisation the API key belongs to.
Response Body
201
The created campaign, at status 'created'.
batch_idBatch Id | null
The batch created from contacts on this request, if any were supplied. Null on every other read — a campaign can hold many batches; list them with GET /v2/campaigns/{campaign_id}/batches.
total_contactsTotal Contacts
Contacts across every batch.
0skipped_countSkipped Count | null
Contacts of that list that were left out — each names an agent_language the agent doesn't have. Null when every contact was added, and on every other read.
skipped_rowsSkipped Rows | null
The contacts left out, each with its CSV line (the header is line 1) or its position in contacts, and why: the first 100. Null when every contact was added, and on every other read.
@minItems 0
@minItems 0
@minItems 0
@minItems 0
@minItems 0
campaign_idRequiredCampaign Id
Campaign identifier.
organization_idOrganization Id | null
The organisation this campaign belongs to. Always your own.
nameRequiredName | null
Human label for the campaign.
agent_idRequiredAgent Id | null
Agent that places every call.
phone_numbersPhone Numbers
Caller IDs this campaign dials from.
@minItems 0
@minItems 0
@minItems 0
@minItems 0
@minItems 0
phone_number_strategyPhone Number Strategy
round_robin | random | least_loaded.
"round_robin"phone_number_indexPhone Number Index
Round-robin cursor: the position the next call starts from.
0timezoneTimezone
IANA timezone the working hours and the window are evaluated in.
"UTC"working_hours_enforcedWorking Hours Enforced
False dials around the clock, subject only to start_time/end_time.
falseworking_hoursWorking Hours
Per-day dialling windows, keyed by lowercase day name.
max_concurrent_callsMax Concurrent Calls | null
Ceiling on this campaign's SIMULTANEOUS calls. Omit for no campaign ceiling. It can only LOWER what the organisation's limit and the fair share across active campaigns already allow — raising it past the org limit has no effect.
retry_policyRequiredRetry Policy
What happens after a busy or a no-answer.
follow_up_policyRequiredFollow Up Policy
Callbacks after the first cycle ends.
transliterationRequiredTransliteration
Name-script rewriting inherited by every batch.
start_timeStart Time | null
ISO-8601 UTC. Nothing dials before this.
end_timeEnd Time | null
ISO-8601 UTC. Nothing dials after this; the campaign then expires.
default_call_variablesDefault Call Variables
Prompt variables applied to every call, overridable per contact.
default_call_contextDefault Call Context
Free-form context applied to every call.
statusRequiredStatus
created | active | paused | completed | cancelled | expired | archived. A campaign is born 'created' and becomes 'active' when its first batch dials. 'archived' is what DELETE leaves behind; 'cancelled' is what the cancel action does.
metadataMetadata
Whatever you stored on the campaign.
total_batchesTotal Batches
Batches created under this campaign.
0active_batchesActive Batches
Batches not yet finished.
0completed_contactsCompleted Contacts
Contacts that reached a completed call, counted ONCE regardless of how many attempts it took. Use this for progress, not completed_calls.
0completed_callsCompleted Calls
Calls that completed, retries included.
0failed_callsFailed Calls
Calls that failed.
0busy_callsBusy Calls
Calls that hit a busy line.
0no_answer_callsNo Answer Calls
Calls that rang out.
0voicemail_callsVoicemail Calls
Calls answered by a voicemail box. A SUBSET of no_answer_calls, never a peer of it — do not add it to the others to form a total.
0in_progress_callsIn Progress Calls
Calls live right now.
0follow_up_callsFollow Up Calls
Follow-up calls placed.
0follow_up_exhaustedFollow Up Exhausted
Contacts whose follow-up attempts ran out.
0created_atCreated At | null
ISO-8601 UTC.
updated_atUpdated At | null
ISO-8601 UTC.
started_byStarted By | null
Who started its latest run: {type: member | api_key | system, id, at}. A person is their member id. Null on a campaign that has not started.
approved_byApproved By | null
The workspace Admin who approved that start, when the workspace requires approval: {type: member, id, at, request_id}. Null when it started without needing one.
202
Only for an API key a workspace MEMBER minted, when the workspace requires an Admin's approval for this action. Nothing has changed yet: the action is recorded as an approval request and happens, exactly as sent, when any Admin approves it. Admin-minted and older keys never get this answer. The resource this call created or touched is included under its own name (agent, campaign or batch).
statusStatus
Always pending_approval.
"pending_approval"codeCode
Always approval_required.
"approval_required"detailRequiredDetail
What happens next, in a sentence.
approval_request_idRequiredApproval Request Id
The approval request's id.
approval_requestRequiredApproval Request
The request this call opened or added to.
400
1000 — malformed or contradictory input: a body that is not a JSON object, a form field that is not valid JSON, a CSV that is missing, unreadable or longer than 50,000 rows, more than 50,000 contacts, an unknown timezone, a start_time or end_time without a timezone offset, an end_time at or before start_time or more than 365 days ahead, or no caller ID to dial from. 1002 — a required parameter is missing.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
401
1100 — missing, unknown or inactive API key. 1101 — a browser session token was presented.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
402
1400 — the organisation's credit balance cannot fund a call, so nothing was started. Top up and retry.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
403
1102 — the key is valid but lacks the scope this endpoint requires, or names another organisation. GET /v2/scopes reports which scopes the key holds.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
404
1200 — no such campaign, or no such agent, in this organisation. A campaign belonging to another organisation answers 404, never 403, so ids cannot be probed.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
409
1201 — the contacts would take the organisation past 250,000 contacts waiting to be dialled, summed over all of its campaigns. Nothing was written. It clears as the queue drains: retry later, or cancel a campaign you no longer need. 1202 — the agent is switched off, so it takes no new calls: switch it on and try again. 1203 — the agent was deleted, so it takes no new calls.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
422
1001 — the body failed validation — an unknown field, a value out of range, or a working-hours time that is not 24-hour HH:MM; the message names the first offending field. Bad contacts are refused as a list, naming up to 20 of them: CSV line numbers (the header is line 1) or 1-based positions in contacts.
errorRequiredinteger
Stable integer code from the error table. Branch on this, not on the message.
messageRequiredstring
One human-readable sentence. Wording may change; the code will not.
429
1300 — per-organisation rate limit exceeded. Carries Retry-After.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
503
1501 — a service this endpoint depends on is unreachable or returned a 5xx.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
504
1502 — a service this endpoint depends on did not respond in time.
errorRequiredError
Stable integer code from the /v2 error table. Branch on this.
messageRequiredMessage
One human-readable sentence. Wording may change; the code will not.
The created campaign, at status 'created'.

