User Tools

Site Tools


api:pbx:events

API : PBX : Events

A PBX event is a rule of the form when this happens on the PBX, do that. When the trigger happens - a call starts, rings, is answered or ends, a switch is turned on or off, something happens in a queue or a conference room, a user logs in to or out of a hotdesk phone, or a point in time is reached - the event runs its actions:

  • Webhook - call a URL of your own, with details of what happened.
  • SMS - send an SMS to up to 10 recipients.
  • Call - place a call, e.g. ring a group and connect whoever answers to an announcement or another number.
  • Set variable - set a PBX variable.
  • Set switch - turn a PBX switch on or off.
  • Queue - put an extension in a queue or take it out, take it out of every queue, or empty a queue.

An event can have any combination of the six actions. Texts in the webhook, the SMS and the variable value can hold variables such as %A_NUMBER%, which are filled in when the event runs.

A customer can have at most 100 events. Each run of an event is logged with the result of each action, and every event can be tested with values of your own choosing.

API Description
List List the events of a customer
Get Get a single event
Create Create a new event
Update Update an event
Delete Delete an event
Secret Give the webhook of an event a new secret
Log The latest runs of an event and how each action went
Test Run the actions of an event now, with variables of your own choosing

Triggers

The type of an event decides what triggers it, and its target can limit it to a number, a switch, a queue, a conference room or a phone.

Type Triggered when target
CALL Any of CALL_START, CALL_RING, CALL_ANSWER and CALL_END below. Empty for every call, or a phone number or the start of one, e.g. +4570 or 4570. It is matched against both the calling and the called number, and a leading + is ignored on both.
CALL_START A call starts.
CALL_RING A call rings.
CALL_ANSWER A call is answered.
CALL_END A call ends.
SWITCH A switch changes value - from off to on or from on to off. Required. The exact name of the switch, prefixed with SHARED: for a shared switch or <employee-id>: for a personal switch, as in Switch set.
SWITCH_ON A switch is turned on.
SWITCH_OFF A switch is turned off.
QUEUE A caller enters a queue, is answered, hangs up while waiting or times out - and when a member joins or leaves the queue. Empty for every queue, or the id of a queue.
QUEUE_MEMBER_JOIN A member joins a queue.
QUEUE_MEMBER_LEFT A member leaves a queue.
CONFERENCE A participant enters or leaves a conference room. Empty for every conference, or the id of a conference.
HOTDESK A user logs in to (grabs) or logs out of (releases) a hotdesk SIP phone. A login that pushes another hotdesk phone off the extension, or moves the phone away from another extension, also counts as a logout of that phone or extension. So does a phone released automatically, when the Communicator that used it has disconnected. Empty for every hotdesk phone, the id of an extension, or the MAC address of a SIP phone written as 00:15:65:AA:BB:CC (upper or lower case).
TIMER One of the times in timers is reached: every week on a day and time, or once on a date and time - see Create. Times are Danish time. Not used.

Good to know:

  • A call can have several legs - e.g. when a group rings five phones. An event runs once per call for each phase (start, ring, answer, end), on the first leg that matches its target.
  • Secret numbers are never shown. For a caller with a secret number calling in from outside, a queue caller with a secret number and a conference participant with a secret number, the number and name are empty in the variables, and a target number is not matched against the number. A conference participant who is not one of the customer's employees counts as secret, unless the PBX knows that the number is not secret.
  • Setting a switch to the value it already has does not trigger an event, and neither does deleting a switch.
  • A switch or variable set by the set switch or set variable action of an event, a member put in or taken out of a queue by the queue action, and a call placed by the call action, never trigger events themselves, so events cannot trigger each other in a loop. So an extension that an event puts in a queue does not trigger QUEUE or QUEUE_MEMBER_JOIN events - see Queue.
  • QUEUE runs once per caller for each of entered, answered, cancelled and timed out, and every time a member joins or leaves. QUEUE_MEMBER_JOIN and QUEUE_MEMBER_LEFT run every time a member joins or leaves, and never for the callers.
  • TIMER follows Danish summer and winter time: when summer time ends, the repeated hour runs once; a time that does not exist when summer time starts (02:00-02:59 on the last Sunday of March) does not run that day.
  • An event with enabled false is kept but never triggered. It can still be tested.

