WEB API

accessing party for end-users

Quickstart - Enroll End-Users

Info: Stellantis Accessing Party for End-Users API for ex Groupe PSA brands (Citroën, DS, Peugeot, Opel and Vauxhall) is available only on request.

Requirement:

Tutorial Output:

  • 🪙 An Access Token allowing requesting data about a user enrolled in your App.

This tutorial explains how to enroll an End User in order to obtain an Access Token using the OAuth2 Authorization Code Flow. This token allows requesting data about the user.

First, we will need to verify if the vehicle is eligible for your use case. Then, we will see how to implement the OAuth2 Authorization Code Flow by creating the consent request in your App and finally, to exchange the Authorization Code against the access token.

Manufacturers Brands & Realms #

In this tutorial API calls require the manufacturer {brand.tld} or <realm>. They both depends on the vehicle brand and should be retrieved on your side based on the vehicle VIN. Available realms are:

Manufacturer Brand {brand.tld} <realm>
Peugeot peugeot.com clientsB2CPeugot/OTPPeugeot
Citroën citroen.com clientsB2CCitroen/OTPCitroen
DS driveds.com clientsB2CDS/OTPDS
Opel opel.com clientsB2COpel/OTPOpel
Vauxhall vauxhall.co.uk clientsB2CVauxhall/OTPVauxhall

1️⃣ Vehicle Capabilities #

First of all, before enrolling an end user, you must make sure that the targeted vehicle is eligible to your application use case.

Indeed, as explained in Data Availability & Scopes, all vehicles are not able to generate all the data listed in API Reference. When data is not generated by a vehicle, the field or object is not outputted in the API response.

That’s why you need to verify that a vehicle is able to provide the data required for your application use case. It can be done using the capability API.

b2c-vhcl-capabilities

The capability API will return the technical capabilities of the vehicles in terms of datasets. These capability datasets are subsets of vehicles scopes. Relations between APIs endpoints, data & scopes can be found in the Data Catalog (each data has a “B2C Scopes” section) and the API Reference (APIs endpoints have an “authorization>scope” dropdown).

Using the response of the capability API, and knowing the required data for your use case, you can determine whether the vehicle is eligible to your service or not. If the vehicle is not eligible, you must not enroll the user under this use case, but you can find a fallback use case fitting the vehicle capabilities.

For example if your use case requires the vehicle location, but the scope data:position is not returned in the capability API response for this vehicle, then you should probably not offer your service to this user.

Technical capability & Vehicle eligibility: a data being available within the capability API doesn’t mean that the user is able to subscribe third party services, checkout vehicle services.

Capability API Example

Type Name Value Description Required
Path Parameter {vin} {vin} Vehicle VIN to check. Yes
Query Parameter client_id <client_id> Client Id of your application. Yes
Query Parameter extension onboardCapabilities Filters only capability data of this API. No
Header x-introspect-realm <realm> Vehicle manufacturer realm. Yes
File Client Certificate path/to/client_certificate.pem Your SSL certificate for authentication in Stelantis network. Yes
File Private Key path/to/key.pem Your Private Key file. Yes
File CA Certificate path/to/ca_certificate.pem Stellantis CA Cert for peer verification. Yes
1
2
3
4
5
6
7
8
9
$ curl \
  --request GET \
  --url 'https://api-cert.groupe-psa.com/connectedcar/v3/vehicle/{vin}' \
  --data-urlencode 'client_id=<client_id>' \
  --data-urlencode 'extension=onboardCapabilities' \
  --header 'x-introspect-realm: <realm>' \
  --key 'path/to/key.pem' \
  --cert 'path/to/client_cert.pem[:<cert_password>]' \
  --cacert 'path/to/ca_cert.pem' \
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT
Content-Type: application/json

{
  "createdAt": "2023-09-12T09:41:10.911Z",
  "vin": "VR1AB12C3D45678909",
  "motorization": "Thermic",
  "attributes": {...},
  "_embedded": {
    "extension": {
      "onboardCapabilities": {
        "data": [
          "data:telemetry", "telemetry:environment", "data:telemetry:privacy",
          "data:telemetry:vehicle", "data:telemetry:vehicle:ignition", "data:telemetry:vehicle:preconditioning",
          "data:telemetry:vehicle:energies", "data:telemetry:vehicle:engines", "data:telemetry:vehicle:doorsState",
          "data:telemetry:vehicle:powertrain", "data:telemetry:vehicle:battery", "data:telemetry:vehicle:safety",
          "data:telemetry:vehicle:odometer", "data:telemetry:vehicle:kinetic", "data:telemetry:vehicle:transmission",
          "data:telemetry:vehicle:adas", "data:telemetry:vehicle:lightingSystem", "data:telemetry:vehicle:maintenance",
          "data:telemetry:vehicle:drivingBehavior", "data:telemetry:vehicle:wipingBlades", "data:telemetry:vehicle:alarm",
          "data:position", "data:trip", "data:alert", "data:collision"
        ],
        "remote": {
          "operation": [
            "remote:door:write", "remote:preconditioning:write",
            "remote:horn:write", "remote:charging:write",
            "remote:lights:write", "remote:wakeup:write", "remote:navigation:write"
          ],
          "details": {
            "charging": {
              "supported": true, 
              "immediate": { "start": true, "stop": true },
              "schedule": { "programs": {"supported": true,"size": 0}, "nextDelayedTime": true },
              "preferences": { "level": true, "type": true }
            },
            "preconditioning": { "supported": true, "programs": { "size": 0 } },
            "door": { "supported": true },
            "horn": { "supported": true },
            "lights": { "supported": true },
            "wakeup": { "supported": true },
            "navigation": { "supported": true }
          }
        }
      }
    }
  }
}

