Click to Call¶
HTTP endpoint to originate a call from an external application: QVOICE Platform rings the authenticated user's extension first and, once that leg answers, dials the destination.
Who receives the call¶
The call always rings the user that owns the access token used in the request.
There is no parameter to choose the agent. The endpoint takes the destination from the URL path and the caller from the authentication context:
- The access token carries an
identityclaim in the form<user_id>:<account>. - The backend resolves that claim to a user document and uses the user's
presence_id(their extension) as the first leg of the call. - The destination in the path is dialled only after that extension answers.
So, to make a call ring agent A, the request must be authenticated with agent A's token. A token belonging to an administrator will ring the administrator's own extension, not the agent's.
One token, one extension
The X-Account-ID header does not change who receives the call. This endpoint always uses
the account and user encoded in the token. If your integration places calls on behalf of several
agents, it must obtain and store one access token per agent.
The user needs an extension and a registered device
If the authenticated user has no presence_id, or has no phone / softphone registered, there is
nothing to ring and the call never gets to the destination.
Authentication¶
Use the standard QVOICE Platform login endpoint to obtain an access token for the user that should receive the call:
curl -X POST "https://{portalURL}:9443/ucp/v2/login" \
-H "Content-Type: application/json" \
-d '{
"username": "agent@example.com",
"password": "yourPassword",
"domain": "yourTenant"
}'
Response
{
"user": { "...": "..." },
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Send the access_token as Authorization: Bearer <token> on every Click to Call request. When it
expires, obtain a new one with the refresh token or by logging in again.
API keys are not supported on this endpoint
An API key (X-API-Key) authenticates the account, not a person: it resolves to a virtual
admin user with no extension, so there is no phone to ring and the request fails. Click to Call
requires a real user's access token.
Endpoint¶
| Method | POST |
| URL | https://{portalURL}:9443/ucp/v2/c2c/{destination} |
| Headers | Authorization: Bearer <access_token> |
| Body | none |
| Success | 201 Created with a JSON body carrying the call's origination_call_id |
Check your platform version
origination_call_id was added after QVOICE Platform 2.119.96. Platforms older than
that answer this endpoint with 500 Internal Server Error even though the
call is placed correctly, and return no id. If you get a 500 and
the call still rings, your platform predates this change — ask your
QVOICE Platform contact when it will be upgraded.
Path parameter¶
| Name | Description |
|---|---|
destination |
Number or extension to dial once the user's extension answers. Anything the account's dialplan accepts: an internal extension (2001) or an external number (5491155551234). URL-encode it if it contains + or other reserved characters. |
Example¶
curl -i -X POST "https://{portalURL}:9443/ucp/v2/c2c/5491155551234" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
HTTP/1.1 201 Created
Content-Type: application/json
{"origination_call_id":"2e096f37105f5ba3152d5a38a7daedb5584e-clicktocall"}
Response body¶
| Field | Description |
|---|---|
origination_call_id |
Unique identifier of the call, assigned before it is dialled. Every CDR the call produces carries it in its own origination_call_id field — that is what you match on. Treat it as an opaque string: do not parse it or rely on its length or format. Empty in the rare case the platform accepted the origination without reporting an id; the call is still placed. |
origination_call_id is not the CDR's call_id
The CDRs also have a call_id, and it is a different value — assigned by
the media server when each leg is created, and therefore not knowable when
this request returns. Matching the value returned here against a CDR's
call_id will never find anything. Match origination_call_id to
origination_call_id.
Responses¶
| Code | Meaning |
|---|---|
201 Created |
The call was accepted and is being originated. The body carries the origination_call_id. |
401 Unauthorized |
Missing, malformed or expired token. |
5xx |
The user document could not be read, or the platform rejected the origination. |
Call behaviour¶
- The request returns
201as soon as the platform accepts the origination — it does not wait for anyone to answer. A201therefore means "the call was launched", not "the call was connected". - The authenticated user's extension rings first. If that user does not answer, the destination is never dialled.
- When the user answers, the destination is dialled and both legs are bridged.
- The response carries the call's
origination_call_id, so you can record it against your own data at the moment you place the call and look the call up afterwards.
Finding the call in the CDRs¶
A single Click to Call produces more than one CDR, one per leg. The ones that
carry the origination_call_id this endpoint returned are the Click to Call
legs: the leg to the destination — with its answer time and duration — and the
loopback leg that created it. The legs that ring the user's own phones do not
carry it; they are separate legs of the bridge.
The lookup¶
Use the CDR report endpoint, with the same access token you used to place the call:
curl -X GET "https://{portalURL}:9443/api/v2/reports/cdrs?startDate=1756684800&endDate=1756771200&origination_call_id=a592c2f8f7dcbd32304266fe6b7b5c727613-clicktocall" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
| Parameter | Required | Description |
|---|---|---|
startDate |
Yes | Start of the search window, Unix timestamp in seconds. |
endDate |
Yes | End of the search window, Unix timestamp in seconds. |
origination_call_id |
— | The id returned when the call was placed. Matched exactly, not as a substring. |
One call at a time
This is a lookup, not a bulk filter: an origination_call_id identifies a
single call, so the response is a single row. It is not available on the CSV
export for that reason — there would be nothing to export. If you need to
reconcile many calls at once, tell us and we will look at it as its own
change.
You get back one row per call — the leg to the destination, whose to is the
number that was dialled — in the same shape as any CDR report row. The lookup is
not paginated: one id identifies one call.
The date range is required, and it is not a formality
Call records are stored per month and the id is not indexed on its own, so the range is what tells the platform where to look and what keeps the lookup fast. It cannot be omitted.
Store the time you placed the call next to the id. Without it you have no window to search and the call cannot be found later. The response to the Click to Call request is the natural moment to record both.
Which field is which¶
| Field | Where it appears | Use it to |
|---|---|---|
origination_call_id |
On the Click to Call legs; equals what this endpoint returned | Find the call you placed |
interaction_id |
On every leg, including the ones that rang the user's own devices | Get the complete picture of the call |
call_id |
One per leg, assigned by the media server | Address a single leg |
If you need the device legs too, take the interaction_id from the row you found
and query on that instead.
The leg tagged custom_sip_headers.fonouc_call_type = "clicktocall" is the one
QVOICE Platform's own on-screen reports display, and the one this lookup
returns.
The first time a user places a Click to Call, QVOICE Platform provisions the underlying click-to-call resource for that user automatically and stores it on the user document. No manual setup per user is required.
The UCP feature toggle does not gate this endpoint
The account feature UCP click2call only shows or hides the button inside UCP. The API answers the same whether that toggle is on or off.
Typical integration¶
A CRM or web application that wants a "call this contact" button:
- When the agent signs in to your application, log them in to QVOICE Platform with their own
credentials and keep their
access_token. - On click,
POST /ucp/v2/c2c/{contact_number}with that agent's token. - The agent's phone rings; when they pick up, the contact is dialled.
If your application serves many agents, store one token per agent — never share a single token, or every call will ring the same extension.