Actions

The actions of an event run in this order: set switch, set variable, queue, webhook, SMS, call. Each action runs on its own - one that fails does not stop the others - and the result of each is written to the run log.

Because the set actions run first, a webhook or SMS of the same event that uses %SWITCH:name% or %VARIABLE:name% sees the value that was just set.

Webhook

The webhook calls the URL of the event. The URL must be http or https, and may not point at a private, loopback or otherwise non-public address, or at Telecom X's own network. Variables may be used in the path and the query string of the URL, but not in the host name.

Method How the request is sent
GET A GET request to the URL, with the variables filled in.
POST A POST request to the URL without its query string. The query string is sent as the body, with the content type application/x-www-form-urlencoded. Write the URL as for GET, e.g. https://hooks.example.com/pbx?caller=%A_NUMBER%&event=%EVENT_TYPE%.
POSTJSON A POST request to the URL, with the json of the event as the body and the content type application/json.

Every request carries these headers:

Header Value
User-Agent TelecomX-PbxEvents/1.0
X-TCX-Event-Id The id of the PBX event.
X-TCX-Signature sha256= followed by the HMAC-SHA256, in lower case hex, of the request body (POST and POSTJSON) or of the complete URL that was called (GET), with the webhook secret as the key.
Content-Type For POST and POSTJSON, as above.

