AISARAISAR
REST API

Calls

Programmatic voice-call control: originate an outbound call to a contact (`POST /calls/originate`) and hang up an active call (`POST /calls/{call}/hangup`). This is a call-control API, not a browser softphone — it only hands the call to the AISAR telephony gateway; the audio itself travels over SIP/WebRTC outside HTTP. The whole group requires an active subscription and the `telephony` plan feature; on top of that the calling user must be a provisioned telephony operator (have an active SIP endpoint) and hold the `telephony.use` permission in the current company — otherwise `403 { "data": { "message": "Telephony is not available for your account." } }`. `POST /calls/originate` takes `contact_id` and `channel_id` (both required integers). The channel picks the dial path: a WhatsApp/WABA channel calls the contact over WhatsApp, while a `sip_telephony` channel dials the contact's phone number over a SIP trunk. Both the contact and the channel must belong to the current company (otherwise `404 { "data": { "message": "Contact or channel not found." } }`). On success (200) it returns the created call record: `{ "data": { "call": { id, direction: "outbound", status: "ringing", to_number, from_number, asterisk_channel_id, timeline_message_id } } }` — the call starts in `ringing` and a call card is appended to the conversation timeline (`timeline_message_id`). Subsequent status transitions (`ringing → answered → completed`, or `rejected` / `missed` / `failed`) arrive via real-time events and `call.*` webhooks, not in this response body. WhatsApp calls require a valid call permission from the contact: without it `originate` returns `422` with an `eligibility` block where `call.can_call = false`. Check readiness and request permission ahead of time via `GET /v1/contacts/{contact}/outbound-eligibility` and `POST /v1/contacts/{contact}/call-permission/request` (the Contacts group). SIP channels need no permission (`eligibility.call.permission_status = "not_required"`), only a phone number on the contact — otherwise `422 { "data": { "message": "Contact has no phone number to dial." } }`. Other `originate` outcomes: `403` with an `eligibility` block — the channel or contact cannot be called (no WhatsApp account, operator not enabled); `502` — the telephony gateway failed to originate the call; `422` — request-body validation error (missing `contact_id` / `channel_id`). `POST /calls/{call}/hangup` ends an active call. Beyond telephony availability, only a party to the call (the assigned operator or a call leg) or a user with company telephony-management rights may hang it up — otherwise `403`. Success: `{ "message": "Hangup requested." }` (the teardown request is handed to the gateway; actual termination is confirmed by a `call.*` event). Note: the `hangup` response, unlike `originate`, is not wrapped in `data`.

Endpoints

MethodPath
POST/v1/calls/originate

Originate an outbound call to a contact over a WhatsApp or SIP channel.

POST/v1/calls/{call}/hangup

Hang up an active call (allowed for a call party or a telephony manager).

Examples

Originate a WhatsApp call

Request

bash
curl -X POST https://api.aisar.app/v1/calls/originate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": 42,
    "channel_id": 7
  }'

Response

json
{
  "data": {
    "call": {
      "id": 5001,
      "direction": "outbound",
      "status": "ringing",
      "to_number": "7700000XXXX",
      "from_number": "7708550XXXX",
      "asterisk_channel_id": "1720787000.4521",
      "timeline_message_id": 88214
    }
  }
}

Originate a call over a SIP trunk

Request

bash
curl -X POST https://api.aisar.app/v1/calls/originate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": 42,
    "channel_id": 12
  }'

Response

json
{
  "data": {
    "call": {
      "id": 5002,
      "direction": "outbound",
      "status": "ringing",
      "to_number": "7700000XXXX",
      "from_number": "7727000XXXX",
      "asterisk_channel_id": "1720787100.4530",
      "timeline_message_id": 88221
    }
  }
}

WhatsApp call without the contact's permission (422)

Request

bash
curl -X POST https://api.aisar.app/v1/calls/originate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": 42,
    "channel_id": 7
  }'

Response

json
{
  "data": {
    "message": "No valid call permission or daily attempts exhausted.",
    "eligibility": {
      "message": {
        "window_open": true,
        "window_expires_at": 1720800000,
        "requires_template": false
      },
      "call": {
        "channel_kind": "whatsapp",
        "can_call": false,
        "permission_status": "none",
        "permission_expires_at": null,
        "can_request_permission": true,
        "request_cooldown_until": null,
        "attempts_remaining": 5,
        "has_whatsapp_account": true
      }
    }
  }
}

Hang up an active call

Request

bash
curl -X POST https://api.aisar.app/v1/calls/5001/hangup \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Response

json
{
  "message": "Hangup requested."
}

Telephony not available for the account (403)

Request

bash
curl -X POST https://api.aisar.app/v1/calls/originate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": 42,
    "channel_id": 7
  }'

Response

json
{
  "data": {
    "message": "Telephony is not available for your account."
  }
}