Quote-as-a-service Vehicles API Manual

Please be aware that this documentation is currently under construction and not up-to-date with the API specs.

What does it do?

It automates as far as possible the process of submitting the vehicle registration paper (aka "Fahrzeugschein") of a electric vehicle for consideration for the THG quote by the Umweltbundesamt (German Environment Agency). This includes parsing all required data from an image of the document and providing a current status. You therefore have to provide little more than the document.

The validation process

We are parsing the necessary data from the uploaded documents as far as possible automatically. As a human has to confirm the values, we cannot give an immediate result. As soon as the confirmation has happened our API will return (GET request) the parsed data (e.g. the full name of the vehicle owner) as well as a updated status and time stamp.

Our data models

Vehicle

A new vehicle (POST request) at first represents a validation task for us. For that you upload a vehicle document (Fahrzeugschein) as an image or PDF file of only the front. Once the vehicle document was parsed, we return that data as well as the final validation status of the vehicle.

Preparing the vehicle documents for the upload

Fetching vehicle information

Instead of fetching all your vehicles (GET /vehicles ) you may limit it by one or more criteria. The following list shows you an example for each filter:

Response Sample url Response
Vehicles of one certain status /vehicles?status=pending All vehicles that currently have the status pending.
Vehicles that received a status update since the given time stamp (in UTC format). /vehicles?changed_since=2022-01-01T13:00:00Z All vehicles that received a status update since 1PM on 1st of January 2022
Vehicles with a specific value in the field customer_reference1 /vehicles?=customer_reference1=ABC123 All vehicles that have ABC123 in the field customer_reference1
Vehicles with a specific value in the field customer_reference2 /vehicles?=customer_reference2=XYZ987 All vehicles that have XYZ987 in the field customer_reference2
Vehicles with a specific value in the field customer_reference3 /vehicles?=customer_reference3=ZZZ All vehicles that have ZZZ in the field customer_reference3

You can use more than one parameter at the same time by combining them with &.

Export

You can use the /vehicles/export end point to receive a CSV file of all your vehicles. Optionally you may set the same filters on the end point as described above.

Pagination

Important: For avoiding too slow requests we limit the /vehicles output by default to 100 records. You can send the integer parameters page and size with your request to slice and paginate your records. The size indicates the maximum number of items to return. Currently we limit these to 5000 records per page. The page indicates the page number in relation to the complete set of unpaginated items.

Example: You have 110 vehicles in total and want to fetch all with pages of 100 each. Since they are more than 100 but less than 201 you will need two requests.

VehicleRegistrant

A vehicle registrant is an instance (person or organization) that is authorized to apply for the quota of one or multiple vehicles at the Umweltbundesamt. Two scenarios are possible:

  1. You manage the data of the vehicle owners on your side. In that case you can simply create a single vehicle registrant and link all vehicles to it.
  2. You need a storage for the data of the vehicle owners (address, etc.). In that case you can create a vehicle registrant for each person/organization that holds vehicles.

UBARequest

A UBA request represents your request for the UBA to approve a specific vehicle for a specific year. You can create UBARequests:

You can either wait for your vehicle to be successfully validated to be sure that you only create UBARequests for vehicles that are actually eligible for the quota or if that makes your process easier you can already create a request directly after creating the vehicle.

Be aware that our system will automatically delete any existing UBARequest after parsing new documents if it is no longer deemed eligible. E.g. when the same vehicle document was uploaded a 2nd time or a previous owner already requested it.

As we communicate with the UBA in bulk quantities, creation of a UBARequest does not allow foreseeing when it will be sent to the UBA nor when we will receive a reply. You can however always fetch the record's information about the communication with the UBA (see UBAResponse).

You can put a hold on the start of the UBA process when you pass a future timestamp like ready_for_uba=2022-01-01T00:00Z on creation. This can be useful if your process requires to wait for e.g. the passing of the mandatory revocation period (aka Widerufsfrist). The UBA process on our side will then exclude the requests from submissions to the UBA until the timestamp has arrived.

