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.
- Register:
POST /v2/webhook-subscriptionswithtype(one event type from the table below, spelled exactly) andurl(an absolute URL with a scheme, at most 500 characters;http://passes validation, but usehttps://). Answers201with{"id": 1234}. When the account already has an active subscription for the sameurlandtype, nothing is created and the answer is200with the existing{"id": …}; the same URL with anothertypeis a new subscription with its own id. A wrong type or URL answers422with{"errors": {"type": ["The selected type is invalid."]}}or{"errors": {"url": ["The url field must be a valid URL."]}}. - Receive: NUACOM posts to the URL for every event of that type on the account (see Delivery). Answer
2xxwithin 5 seconds and do the work afterwards. - List:
GET /v2/webhook-subscriptionsanswers{"data": [{"id": 1234, "type": "call_event", "url": "https://…"}]}: every subscription of the account, whoever registered it. - Delete:
DELETE /v2/webhook-subscriptions/{webhookSubscription}answers204and deliveries stop at once. An id that does not exist, or belongs to another account, answers404with{"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_idis the telephony id of the call, thecall_idthe call log shows; every leg of a transferred or queued call shares it.idrepeats it (kept for older integrations).hook_idis the subscription that produced the delivery: call payloads carry noevent_type.call_directionisinbound,outboundorinternal.call_statusisringing, thenconnectedonce answered, then a final value such ascompleted,busy,failedorno answeroncecall_terminatedis"1".call_terminatedandagent_hangup_requestare"1"or"";call_answeredis"true"or"false".call_caller_numberandcall_callee_numberare in international format with a00prefix when the number is valid for the account's country, otherwise as reported (an extension, an anonymous caller). The_localvariants are the national form as reported.call_answered_byandcall_initiated_byare the extension that answered or placed the call, as an integer, ornull.call_in_queueisQueue <number> <name>when the call went through a queue (the raw identifier when the queue cannot be matched), otherwisenullor empty.durationandtotal_durationarenulluntil the call ends, then the connected seconds and the total seconds including ringing (the call log'sbillsecandduration).notesis every note on the call joined with|,tagsevery tag name joined with,; bothnullwhen there are none.cause,agent_status,customer_status,call_channel_local,call_transferedandstarted_at_unixare passed through from the phone system (call_transferedis"1"once the call has been transferred,"0"otherwise).sync_specific_contact_idis reserved for NUACOM's own CRM integrations.recording_linkis present only on ended-call events (call_event,inbound_call_event,outbound_call_event,outbound_answered_call_event, andcall_event_listenersonce 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
POSTper event and per matching subscription, withContent-Type: application/jsonand 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_statusandcall_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
n8nis deactivated when the endpoint answers404(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
2xxfirst and process later, and be idempotent. One call produces several events (call_initiated,call_answeredorcall_missed, the ended event,call_updated), every leg of a transferred or queued call shares thecall_id, and the same ended event can occasionally be delivered twice (for example after a parked call is retrieved; the repeat may reportcall_answeredas"false"). Key your records oncall_idplus the stage (call_status,call_terminated) and let the call log settle the final state.
After the event
GET /v2/call-logs/{callId}with the payload'scall_idreturns every leg of the call, each with its owncall_unique_id. The row may not exist yet when the ended event arrives; retry after a moment.- Recording:
GET /v3/call-recording/{callId}withcall_id(MP3 by default), or therecording_linkof the payload. - Transcript:
GET /v2/speech-to-text/{callUniqueId}with a leg'scall_unique_id, oncecall_updatedwithupdate_typeai_analysishas arrived. - Notes and tags:
GET /v2/call-notes?callIDs[]=…andGET /v2/call-tags?callIDs[]=…; write back withPOST /v3/call-notesandPOST /v2/add-call-tag, which themselves firenote_addedortag_addedand thencall_updated. - Messages: fetch with
GET /v2/conversations/messages/{messageId}, reply withPOST /v2/conversations/{conversation}/messages; see Conversations.
Testing your receiver
- Use a URL reachable from the internet over HTTPS.
localhostand 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; registermessage_receivedand text one of your numbers. - Registering the same URL and type twice is harmless:
200with the same id. Several types may share one URL;hook_id(call events) orevent_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_analysisupdates need call transcription).
Pitfalls
- Call-event booleans are strings:
"1"/""forcall_terminatedandagent_hangup_request,"true"/"false"forcall_answered("1"/""oncall_initiated).statusinsms_delivery_statusis an integer. Never test for JSONtrue. - Call payloads have no
event_type;sms_delivery_statushas neitherevent_typenorhook_id. Give each family its own URL, or route on the keys. call_event_listenersdelivers at least three times per call,call_eventonce. The same URL subscribed to overlapping types (call_eventandinbound_call_event,call_missedandinbound_missed_call_event) receives the same event once per subscription.call_missedincludes outbound calls nobody answered;inbound_missed_call_eventis inbound only.call_event,call_initiated,call_answered,call_missedandcall_event_listenersinclude internal extension-to-extension calls.tag_addedis not sent when the tag is already on the call.call_updatedfires 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 andsms_delivery_status, as stored in message events, as reported inivr_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_sentmeans handed to the carrier, not delivered. Delivery outcomes come assms_delivery_status, for SMS only.recording_linkgives access to the audio without any header, and the subscription URL carries your secret. Keep payloads, URLs and logs confidential.