b2c-enroll-users

2️⃣•1️⃣ Authorization Request #

In order to enroll an End User, you have to trigger the following HTTP authorization request from your App in the End User device. This process is called OAuth2 Authorization Code Flow and is widely defined in RFC 6749.

This will prompt an external user agent (web view) where the End User will be able to log in and authorize access to its Stellantis account. The following parameters are required:

Scopes: scopes are sets of data that a Ressource Owner can share to a third-party application through this authorization process. List of scope is available here. You can also browse individual data and their scopes in the Data Catalog.

Type Name Value Description Required
Path Parameter {brand.tld} {brand.tld} Vehicle manufacturer brand.tld. Yes
Query Parameter client_id <client_id> Client id of your application. Yes
Query Parameter response_type code Use OAuth2 Authorization code flow. Yes
Query Parameter scope <scope1>,<scope2>,etc. The list of scopes requested to your customer (Ressource Owner). Yes
Query Parameter redirect_uri <redirect_uri> The URI address to redirect the user at the end of the authorization process. Yes
Query Parameter state <state> A state value generated on your side, it will be returned along with the Authorization Code in the redirection. Recommended, checking this parameter when it’s returned allows tracking and identifying the request, and prevent attacks. No
Query Parameter local <local> End user browser language (by default, en-GB). Possible values: ar-EG,cs-CZ,da-DK,de-AT,de-CH,de-DE,en-AU,en-EG,en-GB,en-IE,en-JP,en-MT,en-NZ,en-ZA,es-AR,es-CL,es-ES,es-MX,et-EE,fi-FI,fr-BE,fr-CH,fr-DZ,fr-FR,fr-LU,fr-MA,fr-MQ,fr-RE,fr-TN,hr-HR,hu-HU,it-CH,it-IT,nb-NO,nl-BE,nl-NL,pl-PL,pt-BR,pt-PT,ru-RU,ru-UA,sk-SK,sl-SI,sv-SE,tr-TR,uk-UA,ar-DZ,ar-MA,ar-PS,el-GR,es-CO,es-PE,fa-IR,fr-GF,fr-GP,fr-MG,he-IL,ja-JP,ko-KR,en-PS,ar-SA,en-SA,ar-AE,en-AE,en-SG,es-UY,en-MY,en-BN,ro-RO,mk-MK,es-EC,en-ES,fr-LB,en-LB,bg-BG,sr-RS,en-CY,fr-NC,es-CR,es-PY,is-IS,en-IS,fr-SN,en-IN,en-JO,en-MU,lv-LV,lt-LT,zh-TW. No
GET | https://idpcvs.{brand.tld}/am/oauth2
/authorize
1
2
3
4
5
6
7
8
9
$ curl \
  --request GET \
  --url 'https://idpcvs.{brand.tld}/am/oauth2/authorize' \
  --data-urlencode 'client_id=<client_id>' \
  --data-urlencode 'response_type=code' \
  --data-urlencode 'scope=vehicle_read,remote_write' \
  --data-urlencode 'redirect_uri=<app_callback_uri>' \
  --data-urlencode 'state=<state>' \
  --data-urlencode 'local=<local>'

2️⃣•2️⃣ Prompt Triggering #

Before the Authorization prompt is triggered, Stellantis performs the following checks on the request:

  • The App should be registered and the should exists.
  • The requested scopes should match the one declared in the App Registration.

In cases these checks are performed successfully, the redirection prompt is sent to the End User device triggering an external user agent (web view) on the End User device.

2️⃣•3️⃣ User Inputs #

When the external user agent authorization prompt is triggered by the End User device, you have no more access to the authorization process.

In the prompted external user agent (web view), the user will follow the consent process to give access to its Stellantis Account.

The journey of the End User in the prompted external user agent is detailed in the User Journey section.

User Journey: if not previously performed for another Stellantis Connected service, the following steps are required on the End User side before being able to authorize access to its Stellantis Account:

  • Create an account in Stellantis system.
  • Link a vehicle to the account.
  • Enable connectivity for partners (accessing party).

Closing the App: If the user needs to leave your application during the previous process, the authorization request might fail. In this case, the user needs to restart the authorization process from the start.

2️⃣•4️⃣ Authorization Code #

In the OAuth2 Authorization Code Flow, after the Resource Owner consent to share access to its data, an automatic redirection is triggered to the Accessing Party URI. This restriction includes an Authorization Code as an URI parameter. This Authorization Code can be exchanged against an Access Token.

