Quality On Demand
Dynamically customize and request improved network quality, ensuring better performance for critical applications, when and where it is needed.
Getting Started - Quality-On-Demand API
Introduction
The Quality-On-Demand (QoD) API provides a programmable interface for developers and other users (API consumers) to request stable latency or throughput managed by networks without the necessity to have an in-depth knowledge of the underlying network complexity (e.g. the 4G/5G system in case of a mobile network).
Industrial (IoT), VR/Gaming, live video streaming, autonomous driving and many other scenarios demand network communication quality and are sensitive to any change in transmission conditions. Being able to request a stable latency (reduced jitter) or prioritized throughput from the network can improve user experience substantially.
The QoD API offers application developers the capability to request stable latency (reduced jitter) or throughput for specified application data flows between application clients (within a user device) and application servers (backend services). The developer has a pre-defined set of Quality of Service (QoS) profiles which they can choose from depending on their latency or throughput requirements.
API Scope
The current API implementation is applicable to any mass-market Orange customer.
This API has few limitations:
- for the device identifer only phone number is accepted. It is not possible to provide ip address in the request.
- all the flows from/to this device will benefit from the quality on demand request. As such, the application server ip/port will not be considered but as providing an ip is mandatory in the CAMARA specification (a ip v4 or a v6) we expect to have an ip valued.
Subscribe to the API
You have to register your application on the Orange Developer Console and subscribe to it.
API Authentication
HTTPS requests to the REST API are protected with CIBA 3-Legged OAuth.
In short, the API Invoker (e.g. Application Backend or Aggregator) before requesting Quality-On-Demand services, needs to request an Access Token from Orange Authorization Server. The process follows the OpenID Connect Client-Initiated Backchannel Authentication (CIBA) flow for 3-legged authentication.
Step 1: Request the OAuth authorization code from the Application Backend
The API Invoker provides in the authorization request (/bc_authorize) a login_hint with a valid User identifier together with the application credentials (client_assertion & client_assertion_type) and indicates the Purpose for processing Personal Data. The Orange implementation follows the CAMARA scope definition. The scope must be set to:
openid dpv:<dpvValue> quality-on-demand:sessions:<operation>. dpv stands for Data Privacy Vocabulary.
Note on JWT usage via client assertion:
client_assertion is a JWT used by a client to authenticate itself to an authorization server, while client_assertion_type specifies the type of assertion being used, typically indicating it is a JWT. Together, they facilitate secure client authentication in OAuth 2.0 and OpenID Connect protocols. API consumer has to define JWK keystore in settings tab of the application in Orange Developer.
Current supported DPV values:
- FulfilmentOfContractualObligation
Current supported date claims:
- quality-on-demand:sessions:create for the POST/sessions operation
- quality-on-demand:sessions:read For the GET/sessions/{id} operation
- quality-on-demand:sessions:delete for the DELETE/sessions/{id} operation
- quality-on-demand:sessions:update for the POST/sessions/{id}/extend operation
- quality-on-demand:sessions:retrieve-by-device for the POST/retrieve-sessions operation
Examples:
- to create a QoS session, in a context of fulfilment of contractual obligation, the scope in the
bc-authorizemust be set toopenid dpv:FulfilmentOfContractualObligation quality-on-demand:sessions:create - to be able to manage all operations, in a context of fulfilment of contractual obligation, the scope in the
bc-authorizemust be set toopenid dpv:FulfilmentOfContractualObligation quality-on-demand:sessions:retrieve-by-device quality-on-demand:sessions:update quality-on-demand:sessions:delete quality-on-demand:sessions:read quality-on-demand:sessions:create
Orange Authorization Server will check if the owner of the phone number did not opt-out to authorize access to this data. If this is not the case, a response 200 is sent back with an authorization request identifier (auth_req_id). If the resource owner or OpenID Provider denied the request, an error 403 Forbidden is sent back.
Request:
curl -X POST \
'hhttps://api.orange.com/es/openapi/oauth/v2/bc-authorize' \
--header 'Accept: */*' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'login_hint=tel%3A%2B34665682532' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion=eyJhbGciOi...zif0Tjxzw' \
--data-urlencode 'openid dpv:FulfilmentOfContractualObligation quality-on-demand:sessions:retrieve-by-device quality-on-demand:sessions:update quality-on-demand:sessions:delete quality-on-demand:sessions:read quality-on-demand:sessions:create'
Response:
200
Content-Type: application/json
{
"auth_req_id": "69957d77-...-b29f22e61307",
"expires_in": 120,
"interval": 2
}
Step 2: Request the OAuth access token
Once the client application gets the authorization code, the API Invoker polls the token endpoint by making an "HTTP POST" request by sending the grant_type (urn:openid:params:grant-type:ciba), auth_req_id (OperatorAuthReqId) and the application credentials (client_assertion & client_assertion_type) parameters.
If the transaction succeeds, in the POST response, the access_token is provided.
Request:
curl -X POST \
'https://api.orange.com/es/openapi/oauth/v2/token' \
--header 'Accept: */*' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion=eyJhbGciOi...zif0Tjxzw' \
--data-urlencode 'auth_req_id=69957d77-...-b29f22e61307' \
--data-urlencode 'grant_type=urn:openid:params:grant-type:ciba'
Response:
200
Content-Type: application/json
{
"access_token": "OFR_28FpNfX...7lwFAg3LR0vmHCOmJAg7Si",
"token_type": "Bearer",
"expires_in": 119
}
Step 3: Access protected resources using OAuth access token
In order to call our API, the access_token is mandatory.
Specific documentation about Quality-On-Demand resources is provided below.
API Description
Base URL
The Base URL is the first part of the full invocation URL, just before the resource paths defined in the API reference.
The Base URL is comprised of the scheme ('https'), the authority (i.e. the Fully Qualified Domain Name) and the API base path.
Whenever you request this API and encounter a 404 (Not Found) HTTP status code, please check first that the Base URL is correct.
The Base URL for this API is: https://api.orange.com/camara/oes/quality-on-demand/v1
The documentation below assumes that, whenever you make requests on this API, you are prepending the Base URL to the resource paths defined for this API.
Resources
This API manages the sessions resources. Available operations allows to
- create session
- retrieve a session from its id
- retrieve session without id (from phone number)
- delete session
- extend a session
/sessions (POST)
Create a new QoS session to manage latency/throughput priorities.
Notes:
- The access token must be a 3-legged access token.
- The optional device identifier MUST NOT be provided, as the subject will be uniquely identified from the access token.
Request
| Parameter | Description | Usage | Location | Type |
|---|---|---|---|---|
device | End-user equipment able to connect to a mobile network. Must not be passed as the device is deducted from the token. | Optional | body | Object |
device.phoneNumber | The subscriber's phone number in E.164 format (prefixed with '+'). | Must not be passed as the device is deducted from the token | body | String |
device.ipv4Address | IPv4 address of the device. | Not considered in our implementation | body | Object |
device.ipv4Address.publicAddress | Public IPv4 address. | Not considered in our implementation | body | String |
device.ipv4Address.privateAddress | Private IPv4 address. | Not considered in our implementation | body | String |
device.ipv4Address.publicPort | Public port number. | Not considered in our implementation | body | Integer |
device.ipv6Address | IPv6 address of the device. | Not considered in our implementation | body | String |
applicationServer | A server hosting backend applications. | Required - not considered in our implementation but an IP MUST be provided | body | Object |
applicationServer.ipv4Address | IPv4 address of the application server. An ipv4 or ipv6 MUST be provided. | Optional | body | String |
applicationServer.ipv6Address | IPv6 address of the application server. An ipv4 or ipv6 MUST be provided. | Optional | body | String |
devicePorts | Ports used locally by the device. | Not considered in our implementation | body | Object |
devicePorts.ranges | Range of TCP or UDP ports. | Not considered in our implementation | body | Array |
devicePorts.ports | Array of TCP or UDP ports. | Not considered in our implementation | body | Array |
applicationServerPorts | Ports on the application server. | Not considered in our implementation | body | Object |
applicationServerPorts.ranges | Range of TCP or UDP ports. | Not considered in our implementation | body | Array |
applicationServerPorts.ports | Array of TCP or UDP ports. | Not considered in our implementationn | body | Array |
qosProfile | A unique name for identifying a specific QoS profile. QoS Profiles are available via QoS profiles API. As of now 2 profiles are configured: TI_L_B2B and TI_S_B2B | Required | body | String |
duration | Requested session duration in seconds. | Required | body | Integer |
sink | The address to which events about status changes shall be delivered. Not managed in curent version. | Optional | body | String |
sinkCredential | Authentication information for the notification endpoint. Not managed in curent version. | Optional | body | Object |
sinkCredential.credentialType | Type of credential (MUST be set to ACCESSTOKEN). Not managed in curent version. | Optional | body | String |
sinkCredential.accessToken | Access token for notification endpoint. Not managed in curent version. | Optional | body | String |
sinkCredential.accessTokenExpiresUtc | Expiration time of the access token. Not managed in curent version. | Optional | body | String |
sinkCredential.accessTokenType | Type of access token (MUST be set to bearer). Not managed in curent version. | Optional | body | String |
As an example:
curl -X POST \
'https://api.orange.com/camara/oes/quality-on-demand/v1/sessions' \
--header 'Accept: */*' \
--header 'Authorization: Bearer xxxxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"applicationServer": {
"ipv6Address": "2001:db8:85a3:8d3:1319:8a2e:370:7344"
},
"qosProfile": "TI_S_B2B",
"duration": 60
}
'
Response
a/ If the transaction succeeds
On success, the Quality-On-Demand API returns a 201 Created HTTP status code with JSON data including the session information.
| Parameter | Description | Usage | Location | Type |
|---|---|---|---|---|
sessionId | Session ID in UUID format. | Return | body | String |
duration | Session duration in seconds. | Return | body | Integer |
device | Device information (if provided in request). | Return | body | Object |
applicationServer | Application server information. | Return | body | Object |
devicePorts | Device ports (if provided in request). | Return | body | Object |
applicationServerPorts | Application server ports (if provided in request). | Return | body | Object |
qosProfile | QoS profile name. | Return | body | String |
sink | Notification endpoint (if provided in request). | Return | body | String |
sinkCredential | Notification credentials (if provided in request). | Return | body | Object |
startedAt | Date and time when the QoS status became "AVAILABLE". | Return | body | String |
expiresAt | Date and time of the QoS session expiration. | Return | body | String |
qosStatus | Current status of the requested QoS session (REQUESTED, AVAILABLE, UNAVAILABLE). | Return | body | String |
statusInfo | Reason for the new qosStatus (if applicable). | Return | body | String |
As an example:
HTTP/1.1 201 Created
{
"device": {
"phoneNumber": "+3466568253"
},
"applicationServer": {
"ipv6Address": "2001:db8:85a3:8d3:1319:8a2e:370:7344"
},
"qosProfile": "TI_S_B2B",
"sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"duration": 3600,
"startedAt": "2026-06-01T12:00:00Z",
"expiresAt": "2026-06-01T13:00:00Z",
"qosStatus": "AVAILABLE",
}
b/ If the transaction fails
Please visit the most frequent errors section in this page.
/sessions/{sessionId} (GET)
Get QoS session information for a specific session.
Notes:
- The access token must be 3-legged.
- With a 3-legged access token is used, the end user (and device) associated with the session must also be associated with the access token.
- The session must have been created by the same API client given in the access token.
Request
| Parameter | Description | Usage | Location | Type |
|---|---|---|---|---|
sessionId | Session ID that was obtained from the createSession operation. | Required | path | String |
As an example:
curl -X GET \
'https://api.orange.com/camara/oes/quality-on-demand/v1/sessions/' \
--header 'Accept: */*' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer xxxx'
Response
a/ If the transaction succeeds
On success, the Quality-On-Demand API returns a 200 OK HTTP status code with JSON data including the session information.
The response structure is the same as for the /sessions POST endpoint.
As an example:
HTTP/1.1 200 OK
{
"device": {
"phoneNumber": "+3466568253"
},
"applicationServer": {
"ipv6Address": "2001:db8:85a3:8d3:1319:8a2e:370:7344"
},
"qosProfile": "TI_S_B2B",
"sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"duration": 3600,
"startedAt": "2026-06-01T12:00:00Z",
"expiresAt": "2026-06-01T13:00:00Z",
"qosStatus": "AVAILABLE",
}
b/ If the transaction fails
Please visit the most frequent errors section in this page.
/sessions/{sessionId} (DELETE)
Delete a QoS session and release related resources.
Notes:
- The access token is 3-legged.
- The subject associated with the session must also be associated with the access token.
- The session must have been created by the same API consumer given in the access token.
Request
| Parameter | Description | Usage | Location | Type |
|---|---|---|---|---|
sessionId | Session ID that was obtained from the createSession operation. | Required | path | String |
As an example:
curl -X DELETE \
'https://api.orange.com/camara/oes/quality-on-demand/v1/sessions/' \
--header 'Accept: */*' \
--header 'Authorization: Bearer xxxx'
Response
a/ If the transaction succeeds
On success, the Quality-On-Demand API returns a 204 No Content HTTP status code.
HTTP/1.1 204 No Content
b/ If the transaction fails
Please visit the most frequent errors section in this page.
/sessions/{sessionId}/extend (POST)
Extend the duration of an active QoS session.
Notes:
- The access token is 3-legged.
- The subject associated with the session must also be associated with the access token.
- The session must have been created by the same API consumer given in the access token.
- The overall duration of the QoS session, including the additional extended duration, shall not exceed the maximum duration limit fixed for the QoS Profile.
Request
| Parameter | Description | Usage | Location | Type |
|---|---|---|---|---|
sessionId | Session ID that was obtained from the createSession operation. | Required | path | String |
requestedAdditionalDuration | Additional duration in seconds to be added to the current session duration. | Required | body | Integer |
As an example:
curl -X POST \
'https://api.orange.com/camara/oes/quality-on-demand/v1/sessions//extend' \
--header 'Accept: */*' \
--header 'Authorization: Bearer xxxx' \
--header 'Content-Type: application/json' \
--data-raw '{ "requestedAdditionalDuration": 120 }'
Response
a/ If the transaction succeeds
On success, the Quality-On-Demand API returns a 200 OK HTTP status code with JSON data including the updated session information.
The response structure is the same as for the /sessions POST endpoint, with updated duration and expiresAt values.
As an example:
HTTP/1.1 200 OK
{
"device": {
"phoneNumber": "+3466568253"
},
"applicationServer": {
"ipv6Address": "2001:db8:85a3:8d3:1319:8a2e:370:7344"
},
"qosProfile": "TI_S_B2B",
"sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"duration": 5400,
"startedAt": "2026-06-01T12:00:00Z",
"expiresAt": "2026-06-01T13:00:00Z",
"qosStatus": "AVAILABLE",
}
b/ If the transaction fails
Please visit the most frequent errors section in this page.
/retrieve-sessions (POST)
Get QoS session information for a device.
Notes:
- The access token is 3-legged.
- The subject associated with the session must also be associated with the access token. In this case the optional
deviceparameter MUST NOT be provided in the request. - The session must have been created by the same API consumer given in the access token.
- If no QoS session is found for the requested device, an empty array is returned.
- This call uses the POST method instead of GET to comply with the CAMARA Commonalities guidelines for sending sensitive or complex data in API calls.
Request
| Parameter | Description | Usage | Location | Type |
|---|---|---|---|---|
device | End-user equipment able to connect to a mobile network. Must not be passed when the device is deducted from the token. | Optional | body | Object |
device.phoneNumber | The subscriber's phone number in E.164 format (prefixed with '+'). | Optional | body | String |
device.ipv4Address | IPv4 address of the device. | Optional | body | Object |
device.ipv6Address | IPv6 address of the device. | Optional | body | String |
As an example:
curl -X POST \
'https://api.orange.com/camara/oes/quality-on-demand/v1/retrieve-sessions' \
--header 'Accept: */*' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer xxxx' \
--data-raw '{}'
Response
a/ If the transaction succeeds
On success, the Quality-On-Demand API returns a 200 OK HTTP status code with JSON data including an array of session information. An empty array is returned if no sessions are found.
As an example:
HTTP/1.1 200 OK
[
{
"device": {
"phoneNumber": "+3466568253"
},
"applicationServer": {
"ipv6Address": "2001:db8:85a3:8d3:1319:8a2e:370:7344"
},
"qosProfile": "TI_S_B2B",
"sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"duration": 5400,
"startedAt": "2026-06-01T12:00:00Z",
"expiresAt": "2026-06-01T13:00:00Z",
"qosStatus": "AVAILABLE",
}
]
Empty response example:
HTTP/1.1 200 OK
[]
b/ If the transaction fails
Please visit the most frequent errors section in this page.
Most frequent errors
- If invalid input is provided, in particular for the device identifier, a 400 error is triggered.
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"status": 400,
"code": "INVALID_ARGUMENT",
"message": "Client specified an invalid argument, request body or query param."
}
- If the client provided a phone number within the access token but also provides it in the API payload, an error 422 is sent back.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/json
{
"status": 422,
"code": "UNNECESSARY_IDENTIFIER",
"message": "The device is already identified by the access token."
}
- If there is a conflict with an existing session for the same device:
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"status": 409,
"code": "CONFLICT",
"message": "Conflict with an existing session for the same device."
}
- If session extension is not allowed in the current state:
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"status": 409,
"code": "QUALITY_ON_DEMAND.SESSION_EXTENSION_NOT_ALLOWED",
"message": "Extending the session duration is not allowed in the current state (UNAVAILABLE). The session must be in the AVAILABLE state."
}
There are some cases where your client application will no longer gain access to API resources, and get an error back.
Please check the following points:
- Here, you attempt to use an expired or revoked access_token and you get an invalid token error. You will have to request a new access_token. As an example:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"status": 401,
"code": "UNAUTHENTICATED",
"message": "Request not authenticated due to missing, invalid, or expired credentials. A new authentication is required."
}
- Here, you removed your subscription to the API so that the capability to generate an access_token is not allowed anymore. As an example:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"status": 403,
"code": "PERMISSION_DENIED",
"message": "Client does not have sufficient permissions to perform this action."
}
- Resource not found (it can be an error in the path suffix of the URL, or an unknown session ID):
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"status": 404,
"code": "NOT_FOUND",
"message": "The specified resource is not found."
}
- Device identifier not found:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"status": 404,
"code": "IDENTIFIER_NOT_FOUND",
"message": "Device identifier not found."
}
- Exceeded requests per hour/minute limit:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"status": 429,
"code": "TOO_MANY_REQUESTS",
"message": "Rate limit reached."
}