NUACOM API

Integrations

Webhooks

Webhooks push events from your NUACOM account to a URL you choose, as they happen: a call ringing, answered or ended, a note or tag on a call, a key pressed in an IVR menu, a voicemail, an inbound or outbound message, the delivery result of an SMS. You register the URL once per event type and NUACOM sends a JSON POST for every matching event on the account, so you never poll the call log. This group holds the three subscription endpoints: register, list and delete. The payloads point back into Call Logs and Conversations, where you fetch the full record; the Integrations guide shows where webhooks sit in a complete integration.

Workflow (what the portal does)

The portal does not manage subscriptions: you register them from your own code, as automation tools such as n8n do through the same endpoints.

  1. Register: POST /v2/webhook-subscriptions with type (one event type from the table below, spelled exactly) and url (an absolute URL with a scheme, at most 500 characters; http:// passes validation, but use https://). Answers 201 with {"id": 1234}. When the account already has an active subscription for the same url and type, nothing is created and the answer is 200 with the existing {"id": …}; the same URL with another type is a new subscription with its own id. A wrong type or URL answers 422 with {"errors": {"type": ["The selected type is invalid."]}} or {"errors": {"url": ["The url field must be a valid URL."]}}.
  2. Receive: NUACOM posts to the URL for every event of that type on the account (see Delivery). Answer 2xx within 5 seconds and do the work afterwards.
  3. List: GET /v2/webhook-subscriptions answers {"data": [{"id": 1234, "type": "call_event", "url": "https://…"}]}: every subscription of the account, whoever registered it.
  4. Delete: DELETE /v2/webhook-subscriptions/{webhookSubscription} answers 204 and deliveries stop at once. An id that does not exist, or belongs to another account, answers 404 with {"message": "Not Found."}.
# Register: 201 with the new id, 200 with the existing id if already registered
curl -X POST https://api.api-nuacom.com/v2/webhook-subscriptions \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"type":"call_event","url":"https://example.com/hooks/nuacom/8f3a1c0d9e"}'
# → {"id":1234}

# List
curl https://api.api-nuacom.com/v2/webhook-subscriptions \
  -H 'Authorization: Bearer YOUR_API_KEY'
# → {"data":[{"id":1234,"type":"call_event","url":"https://example.com/hooks/nuacom/8f3a1c0d9e"}]}

# Delete
curl -X DELETE https://api.api-nuacom.com/v2/webhook-subscriptions/1234 \
  -H 'Authorization: Bearer YOUR_API_KEY'
# → 204

Event types

type Sent when Payload
call_event A call ends, whatever its direction or outcome. The usual choice. Call
inbound_call_event An inbound call ends. Call
inbound_missed_call_event An inbound call ends unanswered. Call, no recording link
outbound_call_event An outbound call ends. Call
outbound_answered_call_event An outbound call that was answered ends. Call
call_missed A call ends unanswered, inbound or outbound. Call, no recording link
call_initiated A call starts ringing. Call, raw form
call_answered A call is answered (connected, not yet ended). Call, no recording link
call_event_listeners Every stage of every call: ringing, connected, ended. The most deliveries. Call
call_updated A note or tag on a call changed, or the AI analysis of a call completed. Call updated
note_added A note is added to a call. Note
note_updated A note on a call is edited. Note
note_removed A note is deleted from a call. Note, id only
tag_added A tag is assigned to a call (not when it is already on it). Tag
tag_removed A tag is removed from a call, one event per call. Tag, id only
ivr_option_selected A caller presses a key in an IVR menu. IVR
voicemail_received A voicemail reaches an extension's mailbox. Voicemail
message_received An inbound SMS, WhatsApp or MMS message arrives. Message
message_sent An outbound message is handed to the carrier. Message
sms_delivery_status An SMS you sent is delivered or fails. SMS delivery

Payloads

Every delivery is one JSON object. Booleans in call events are strings, shown below as sent; everything else is typed as shown.

Call events

The nine call types share one shape, built from the phone system's event at the moment it fires:

{
  "call_id": "srv01-core1-1761660245.12345",
  "call_at": "2026-10-28T14:04:05Z",
  "started_at_unix": "1761660245",
  "duration": 95,
  "total_duration": 112,
  "call_caller_number": "00353861234567",
  "call_caller_number_local": "0861234567",
  "call_callee_number": "0035315540200",
  "call_callee_number_local": "015540200",
  "call_direction": "inbound",
  "call_status": "completed",
  "call_terminated": "1",
  "call_answered": "true",
  "agent_hangup_request": "",
  "cause": "NORMAL_CLEARING",
  "call_transfered": "0",
  "call_answered_by": 101,
  "call_initiated_by": null,
  "call_in_queue": "Queue 4 Sales",
  "call_channel_local": "0211234567",
  "agent_status": "answered",
  "customer_status": "connected",
  "sync_specific_contact_id": null,
  "notes": "Asked for a callback | Sent the brochure",
  "tags": "Sales, Callback",
  "hook_id": 1234,
  "id": "srv01-core1-1761660245.12345",
  "recording_link": "https://api.api-nuacom.com/integration/recording?id=srv01-core1-1761660245.12345&integration=&client_id=IE-00123&token=…"
}
  • call_id is the telephony id of the call, the call_id the call log shows; every leg of a transferred or queued call shares it. id repeats it (kept for older integrations). hook_id is the subscription that produced the delivery: call payloads carry no event_type.
  • call_direction is inbound, outbound or internal. call_status is ringing, then connected once answered, then a final value such as completed, busy, failed or no answer once call_terminated is "1". call_terminated and agent_hangup_request are "1" or ""; call_answered is "true" or "false".
  • call_caller_number and call_callee_number are in international format with a 00 prefix when the number is valid for the account's country, otherwise as reported (an extension, an anonymous caller). The _local variants are the national form as reported.
  • call_answered_by and call_initiated_by are the extension that answered or placed the call, as an integer, or null.
  • call_in_queue is Queue <number> <name> when the call went through a queue (the raw identifier when the queue cannot be matched), otherwise null or empty.
  • duration and total_duration are null until the call ends, then the connected seconds and the total seconds including ringing (the call log's billsec and duration).
  • notes is every note on the call joined with |, tags every tag name joined with , ; both null when there are none.
  • cause, agent_status, customer_status, call_channel_local, call_transfered and started_at_unix are passed through from the phone system (call_transfered is "1" once the call has been transferred, "0" otherwise). sync_specific_contact_id is reserved for NUACOM's own CRM integrations.
  • recording_link is present only on ended-call events (call_event, inbound_call_event, outbound_call_event, outbound_answered_call_event, and call_event_listeners once the call ended) and only when the answering (inbound) or calling (outbound) extension has recording enabled for that direction. It downloads the recording with no header: the link carries its own token, so anyone holding it can fetch the audio. Recordings expire (see Call Logs).

call_initiated is sent in raw form: no id, numbers as reported by the phone system, call_answered as "1" or "", duration and total_duration null.

Notes and tags

{"call_id": "srv01-core1-1761660245.12345", "event_type": "note_added", "note": {"id": 822528, "value": "Asked for a callback"}}

note_updated has the same shape; note_removed carries only note.id.

{"call_id": "srv01-core1-1761660245.12345", "event_type": "tag_added", "tag": {"id": 56, "value": "Sales", "color": "#ff9f1a"}}

tag.id is the catalogue tag id from GET /v3/call-tags; tag_removed carries only tag.id. Every note and tag event is followed by a call_updated event.

Call updated

{"event_type": "call_updated", "call_id": "srv01-core1-1761660245.12345", "call_direction": "inbound", "update_type": "notes"}

update_type is notes, tags or ai_analysis (the transcription and keyword analysis of the call completed). call_direction is the call log's direction, or "" when the call is not in the log yet. The payload carries none of the new data: fetch the notes, tags or transcript afterwards.

IVR

{"event_type": "ivr_option_selected", "call_id": "srv01-core1-1761660245.12345", "caller_number": "353861234567", "ivr_id": "3", "option_selected": "2"}

ivr_id is the menu id used by IVR and option_selected the key pressed, both strings. caller_number is passed through as the phone system reports it.

Voicemail

{"event_type": "voicemail_received", "extension": "101", "msgnum": 3}

The payload announces the arrival only: no caller, no audio. msgnum is the message's index in that mailbox and shifts as older messages are deleted, so it is not a stable id, and no endpoint returns an individual voicemail.

Messages

{"event_type": "message_received", "message_id": "01K8QF3M8Q7Z2N4P6R8T0V2X4Y", "channel": "sms", "sender_number": "353861234567", "receiver_number": "35315540200", "content": "Hi, can you call me back?", "created_at": "2026-10-28T14:04:05+00:00"}
{"event_type": "message_sent", "message_id": "01K8QF4A2B6C8D0E2F4G6H8J0K", "channel": "sms", "sender_number": "NUACOM", "receiver_number": "353861234567", "content": "Your appointment is confirmed.", "status": "sent", "created_at": "2026-10-28T14:04:05+00:00"}

channel is sms, whatsapp or mms. On message_received, sender_number is the contact and receiver_number the number or sender name they wrote to; on message_sent it is the other way round. Numbers are passed through as stored on the conversation. message_id is the id for GET /v2/conversations/messages/{messageId}; created_at is ISO 8601. message_sent fires once per message, when it is handed to the carrier, and status is always sent; for US 10DLC campaigns it arrives once the carrier has assigned the sending number.

SMS delivery status

{"error_code": "", "error_description": "", "status": 2, "status_string": "DELIVERY_SUCCESS", "reference": "appointment-123", "number": "00353861234567"}

Sent for SMS only, once the carrier reports a final outcome: status is 2 (delivered) or 4 (failed); intermediate statuses are not sent. status_string is DELIVERY_SUCCESS or DELIVERY_FAILURE and error_code / error_description are the carrier's; on some carriers all three are null, so branch on status. reference is the to[].reference you gave POST /v2/sms/send; without one it falls back to a message identifier (which one depends on the carrier), so always send a reference. number is the recipient in international format with a 00 prefix. This is the one payload without event_type. WhatsApp delivery has no webhook: read it with POST /v2/conversations/messages/{message}/statuses.

Delivery

sequenceDiagram
    participant Event as Event on your account
    participant NUACOM as NUACOM
    participant You as Your registered URL

    Event->>NUACOM: call ends / note or tag changes / message arrives / …
    NUACOM->>You: POST matching event, Content-Type: application/json
    alt answers 2xx within 5 seconds
        You-->>NUACOM: 2xx
        Note over NUACOM,You: delivery recorded, nothing more sent
    else times out, non-2xx, or no answer
        You-->>NUACOM: timeout / error / nothing
        Note over NUACOM,You: never retried; response body ignored either way
    end
  • One POST per event and per matching subscription, with Content-Type: application/json and the JSON object as the body. There is no signature and no authentication header.
  • The request times out after 5 seconds. Whatever your endpoint answers, any status or none, the event is sent once and never retried, and your response body is ignored. NUACOM logs each delivery with your response status, body and headers, which support can check with you.
  • Deliveries are queued and asynchronous: they usually arrive within seconds, and the events of one call are not guaranteed to arrive in order. Read call_status and call_terminated, not the arrival order.
  • Protect the endpoint with a secret in the URL itself, a long random path segment or query parameter, and reject requests without it: the URL is stored and sent verbatim. Everyone with API access to the account can read it in the list, so treat it like the API key.

Warning

Deliveries carry no signature and are never retried. Answer 2xx within 5 seconds, store the payload first and process it afterwards, and treat a missing event as something to reconcile from the call log, not as something that will arrive later.

  • Certificates: for call, call_updated, IVR, voicemail and message events the certificate of your endpoint is not verified; for note, tag and SMS delivery events it is. Use a certificate from a public CA so every family gets through.
  • Automatic deactivation: a subscription whose URL contains n8n is deactivated when the endpoint answers 404 (n8n drops a workflow's webhook without unsubscribing). It is not deleted: it stays in the list, and registering the same URL and type again creates a new subscription (201). No other failure deactivates anything.
  • What the receiver must do: answer 2xx first and process later, and be idempotent. One call produces several events (call_initiated, call_answered or call_missed, the ended event, call_updated), every leg of a transferred or queued call shares the call_id, and the same ended event can occasionally be delivered twice (for example after a parked call is retrieved; the repeat may report call_answered as "false"). Key your records on call_id plus the stage (call_status, call_terminated) and let the call log settle the final state.

After the event

Testing your receiver

  • Use a URL reachable from the internet over HTTPS. localhost and office-only addresses never receive anything, and nothing in the API tells you so.
  • A request-inspection ("request bin") service gives you a disposable URL that shows every delivery raw. Register it for call_event_listeners, place one call to the account and you see ringing, connected and ended in sequence; register message_received and text one of your numbers.
  • Registering the same URL and type twice is harmless: 200 with the same id. Several types may share one URL; hook_id (call events) or event_type (the others) tells them apart.
  • There is no test-event endpoint: trigger real events. Delete the disposable subscription when you are done.

Roles and access

  • Any authenticated user of the account, with an API key or a session token, can register, list and delete subscriptions. There is no admin requirement; without a token the endpoints answer 401.
  • Subscriptions belong to the account, not to the user or key that created them: every user sees the same list and can delete any entry. An id from another account answers 404.
  • Deliveries are account-wide: a subscription receives every matching event of the account, regardless of the call-log visibility of the user who registered it. The recording link follows the extension's recording settings, not the user's recording access.
  • What arrives follows what the account uses (an account without the WhatsApp add-on has no WhatsApp messages, ai_analysis updates need call transcription).

Pitfalls

  • Call-event booleans are strings: "1" / "" for call_terminated and agent_hangup_request, "true" / "false" for call_answered ("1" / "" on call_initiated). status in sms_delivery_status is an integer. Never test for JSON true.
  • Call payloads have no event_type; sms_delivery_status has neither event_type nor hook_id. Give each family its own URL, or route on the keys.
  • call_event_listeners delivers at least three times per call, call_event once. The same URL subscribed to overlapping types (call_event and inbound_call_event, call_missed and inbound_missed_call_event) receives the same event once per subscription.
  • call_missed includes outbound calls nobody answered; inbound_missed_call_event is inbound only. call_event, call_initiated, call_answered, call_missed and call_event_listeners include internal extension-to-extension calls.
  • tag_added is not sent when the tag is already on the call. call_updated fires after every note and tag change whether or not you subscribed to those types.
  • Number formats differ by family: 00-prefixed international in call events and sms_delivery_status, as stored in message events, as reported in ivr_option_selected.
  • Validation checks the shape of the URL, not that it answers: a typo registers fine and simply never receives anything.
  • The list includes subscriptions that were deactivated automatically and does not say which. If an n8n URL stops receiving, delete it and register again.
  • message_sent means handed to the carrier, not delivered. Delivery outcomes come as sms_delivery_status, for SMS only.
  • recording_link gives access to the audio without any header, and the subscription URL carries your secret. Keep payloads, URLs and logs confidential.