Skip to content

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 identity claim 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

  1. The request returns 201 as soon as the platform accepts the origination — it does not wait for anyone to answer. A 201 therefore means "the call was launched", not "the call was connected".
  2. The authenticated user's extension rings first. If that user does not answer, the destination is never dialled.
  3. When the user answers, the destination is dialled and both legs are bridged.
  4. 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:

  1. When the agent signs in to your application, log them in to QVOICE Platform with their own credentials and keep their access_token.
  2. On click, POST /ucp/v2/c2c/{contact_number} with that agent's token.
  3. 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.