Skip to main content
When someone calls your Chirp phone number, Chirp notifies your application via a webhook so you can decide how to handle the call.

Prerequisites

Before receiving calls, you need:
  • A phone number assigned to your application
  • A webhook configured for the calls.initiated event
See Webhooks Setup for configuration details.

How It Works

  1. A caller dials your Chirp phone number
  2. Chirp creates a call and sends a calls.initiated webhook to your application
  3. Your application responds with a call command (answer, reject, or send to voicemail)
  4. Chirp executes the command and continues the call flow

Webhook Payload

When an inbound call is received, Chirp sends a POST request to your webhook URL:
calls.initiated Webhook Payload
{
  "event": "calls.initiated",
  "eventId": "evt_call_2DbBs7GWhGvVNJGrDXr5RG0mBWI_initiated",
  "timestamp": "2026-04-13T12:00:00.000Z",
  "data": {
    "call": {
      "id": "call_2DbBs7GWhGvVNJGrDXr5RG0mBWI",
      "direction": "inbound",
      "from": "+15559876543",
      "to": "+15551234567",
      "fromChannel": "pstn",
      "toChannel": "pstn",
      "livekitRoomName": "call-abc123"
    },
    "app": {
      "id": "app_2DbBs7GWhGvVNJGrDXr5RG0mBWI"
    }
  }
}

Payload Fields

event - The event type (calls.initiated) eventId - Unique event identifier for idempotency timestamp - ISO 8601 timestamp of when the event was generated data.call.id - Unique call identifier, used to send commands data.call.direction - Always inbound for received calls data.call.from - The caller’s phone number or identifier data.call.to - Your Chirp phone number that received the call data.call.fromChannel - Channel of the caller (pstn, webrtc, or whatsapp) data.call.toChannel - Channel of the recipient (pstn, webrtc, or whatsapp) data.call.livekitRoomName - LiveKit room name (present when channel is webrtc) data.call.metadata - Custom metadata (if any) data.app.id - The application that owns the call

Answering a Call

To answer an inbound call, send an answer command:
Answer a Call
curl -X POST https://api.buildwithchirp.com/v1/calls/call_2DbBs7GWhGvVNJGrDXr5RG0mBWI/commands \
  -H "Authorization: Bearer YOUR_LIVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "answer"
  }'

Rejecting a Call

To reject an inbound call, send a reject command with an optional reason:
Reject a Call
curl -X POST https://api.buildwithchirp.com/v1/calls/call_2DbBs7GWhGvVNJGrDXr5RG0mBWI/commands \
  -H "Authorization: Bearer YOUR_LIVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "reject",
    "reason": "User busy"
  }'
The caller hears a busy tone or rejection message depending on the channel.

Sending to Voicemail

To send an inbound call directly to voicemail:
Send to Voicemail
curl -X POST https://api.buildwithchirp.com/v1/calls/call_2DbBs7GWhGvVNJGrDXr5RG0mBWI/commands \
  -H "Authorization: Bearer YOUR_LIVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "send_to_voicemail"
  }'
The caller hears a voicemail greeting and can leave a message. You can access voicemail recordings through the Voicemails API.

Default Behavior

If your application does not respond to the calls.initiated webhook within 30 seconds, Chirp uses the default behavior configured in your application’s calling settings. This can be:
  • Voicemail - The call is sent to voicemail automatically
  • Reject - The call is rejected with a busy signal
Make sure your webhook endpoint responds quickly. If your application takes too long to process the webhook, the caller will be waiting in silence. Send the call command as soon as possible and handle any additional logic asynchronously.

Example: Webhook Handler

Here is a complete example of a webhook handler that answers calls during business hours and sends them to voicemail otherwise:
Inbound Call Handler
app.post("/webhooks/calling", async (req, res) => {
  const { event, data } = req.body;

  // Acknowledge receipt immediately
  res.status(200).send("OK");

  if (event === "calls.initiated" && data.call.direction === "inbound") {
    const hour = new Date().getUTCHours();
    const isBusinessHours = hour >= 14 && hour < 22; // 9am-5pm ET

    const command = isBusinessHours
      ? { type: "answer" }
      : { type: "send_to_voicemail" };

    await fetch(
      `https://api.buildwithchirp.com/v1/calls/${data.call.id}/commands`,
      {
        method: "POST",
        headers: {
          "Authorization": "Bearer YOUR_LIVE_API_KEY",
          "Content-Type": "application/json",
        },
        body: JSON.stringify(command),
      }
    );
  }
});
You can combine call commands to build complex call flows. For example, answer a call, then transfer it to another number, or answer and start recording. See Call Commands for all available options.