Please be aware that this documentation is currently under construction and not up-to-date with the API specs.
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.
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.
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.
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 &
.
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.
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.
GET /vehicles?page=1&size=100
will return the first 100 vehicles (GET /vehicles
would implicitly behave the same)GET /vehicles?page=2&size=100
will retrieve the remaining 10 vehiclesA 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:
A UBA request represents your request for the UBA to approve a specific vehicle for a specific year. You can create UBARequest
s:
You can either wait for your vehicle to be successfully validated to be sure that you only create UBARequest
s 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.
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:
null
= the outcome is still pendingfalse
= no approval for the THG quota, see rejected_reason
for details.true
= approval for the THG quotaYou can add filter parameters to the url to limit the records being returned:
sent_changed_since=2022-05-17T09:01:09Z
= all vehicles which were sent to UBA since the given time stampreply_changed_since=2022-05-17T09:01:09Z
= all vehicles where a reply from UBA was received since the given time stampWhen 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
.
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).
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.
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.
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.
/vehicleregistrant
and click on the button "Try it out".Now we can see the response of the server:
curl
command that you can use in a terminal to trigger the same request again. As you can see the browser inserted the token obtained from the login automatically. Keep in mind that it is only valid for no longer than one hour.
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.
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
Lets run it:
./snippet1.sh
Enter the user code "DXXXXXXX9" on https://microsoft.com/devicelogin
Press any key to continue afterwards.
At this stage open https://microsoft.com/devicelogin and follow the instructions there:
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.
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.
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"
}
]
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.
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.
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.
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.
Yes, but only partially. The fields customer_reference1
, customer_reference2
and customer_reference3
can be modified freely at any time with a PATCH
request.
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.
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 . |
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.
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.
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 |
Not at the moment, but we want to introduce this as a feature in the future.