Please also include an additional time buffer as a considerate amount of time may pass between a owner informing you of revoking this vehicle and you disabling the request via our API (see DELETE endpoint).

For time being you can set ready_for_uba only at creation and not modify it later on.

UBAResponse

An UBA response represents the reply from the UBA for a specific vehicle for a specific year. If no communication was sent/received you will only get a 404 status code response. Otherwise the response includes the time stamp of when we submitted the data to the UBA and when we received a reply from the UBA. The field confirmed shows you the outcome of the corresponding request:

Filtering responses

You can add filter parameters to the url to limit the records being returned:

When the same vehicle is reported in multiple years each vehicle/year will be returned as list item.

Don't forget that the given time stamp is expected in UTC format with a trailing Z.

Customer reference fields

The models Vehicle and VehicleRegistrant provide fields (customer_reference respectively customer_reference1, customer_reference2 and customer_reference3) where you may store any string value e.g. an unique identifier from your database system. They can later be used in GET requests to look up specific vehicles, if you cannot mirror our database ids to your system.

Note: To leave as much flexibility as possible to our clients, this column can hold duplicate values (no uniqueness).

FAQ

Is there a demo account? / May I use an additional account as a test environment / sandbox?

Yes, we can create a empty test account for you and optionally set up a demo data set (e.g. vehicles of different status). There you can try out all API end points. You may continue using this test account once you have moved to a production account for further testing your implementation.

How does the authentication work?

For our service to be able to acknowledge and process your requests, a token has to be sent along. To increase security the token only has a limited validity. You can try it out directly in your browser as soon as you have an account. We prepared a step-by-step guide for the browser UI.

How can I try out the API requests in my browser?

Simply contact us for a test account or when you sign up to our service, provide us with one or more email addresses for setting up user accounts.

  1. Click on the "Authorize" button on the API page.
    Screenshot with Authorize button marked
  2. Tick the check box for user_impersonation and click on "Authorize".
    Screenshot with Microsoft Authorization popup
  3. Fill in your email address (if you have logged in before, this will be skipped).
    Screenshot with Microsoft cloud login
  4. Fill in your account password (if you have logged in before, this will be skipped).
    Screenshot with Microsoft cloud login II
  5. Click on "Close".
    Screenshot after successfull login
  6. When the padlock icon is closed now, you are ready to try out the API requests from your browser.
    Screenshot after successfull login II
  7. Expand the section of /vehicleregistrant and click on the button "Try it out".
    Screenshot of vehicleregistrant POST
  8. You can directly edit the sample JSON data before you send it to the server. Make sure that the email address here has not yet been used. Then click on the "Execute" button.
    Screenshot of vehicleregistrant POST data
  9. Now we can see the response of the server:

What are access tokens and refresh tokens?

How can I obtain access and refresh tokens for automated processes?

We utilize the Microsofts OAuth 2.0 device authorization grant flow specifically for devices with limited or no input capabilities. Have a look at the following sample snippets. They require two basic terminal tools: curl and jq. Of course you may simply adapt the code for any other tool that can make HTTP requests and read JSON data.

You can download the entire bash sample script comprising of an extended version of the three snippets here. It will fetch tokens and use them to create a new vehicle registrant.

Code snippet 1: Getting access and refresh token:

This bash snippet shows you how you can obtain token after a one-time human login that afterwards allows your script/automation to obtain further tokens without human interaction. For security this standard does not allow obtain tokens without prior human login.

#!/bin/bash

client_id=34b0a670-0f53-4772-bc68-c77b3be58e8e
tenant_id=f7731598-1da9-4b5f-a888-ceaf1d14fae0