Every webhook has a secret. One is generated when the webhook is created, and you can set your own or generate a new one. The secret is shown only to users who may change the event: by Get, in the answers to Create, Update and Secret - never by List. Check the signature to be sure a request comes from your PBX event and was not changed on the way.

  • The request times out after 5 seconds.
  • A response with status 200-299 is a success. The response body is not used.
  • Redirects are not followed - a 3xx response counts as a failure.
  • The host name is looked up when the request is sent, and the request is refused if the name points at a private, loopback or otherwise non-public address, or at Telecom X's own network - also an IPv6 address that carries such an IPv4 address (NAT64, 6to4).
  • A user name and password in the URL (https://user:password@hooks.example.com/) is sent as basic authentication.
  • A failed request is not retried.

For GET, the signature is made over the URL exactly as it is called: the URL of the event with the variables filled in, e.g. https://hooks.example.com/pbx?caller=%2B4512345678. If your server sits behind a proxy, rebuild the URL as it is written in the event before you check it. Do not put a #fragment in a GET webhook URL - it is signed, but not sent.

Example: checking the signature in Node.js

const http = require('node:http')
const crypto = require('node:crypto')
 
const SECRET = process.env.PBX_EVENT_SECRET     // the webhook secret of the event
const BASE = 'https://hooks.example.com'        // scheme and host of the webhook URL, as written in the event
 
function signatureOk(signed, header) {
  const expected = Buffer.from('sha256=' + crypto.createHmac('sha256', SECRET).update(signed).digest('hex'))
  const received = Buffer.from(String(header || ''))
  return expected.length === received.length && crypto.timingSafeEqual(expected, received)
}
 
http.createServer((req, res) => {
  const chunks = []
  req.on('data', chunk => chunks.push(chunk))
  req.on('end', () => {
    const body = Buffer.concat(chunks).toString('utf8')
    // GET is signed over the complete URL, POST and POSTJSON over the raw body
    const signed = req.method === 'GET' ? BASE + req.url : body
 
    if (!signatureOk(signed, req.headers['x-tcx-signature'])) {
      res.writeHead(401).end()
      return
    }
    console.log('PBX event', req.headers['x-tcx-event-id'], req.method === 'GET' ? req.url : body)
    res.writeHead(204).end()
  })
}).listen(8080)

Check the signature against the raw body, before it is parsed - a body that is parsed and serialised again can differ by a single character, and then the signature does not match.

SMS

The SMS action sends message to each of the recipients (at most 10, in E.164 format, e.g. +4520123456). The message can hold variables.

The sender is from - a phone number in E.164 format, or 1-11 letters, digits, spaces, dots, underscores or dashes. A phone number is sent without its +, and a sender can be at most 11 characters, so a number with more than 11 digits is cut to its first 11. Without from, the sender is the customer's name, cut to 11 characters.

Each SMS is billed to the customer like any other SMS sent from the platform.

Call

The call action rings destination the way the customer's PBX rings that number on an internal call: an extension rings its phones, a group rings its members, and an external number is called like any other outbound call of the PBX. The destination must be an extension, a group or an external number - any other destination is refused, and the run log shows the call as failed. Emergency numbers are never called by the event, not even through a redirect - but once a person has answered, that person can always transfer the call to an emergency number.

The destination rings as long as it normally does - e.g. a large group that rings its members one after the other is not cut short. When a person answers, he or she is connected to source, which is called as an internal call of the customer - e.g. a playback with an announcement, or another extension. Start audio and the recording consent prompt are skipped for these calls.

With autoAnswer, SIP phones that support it answer the call by themselves (as when paging a group), which is useful for announcements over the phones' speakers. Only the phones of the destination itself answer by themselves - not phones the call is redirected to. The caller name shown is PBX event.

A call placed by the call action never triggers events itself - also not after the person who answered transfers it. On Realtime, its events carry source PBX_EVENTS.

Set variable and set switch

Set variable sets a PBX variable to value (variables are filled in, at most 1024 characters). Set switch turns a PBX switch on or off. The names follow the same rules as everywhere else: no prefix for a system variable or switch, SHARED: for a shared one, and <employee-id>: for a personal one.

The change is announced on Realtime like any other change, with source PBX_EVENTS, and it never triggers other events.

Queue

The queue action changes the members of a queue of the customer. Its action says how:

Action What it does
JOIN Puts extension in queue with priority (1-20, default 1). If the queue remembers the priority of its members (member.persistPriority), an extension that has been a member before gets its last priority instead. An extension that is already a member is left as it is. With exclusive, every other member who is not permanent is then taken out of the queue.
LEAVE Takes extension out of queue. If it is not a member, nothing changes.
LEAVE_ALL Takes extension out of every queue of the customer. queue is not used.
EMPTY Takes every member who is not permanent out of queue - except extension, the member to keep. Without extension, no one is kept.

queue and extension are the id of a queue and of an extension of the customer - or TRIGGER, to take them from what triggered the event:

Setting TRIGGER takes Allowed for the types
queue The queue of the event (the variable QUEUE_ID): the queue of a QUEUE, QUEUE_MEMBER_JOIN or QUEUE_MEMBER_LEFT event, or the queue that placed the call leg of a CALL event. CALL, CALL_START, CALL_RING, CALL_ANSWER, CALL_END, QUEUE, QUEUE_MEMBER_JOIN and QUEUE_MEMBER_LEFT
extension The extension of the member who joined or left (MEMBER_EXTENSION_ID), else the extension of the event (EXTENSION_ID): the extension of the call leg, or of the hotdesk login or logout. The same, and HOTDESK

If what triggered the event has no queue or no extension - e.g. a call that did not come through a queue, or a member who is an external number - the action fails with no_trigger_queue or no_trigger_extension in the run log. A test takes them from the variables you send.

Good to know:

  • The queue does not have to let its members join and leave by themselves (member.dynamic) - an event is made by a manager, who may change the members of any queue. But if only the members of a group may join the queue (member.group), JOIN of an extension that is not in the group fails with not_permitted.
  • Permanent members are never taken out. LEAVE of a permanent member fails with permanent_member, and LEAVE_ALL, EMPTY and exclusive leave them in the queue.
  • A queue that locks its last member (member.lockLastMember) never loses it: LEAVE of the last member fails with last_member_locked, LEAVE_ALL skips that queue, and EMPTY fails with last_member_locked, and changes nothing, when nobody would be left.
  • EMPTY with extension TRIGGER keeps the member who triggered the event. If that member has already left the queue again when the event runs, nothing is taken out.
  • The change is announced on Realtime as QUEUE_MEMBER_JOIN and QUEUE_MEMBER_LEFT, with source PBX_EVENTS, and it shows up in the queue statistics like any other join or leave. It never triggers other events: when an event puts an extension in a queue, an event of type QUEUE_MEMBER_JOIN on that queue does not run. Use exclusive on the JOIN to take the others out at the same time.

Example: one member at a time

An event of type QUEUE_MEMBER_JOIN with the queue action EMPTY, and TRIGGER as both queue and extension: whenever someone joins the queue, everyone else is taken out of it - except permanent members. Without a target it works on every queue of the customer; give the id of a queue as target to limit it to that queue. See the request in Create.

Variables

A variable is written as its name between percent signs, e.g. %A_NUMBER%. In addition to the variables of the trigger below, two kinds of lookups can be used:

Variable Description
%VARIABLE:name% The current value of the PBX variable name, e.g. %VARIABLE:SHARED:status%. Empty if the variable does not exist.
%SWITCH:name% The current value of the PBX switch name: 1 for on, 0 for off, empty if the switch does not exist.

A variable that is not known, or has no value for this run, becomes empty. Variable names are upper case letters and underscores.

Variables are filled in in the webhook URL, the webhook JSON, the SMS message and the value of set variable. They are not filled in in the call numbers, the SMS recipients and sender, or the names of the variable and switch to set. A value is escaped for the place it is put:

Place Escaping
Webhook URL (GET and POST) Percent-encoded, so +4512345678 becomes %2B4512345678.
Webhook JSON (POSTJSON) Escaped as the content of a JSON string - quotes and backslashes are escaped, but no quotes are added. Put every variable inside a JSON string: { "caller": "%A_NUMBER%", "duration": "%CALL_DURATION%" }.
SMS message, variable value As it is.

Variables of every run

Variable Description
EVENT_TYPE What happened: CALL_START, CALL_RING, CALL_ANSWER, CALL_END, SWITCH_ON, SWITCH_OFF, QUEUE_ENTERED, QUEUE_ANSWERED, QUEUE_CANCELLED, QUEUE_TIMED_OUT, QUEUE_MEMBER_JOIN, QUEUE_MEMBER_LEFT, CONFERENCE_ENTERED, CONFERENCE_LEFT, HOTDESK_LOGIN, HOTDESK_LOGOUT, TIMER or TEST.
TIME Danish time of the run, e.g. 2026-11-04 14:30:05.
TIMESTAMP Time of the run as Unix time (seconds since 1970-01-01 UTC).
GUID A random unique id, new for each run.
EVENT_ID Id of the PBX event.

CALL, CALL_START, CALL_RING, CALL_ANSWER, CALL_END

Variable Description
A_NUMBER Number of the calling party. Empty for a caller with a secret number calling in from outside.
A_NUMBER_NO_PREFIX A_NUMBER without a Danish +45 prefix.
A_NAME Name of the calling party, if known.
B_NUMBER The called number.
B_NUMBER_NO_PREFIX B_NUMBER without a Danish +45 prefix.
B_NAME Name of the called party, if known.
DIRECTION I for a call in from outside, O for a call out from the PBX.
CALL_ID Id of the call - the same in every phase of the call.
START Danish time the call started, e.g. 2026-11-04 14:30:05.
CALL_DURATION Seconds since the call started. CALL_ANSWER and CALL_END only.
CONVERSATION_DURATION Seconds since the call was answered, 0 if it was not. CALL_END only.
EXTENSION_ID Id of the extension of the call leg, if any.
QUEUE_ID Id of the queue that placed the call leg, if any.
QUEUE_NAME Name of that queue.

SWITCH, SWITCH_ON, SWITCH_OFF

Variable Description
SWITCH_NAME Name of the switch, with its prefix.
SWITCH_STATE 1 if the switch is now on, 0 if off.
PREVIOUS_STATE 1 or 0 for the value before the change, empty if the switch did not exist.

QUEUE, QUEUE_MEMBER_JOIN, QUEUE_MEMBER_LEFT

QUEUE_MEMBER_JOIN and QUEUE_MEMBER_LEFT are member events only.

Variable Description
QUEUE_ID Id of the queue.
QUEUE_NAME Name of the queue.
A_NUMBER Number of the caller (caller events). Empty for a caller with a secret number.
A_NUMBER_NO_PREFIX A_NUMBER without a Danish +45 prefix.
A_NAME Name of the caller, if known.
POSITION Position of the caller in the queue.
WAIT_TIME Seconds the caller waited. QUEUE_ANSWERED, QUEUE_CANCELLED and QUEUE_TIMED_OUT only.
ANSWERED_BY Id of the extension, or the number, that answered. QUEUE_ANSWERED only.
MEMBER_EXTENSION_ID Id of the extension of the member (member events).
MEMBER_NUMBER Number of the member (member events).
MEMBER_NAME Name of the member (member events).

CONFERENCE

Variable Description
CONFERENCE_ID Id of the conference.
CONFERENCE_NAME Name of the conference.
A_NUMBER Number of the participant. Empty for a secret number - see Triggers.
A_NUMBER_NO_PREFIX A_NUMBER without a Danish +45 prefix.
A_NAME Name of the participant, if known. Empty for a secret number.
CALLERS_IN_ROOM Number of participants in the room after the participant entered or left, when known.
DURATION Seconds the participant was in the room. CONFERENCE_LEFT only.

HOTDESK

Variable Description
EXTENSION_ID Id of the extension.
EXTENSION_NUMBER Number of the extension.
EXTENSION_NAME Name of the extension.
SIP_PHONE_ID Id of the SIP phone.
SIP_PHONE_MAC MAC address of the SIP phone as 12 hex digits without separators, if known.
EMPLOYEE_ID Id of the employee the extension belongs to, if any.

TIMER

Only the variables of every run.

Limits

Limit Value
Events per customer 100
SMS recipients per event 10
Timers per TIMER event 100
Webhook requests 60 per customer per minute
SMS 10 per customer per minute - each recipient counts as one
Calls 10 per customer per minute
Switches and variables set 120 per customer per minute, together
Queue actions 30 per customer per minute

The per minute limits count the last 60 seconds, and test runs count too. One event can only be tested once at a time - see Test. An action that would go over its limit is skipped, and the run log shows it as failed with the error rate_limited. An SMS action with more recipients than are left of the limit is skipped as a whole.

Delivery

Events run when the PBX reports what happened, and each event runs at most once for each thing that happened. Delivery is best effort: if the service that runs the events is unavailable - e.g. during maintenance - what happens on the PBX in that time does not trigger events, and is not caught up afterwards. A TIMER that is a few minutes late runs when it can. A failed webhook, SMS or call is not retried.

Do not use PBX events for anything that must never be missed, such as billing or statistics - use the CDR for that.

Run log

Every run of an event - triggered or a test - is logged with what triggered it, the variables it ran with, and for each action whether it succeeded, the HTTP status, the error and how long it took. Runs are kept for 30 days. See Log.

When an event is deleted, its runs can no longer be read, and they are removed when their 30 days are up.

api/pbx/events.txt · Last modified: by Telecom X AI

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki