Place your first call

Create an Outbound Campaign, add contacts, assign an agent, and launch it — for a human agent Campaign or an AI Voice Agent (AIVA) Campaign.

This guide takes a Campaign from nothing to placing a real call, using the Campaigns API — for a human agent Campaign and an AI Voice Agent (AIVA) Campaign.

☝️

Before you start
Set up authentication with OAuth or Basic Auth. For an AI Voice Agent Campaign, you'll also need an agent already configured in the Aircall Dashboard — that's where you get its aiva_agent_id. There's no API endpoint to list or create AI Voice Agents themselves.

1. Create a Campaign

A Campaign starts in draft. Nothing is dialled and nothing is charged until you launch it, so it's safe to create one while you're still setting things up.

Human agent Campaign

Calls placed by a human agent Campaign use the caller ID of whichever agent picks up, so caller_id_strategy: "agent_number_pool" is the simplest choice to get started:

const accessToken = "YOUR_ACCESS_TOKEN";

const campaignBody = {
  name: "Q3 outreach",
  description: "Warm leads from the trade show",
  caller_id_strategy: "agent_number_pool",
  call_attempts: 3,
  external_ringing_timeout: 30,
};

async function createCampaign(body) {
  const response = await fetch("https://api.aircall.io/v1/campaigns", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  const data = await response.json();

  if (!response.ok) {
    throw new Error(data.message || "Failed to create campaign");
  }

  return data.campaign;
}

createCampaign(campaignBody)
  .then((campaign) => console.log("Campaign created:", campaign))
  .catch((error) => console.error(error.message));

AI Voice Agent Campaign

An AI Voice Agent Campaign must name its agent (aiva_agent_id, from your Dashboard configuration) and dial from a specific number pool rather than an agent's own number, so it also needs caller_id_numbers. Fetch an eligible one first:

async function listEligibleNumbers() {
  const response = await fetch(
    "https://api.aircall.io/v1/campaign_eligible_numbers",
    {
      headers: { Authorization: `Bearer ${accessToken}` },
    }
  );

  const data = await response.json();
  return data.campaign_eligible_numbers;
}

It also requires calling_hours — the weekly window AIVA is allowed to dial in:

async function createAivaCampaign() {
  const [eligibleNumber] = await listEligibleNumbers();

  const aivaCampaignBody = {
    name: "Q3 outreach (AI Voice Agent)",
    agent_type: "aiva",
    aiva_agent_id: "YOUR_AIVA_AGENT_ID",
    caller_id_strategy: "specific_number_pool",
    caller_id_numbers: [eligibleNumber.number_id],
    calling_hours: {
      timezone: "America/New_York",
      slots: [
        {
          from_time: "09:00",
          to_time: "18:00",
          days: ["MON", "TUE", "WED", "THU", "FRI"],
        },
      ],
    },
  };

  return createCampaign(aivaCampaignBody);
}

createAivaCampaign()
  .then((campaign) => console.log("AIVA campaign created:", campaign))
  .catch((error) => console.error(error.message));
📘

agent_type is fixed at creation — there's no endpoint that converts a Campaign from human agent to AIVA, or back. Human agent and AI Voice Agent Campaigns are separate per-company entitlements, so a request can 422 with one enabled and not the other.

2. Add contacts to the Campaign

POST /v1/campaigns/:id/contacts queues up to 50 contacts in one synchronous call — a good fit to get your first calls going. For larger lists, use the Campaign contact import flow instead (CSV upload, processed in the background — see the API reference).

async function addContacts(campaignId, countryCode, contacts) {
  const response = await fetch(
    `https://api.aircall.io/v1/campaigns/${campaignId}/contacts`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ country_code: countryCode, contacts }),
    }
  );

  return response.json();
}

The fields a contact accepts depend on the Campaign's agent_type. On a human agent Campaign:

const humanContacts = [
  {
    phone_number: "06 12 34 56 78",
    first_name: "Gary",
    last_name: "Jennings",
    company_name: "Acme Corp",
  },
];