fetch_token () {
    response=$(curl -s https://login.microsoftonline.com/${tenant_id}/oauth2/v2.0/devicecode \
        -d 'scope='$client_id'/user_impersonation offline_access&client_id='$client_id)

    device_code=$(printf '%s' "$response" | jq -r .device_code)
    user_code=$(printf '%s' "$response" | jq .user_code)

    echo "Enter the user code $user_code on https://microsoft.com/devicelogin"
    read -p "Press any key to continue afterwards."
    response=$(curl -s https://login.microsoftonline.com/${tenant_id}/oauth2/v2.0/token \
        -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id='$client_id \
        -d '&device_code='$device_code)

    access_token=$(printf '%s' "$response" | jq -r .access_token)
    refresh_token=$(printf '%s' "$response" | jq -r .refresh_token)

    echo "Access token: $access_token"
    echo "-------"
    echo "Refresh token: $refresh_token"
    echo ""
}

fetch_token
  1. Lets run it:

    ./snippet1.sh
    Enter the user code "DXXXXXXX9" on https://microsoft.com/devicelogin
    Press any key to continue afterwards.
    
  2. At this stage open https://microsoft.com/devicelogin and follow the instructions there:

Screenshot of Microsoft device login I Screenshot of Microsoft device login II Screenshot of Microsoft device login III

  1. Press any key in the terminal and the script will continue to obtain the tokens and output them on the screen:
    Access token: eyJ0eXAiOm9pZCI6clL7al69TWihRqH6U0fYPi...(shortened)...shyIjG
    -------
    Refresh token: 0.AV0AmBVz96kdX0uoiM6vHRT64HmsDRTD3JQ...(shortened)...CPLXm9
    

You can use these two tokens now in any of your automations/scripts without further human interaction.

Code snippet 2: Refreshing tokens

In the previous snippet we have obtained both an access and a refresh token. Since the access token will expire latest 60 minutes after obtaining it, we now show how to make use of the refresh token. You may obtain a new access token at any time, you don't need to wait for expiration of the previous one.

Add this to the sample snippet 1 and run it:

    response=$(curl -s https://login.microsoftonline.com/${tenant_id}/oauth2/v2.0/token \
         -d 'refresh_token='$refresh_token \
         -d '&grant_type=refresh_token&scope='$client_id'/user_impersonation offline_access')

    access_token2=$(printf '%s' "$response" | jq -r .access_token)
    refresh_token2=$(printf '%s' "$response" | jq -r .refresh_token)

    echo "Access token2: $access_token2"
    echo "Refresh token2: $refresh_token2"

It provides you with a new access token as well as a new refresh token. So by using the current refresh token, you or your integration can within 90 days get a new refresh token and therefore create an endless chain.

Code snippet 3: Fetching the vehicles list

Now we want to put the access token to actual use and fetch all verified vehicles of our account. Add this to the snippet 1 (you don't need snippet 2 for this):

    curl -s https://qaas-vehicles.azurewebsites.net/vehicles?status=verified \
        -H "accept: application/json" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $access_token" | jq .

Here is a sample output of the response, a list of two vehicles as a list of JSON objects:

    [
     {
        "id": 45,
        "vehicleregistrant_id": 3,
        "owner_first_name": "Maike",
        "owner_last_name_or_organization": "Maier",
        "vin": "WVATZTE2ZMP020124",
        "vehicle_class": "M1",
        "status": "verified",
        "status_reason": "",
        "vehicle_registration": "2015-05-01",
        "status_update": "2022-05-17T09:01:09Z",
        "customer_reference1": "DEF456",
        "customer_reference2": "AB-54634534",
        "customer_reference3": "",
        "created": "2022-05-17T06:43:10Z"
      },
      {
        "id": 170,
        "vehicleregistrant_id": 37,
        "owner_first_name": "Max",
        "owner_last_name_or_organization": "Power",
        "vin": "WVAZZTE1ZMP020123",
        "vehicle_class": "M1",
        "status": "verified",
        "status_reason": "",
        "vehicle_registration": "2020-11-04",
        "status_update": "2022-06-23T07:09:52Z",
        "customer_reference1": "123ABC",
        "customer_reference2": "GUI2D2",
        "customer_reference3": "",
        "created": "2022-06-22T16:15:22Z"
      }
    ]

Is there a limit of how many requests I can make?

At the moment there is no limit. However please consider that our infrastructure scales up and down automatically based on the current traffic. It therefore cannot foresee a sudden peak when you start a bulk operation with thousands of requests. You most likely will then start seeing failing requests or timeouts. In your own interest avoid going from no traffic or base load traffic to a multitude of it.

I'm getting timeouts for my requests. What can I do?

The serverless infrastructure puts the service into a sleep mode when there is no or little traffic. In that case you may encounter that there is no server response even after 30 seconds. Please simply send out the same request a 2nd time. In the meantime the service should be booted up and ready again. We are working to avoid such situations as good as possible. Let us know if you face this problem.

How long does the validation take and when will I receive an update?

Since the validation involves human confirmation of the automated screening, we cannot give an exact timeline, but we try to provide an result within two working days or earlier.

Can I withdraw a vehicle e.g. because of a customer using their revocation period (Wiederufsrecht)?

For this use case you should create the UBARequest record with a future timestamp like ready_for_uba=2022-01-01T00:00Z. We will then wait for this timestamp to arrive until we sent it to the Umweltbundesamt. Once this was done it can not be revoked.

Can I edit a vehicle?

Yes, but only partially. The fields customer_reference1, customer_reference2 and customer_reference3 can be modified freely at any time with a PATCH request.

Can I delete a vehicle?

No, since we need to keep track of all your validation requests, we cannot support deletion of vehicles. If you want to submit the same vehicle for validation again after an unsuccessful attempt, simply create a new vehicle. In case you have a problem with a specific vehicle, please get in touch with us for support.

What do the different status of a vehicle mean?

Status Code Meaning
pending The vehicle has not started or completed the validation process yet. You can assume that it will leave this state within 2-3 working days.
verified The validation process has completed and was successful. Meaning that this vehicle can be submitted to the Umweltbundesamt
not_verified_unreadable The provided vehicle documents files were insufficient (e.g. badly cropped, unclear, too small to read)
not_verified_wrong_document_type The uploaded files are not the required documents. We need the Fahrzeugschein Teil 1.
not_verified_wrong_fuel_code The vehicle is not purely driven by electric power and hence not eligible for the THQ quota.
not_verified_other_reason Any other reason why the vehicle cannot be further processed. A specific reason is given in GET requests in the field reason_not_verified.

What do I do when a vehicle was not successfully validated?

In this case please review the reason given for the unsuccessful validation in the status field of the response. You may then chose to create a new validation request. The result of the previous request is final and cannot be edited or restarted.

How can I follow the process of my vehicles and react to updates?

You can specify a time stamp when requesting a vehicle list like: /vehicles?changed_since=2022-01-01T12:34:56Z The response will only hold vehicles that received a new status since the given time stamp.

Note: The time stamp has to be in the UTC format as shown in the example and end with a Z. Make sure that you don't simply use your system's time zone or you may unintentionally exclude data.

What is the meaning of the HTTP status codes that the API returns?

HTTP Code Methods Meaning Response
200 GET, PATCH Everything ok The requested data JSON list/dictionary / the edited record
201 POST Record created successfully The created record
204 GET Request was valid, but there is nothing to return (empty)
400 PATCH, POST Your request could not be processed. There was one or more errors with the submitted data The validation errors including the affected field names
401 (all) Missing or invalid token, you could not be authorized (empty)
403 (all) Invalid user, access denied (empty)
404 GET, PATCH Record not found (wrong id) (empty)
422 PATCH, POST Request not processable The reason for the error
500 (all) Unexpected server error, request was likely not processed (empty) Please contact us in this case

Can the API execute a webhook when a status update occurs?

Not at the moment, but we want to introduce this as a feature in the future.