If the account is set up properly (check out step 2.3), and the End-User authorizes your App to access the requested scopes, the redirection URI contains the Authorization Code.

The redirect URI will contain the following data as parameters:

  • The list of scopes authorized by the user
  • State value generated on your side from the initial request, for your own use.
  • The authorization code, this code should be exchanged for an access token.

State: This state is for your own use. In order to mitigate CSRF attacks, you should make sure that the state value received in 2.3 is a state value that you have generated in step 2.1.
This state being generated on your side, you can also use it to embed data in step 2.1 for future use in step 2.3. Doing so, it’s possible to track the request and to notify your user if a consent request has been initialized but not completed.

GET | https://idpcvs.{brand.tld}/am/oauth2
/authorize
1
2
3
4
5
6
HTTP/1.1 302 Found
Date: Day, XX Mon YYYY HH:MM:SS GMT
Location: http://<app_callback_uri>
          ?scope=ConnectivityForPartners,Remote
          &state=<state>
          &code=<code>

3️⃣ Access Token #

b2c-request-access-token

The Authentication Code obtained in step 2 should be exchanged against an Access Token.

Client Secret: Exchanging the authentication code for an Access Token must be done from your own server and not from the Ressource Owner device. Furthermore the API used for this purpose requires your Application Client Secret and this data must remain confidential.

This API allows requesting access token, it requires the following parameters:

Type Name Value Description Required
Path Parameter {brand.tld} {brand.tld} Vehicle manufacturer brand.tld. Yes
Header authorization: Basic <base64(<client_id>:<client_secret>)> Your App Client Id and Secret encoded in base64. Yes
Header content-type <application/x-www-form-urlencoded> Content type. Yes
Data grant_type authorization_code Use OAuth2 Authorization code flow. Yes
Data redirect_uri <redirect_uri> The URI redirection address. It must be the same as in step 2.1. Yes
Data code <code> The authorization code received in step 2.4. Yes
POST | https://idpcvs.{brand.tld}/am/oauth2
/access_token
1
2
3
4
5
6
7
8
$ curl \
  --request POST \
  --url 'https://idpcvs.{brand.tld}/am/oauth2/access_token' \
  --header 'authorization: Basic <base64(<client_id>:<client_secret>)>'\
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=authorization_code' \
  --data 'redirect_uri=<app_callback_uri>' \
  --data 'code=<code>' \
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT

{
  "scope": "openid profile",
  "expires_in": <time>,
  "token_type": "Bearer",
  "refresh_token": <refresh_token>,
  "id_token": <id_token>,
  "access_token": <access_token>
}

Renew Token #

In case the access token expires, you should renew the set of tokens using the following API call from your server.

This request requires that you still have access to the refresh token of step 3.

Type Name Value Description Required
Path Parameter {brand.tld} {brand.tld} Vehicle manufacturer brand.tld. Yes
Query Parameter realm <realm> Vehicle manufacturer realm. Yes
Header authorization: Basic <base64(<client_id>:<client_secret>)> Your App Client id and secret encoded in base64. Yes
Header content-type <application/x-www-form-urlencoded> Content type. Yes
Data grant_type refresh_token Token renewal. Yes
Data refresh_token <refresh_token> Refresh token received in step 3. Yes
POST | https://idpcvs.{brand.tld}/am/oauth2
/access_token
1
2
3
4
5
6
7
8
$ curl \
  --request POST \
  --url 'https://idpcvs.{brand.tld}/am/oauth2/access_token' \
  --data-urlencode 'realm=<realm>'
  --header 'authorization: Basic <base64(<client_id>:<client_secret>)>'\
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=refresh_token' \
  --data 'refresh_token=<refresh_token>' \
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT

{
    "scope": "openid profile",
    "expires_in": <time>,
    "token_type": "Bearer",
    "refresh_token": <refresh_token>,
    "id_token": <id_token>,
    "access_token": <access_token>
}

Revoke Token (Logout User) #

Type Name Value Description Required
Path Parameter {brand.tld} {brand.tld} Vehicle manufacturer brand.tld. Yes
Query Parameter realm <realm> Vehicle manufacturer realm. Yes
Header authorization: Basic <base64(<client_id>:<client_secret>)> Your App Client id and secret encoded in base64. Yes
Header content-type <application/x-www-form-urlencoded> Content type. Yes
Data token <refresh_toke OR access_token> Access or refresh token. Yes

You should implement a feature in your application if the End User wants to log out.

This action should lead to the revocation of the token:

POST | https://idpcvs.{brand.tld}/am/oauth2
/token/revoke
1
2
3
4
5
6
7
$ curl \
  --request POST \
  --url 'https://idpcvs.{brand.tld}/am/oauth2/token/revoke' \
  --data-urlencode 'realm=<realm>' \
  --header 'authorization: Basic <base64(<client_id>:<client_secret>)>'\
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'refresh_token=<refresh_toke OR access_token>' \
1
2
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT

Note: depending on your needs, it’s possible to revoke the access token or the renew token. Revoking a refresh token implies the revocation of all associated access token.

Access User Data

Once you have an Access Token, you can build the HTTP request to access this user data.