addContacts("YOUR_CAMPAIGN_ID", "FR", humanContacts).then((result) =>
  console.log(`Queued ${result.queued_count}, rejected ${result.rejected_count}:`, result.contacts)
);

On an AI Voice Agent Campaign, replace the descriptive fields with aiva_variables — a value for every {{variable}} your agent's first message uses:

const aivaContacts = [
  {
    phone_number: "+33612345678",
    aiva_variables: {
      first_name: "Gary",
      plan: "Pro",
    },
  },
];

addContacts("YOUR_AIVA_CAMPAIGN_ID", "FR", aivaContacts).then((result) =>
  console.log(`Queued ${result.queued_count}, rejected ${result.rejected_count}:`, result.contacts)
);
⚠️

A contact missing one of the AI Voice Agent's declared variables isn't rejected outright — it comes back with status: "invalid" and reason: "variable_missing" in the response, while the rest of the batch still queues. Always check queued_count and contacts[].status rather than assuming a 201 means everyone made it in.

3. Assign an agent to the Campaign

Who ends up on the other end of the call is set differently depending on the Campaign's type.

Human agent Campaigns

A human agent Campaign has no one to call with until you assign it:

async function assignUsers(campaignId, userIds) {
  await fetch(`https://api.aircall.io/v1/campaigns/${campaignId}/users`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ user_ids: userIds }),
  });
}

assignUsers("YOUR_CAMPAIGN_ID", [456, 789])
  .then(() => console.log("Users assigned"))
  .catch((error) => console.error(error.message));

You can assign whole teams the same way with POST /v1/campaigns/:id/teams, and check who will actually be called — direct assignments and team membership combined — with GET /v1/campaigns/:id/agents. See the API reference for both.

AI Voice Agent Campaigns

There's no separate assignment call here: the agent is the aiva_agent_id you passed to Create a Campaign, and it's fixed for the life of the Campaign — the same field that answers "who places the calls" also answers "who is assigned." Skip straight to launching it.

4. Launch the Campaign

This is the step that starts dialling. POST /v1/campaigns/:id/launch builds the call queue from the contacts you added and moves the Campaign to in_progress.

async function launchCampaign(campaignId) {
  const response = await fetch(
    `https://api.aircall.io/v1/campaigns/${campaignId}/launch`,
    {
      method: "POST",
      headers: { Authorization: `Bearer ${accessToken}` },
    }
  );

  const data = await response.json();

  if (!response.ok) {
    throw new Error(data.message || "Failed to launch campaign");
  }

  return data.campaign;
}

launchCampaign("YOUR_CAMPAIGN_ID")
  .then((campaign) => console.log("Campaign is live:", campaign.status))
  .catch((error) => console.error(error.message));
⚠️

This places real calls to real people the moment it succeeds. Double-check your contact list and, for a human agent Campaign, that agents are actually assigned and available before calling this.

📘

For an AI Voice Agent Campaign, a 422 here can mean the AIVA compliance notice hasn't been acknowledged yet. That acknowledgment can only be given from the Aircall Dashboard — open the Campaign there, review the notice, and acknowledge it before retrying the launch call.

5. Bonus: check on your Campaign's progress

Once a Campaign is in_progress, poll GET /v1/campaigns/:id/stats to see how it's going — no more than once a minute per Campaign, since the numbers are computed on read.

async function getCampaignStats(campaignId) {
  const response = await fetch(
    `https://api.aircall.io/v1/campaigns/${campaignId}/stats`,
    {
      headers: { Authorization: `Bearer ${accessToken}` },
    }
  );

  const data = await response.json();
  return data.stats;
}

getCampaignStats("YOUR_CAMPAIGN_ID").then((stats) =>
  console.log("Campaign stats:", stats)
);

What's next

You now have everything needed to take a Campaign from draft to placing real calls, whether the calls are handled by a human agent or by AIVA. From here, see the API reference for:

  • The Campaign contact import flow, for CSV-sized contact lists.
  • Campaign Outcomes, so agents can tag how each call went.
  • Campaign queue items, to inspect or reorder who gets called next.
  • Pause and Resume, to control a running Campaign without losing its queue.

What’s Next

Did this page help you?