Manage agent availability with Google Calendar
Sync availability with external systems like calendars or workforce management tools so agents only receive calls when they're free.
What we’re building
By the end of this guide, you'll have a working integration that controls agent availability on Aircall from an external system. You'll be able to read the current state of every agent on the account, set agents as unavailable when something happens outside Aircall (a calendar event, a shift change, a WFM schedule), and restore them when it's over.
Prerequisites
Before we begin coding, we need to have:
- An Aircall account - sign up here if you don't have one yet.
- An API ID and API token (Basic Auth) or a valid OAuth access token. See Authentication.
- A publicly accessible HTTPS endpoint to receive webhook events. If you don’t have yet, set up your webhooks first here.
How agent availability works
Every Aircall user has an availability_status that determines whether they can receive inbound calls. The status updates automatically as agents log in, take calls, and enter wrap-up — but you can also set it programmatically via the API.
This is the foundation for integrations that sync availability with external systems: a calendar marks the agent as busy, your integration sets them to unavailable on Aircall, and they stop receiving calls until the event ends.
Availability states
The PUT /v1/users/:id endpoint accepts three values for availability_status: available, custom, and unavailable. Your integration will mostly toggle between available and unavailable.
The dedicated GET /v1/users/availabilities endpoint returns more granular states — including do_not_disturb, in_call, after_call_work, and offline. These are read-only; Aircall sets them automatically when an agent takes a call, enters wrap-up, or logs out. The distinction matters: always read from the availability endpoint before writing, so you don't try to update an agent who's mid-call or offline.
Watch out for customAgents using working-hours mode show as custom. You can override it, but restoring to available later permanently switches them out of working-hours mode. If you need to support agents on custom, store their original state and restore to custom instead of available.
Unavailable substatuses
When you set an agent to unavailable, you can include a substatus — one of out_for_lunch, on_a_break, in_training, doing_back_office, or other. This shows up in the Aircall dashboard so supervisors can see why an agent is out.
If you're building a calendar or scheduling integration, use other — it avoids misattributing the time to a specific activity. If you're building a workforce management integration, map your WFM activity codes to the matching Aircall substatuses so dashboard reporting stays consistent across both tools.
Read current availability
Use GET /v1/users/availabilities to fetch the current availability of all agents on the account. This is useful for checking state before updating it, or for building a real-time view of who's available.
require('dotenv').config();
const credentials = Buffer.from(
`${process.env.AIRCALL_API_ID}:${process.env.AIRCALL_API_TOKEN}`
).toString('base64');
const res = await fetch('https://api.aircall.io/v1/users/availabilities', {
headers: { 'Authorization': `Basic ${credentials}` }
});
const { users } = await res.json();
// Filter to only available agents
const available = users.filter(u => u.availability === 'available');
console.log(`${available.length} agent(s) available`);
Building a dashboard?If you need a full real-time view of agent availability with webhook-driven updates, see Build an agent activity dashboard.
Update availability
Use PUT /v1/users/:id to change an agent's availability_status. This is the core operation for any external system that needs to control when agents receive calls.
Set an agent as unavailable
When an external event starts — a calendar meeting, a training session, a shift break — set the agent to unavailable so calls route to other team members.
require('dotenv').config();
const credentials = Buffer.from(
`${process.env.AIRCALL_API_ID}:${process.env.AIRCALL_API_TOKEN}`
).toString('base64');
async function setUnavailable(userId, substatus = 'other') {
const res = await fetch(`https://api.aircall.io/v1/users/$USERID`, {
method: 'PUT',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
availability_status: 'unavailable',
substatus
})
});
if (!res.ok) {
const err = await res.json();
throw new Error(`Failed to update availability: ${res.status} — ${err.message}`);
}
return await res.json();
}
module.exports = { setUnavailable };
// Example: set agent 456 as unavailable for a meeting
// await setUnavailable(456, 'other');Set an agent as available
When an external event ends — a calendar meeting, a training session, a shift break — restore the agent to available so they start receiving calls again.
async function setAvailable(userId) {
const res = await fetch(`https://api.aircall.io/v1/users/$USERID`, {
method: 'PUT',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
availability_status: 'available'
})
});
if (!res.ok) {
const err = await res.json();
throw new Error(`Failed to update availability: ${res.status} — ${err.message}`);
}
return await res.json();
}
module.exports = { setAvailable, ... };Track availability changes with webhooks
Subscribe to user webhook events to track availability changes in real time. This lets your integration react when an agent's status changes — whether that change was triggered by your integration, the agent, or Aircall itself.
New to webhooks?If you haven't set up a webhook endpoint yet, start with the Webhooks guide first — it covers creating webhooks, handling payloads, and best practices for keeping your endpoint reliable.
import express from 'express';
const app = express();
app.use(express.json());
app.post('/aircall/webhook', (req, res) => {
res.sendStatus(200);
const { event, data } = req.body;
switch (event) {
case 'user.opened':
// Agent is now accepting calls.
// Update your external system (WFM, calendar, CRM sidebar).
syncStatusToExternalSystem(data.user.id, 'available');
break;
case 'user.closed':
// Agent stopped accepting calls.
syncStatusToExternalSystem(data.user.id, 'unavailable');
break;
case 'user.connected':
// Agent logged in to the phone app.
syncStatusToExternalSystem(data.user.id, 'online');
break;
case 'user.disconnected':
// Agent logged out.
syncStatusToExternalSystem(data.user.id, 'offline');
break;
}
});
async function syncStatusToExternalSystem(userId, status) {
// TODO: update your WFM tool, calendar, or CRM with the new status.
}
app.listen(3000);Example: sync with an external calendar
The most common use case for programmatic availability management is calendar sync — marking agents as unavailable during meetings so they don't receive calls. The pattern works the same regardless of the calendar provider (Google Calendar, Outlook, iCal):
- Your backend receives an event notification from the calendar system.
- It maps the calendar user to an Aircall user ID.
- When the event starts, it sets the agent to unavailable.
- When the event ends, it checks the agent's current state and restores available if appropriate.
The example below shows a lightweight Express server that handles these lifecycle events. Replace the calendar webhook handling with whatever your provider uses — the Aircall calls are identical.
import 'dotenv/config';
import express from 'express';
const app = express();
app.use(express.json());
const BASE = 'https://api.aircall.io/v1';
const credentials = Buffer.from(
`${process.env.AIRCALL_API_ID}:${process.env.AIRCALL_API_TOKEN}`
).toString('base64');
const headers = {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
};
// --- Aircall helpers ---
async function getAvailability(userId) {
const res = await fetch(`${BASE}/users/$USERID`, { headers });
const { user } = await res.json();
return user.availability_status;
}
async function setAvailability(userId, status, substatus) {
const body = { availability_status: status };
if (substatus) body.substatus = substatus;
const res = await fetch(`${BASE}/users/$USERID`, {
method: 'PUT',
headers,
body: JSON.stringify(body)
});
if (!res.ok) {
const err = await res.json();
console.error(`Aircall API error ${res.status}:`, err);
return null;
}
return await res.json();
}
// --- Calendar webhook handler ---
// Adapt this endpoint to your calendar provider's webhook format.
// The two fields you need: the event type (started/ended)
// and a way to map the calendar user to an Aircall user ID.
app.post('/webhooks/calendar', async (req, res) => {
res.sendStatus(200);
const { type, aircall_user_id } = req.body;
try {
if (type === 'event.started') {
await setAvailability(aircall_user_id, 'unavailable', 'other');
console.log(`Agent ${aircall_user_id} set to unavailable (calendar event started)`);
}
if (type === 'event.ended') {
// Only restore if the agent is still in a state we set.
const current = await getAvailability(aircall_user_id);
if (current === 'unavailable') {
await setAvailability(aircall_user_id, 'available');
console.log(`Agent ${aircall_user_id} restored to available`);
} else {
console.log(`Agent ${aircall_user_id} is ${current} — skipping restore`);
}
}
} catch (err) {
console.error('Calendar sync error:', err);
}
});
app.listen(3000);
User mappingThe example assumes your calendar webhook payload includes an
aircall_user_idfield. In practice, you'll need a mapping table — store the association between each calendar account (email) and Aircall user ID when agents connect their accounts in your integration.
Updated 2 months ago