API Reference · Campaigns

Upload contacts (CSV)

Adds the rows of a CSV file to a campaign as contacts to call. Every column comes along, so the AI employee can use it as a {{placeholder}} — a name, an amount due, a date.

POST/campaigns/{campaign_id}/contacts

https://calling.ubiqproject.com/api/v1/campaigns/{campaign_id}/contacts · Auth: API key (full access)

Request

Path parameters

campaign_idstring (UUID)required
The campaign's ID.

Body

filefile (multipart/form-data)required
A UTF-8 CSV file with a header row.
contacts.csv
Name,Phone,EMI Amount,Due Date
Rahul Verma,+919876543210,4500,15 Oct
Anita Desai,+919812345678,12000,18 Oct
Imran Khan,+919898012345,7800,20 Oct
Request
curl -X POST https://calling.ubiqproject.com/api/v1/campaigns/9b4e6f12-8a3c-4d71-b05e-2f6a8c1d7e93/contacts \
  -H "Authorization: Bearer vk_live_YOUR_KEY" \
  -F "file=@contacts.csv"

The phone column

One column must hold the phone number. Daffy Voice recognises these headers, in any case, with spaces, underscores or hyphens:

  • phone, phone number (also phone_number, Phone-Number)
  • mobile, mobile number
  • contact number

Rows with no phone number are skipped. Every other column is kept under its header name, for the AI employee to use. Write phone numbers in international format, such as +919876543210 — they aren't checked here, and a bad number simply fails when it's dialled.

Response

The campaign, with its contact counts updated.

Response · 200
{
  "id": "9b4e6f12-8a3c-4d71-b05e-2f6a8c1d7e93",
  "name": "October EMI reminders",
  "agent_id": "3f8c2a1e-6b4d-4c7a-9e21-5d0f7b8a9c34",
  "agent_name": "Priya",
  "phone_number": "+918046801234",
  "created_by": "",
  "concurrency": 5,
  "contacts_total": 3,
  "contacts_done": 0,
  "contacts_failed": 0,
  "contacts_pending": 3,
  "state": "draft",
  "created_at": "2026-10-08T09:14:22.512341Z",
  "started_at": null,
  "config": {
    "daily": {
      "enabled": true,
      "start": "10:00",
      "stop": "19:00",
      "timezone": "Asia/Kolkata"
    },
    "retry": {
      "enabled": true,
      "max_attempts": 3,
      "schedule": "custom",
      "days": 0,
      "hours": 2
    }
  }
}

Response fields

idstring (UUID)
The campaign's ID.
namestring
The campaign's name.
agent_idstring (UUID)
The AI employee that makes the calls.
agent_namestring
That AI employee's name.
phone_numberstring | null
The number calls are made from. null when the AI employee's own number is used.
created_bystring | null
Email of the dashboard user who created it. Empty when created with an API key.
concurrencyinteger
How many calls may run at the same time (1–20).
contacts_totalinteger
Every contact in the campaign, including ones your conditions filtered out.
contacts_doneinteger
Contacts whose call connected and finished.
contacts_failedinteger
Contacts that couldn't be reached after all attempts, plus contacts your conditions filtered out (never called).
contacts_pendinginteger
Contacts waiting to be called or on a call right now.
statestring
draft (not started), running, paused or done (every contact handled).
created_atstring (ISO 8601)
When it was created.
started_atstring | null
When it first started calling. null for a draft.
configobject
Calling hours, retries, conditions and more — see Create a campaign → config.

Errors

Responses

No valid contacts (need a phone/phone_number column)400
No phone column was found, or every row's phone was empty.
Could not parse CSV: …400
The file isn't a readable UTF-8 CSV.
Campaign not found404
No campaign with this ID in your company.
Validation list422
No file field in the form.

When the new contacts are called

In a running campaign they're called straight away (within calling hours); in a draft or paused one, once you start it. A campaign that is already done doesn't pick them up — and Restart calls everyone again, not just the new rows. Create a new campaign for a new list instead.

Duplicates are skipped

A phone number that appears twice in the file, or is already waiting or on a call in this campaign, is added only once. Numbers whose calls have already finished can be added again.