TandemDrive API
Welcome to the TandemDrive API guide. This document provides a high-level overview and complements the detailed API specification.
The TandemDrive API enables seamless integration of your systems with the TandemDrive platform. By utilizing our API, you can extend and automate various aspects of TandemDrive’s capabilities. To get started, API credentials can be obtained from “TandemDrive Console”.
It is essential to thoroughly review this guide to prevent any potential mistakes when working with our API. Understanding the best practices and guidelines will help you fully leverage the API’s capabilities and avoid certain pitfalls.
If you have any questions or need assistance, please don’t hesitate to reach out to our support team. We are here to ensure you implement the API correctly and maximize its efficiency for your specific use case.
Components
TandemDrive’s architecture consists of the following key components:
-
Main Backend: Handles all processing and data storage operations. This component powers the API’s described in this guide.
-
Console: A web interface for configuring, managing, and monitoring TandemDrive.
For ease of use during API development, the Console allows you to quickly view an entity’s unique ID. When viewing a single entity, simply press
Shift + Yto display its ID.
API Overview
Our API endpoints are grouped into two main categories:
-
Admin API
The Admin API is the core of our system, supporting a wide range of use cases, including data synchronization, automation, and system monitoring. It provides powerful tools for managing and interacting with your TandemDrive instance.
You can access the full specifications for this API here.
-
App API
The App API is specifically tailored for end user applications. It provides a set of dedicated endpoints that perform actions on behalf of end users, ensuring that data access is securely managed according to the user’s permissions.
If you are interested in accessing the App API, please contact our support team.
API Specification
API Changelog
The changelog references the specific TandemDrive product versions where changes
were introduced. You can find the current version on the About page of
TandemDrive Console. Additionally, all API responses include an X-TD-Version
header, which indicates the version of your TandemDrive instance. The API itself
is also versioned, but it uses a single version number that rarely changes.
- v3.55.0:
- Add some filter query parameters to the
GET /cpo-charge-session{,-reimbursement}endpoints.
- Add some filter query parameters to the
- v3.53.1:
- Fix erroneous documentation for
get /msp-roaming-command/{id}.
- Fix erroneous documentation for
- v3.53.0:
- In the responses of the
GET /{end_user_id}/msp-charge-session/{chargesession_id}and theGET /{end_user_id}/msp-charge-sessionendpoints:customer_totalhas been deprecated and replaced by thetotal_amount_excl_vat- and the
total_amount_incl_vatas been added.
- The
subtotalsfield in the response ofGET /{end_user_id}/msp-charge-sessionnow usesamount_excl_vatinstead of the deprecatedvalueand includes the new fieldvat_amount_unrounded.
- In the responses of the
- v3.52.0:
- The
POST /msp-token-subscription/{id}/token-fulfilmentendpoint now only allowsRFIDform factors (which arecardandkeychain) and no longer allows unknown form factors. - Add
energy_creditandmonetary_creditproperties to theGET /{end_user_id}/msp-token-subscription/{subscription_id}response.
- The
- v3.51.0:
- Stabilize
POST /charge-sessionendpoint and add the following fields:total_roaming_amount_incl_vat,total_roaming_flat_amount_excl_vat,total_roaming_energy_amount_excl_vat,total_roaming_time_amount_excl_vat,total_roaming_reservation_amount_excl_vat,session_idandconnector_format.
- Stabilize
- v3.50.0:
- In the
POST /charge-sessionendpoint: deprecateroaming_connectionin favor ofroaming_connection_id, deprecatetotal_cost_excl_vatin favor oftotal_roaming_amount_excl_vatand deprecatetotal_cost_incl_vatin favor oftotal_roaming_amount_incl_vat.
- In the
- v3.47.0:
- Add
connector_standardtoGET /{end_user_id}/msp-charge-session/{chargesession_id}endpoint.
- Add
- v3.46.0:
- Add
GET /msp-charge-cost-calculationendpoint. - Add the
POST msp-token-fulfilment/activate-tokenendpoint. - Added EVSE information to
GET /cpo-charge-session-reimbursement(and webhook).
- Add
- v3.45.1:
- Add
POST /cpo-reimbursement-arrangement/{id}/switch-planendpoint.
- Add
- v3.43.0:
- Add
seller_countytoGET /administrationresponse.
- Add
- v3.41.2:
- Fix specification of billing rollup group by in
GET /billing-boxandGET /billing-box/{id}
- Fix specification of billing rollup group by in
- v3.41.0:
- Add App API endpoints:
POST /{end_user_id}/msp-token-subscription/{subscription_id}/cancelPOST /{end_user_id}/msp-token-subscription/{subscription_id}/switch-plan.
- Add
deactivated_atto EVSE location. - Add optional
new_plan_external_referencetoPOST /msp-token-subscription/{id}/plan-or-customer-correctionrequest body.
- Add App API endpoints:
- v3.39.0:
- Fix specification of
GET /billing-box-delivery-stateto return list ofbilling_box_delivery. - Add
POST /msp-token-subscription/{id}/token-fulfilmentendpoint.
- Fix specification of
- v3.37.0:
- Promoted EVCO ID validation endpoint to “Stable”
- The endpoint is no longer in “Preview”.
- The response now includes a normalized ID for system integrations and a formatted ID for user display.
- Promoted EVCO ID validation endpoint to “Stable”
- v3.36.0:
- Add
billing_rollup_group_byto billing box export, andaverage_amount_per_kwh_excl_taxto rollup.
- Add
- v3.35.0:
- Add
POST /msp-token-subscription/{subscription_id}/plug-and-charge-vehicleendpoint. - Add
external_referencetobilling_box_delivery.
- Add
- v3.34.0:
- Remove
reservation_idandexpiry_datefrom the/msp-roaming-commandand/msp-roaming-command/{id}endpoints, since they are only used for unsupported roaming commands. - Add endpoints to manage billing box delivery state:
GET /billing-box-delivery-statePOST /billing-box-delivery-statePOST /billing-box-delivery-state/{id}
- Remove
- v3.33.0:
- Remove deprecated endpoints, which are currently not used:
PUT /main/v1/cpo-evse/{evse_uid}/reimbursement-mandatoryPUT /main/v1/cpo-evse/{evse_uid}PUT /main/v1/cpo-evse-location/{location_id}PUT /main/v1/cpo-subscription-plan/{id}/{valid_from}
- Remove deprecated endpoints, which are currently not used:
- v3.30.0:
- add the
idand thecontextof the rollup item to theGET /billing-box/{id}endpoint.
- add the
- v3.28.0:
- Add
evse_uidandupdated_atproperties to theGET /cpo-dropout-case/{id}response. - Add an optional
reasonquery parameter to theGET /cpo-dropout-caseendpoint. - Add the
updated_atproperty to theGET /cpo-dropout-caseresponse. - Remove the following properties from the
GET /cpo-dropout-caseresponse:unknown_connector_idunknown_evse_uidunknown_location_id.
- Add
- v3.27.0:
- Add endpoints to manage calculation inputs on a reimbursement plan:
GET /cpo-reimbursement-plan/{id}/calculation-input,PUT /cpo-reimbursement-plan/{id}/calculation-input/{valid_from}/{component_type},PUT /cpo-reimbursement-plan/{id}/calculation-input/{valid_from}/custom/{var_name},DELETE /cpo-reimbursement-plan/{id}/calculation-input/{valid_from}/{component_type},DELETE /cpo-reimbursement-plan/{id}/calculation-input/{valid_from}/custom/{var_name}.
- Changed the endpoint
/msp-token-subscription/{id}/defunctso that also it’s token links are marked defunct. - Changed the endpoint
/cpo-reimbursement-arrangement/{id}/defunctso that also it’s EVSE/reimbursement links are marked defunct. - Changed the endpoint
/cpo-subscription/{id}/defunctso that also it’s EVSE/subscription links are marked defunct.
- Add endpoints to manage calculation inputs on a reimbursement plan:
- v3.26.0:
- Add a
codefield to thebilling_series. - Add the following fields to the response of the
GET /billing-box/{id}endpoint:billing_reviewset_codebilling_reviewset_idbilling_box_reviewset_sequencebilling_series_codebilling_series_external_referencebilling_series_id
- Add
GET /billing-review-setendpoint. - Add the
POST /msp-token-subscription/add-virtual-tokenendpoint.
- Add a
- v3.21.0:
- Add
addresseeto billing address in various endpoints.
- Add
- v3.20.0:
- Add the
customer_external_referencefield to theGET /cpo-charge-session-reimbursementandGET /cpo-charge-session-reimbursement/{id}endpoints. - Deprecate all “Replace-Update” PUT-endpoints and some related endpoints which are no longer necessary. See the migration guide for more details.
- Add the
party_external_referencequery parameter to theGET /billing-boxendpoint.
- Add the
- v3.18.0:
- Deprecate
preferred_locale_idsofbilling-boxofGET /billing-box/{id}endpoint. - Add
locale_idtobilling-boxofGET /billing-box/{id}andGET /billing-boxendpoints.
- Deprecate
- v3.17.0:
- Add new merge endpoints:
POST /end-user/{id}/authorization-customer/{customer_id}POST /end-user/{id}/authorization-msp-token-subscription/{subscription_id}POST /cpo-subscription-plan/{id}/{valid_from}
- Update
POST /msp-token/{evco_id}to support merging. - Add
charge_periodstoPOST /cpo-charge-session.
- Add new merge endpoints:
- v3.16.0:
- App API: Add a new billing box endpoint
GET /{end_user_id}/customer/{customer_id}/billing-box. - App API: Add a new billing document endpoint
GET /{end_user_id}/billing-box/{id}/document/{filename}.
- App API: Add a new billing box endpoint
- v3.15.0:
- Remove validation on
billing_phonefields, increase max length to 40.
- Remove validation on
- v3.12.0:
- Add two new merge endpoints
POST /cpo-evse/{evse_uid}POST /cpo-evse-location/{location_id}
- Add two new merge endpoints
- v3.11.0:
- Update the documentation of implicit fields. It should now be clearer how the API works with regard to optional fields.
- v3.10.0:
- v3.8.0:
- Add
total_roaming_amount_excl_vattocpo_chargesessionand deprecatetotal_sales_amount_excl_vat, and adapt thePOST /cpo-charge-sessionsimilarly.
- Add
- v3.6.0:
- Add
tariff_codeto{GET, POST} /cpo-chargesessionandGET /cpo-charge-session-reimbursement{/:id}endpoints.
- Add
- v3.4.0:
- Move:
PUT /msp-token-subscription/{id}/plan-or-customer-correctionPUT /msp-token-subscription/{id}/energy-creditPUT /msp-token-subscription/{id}/monetary-creditto:POST /msp-token-subscription/{id}/plan-or-customer-correctionPOST /msp-token-subscription/{id}/energy-creditPOST /msp-token-subscription/{id}/monetary-credit, for backwards compatibility the old versions remain functional.
- Move:
- v3.3.0:
- Extend the billing box filter of
GET /billing-boxandbilling-boxwithreviewset_id.
- Extend the billing box filter of
- v3.0.0:
- Add foreign currency totals to
billing_box(#5494).
- Add foreign currency totals to
- v2.79.0:
- Add the
billing_series_external_referencefield toGET /billing-box. - Add the
series_external_referencequery filter toGET /billing-box. - Add
connector_idproperty tomsp_roaming_command_start_session. - Add mandatory
time_zoneto/biling-custom-item.
- Add the
- v2.78.0:
- Add the
cdr_idandcpo_idtoGET /billing-box/{id}/cpo-charge-session-reimbursement. - Add the
cpo_cdr_idandcpo_idtoGET /billing-box/{id}/msp-charge-session. - Added a list of
roaming_agreementsfor each roaming partner to theGET /roaming-partnerendpoint.
- Add the
- v2.76.0:
- Volumes in
GET /billing-box/{id}are now maps with units as their keys.
- Volumes in
- v2.75.0:
- Added several new update endpoints to the admin API that allow updating only
the specified fields. This makes them more versatile for partial updates
and, in many cases, a better alternative to the corresponding
PUTendpoints.POST /billing-address/{id}POST /billing-direct-debit-mandate/{id}POST /customer/{id}POST /msp-token-link/{id}POST /msp-token-subscription/{id}POST /cpo-reimbursement-arrangement/{id}POST /cpo-reimbursement-link/{id}POST /cpo-subscription/{id}POST /cpo-subscription-evse-link/{id}POST /end-user/{id}
- Various changes to the
GET /billing-boxandGET /billing-box/{id}endpoints.
- Added several new update endpoints to the admin API that allow updating only
the specified fields. This makes them more versatile for partial updates
and, in many cases, a better alternative to the corresponding
- v2.74.0:
- Change
GET /msp-charge-session-pricedandGET /cpo-charge-session-reimbursementfrom optional fields to nullable.
- Change
- v2.73.0:
- Add explicit billing box state which also contains
readytobilling_boxschema. - Add
deferredtobilling_boxschema. - Adapt the
GET /billing-boxendpoint to the explicit billing box state and the newreadystate. - Add the
GET /billing-box/{id}endpoint, which retrieves a box and its details.
- Add explicit billing box state which also contains
- v2.72.0:
- Rename the
GET /billing-box/{id}/cpo-charge-sessionendpoint to/billing-box/{id}/cpo-charge-session-reimbursement. - Add the
GET /cpo-charge-session-reimbursement/{id}endpoint. - Add
vat_country,vat_kind,vat_percentageto thecpo_chargesession_reimbursement. - Add the
rate_label,rate_unitandrate_represent_as_percentagefields to the following endpoints:GET /msp-charge-session-pricedGET /msp-charge-session-priced/{id}GET /{end_user_id}/msp-charge-session/{chargesession_id}.
- Add
usernameproperty toenduser. - Add
mark_transferredquery parameter to/billing-box/{id}/finalizewhich if set totruemarks the box as transferred if not already done.
- Rename the
- v2.70.0:
- Rename the
GET /billing-box/{id}/cpo-charge-session-exportendpoint to/billing-box/{id}/cpo-charge-session, and addvat_countryfield. Thevat_countryfield has been added to this endpoint and in thesubtotalsfield, thelabelandunithave been removed, and thevat_kindfield has been added. - Rename the
GET /billing-box/{id}/msp-charge-session-exportendpoint to/billing-box/{id}/msp-charge-session. - Add the
GET /billing-box/{id}/cpo-charge-session-roamingendpoint. - Improve the documentation about eMobility identifiers, and EVSE ID and EVCO ID specifically. In practice we already accepted identifiers without separators, which is now better documented. In a future release we want to follow the recommendation by IDACS to not use separators in machine-to-machine communication.
- Rename
document_typeproperty ofbilling_seriestoprimary_document_type. - Add explicit billing box state which also contains
ready. - Adapt the
GET /billing-boxendpoint to the explicit billing box state.
- Rename the
- v2.69.0:
/billing-box/{id}/finalizewill not mark the boxtransferredif not already done./billing-box/{id}/transferredhas to be explicitly called.- Add the
GET /billing-boxendpoint. - Rename the
GET /billing-box/:id/msp-charge-session-exportendpoint to/billing-box/:id/msp-charge-session. In thesubtotalsfield of the endpoint,vat_rate_country_codeis renamed tovat_country,vat_rate_kindtovat_kindandvat_rate_percenttovat_percent. Thelabelandunitfields of thesubtotalsare removed.
- v2.66.0:
- Remove
detailsquery parameter from,GET /msp-charge-session-priced/{id},GET /msp-charge-session-priced/, endpoints. - Add the
GET /billing-product-categoryendpoint.
- Remove
- v2.64.0:
- Add the
GET /billing-box/{id}/documentendpoint. - Remove
connection_pub_idfromevse_group.
- Add the
- v2.63.0
- Add
vat_countrytobilling_box_msp_chargesession.yamlandvat_countryon it’ssubtotalis deprecated. - Add
vat_countrytomsp_chargesession_priced.yamlandvat_countryon it’ssubtotalis deprecated. - Add
vat_countrytomsp-charge-session.yamlandvat_countryon it’ssubtotalis deprecated.
- Add
- v2.61.0
- Add
rollup_group_bytobilling-box. - Deprecate
rollup_methodofbilling-box. - Add
billing_rollup_group_bytocustomer. - Deprecate
billing_rollup_methodofcustomer. - Add
plantorollup_group_by.
- Add
- v2.57.0
- Add
period_from,period_until,volumeanditem_countto rollup line ofbilling-box.
- Add
- v2.56.0
- Deprecate the
received_atfield onPOST /cpo-charge-session. - Add the
unitandlabelproperties tobilling_item_cpo_chargesession. - Add the
unit,var_nameandlabelproperties tobilling_box_msp_chargesession. - Add billing document upload to billing-box finalize endpoint.
- Add a
defunctproperty tobilling_direct_debit_mandate. - Add the
PUT /billing-direct-debit-mandate/{id}/defunctendpoint. - Certain identifier fields which were previously uppercased by TD, will be returned in their original casing. This change will happen in the next several API releases. Uppercasing these fields was an oversight from our side, since some parties expect us to return the original, unaltered value. These identifiers are still treated as being case insensitive (uniqueness and equality).
- Deprecate the
- v2.52.0
- Add
total_idle_secondsandremarkproperties tomsp_chargesession. - Add
evse_idto/msp-charge-session-progressresponse.
- Add
- v2.50.0
- Add new optional property
brand_codetomsp_tokenandPUT /msp-token/{evco_id}andPOST /msp-token/{evco_id}request bodies. - Add
codeproperty toGET /msp-token-brandresponse. - Make EVSE uid optional for
/msp-roaming-command/start-session. - Add session
status,total_energy_kwh, andlast_updateto/msp-charge-session-progressresponse. - Add
X-TD-Unknown-Fieldsheader.
- Add new optional property
- v2.48.0
- Add the
cpo_id,cpo_cdr_idandroaming_connectionproperties tomsp_chargesession_original. - Add
latitudeandlongitudetocpo_evse_location, add these fields as optional parameters toPUT /cpo-evse-location/{id}andPOST /cpo-evse-location. - Add the
publish_fromandpublish_untilproperties to the following endpoints:GET /cpo-subscription-planGET /cpo-subscription-plan/{id}GET /cpo-reimbursement-planGET /cpo-reimbursement-plan/{id}GET /msp-token-subscription-plan
- Deprecate the
valid_fromandvalid_untilproperties of theGET /cpo-reimbursement-planandGET /cpo-subscription-plan/{id}endpoints.
- Add the
- v2.47.0
- Add an optional
usedquery parameter to theGET /msp-tokenendpoint. - Remove
idproperty frommsp_token, since it could not be used anywhere. - Add optional
token_series_idandtoken_uid_type_idtoPOST /msp-token-subscriptionto also create a token when creating a subscription. - Add the
arrangement_external_referenceproperty tocpo_chargesession_reimbursement, deprecatingarrangement_reference. - Deprecate the
POST /cpo-evseendpoint. - Deprecate the
POST /cpo-evse-locationendpoint. - Add the
plan_external_referenceproperty tocpo_subscription. - Add an optional body containing an
external_updated_atproperty to the following endpoints:DELETE cpo-reimbursement-arrangement/{id}/valid-untilPUT cpo-reimbursement-arrangement/{id}/defunctDELETE cpo-reimbursement-link/{id}/valid-fromDELETE cpo-reimbursement-link/{id}/valid-untilPUT cpo-reimbursement-link/{id}/defunctDELETE cpo-subscription/{id}/valid-untilPUT cpo-subscription/{id}/defunctDELETE cpo-subscription-evse-link/{id}/valid-fromDELETE cpo-subscription-evse-link/{id}/valid-untilPUT cpo-subscription-evse-link/{id}/defunctDELETE msp-token-link/{id}/valid-fromDELETE msp-token-link/{id}/valid-untilPUT msp-token-link/{id}/defunctDELETE msp-token-subscription/{id}/valid-untilPUT msp-token-subscription/{id}/defunct
- Add an optional field
external_updated_atto the request body of the following endpoints:PUT cpo-reimbursement-arrangement/{id}/valid-fromPUT cpo-reimbursement-arrangement/{id}/valid-untilPUT cpo-reimbursement-link/{id}/valid-fromPUT cpo-reimbursement-link/{id}/valid-untilPUT cpo-subscription-evse-link/{id}/valid-fromPUT cpo-subscription-evse-link/{id}/valid-untilPUT cpo-subscription/{id}/valid-fromPUT cpo-subscription/{id}/valid-untilPUT msp-token-link/{id}/valid-fromPUT msp-token-link/{id}/valid-untilPUT msp-token-subscription/{id}/valid-fromPUT msp-token-subscription/{id}/valid-until
- Make the request body for the following endpoints optional:
PUT cpo-reimbursement-link/{id}/valid-untilPUT cpo-subscription-evse-link/{id}/valid-untilPUT cpo-subscription/{id}/valid-untilPUT msp-token-link/{id}/valid-untilPUT msp-token-subscription/{id}/valid-until
- Add an optional
- v2.45.0
- Add
GET,PUT/end-user/{id}/authorization/customerendpoints. - Add
GET,PUT/end-user/{id}/authorization/msp-token-subscriptionendpoints. - Add
GET /end-userandPOST /end-user/endpoints. - Add
GET /end-user/{id},PUT /end-user/{id}andGET /end-user-siteendpoints. - Add
GET /end-userandPOST /end-userendpoints. - Add an optional
form_factorproperty tomsp_token, addGET /msp-token-form-factorendpoint. - Add abbility to try API calls from within the documentation.
- Change
**STATE: PREVIEW**, to a previewx-badge.
- Add
- v2.42.0
- Add
GET /msp-dropout-case,GET /msp-dropout-case/{id},GET / cpo-dropout-case,GET /cpo-dropout-case/{id}endpoints.
- Add
- v2.40.0
- Add
GET /msp-roaming-command/{id},GET /msp-charge-session-progress, make roaming commands explicit in the URL.
- Add
- v2.39.0
- Add optional
party_idparameter toPOST /billing-custom-item. - Add optional
page_sizeparameter to routes support paging. - Add
linksproperty when retrieving data frommsp-token-subscription. - Add
GETandPOST/msp-token-seriesendpoint. - Add
POST /msp-token-evco-id/validateendpoint.
- Add optional
- v2.36.0:
- Rename
revisiontoreversioninCategory lineofbilling_box. - Add optional
box_category_descriptiontobilling_custom_itemendpoint. - Add
GET /msp-roaming-command,POST /msp-roaming-commandandGET /msp-roaming-command/{id}endpoints. - Add
PUT /cpo-evse/{evse_uid}/reimbursement-mandatoryendpoint. - Add
PUT /cpo-evse/{evse_uid}/dropout-all-chargesessionsendpoint. - Add the
admin_idproperty tobilling_box,cpo_chargesession_reimbursement,msp_chargesession_priced,cpo_reimbursement_arrangement,cpo_subscription,msp_token_subscription. - Prefer
400for any client error instead of using other HTTP status codes.
- Rename
- v2.32.0:
- Add a demo-mode to the MSP EVSE-group endpoint. This allows one to get demo responses when no EVSE information is available, such as when testing.
- v2.31.0:
- Add
PUT /msp-token-subscription/{id}/plan-or-customer-correctionendpoint.
- Add
- v2.30.0:
- Add
linksproperty to bothcpo_reimbursement_arrangementandcpo_subscription. - Add
DELETE /cpo-reimbursement-arrangement/{id}/calculation-input/{valid_from}/{component_type}andDELETE /cpo-reimbursement-arrangement/{id}/calculation-input/{valid_from}/custom/{var_name}, endpoints. - Add
bank_account_name,bicandibanproperties to/direct-debit-mandateendpoints. See the specifications of the endpoint on how this works.
- Add
- v2.29.0:
- Add
PUT /cpo-reimbursement-arrangement/defunct. - Add
GET /billing-series, addext_ref.support toGET /billing-series/{id}. - Add
GET /msp-charge-session-originalandGET /msp-charge-session-original/{id}. - Add
POST /cpo-charge-session. - Remove paging from
GET /cpo-reimbursement-plan. - Add
linksproperty when retrieving data fromcpo_reimbursement_arrangementandcpo_subscription. - Remove
token_evco_idfromPOST /cpo-charge-session.
- Add
- v2.27.0:
- Add
vat_kind,vat_percentandvat_countrytomsp_chargesession_priced_subtotal. - Add
external_updated_atto/cpo-evseendpoints. - Add upsert behavior to
PUT /cpo-evse/{evse_uid}. - Add connectors to
PUT /cpo-evse/{evse_uid},POST /cpo-evse,GET /cpo-evse,GET /cpo-evse/{evse_uid}. - The status code
422was sometimes used for validation errors while in many cases we used400. From now on we only use status code400when we can’t parse or validate the incoming data.
- Add
- v2.26.0:
- Add
remimbursement_vatfield to customers. - Add
buyer_referenceto MSP token subscriptions, CPO reimbursement arrangements, and CPO subscriptions. - Add
administration_locale_idtoGET /billing_box_transfer. - Add
locale_idtoGET /administration. - Add
external_updated_atto/cpo-evse-locationendpoints. - Add upsert behavior to
PUT /cpo-evse-location/{location_id}.
- Add
- v2.22.0
- Add
GET /cpo-reimbursement-link. - Add upsert behavior to
PUT /cpo-reimbursement-link. - Add
PUT /cpo-subscription-evse-link, incl. upsert behavior. - Add
invalidate_overlapping_mandateproperty toPOSTandPUTbilling-direct-debit-mandate. - Add
subscription_external_referenceproperty tomsp_chargesession_priced. - Add
chargesession_idto pricing results of MSP charge sessions.
- Add
- v2.21.0
- Add
PUT /msp-token-subscription/{id}. - Add
PUT /msp-token-link/{id}. - Add upsert behavior to
PUT /cpo-reimbursement-arrangement/{id}.
- Add
- v2.13.0
- Replace
POST /cpo-reimbursement-arrangement/{id}/calculation-inputwithPUT /cpo-reimbursement-arrangement/{id}/calculation-input/{valid_from}/{component_type}andPUT /cpo-reimbursement-arrangement/{id}/calculation-input/{valid_from}/custom/{var_name}. - Remove
DELETE /cpo-reimbursement-calculation-input/{id}, inputs can now be removed by explicitly setting avaluetonullusing the newPUTendpoints. - Change response
GET /cpo-reimbursement-arrangement/{id}/calculation-inputto return an array of calculation inputs.
- Replace
- v2.11.0
- Rename
total_excl_vattototal_amount_excl_vatofcpo_chargesession_reimbursement - Change
cpo_idofcpo_evse_locationto be optional
- Rename
- v2.8.0
- Add
charging_periodsto reimbursement.
- Add
- v2.7.0 2023-08-25:
- Add
GET /msp-token-subscription-planendpoint. - Add support for adjusting the page size (preview).
- Add support for determining the rate and tariff at an location (preview).
- Add
- v2.6.0 2023-08-10:
- Add
subtotal_incl_vatto rollup of billing box. - Fix specification of
latitudeandlongitudeproperties to be decimals.
- Add
- v2.1.0 2023-07-10:
- Add support for using prefixed external references to the
customer,billing-addressanddirect-debit-mandateendpoints. - Extend a number of request and response bodies with an
external_referenceor.._external_referenceproperty. - Make
valid_untilan optional parameter forPOST /billing-direct-debit-mandate. - Add optional
locale_idtocustomer. - Add
external_updated_attodirect_debit_mandate. - Add
GET direct-debit-mandate/{id}. - Add
PUT direct-debit-mandate, including upsert behavior. - Add upsert support to
PUT /billing-address. - Add
external_updated_attobilling_address, and ignore updates older than last observedexternal_updated_at. - Add
external_updated_attocustomer, and ignore updates older than last observedexternal_updated_at. - Add
GET billing_address/{id}.
- Add support for using prefixed external references to the
- v1.43.0 2023-06-14:
- Add
customer_id,currency_codetomsp-priced-charge-session. - Add
currency_codetocpo-charge-session-reimbursement. - Add
enduser/registerendpoint - Add
id,customer_id,currency_codetomsp-priced-charge-session. - Add
currency_codetocpo-charge-session-reimbursement. - Add
idtomsp-charge-session. - Add
GET /msp-charge-session-priced/{id}. - Add possibility to use
idasaftervalue forGET /msp-charge-session-pricedandGET /msp-charge-session.
- Add
- v1.39.0 2023-05-25:
- First end user endpoints (preview).
- v1.37.0 2023-05-11
- Further improvement on billing endpoints (preview).
- v1.36.0 2023-05-02
- CPO endpoints for EVSEs, reimbursements and charge sessions.
- Customer data has been extended with billing data (preview).
- Several billing endpoints (preview).
- v1.34.0 2023-04-07
- Finalized CPO arrangement related endpoints.
- v1.31.0 2023-02-21
- Add
GET roaming-partnerendpoint.
- Add
- v1.28.1 2023-01-04
- Remove
countryas required from MSP charge sessions. This was a mistake in the previous API specifications.
- Remove
- v1.26.0 2022-12-06
- Add
token_evco_idtomsp-priced-charge-session, addratetosubtotal. Addis_currently_valid, andcurrent_admin_idproperties tomsp-token. Add endpoints: GET customer/{id},GET msp-token/{evco_id}, containing alinksproperty, next to themsp-tokenproperties,GET msp-token-link/{id},GET msp-token-subscription/{id},PUT msp-token-link/{id}/defunct.- Remove pagination from:
GET administration,GET msp-token-uid-type,GET msp-token-brand, since the number of items returned from these endpoints will not reach the default page size. This is considered backward compatible because the paging URL parameters are optional. Do note that thepage_sizeproperty has been removed from the response object, since it is no longer needed.
- Add
- v1.24.0 2022-11-14
- Add
initial_admin_idtomsp-token.
- Add
- v1.23.0 2022-10-18
- Change the
valid_fromfield of anmsp_token_linkto be optional, deferring validity to themsp_token_subscription. - Add:
DELETE /msp-token-link/{id}/valid-from,DELETE /msp-token-link/{id}/valid-until,DELETE /msp-token-subscription/{id}/valid-until.
- Change the
- v1.21.0 2022-09-26
- Add
GET version,GET /msp-charge-session/{id}endpoints addX-TD-Versionheader. Add optionaldetailsparameter toGET /msp-charge-session-priced. Changeafterparameter value toevco_idforGET /msp-token. Addac_2phase_splittopower_type.
- Add
- v1.20.0 2022-09-14
- Rename the
streetproperty ofmsp-charge-sessiontoaddress, this is perceived as a non breaking change since no production implementations are present at the moment.
- Rename the
- v1.19.0 2022-09-05
- Add
time_zonetomsp-charge-session. Addreceived_attomsp-charge-session, fix several regular expressions and types in the schemas, fix parameter links formsp-token-uid-typeandmsp-token-brand.
- Add
- v1.17.0 2022-08-18
- Add endpoints for customers, MSP tokens, token subscriptions, token links, several setup related endpoints and alternate pagination.
- v1.7.0 2021-12-14
- Endpoints to retrieve MSP-side charge sessions.
TandemDrive general
Instance
A TandemDrive instance refers to a fully isolated deployment of the TandemDrive platform, for clients who have a Software-as-a-Service (SaaS) agreement. Each instance operates independently, ensuring complete data and operational isolation for its users.
Sellers
A seller is the entity responsible for offering electric vehicle (EV) services, which can include Mobility Service Providers (MSPs) and Charge Point Operators (CPOs).
Within a TandemDrive instance, multiple sellers can coexist, allowing for flexible service offerings and branding strategies.
Administrations
An administration in TandemDrive is a method of organizing and structuring data within a single instance. Administrations can be set up based on criteria like organization, country, or brand, creating distinct segments within the platform, each with its own operational rules. The use of separate administrations has several key implications:
- Each administration operates with a single currency.
- Billing configurations are unique to each administration. For instance, invoicing series are not shared between administrations.
- Administrations are linked to a specific seller, though multiple administrations can share the same seller.
- Subscription plans are managed independently within each administration.
- API access can be limited to one or more administrations, enabling tailored data visibility and security controls.
Some information can be shared across multiple administrations under certain conditions:
- MSP rate plans can be shared between administrations, provided they use the same currency.
- Customers are primarily associated with one administration to facilitate API segmentation but can hold subscriptions in multiple administrations. The billing for these subscriptions is processed according to the rules of its administration.
- The Roaming setup is shared across administrations.
Parties and Customers
In TandemDrive, parties represent entities (referred to as “accounts” or “relations” in other systems) that hold predefined roles, such as Customer or Roaming partner. Parties share a unified data structure, allowing them to participate in billing activities.
However, when using the TandemDrive Console or API, you often interact with more specific concepts like Customer or Roaming partner, rather than directly with the broader concept of a Party.
A Customer is a specific type of Party that utilizes one or more services managed within TandemDrive. These services might include token subscriptions, reimbursements, and charge point subscriptions.
Mobility Service Provider (MSP)
This chapter describes TandemDrive services where you act as an MSP.
Subscription
A subscription represents an agreement between a Customer and a Seller, allowing the Customer to charge their vehicle using the Seller’s services. Each subscription is tied to a specific Subscription Plan, which outlines the financial terms and conditions of the agreement.
Every subscription has a defined start date and time and may optionally include an end date and time when the subscription expires. During the subscription period, one or more tokens (such as charge cards) can be linked to the subscription. Each token can have its own start and/or end validity period.
Token Validity
A token is considered valid (i.e., able to initiate a charge session) when all of the following conditions are met:
- The token is linked to a subscription, and the token’s validity period is
currently active. Additionally, the token link must not be marked as
defunct. - The linked subscription is also within its validity period and is not marked
as
defunct. - The conditions set in the charge authorization rules must permit the token to initiate a charge session.
For more details, see “validity periods”.
A token can only be associated with one subscription at any given time, regardless of the subscription’s validity. To transfer a token from one subscription to another, the following steps must be taken:
-
Set a
valid_untildate on the current token link.It’s recommended to choose a future date to avoid a brief period where the token becomes inactive.
-
Create a new token link for the new subscription, using the same token, and set a
valid_fromdate.To ensure a seamless transition, you can set the
valid_untildate of the old token link and thevalid_fromdate of the new token link to the same value.
For corrections, including retroactive changes, use the
/msp-token-subscription/{id}/plan-or-customer-correction endpoint. This
handles subscription corrections, including moving tokens and repricing charge
sessions, with minimal manual intervention.
Subscription plan
A subscription plan includes a name that should clearly reflect how the plan is marketed to customers.
The publishing period of a subscription plan indicates the intended timeframe for when new subscriptions can be initiated. However, TandemDrive does not enforce this property when creating new subscriptions.
Each subscription plan specifies the rate plan used to calculate the pricing for charge sessions.
Additionally, a subscription plan can include recurring costs, though this is optional.
Subscription plan: use customer reimbursement VAT
An MSP subscription plan includes a property called “Use Customer Reimbursement VAT”, which can be enabled to apply VAT exempt to charge sessions in certain cases. When this property is enabled, the following rules apply:
-
For every charge sessions which is being priced (on the MSP side) we will check if a matching local EVSE can be found (on the CPO side). When no EVSE was found then the normal VAT determination will be used.
-
Valid reimbursement arrangements for the identified EVSE are determined based on the charge session’s start date and time.
-
When exactly one arrangement was found, then the “reimbursement VAT” property of the customer of that arrangement will be used to determine the VAT. This property can have the following values:
-
Applies: The normal VAT determination applies.
-
Exempt: The charge session is exempt from VAT.
-
Unknown: The charge session will drop-out as VAT cannot be determined. This case can be solved by changing the “Reimbursement VAT” property of the customer to “Applies” or “Exempt”.
-
-
When no arrangement was found, then the “reimbursement mandatory” property of the EVSE will determine what happens. If the “reimbursement mandatory” is enabled then the charge session will drop-out. If this property is disabled then the charge session will have the normal VAT determination applied.
-
When multiple arrangements were found, then the charge session will drop-out because this situation is currently not supported by TandemDrive. With multiple arrangements it’s potentially confusing if VAT should be applied or not.
-
Rate plan
Rate plans define the pricing structure for charge sessions. Each rate plan is tied to a specific currency and can be shared across multiple administrations, as long as these administrations use the same currency. This allows businesses to use a consistent rate setup across administrations.
Key components of a rate plan:
-
Rates:
- Specify pricing for various components like energy consumption (kWh), charging time (minutes), or flat fees per session.
-
Rate Rules:
- Determine the conditions under which specific rates are applied.
- Rules are sorted from most specific to most generic.
- The first matching rule determines the rate used to price a charge session.
Advanced Pricing:
For complex pricing scenarios, scripts can be used to implement custom pricing logic. Scripts can be used to set annotations based on custom conditions, and scripts can be used to calculate subtotals for custom rate components. Contact TandemDrive support for assistance with script development.
Charge Sessions
Incoming charge sessions undergo basic validation checks to ensure data integrity. If a charge session fails these checks, it will be categorized as an Invalid charge session, along with the technical reason for its rejection.
If the session passes validation, it proceeds to the pricing phase. During this process, the system determines the applicable subscription and roaming partner, and the associated rate plan is used to calculate the appropriate rates.
Various pricing checks are applied, which can be customized based on administration settings and roaming partner configurations. If pricing is successful, a “pricing result” is generated for the charge session. If there are issues, the session is flagged in a drop-out case for further review.
Charge sessions can be repriced, where the original pricing result is preserved, and a new one is added. As a result, a single charge session may have multiple pricing results. The pricing result endpoint returns these records in chronological order, based on when each pricing result was created.
Each pricing result is assigned a version number. The first pricing result
has version = 1, and subsequent results increment the version number for that
specific charge session.
It’s important to note that a “pricing result” and a “charge session” are distinct entities, each with its own unique ID.
Drop-outs
Charge sessions that cannot be priced are stored in Drop-out Cases. Each case groups charge sessions together that failed for the same reason and share similar properties. The system allows for the reprocessing of these charge sessions, with the option to include additional processing instructions.
Resolving a drop-out case may require changes in the setup, such as adjusting a rate plan or creating a roaming agreement. In many instances, retrying the session after making these adjustments is sufficient. In other cases, it may be necessary to include specific instructions, such as bypassing certain checks or discarding the charge session entirely.
For traceability, resolved drop-out cases are retained for some time.
Charge credits
A token subscription can be assigned charge credits, which can be used to pay for charge sessions. These credits can be allocated either as monetary value or energy (kWh). Once credits are assigned, they will automatically be used for all charge sessions until the credits are fully depleted. After that, the customer is charged as usual. Currently, customers cannot be restricted to pay exclusively with credits (prepaid-only is not supported).
The way credits are applied depends on the components of the rate being charged. Some components, like parking fees, can be excluded from credit usage. Each component also specifies how credits are applied when the customer’s credit balance is insufficient to cover the full session.
Monetary credit values are calculated excluding VAT.
Charge Point Operators (CPO)
This chapter describes TandemDrive services where you act as a CPO.
Charge points and locations
An EVSE (Electric Vehicle Supply Equipment) is an independently operated and managed component of a charging station that can provide energy to one electric vehicle (EV) at a time. Sometimes, EVSEs are informally referred to as “charge points”, but this term can also be used more broadly to refer to entire charging stations.
Each EVSE has a defined location, specifying the geographical place where it is installed. A single location can contain multiple EVSEs.
The “EVSE UID” uniquely identifies an EVSE within the CPO’s platform. Do not
confuse this with the “EVSE ID”. The “EVSE ID” can be reassigned between
different EVSEs. EVSE IDs typically look like this: FR*EDF*E2542AX8769, with
the * separator being optional.
An EVSE can have one or more connectors, which are the sockets or plugs used to connect and charge an electric vehicle.
For more information, see our guide on E-Mobility identifiers.
Safe EVSE configuration
Adding and configuring an EVSE in TandemDrive during active charge sessions can
lead to unexpected behavior. To mitigate this risk, enable the
dropout_all_chargesessions property for the EVSE. This action will gracefully
move incoming charge sessions associated with that EVSE into a dropout case.
After the EVSE setup is fully complete, use the PUT /main/v1/cpo-evse/{evse_uid}/dropout-all-chargesessions endpoint to disable
dropout_all_chargesessions. Use the reprocess_dropouts attribute to ensure
that all previously dropped charge sessions are automatically retried.
Charge sessions
TandemDrive processes incoming charge sessions using the following steps:
-
Basic checks
If a charge session lacks critical information or is malformed, TandemDrive will reject it. When rejected via our API, the response will indicate the specific issue.
If a charge session is received via other methods (such as OCPI) and is invalid, it will be stored as an “invalid charge session”. The same charge session can later be resubmitted with corrected data, and any matching “invalid charge session” will be marked as resolved.
-
Match location, EVSE and connector
Before further processing can occur, TandemDrive must match the EVSE mentioned in the charge session to one in its repository. If a matching EVSE is not found, applicable reimbursement arrangements or roaming agreements cannot be determined.
This step will be retried multiple times. If unsuccessful after repeated attempts, the charge session will be marked as a drop-out, with a drop-out reason such as “Location not found,” “EVSE not found,” or “Connector not found”.
-
Validity checks
Several checks are performed to verify the validity of the charge session, such as:
- Is the session start time earlier than the end time?
- Is the amount of energy consumed reasonable?
- Is the session end time in the past?
- Is the charge session too old?
If any of these checks fail, the charge session is added to a drop-out case. Many thresholds for these checks can be configured.
-
Reimbursements
All reimbursement arrangements valid at the start of the charge session will be applied. Reimbursement amounts are calculated, and the appropriate VAT is determined. If this process fails, the charge session is added to a drop-out case.
Reimbursements can be calculated in two ways, using a setting on the reimbursement plan:
-
Using the CPMS provided reimbursement.
-
Using TandemDrive to calculate the reimbursement.
This setup involves the following concepts:
Cost Components: The specific cost components available for calculations, as defined by the reimbursement plan.
Calculation input: The actual prices associated with each cost component. Calculation inputs can be set in either the reimbursement plan or the individual reimbursement arrangements. The inputs for the arrangement take precedence over the inputs for the plan for the same cost component.
For more complex setups scripts and custom components can be used. Contact TandemDrive support for assistance with script development.
Each customer in TandemDrive has a ‘Reimbursement VAT’ property to determine VAT applicability for reimbursements. This property may also affect the VAT applicability on the MSP side to ensure VAT exempt is applied to both “sides” of a charge session.
-
-
Roaming
TandemDrive calculates the roaming costs that you, as the CPO, charge to the MSP. To activate roaming calculations and billing, this functionality must first be enabled in your TandemDrive instance.
Any roaming agreements valid at the start of the charge session are identified. Roaming costs are then calculated, and the appropriate VAT is determined. If this process fails, the charge session is added to a drop-out case.
Roaming partners generally receive one invoice per billing period, and, if there are items involving different VAT jurisdictions, separate invoices for each VAT country.
Subscriptions
A CPO subscription can be used to charge recurring fees for services such as charge point hosting and maintenance.
A CPO subscription plan includes a name that clearly reflects how the plan is marketed to customers. Subscriptions are managed separately from reimbursement arrangements.
A subscription plan can include recurring costs, which may be billed either per subscription or per linked, valid EVSE.
The publishing period of a subscription plan indicates when new subscriptions can be initiated. However, TandemDrive does not enforce this restriction when creating new subscriptions.
Roaming
In the context of Electric Vehicles (EVs), roaming refers to the cooperation and data exchange between different EV service providers. This ensures that EV drivers can use any charging station with minimal technological, financial, or legal barriers.
Roaming Connection
A roaming connection is a technical link with an external roaming platform. This platform can represent a single roaming partner (peer-to-peer) or multiple partners (a hub). Roaming connections rely on communication protocols, often using OCPI (Open Charge Point Interface). Roaming connections are sometimes also used for internal charge sessions, where the MSP and the CPO are the same organization. TandemDrive operators manage the technical configuration and infrastructure of these roaming connections.
Roaming Partner
A roaming partner is an entity with whom you can establish a roaming agreement. This partner can be either an MSP, a CPO, or both. Roaming partners exchange data such as authorized charging tokens, charge station information and charge session details. A roaming partner can have multiple CPO ID’s and/or multiple MSP ID’s.
Roaming Agreement
A roaming agreement is a formal contract between different EV service providers, such as MSPs and CPOs, that allows customers from one provider to access the charging infrastructure of another.
Roaming CPO ID and MSP ID
Each roaming partner is identified by one or more “party IDs”, which are registered with EV identifier providers. These IDs consist of a country code followed by three characters (e.g., NL-TDR), with an optional separator between the two. These party IDs are crucial as they are used to assign tokens and charge points, enabling roaming partners to communicate with the correct entity.
Billing and VAT
TandemDrive organizes billable items, like charge sessions, into billing boxes, each representing the contents of a (future) billing document. These boxes are then grouped into review-sets, allowing users to review, approve, and invoice related boxes all at once.
When (almost) all billable items for a given period have been received, it’s time to close the corresponding review-set. Once closed, you can review each box within the set. If any box appears to have an issue, you can mark it as “deferred”, enabling you to approve the rest of the review-set while deferring specific boxes until they are ready. Based on your configuration, TandemDrive will proceed to generate billing documents and mark the approved boxes as “finalized”. Deferred boxes can be approved individually when they are ready for invoicing.
A more in-depth explanation of the individual concepts is provided in the sections below.
Positive and negative monetary amounts
Note: This section applies only to billing API endpoints. Other endpoints may interpret monetary amounts differently.
In TandemDrive, a “seller” (MSP or CPO) provides services to customers and roaming partners via the TandemDrive platform.
-
Positive amounts: indicate revenue to the seller (e.g., customer charging fees).
-
Negative amounts: indicate expenses from the seller (e.g., energy reimbursements or reverted charging fees).
Exception: Some billing documents generated by TandemDrive, such as purchase
orders or self-billing invoices, may display negated monetary amounts. This
negation only applies to these documents, while the API retains the original
amounts. When this occurs, the negate_document_amounts property in the billing
box is set to true.
Review-sets
Billing boxes are organized into review-sets, with all boxes in a review-set belonging to the same billing series. A review-set groups billing boxes from the same invoice period. These sets are automatically created when a new period begins (e.g., a new month, quarter, or year). Review-sets can be closed and approved via TandemDrive Console. Approval should only occur after the review-set is thoroughly reviewed, as invoicing begins immediately upon approval.
Besides closing and approving all billing boxes of one review-set in one go, it’s also possible to close and approve a single billing box via TandemDrive Console.
Billing boxes
In TandemDrive, a billing box represents the contents of a billing document, such as an invoice, debit note, or credit note. It is used to gather billable items for a specific party (a customer or a roaming partner), and is tied to a particular invoicing period.
A billing box contains one or more roll-ups, which correspond to the lines that will appear on the invoice. Each roll-up has unique properties, such as product category and VAT kind, ensuring that only similar items are grouped together.
Billable items, such as a charge session, are referred to as billing items. A billing item can have one or more sub-items, representing different cost components. For example, a charge session might include sub-items for energy costs and a one-time fee.
Items with different VAT countries are stored in separate billing boxes.
Box state
A billing box in TandemDrive typically progresses through the following states:
-
Open: TandemDrive can continue adding billing items to this box. It remains open until manually closed.
-
Closed: No new items will be added by TandemDrive. However, manual changes are still possible through the API or Console. The “closed” state allows the box to be reviewed before approval.
-
Approved: The box has been approved, either via the API or Console. Only closed boxes can be approved. Once approved, the box is ready for generating the final billing document, such as an invoice, credit note, or debit note. Document generation can be done either by TandemDrive or an external system.
-
Ready: TandemDrive is done generating documents and the box is ready for invoicing.
-
Finalized: The primary billing document has been created, and the invoice code and date have been set. Only approved boxes can reach this state. If TandemDrive generates the document, it will be available for download.
Transferring boxes
Typically, you’ll want to copy the contents of a billing box into a financial system, such as an invoicing or bookkeeping system.
You can use our API to request boxes that haven’t been transferred yet. Once you’ve successfully stored the box’s content in your system, use the API to mark the box as “transferred”. This process works as a queue, with documents ready for transfer. To prevent duplication, process each box individually, ensuring no box is transferred more than once.
Only approved boxes should be transferred, as boxes that haven’t been approved are still subject to change.
The “transferred” state is distinct from the “finalized” state, as the timing of transfer may vary based on your specific use case. For example:
-
If TandemDrive generates the primary billing document and you’re transferring it to a bookkeeping system that also needs to store the document, the transfer should occur after the box is finalized.
-
If invoices are created outside of TandemDrive, you may want to transfer the box immediately after approval. Later, once the invoice is created, you can mark the box as finalized.
Deferred
The “deferred” state signifies that the box is not yet ready for invoicing. TandemDrive Console users have the option to manually mark boxes as “deferred.” When a box is deferred, it will not be included in the approval process of its associated review-set.
Use this state if there are issues with the box that need resolution before proceeding.
Once any issues with a deferred box have been resolved, the deferred state can be removed, allowing the box to be approved individually.
Withhold
A box may be placed in the “withhold” state to prevent TandemDrive from generating the primary billing document (e.g., the invoice PDF) upon the box’s approval. Other documents will still be generated as needed.
The “withhold” state is helpful for special agreements that require specific formats or contents for their primary billing documents, making the standard document inappropriate. Each party in TandemDrive has a “withhold billing” property, which automatically marks new billing boxes for that party as “withhold.”
When a box is marked as “withhold”, it is still possible to finalize it, using the API, by assigning an invoice code and date and submitting an externally created document.
Unlike “deferred”, which postpones invoicing, “withhold” restricts the generation of the primary billing document by TandemDrive. Once a box is approved, the “withhold” state cannot be newly applied but can still be removed, even after approval, allowing TandemDrive to proceed with generating the primary billing document.
Billing series
A billing series allows you to manage separate collections of billing boxes within the same administration. Each billing series defines its own billing properties, such as:
- Document type (e.g., invoice, debit note, credit note)
- Payment period
- Item roll-up method
- Billing document templates (such as an invoice PDF template)
- Invoice code numbering and pattern
Any administration must have at least one billing series to enable billing. The appropriate billing series is selected according to the billing series rules, which can be viewed and modified in TandemDrive Console.
The billing series rules define if a product item is billed. A billing series rule is defined by an administration, validity period, product category and optionally a series indicator and customer reimbursement VAT. For charge sessions the end timestamp of the session has to match the validity period. For recurring costs the start timestamp of the recurring period has to match the validity period.
VAT
A VAT rate defines the VAT percentage for a specific country and VAT kind. Common VAT kinds include: “standard”, “reduced”, “zero”, and “exempt”. VAT rates have validity periods, allowing for percentage changes over time.
The final VAT percentage is re-determined when a billing box is approved, as billing documents are typically sent post-approval and must use the valid percentage at that time. For instance, if the VAT percentage changes on January 1st and a billing box with many items from December is approved in January, the percentage valid in January will be used.
Determining VAT
To calculate VAT, the following steps are performed:
- Determine the VAT country
- Determine the VAT kind
- Determine the VAT rate
In certain cases, this process can be overridden, such as:
- EU reverse charge applies.
- The reimbursement for a charge session is VAT exempt. In such cases, the charge session pricing may also be VAT exempt.
VAT country

Each seller within TandemDrive can set VAT policies for different countries. These policies define how VAT is handled for various foreign countries. Sellers can choose to use the VAT of the origin country (e.g., the location of a charge session) or the VAT from the seller’s local country. Using the origin VAT country generally means the seller must pay VAT in that country and likely requires a VAT number for that country.
If no policy is defined for a particular country, it can either be flagged as an error that needs resolution or default to the seller’s VAT country. This behavior is configurable for each seller.
VAT kind
Once the VAT country is determined, VAT rules are applied to identify the appropriate VAT kind. VAT rules select the appropriate VAT kind for each item, based on the product category, and optionally the VAT country or VAT indicators. These rules apply to each item’s subtotal. VAT rules have specific validity periods, allowing for changes over time.
VAT rate
Once the VAT country and VAT kind are identified, the VAT rate (percentage) is determined. VAT rates also have validity periods as they may change over time.
VAT examples
Below are examples based on the following setup:
- The MSP is based in the Netherlands.
- The MSP has the following VAT policies:
- Belgium: Use the original VAT country.
- Norway: Use the original VAT country.
- The MSP has a fallback VAT policy to apply VAT from the seller’s country (Netherlands).
- A rate rule for charge sessions applies the standard VAT rate, regardless of the country.
- The VAT rates for each country are as follows:
- Netherlands “standard” VAT: 21%
- Belgium “standard” VAT: 21%
- Norway “standard” VAT: 25%
- Germany “standard” VAT: 19%
Examples of charge sessions based on the location of the session:
- A session in Norway will result in the standard Norwegian VAT rate of 25%.
- A session in Belgium will result in the Belgian VAT rate of 21%.
- A session in Germany will result in the Dutch VAT rate of 21% being applied because no VAT policy for Germany was defined.
Corrections in Billing Boxes
Charge sessions appear only once within a single billing box. However, due to corrections, a single charge session might appear in multiple billing boxes.
To illustrate how corrections are handled, let’s consider a scenario with an initial billing box containing a single priced charge session:
Billing Box
1(Open):
- Category:
MSP Charge session, Count: 1, Volume: 30
- Charge Session:
A, 30.0 kWh, € 18.00
Later, this charge session (A) is repriced to € 17.00. The way this correction
is applied depends on whether the original billing box has already been closed
(i.e., the invoice has been generated).
Scenario 1: Invoice Not Yet Generated (Billing Box 1 is Open)
If Billing Box 1 is still open when the repricing occurs, the charge session
within that box will be directly updated:
Billing Box
1(Open):
- Category:
MSP Charge session, Count: 1, Volume: 30
- Charge Session:
A, 30.0 kWh, € 17.00
Scenario 2: Invoice Already Generated (Billing Box 1 is NOT open)
If the invoice for Billing Box 1 has already been created, the correction will
be handled differently. The original charge session will remain as it was in
the closed box, and a reversal and the corrected charge session will be added
to the next open billing box:
Billing Box
1(Closed):
- Category:
MSP Charge session, Count: 1, Volume: 30
- Charge Session:
A, 30.0 kWh, € 18.00
Billing Box
2(Open):
- Category:
MSP Charge session, reversion, Count: 1, Volume: 30
- Billing Item:
revert, revisesA, € -18.00- Category:
MSP Charge session, Count: 1, Volume: 30
- Charge Session:
A, 30.0 kWh, € 17.00
In this second scenario, Billing Box 2 contains two entries related to the
corrected charge session: a reversal of the original charge (€ -18.00) and the
new, correctly priced charge session (€ 17.00). This ensures that the financial
records are accurate while maintaining a history of the original billing.
Consequently, when a correction occurs after an invoice has been generated,
the same charge session (A) will be present in both the original, closed
billing box (Billing box 1) and the subsequent open billing box
(Billing box 2).
End users
End users are individuals who utilize apps built on top of the TandemDrive App-API. These apps enable users to view data and perform specific actions such as managing subscriptions, downloading invoices, request charge point information and charging vehicles.
Sites
End users are organized into sites. A site is an external back-end system that provides a user interface for end users, typically in the form of web or mobile applications. Each end user belongs to exactly one site.
End user authorizations
End users can have one or more of the following authorizations:
-
MSP token subscriptions
The end user is authorized to use this subscription, enabling them to charge a vehicle and view charging session details. However, they may not necessarily be the owner of the contract.
-
Customers
The end user is authorized to act on behalf of these customers. This includes actions such as adding or canceling subscriptions, viewing invoices, and updating billing information.
Implementation guide
This document serves as a comprehensive resource for developers looking to integrate with our API. The API provides access to key features and services, allowing seamless interaction with the core functionality of the application. Whether you’re building new applications, automating workflows, or integrating third-party services, this guide will walk you through all the essential information to get started.
In the following sections, you’ll find detailed descriptions of the technical details, authentication methods, and best practices to ensure a smooth integration process. We recommend reviewing this guide thoroughly to understand how to implement this API effectively. Do not hesitate to contact us, if you have any questions.
Maintenance
To ensure the stability, security, and performance of our services, the API undergoes regular maintenance, including updates, security patches, and system enhancements. During scheduled maintenance periods, the API may be temporarily unavailable.
When maintenance is in progress, the API will attempt to return an HTTP status
code of 503 (Service Unavailable) to indicate downtime. We strive to minimize
any disruption, and in most cases, maintenance is completed within minutes.
We recommend that you design your integration to handle brief outages and gracefully retry requests during maintenance windows.
Identifiers
TandemDrive uses universally unique identifiers (UUIDs) for most data records. We use UUID version 7, as defined in RFC 9562. However, records created before July 1, 2023, may have been assigned UUID version 4.
In addition to the TandemDrive-generated identifiers, you can assign your own
identifiers to certain types of data. These are called external references,
as they originate outside of TandemDrive. External references must be unique,
though they can be left empty if not needed. Note that these
external_reference fields are case-sensitive.
To view TandemDrive-generated identifiers in the TandemDrive Console, press
Shift + Y.
Identifier or prefixed external reference
To simplify API usage without requiring storage of TandemDrive identifiers in
your client system, several endpoints accept parameters that can either be a
TandemDrive identifier or an external reference. When using an external
reference, it must be prefixed with the string ext_ref. — that is, the literal
string ext_ref followed by a period (.). This prefix helps distinguish
external references.
Identifier or external reference in request body
Similar to the URL parameters, some request bodies can refer to entities using
either an .._id or an .._external_reference property. If both options are
available in the request body specification, you must provide exactly one of
them.
In the following two examples, both creating a billing_address, the reference to
a specific party can be specified using either party_external_reference or
party_id:
{
"party_external_reference": "ABC-123",
"country": "NL",
"external_reference": "ABC-123",
"locality": "Amsterdam",
"valid_from": "2022-06-01T12:00:00+02:00"
}
{
"party_id": "2479037a-21d1-4c09-827b-5922b8a16a80",
"country": "NL",
"external_reference": "ABC-123",
"locality": "Amsterdam",
"valid_from": "2022-06-01T12:00:00+02:00"
}
eMobility Identifiers
For the official definition of EVCO ID (also known as eMA ID) and EVSE ID, please refer to the IDACS e-Mobility Identifiers document, d.d. September 2023.
For machine-to-machine communication, IDACS strongly advise against using separators in identifiers like EVSE and EVCO IDs. Separators should only be used to improve human readability in a user interface.
Our API accepts IDs with or without separators. To align with IDACS guidelines, API responses will be updated to return both the original value and a normalized version.
EVCO ID (EV Contract ID), also known as eMA ID
This identifier represents an EV Contract or Electric Mobility Account (EMA). It should not be confused with the Token UID.
For tokens currently issued by TandemDrive, this identifier uniquely represents each token, to avoid revealing the relationship between tokens and end-customer contracts to roaming partners. TandemDrive adheres to the eMA-ID standard, incorporating a check-digit in all issued tokens. For details on its syntax, please refer to the IDACS document mentioned above.
For EVCO IDs issued by external platforms, these requirements may not apply, though compliance with the eMA-ID standard is recommended.
This identifier is sometimes visible to the end user, unlike the Token UID, which is often not displayed.
Examples of syntactically valid EVCO IDs which comply with eMA-ID:
NLABCC11222344XNL-TDR-CA1B2C3D4-E
Token UID
The Token UID is a unique identifier used by charging systems to recognize a specific charging token.
It’s crucial to distinguish the UID from the EVCO ID / eMA ID.
-
For RFID Tokens (like a key fob or card), the UID is the serial number physically read by the RFID scanner at the charge point.
For RFID tokens (NFC / smartcards), the RFID UID typically serves as the Token UID, following the ISO/IEC 14443 standard. Refer to the “AN10927 MIFARE product and handling of UIDs” document published by NXP for more information about NFC UIDs (ISO/IEC 14443).
-
For ISO 15118 Tokens (used for “Plug & Charge”), the UID is the same as the eMA ID, but without any separators.
-
For virtual Tokens (used for remote start/stop), the UID can be randomly generated. Some CPMSs may not support UIDs longer than 20 characters.
Please be aware that this field is case-insensitive. Only printable ASCII characters, excluding spaces, are allowed. Specifically, all characters in these strings must have Unicode code points ranging from U+0021 to U+007E, inclusive.
EVSE ID
The EVSE ID is a reusable identifier for a charging station (EVSE). For example if a charging station is physically replaced or revised, the replacement may retain the same EVSE ID. For details on its syntax, please refer to the IDACS document mentioned above.
This is different from the EVSE UID.
Examples of syntactically valid EVSE IDs:
NLABCE987ABC654DEFNL*TDR*E1234567*890
EVSE UID
The EVSE UID provides a unique, permanent identifier for an EVSE across all locations associated with the same CPO ID. It cannot be changed, modified, or renamed. This identifier is intended for technical purposes and should not be confused with the more user-friendly EVSE ID.
EVSE UIDs must be handled in a case-insensitive manner (as per OCPI 2.2 and later versions). Only printable ASCII characters, excluding spaces, are allowed. Specifically, all characters in these strings must have Unicode code points ranging from U+0021 to U+007E, inclusive.
Pagination
Some endpoints support pagination, and to ensure stable, reliable, and efficient performance, we use ‘seek’ pagination. The main advantage of ‘seek’ pagination is that the items within a given page remain stable, even when new data is added, allowing for better performance optimization.
The after query parameter is used to retrieve all items that come after a
specified item. In most cases, the id of an item is used for the after
parameter, as shown in the example below. The documentation for each endpoint
specifies which field should be used for the after parameter.
When polling our paginated endpoints periodically, use the last retrieved value
for the next request. Since the after parameter is interpreted in a strict
“greater than” (>) sense, an empty page might be returned, indicating there are
no new items.
For consecutive pages, the link_next field in the response contains the
relative URI to fetch the next set of results. Additionally, the
Link: header
provides the same relative URI for the next page.
Example of a current page result:
{
"items": [...],
"page_size": 500,
"link_next": "/main/v1/msp-chargesession?after=NLTD1_608C9A13-E3DC-4353-8BEE-7437305BC8D7"
}
To retrieve the next page, use the provided link_next value for your next request:
GET https://example-api.on-tandemdrive.com/main/v1/msp-chargesession?after=NLTD1_608C9A13-E3DC-4353-8BEE-7437305BC8D7
Be aware that providing an invalid after value, such as a non-existent identifier, will also result in a 400 Bad Request error.
Initial request
If you want to retrieve all items from the start, you can omit the after
parameter in your initial request. Alternatively, if you only want to fetch
items created after a specific date and time, use the created_gt query
parameter. In this case, the response will not contain any items, but it will
include a link_next to the first page of results after the given date-time. If
no such items exist, the API will return a “400 Bad Request” error.
It’s crucial to understand that the created_gt parameter is mutually exclusive
with all other parameters, including after. Apply any further filtering
criteria by appending them as query parameters to the received link_next URL
in subsequent requests.
Keep in mind that the created_gt parameter should only be used for the very
first request; subsequent pagination and polling should be handled using the
after parameter. This requires the API client to maintain a record of the last
item id received and include it in the after parameter for the next
iteration.
Page size
If one wishes to use a page_size different from the default (currently 500
for most endpoints), the page_size parameter can optionally be specified. See
the specification for minimum, maximum and default values for this parameter.
Pagination how-to
Below you’ll find a set of basic pagination examples. All examples use the GET /customer
endpoint, but the techniques apply to all endpoints that use pagination.
First endpoint use
To start pagination through a set of results provided by a particular endpoint, use the endpoint URL without any parameters.
$ curl -u $public_id:$secret "$base_url/main/v1/customer"
This results in a page of customer data:
{
"items": [
{
"id": "cd1ddcf5-9858-4f90-b132-094e505c0725",
"admin_id": "graant",
"name": "Mr. Grain",
"country": "NL",
"entity_type": "person",
"external_reference": "R00001"
},
...
{
"id": "8bb33876-d098-429e-b9ed-77f81ce6db00",
"admin_id": "silaye",
"name": "Gardening Enterprise",
"country": "GB",
"entity_type": "organisation",
"company_reg_num": "6984621"
}
],
"page_size": 500,
"link_next": "/main/v1/customer?after=8bb33876-d098-429e-b9ed-77f81ce6db00"
}
Retrieve the next page using the link_next value if
more data is available. If the number of items is less than page_size,
no more data is available. It is important to store the id
of the last item so that data retrieval can be continued without duplicates.
Retrieving data starting at a certain moment in time
The created_gt parameter can be used to retrieve data created after the
provided moment in time. This is only meant to be used once, normal pagination
must use the after parameter.
Note that since the created_gt parameter is used in a strict > sense, it
may make sense to choose a moment which is a bit earlier. This helps with
including all required records in subsequent requests.
$ curl -u $public_id:$secret "$base_url/main/v1/customer?created_gt=2021-12-20T00%3A00%3A00Z"
{
"items": [],
"link_next": "/main/v1/customer?after=cd1ddcf5-9858-4f90-b132-094e505c0725",
"page_size": 500
}
The first page of customers created after a given moment can be retrieved using:
$ curl -u $public_id:$secret "$base_url/main/v1/customer?after=cd1ddcf5-9858-4f90-b132-094e505c0725"
The created_at parameter of the customer can be used to filter any
customers that were created between 20221-12-20 and the 2022-01-01. From
this point onward the scenario “First endpoint use” can be used.
Retrieving new data
This scenario implies one has already obtained data using either of the
scenarios above. Use the id from your local data to fetch the last
customer retrieved. We represent the id as 9999. New data can be
retrieved using 9999 as the value for the after parameter:
$ curl -u $public_id:$secret "$base_url/main/v1/customer?after=9999"
Data types
Decimals
For certain values, it’s crucial to avoid treating them as floating-point numbers, which cannot accurately represent all decimal numbers. Floating-point arithmetic can introduce precision errors, making it unsuitable for precise values such as monetary amounts.
To ensure precision, we return decimal values as JSON strings. This forces API users to explicitly handle conversion, ensuring that they choose a data type that preserves decimal accuracy. For more information, refer to RFC7493.
- Fractional Separator: A
.(dot) is used for the fractional part, and no thousands separator is included. - Precision:
- API responses return decimal values with up to 12 decimal places in the fractional part.
- Inputs can include up to 28 digits, and will be rounded to 12 decimal places.
- Range:
- Maximum value:
10¹⁶ - Minimum value:
-10¹⁶ - Exceptions may apply, and will be explicitly noted when applicable.
- Maximum value:
Strings
Most string values in the API are automatically trimmed of leading and trailing
whitespace. Additionally, these string values must not contain any control
characters, except some cases when tabs (\t) and newlines (\n) are allowed.
Each string field has specific minimum and maximum length requirements, which are detailed in the relevant API specifications.
Validity periods
In the API, several entities have an associated “validity period” which defines
the time range during which the entity is considered valid. This period is
represented by two fields: valid_from and valid_until.
-
Time bounds:
- The
valid_fromfield indicates when the validity period starts (inclusive). - The
valid_untilfield indicates when the validity period ends (exclusive). - For example, if a subscription starts at
2023-11-01T00:00+01:00and ends at2023-12-01T00:00+01:00, it will be valid throughout November but not in December. - The
valid_untildate must always be later than thevalid_fromdate. - In some cases a validity period cannot be “empty”, meaning
valid_fromandvalid_untilcannot be the same value. - The
valid_untilfield can benull, which means the validity period continues indefinitely (i.e., valid until further notice). - In some cases,
valid_fromcan also benull, meaning the validity period is valid from the past indefinitely. If bothvalid_fromandvalid_untilarenull, the entity is considered valid indefinitely (i.e., always valid).
- The
-
Granularity:
- Validity periods must be specified in whole minutes. Any seconds or fractions will be truncated by the API.
- Note: For example, if you submit
2023-11-01T23:45:67.912343+01:00, the API will truncate the seconds, and it will become2023-11-01T23:45:00+01:00. - This minute-level precision is intentional because the validity of things like subscriptions and tokens doesn’t benefit from precision beyond minutes. Finer granularity may cause confusion, especially in systems that don’t display seconds or fractions of seconds. As a result, subscriptions cannot begin and end within the same minute.
Stale update prevention
To prevent clients from unintentionally overwriting data due to out-of-order API
calls, several endpoints include an optional external_updated_at field in the
request body, which is a date-time type.
Since external_updated_at can never be reset, there is an extra check that the field is
never set to a date-time in the future. This prevents accidentally making a record
immutable.
When an external_updated_at value (e.g., a) is provided, TandemDrive will
compare it to the last stored external_updated_at value (e.g., b) for the
corresponding record. The request will be processed only if a is more recent
than or equal to b (a >= b). If not, the request will be ignored, and a 204 No Content
response will be returned. This mechanism ensures that outdated updates do not
overwrite newer changes.
Merge endpoints
The response contains a new field called stale_update if the request was an update.
This field specifies whether the request was a stale update or not.
Update Endpoints
TandemDrive provides two types of endpoints for performing updates:
-
Merge-Update Endpoints: These endpoints merge the provided values with existing field values using the
POSTHTTP method, enabling partial updates for greater flexibility and less room for mistakes. -
Replace-Update Endpoints: These endpoints replace all field values in the target resource and use the
PUTHTTP method. Since this approach can be challenging to implement correctly, we introduced the Merge-Update alternative for updating records.These endpoints replace all values for fields defined in the endpoint schema. Optional fields omitted from the request are reset to their default values, which is
nullunless otherwise specified.These endpoints are deprecated and should not be used for new implementations. A migration guide is provided for existing usage of these endpoints. TandemDrive plans to remove these endpoints once all customers have migrated to using the ‘Merge-Update’ alternative.
Merge-Update Endpoints
Merge-Update endpoints allow updates to only the fields specified in the request
body. These endpoints use the POST HTTP method and conform to the JSON Merge
Patch RFC.
Behavior
- Fields not included in the request body remain unchanged.
- Fields that cannot be modified:
- Immutable fields are ignored if an update is attempted. For example, this applies to fields which can only be set upon creation.
- In a future version of our API, attempts to modify these fields may result in an error unless the provided value matches the field’s current state, ensuring consistency and reducing the likelihood of mistakes.
- Updating an immutable field to its existing value is always fine.
Refer to the endpoint-specific documentation for details on this behavior.
Replace-Update Endpoints (deprecated)
Warning: Replace-Update endpoints are more challenging to use correctly. We highly recommend using Merge-Update endpoints for simpler and safer updates. We intend to remove the Replace-Update endpoints once they are no longer in use. A migration guide is provided for existing usage of these endpoints.
Replace-Update endpoints use the PUT HTTP method. According to the HTTP RFC
on Idempotent
Methods,
PUT is idempotent, meaning repeated identical requests yield the same result.
A PUT request replaces all values for fields defined in the endpoint
schema. Optional fields omitted from the request are reset to their default
values, which is null unless otherwise specified.
Example Workflow:
To update only a subset of fields:
- Use a
GETrequest to retrieve the current values. - Modify the desired fields.
- Submit the updated data via a
PUTrequest.
External References vs TandemDrive IDs
TandemDrive allows customers to use their own unique identifiers, referred to as
external_reference, for nearly all stored records. For more information, see
Identifiers.
Specifying a Unique Identifier
To update records via the update endpoints, you must provide a unique identifier
in the path parameter. The behavior depends on whether the identifier is a
TandemDrive ID or an external_reference.
Using a TandemDrive ID as the Path Parameter
- If the specified record does not exist, the update will fail, and no new record will be created.
- You can modify the
external_referenceof the record when using a TandemDrive ID as the path parameter.
Using an External Reference as the Path Parameter
- Use the prefix
ext_ref.(including the dot) for the path parameter which supports using anexternal_reference. - If the record does not exist, TandemDrive will create it automatically.
- When an external reference is used as the path parameter:
- It overrides any
external_referencespecified in the request body. - The record will inherit the path parameter’s value as its
external_reference. - It is not possible to update the
external_referenceto a different value.
- It overrides any
Migration Guide: Transitioning from Replace-Update to Merge-Update
TandemDrive is streamlining its API by replacing the “Replace-Update” mechanism with a more robust and user-friendly “Merge-Update” approach. The “Replace-Update” method, while functional, presents complexities in implementation and potential for unintended data loss.
Action Required: Migrate to Merge-Update Endpoints
We strongly encourage all customers to transition from the existing
“Replace-Update” endpoints to the new “Merge-Update” equivalents. In many cases,
the migration is as simple as changing HTTP PUT requests to POST requests.
Key Change: Handling Unspecified Fields
The primary difference lies in how unspecified fields are handled. Under
“Replace-Update,” omitted fields were often reset to their default values
(typically null). This behaviour was documented but this is easily missed, and
it’s challenging to implement correctly. With “Merge-Update,” only the fields
explicitly included in the request are modified. Existing data in unmentioned
fields remains unchanged.
Simplifying Specialized Endpoints
We’ve also consolidated several specialized endpoints that targeted single or small sets of fields. These are now superseded by the more versatile “Merge-Update” endpoints. To achieve the same functionality, simply include only the desired fields in your POST request.
The endpoints mentioned in the two lists below will be removed, once everyone has migrated to the “Merge-Update” endpoints.
“Replace-Update” endpoints
The endpoints in the list below use the “Replace-Update” approach. In most cases
you can switch to the “Merge-Update” approach by replacing PUT with POST:
-
PUT /main/v1/customer/{id} -
PUT /main/v1/billing-address/{id} -
PUT /main/v1/billing-direct-debit-mandate/{id} -
PUT /main/v1/cpo-reimbursement-arrangement/{id} -
PUT /main/v1/cpo-reimbursement-link/{id} -
PUT /main/v1/cpo-subscription/{id} -
PUT /main/v1/cpo-subscription-evse-link/{id} -
PUT /main/v1/msp-token/{evco_id} -
PUT /main/v1/msp-token-subscription/{id} -
PUT /main/v1/msp-token-link/{id} -
PUT /main/v1/end-user/{id} -
PUT /main/v1/end-user/{id}/authorization-customerbecomesPOST /main/v1/end-user/{id}/authorization-customer/{customer_id} -
PUT /main/v1/end-user/{id}/authorization-msp-token-subscriptionbecomesPOST /main/v1/end-user/{id}/authorization-msp-token-subscription/{subscription_id}
Endpoints which are no longer necessary
The endpoints below were designed to update only a single field, or a select set
of fields. This can now be achieved with our “Merge-Update” endpoints by only
mentioning the fields that should be updated. The DELETE variants of the
endpoints below can be replaced by explicitly setting a field to the null
value.
-
PUT /main/v1/cpo-reimbursement-arrangement/{id}/valid-from -
PUT /main/v1/cpo-reimbursement-arrangement/{id}/valid-until -
DELETE /main/v1/cpo-reimbursement-arrangement/{id}/valid-until -
PUT /main/v1/cpo-reimbursement-link/{id}/valid-from -
PUT /main/v1/cpo-reimbursement-link/{id}/valid-until -
DELETE /main/v1/cpo-reimbursement-link/{id}/valid-until -
DELETE /main/v1/cpo-reimbursement-link/{id}/valid-from -
PUT /main/v1/cpo-subscription/{id}/valid-until -
PUT /main/v1/cpo-subscription/{id}/valid-from -
DELETE /main/v1/cpo-subscription/{id}/valid-until -
PUT /main/v1/cpo-subscription-evse-link/{id}/valid-until -
PUT /main/v1/cpo-subscription-evse-link/{id}/valid-from -
DELETE /main/v1/cpo-subscription-evse-link/{id}/valid-until -
DELETE /main/v1/cpo-subscription-evse-link/{id}/valid-from -
PUT /main/v1/msp-token-subscription/{id}/valid-from -
PUT /main/v1/msp-token-subscription/{id}/valid-until -
DELETE /main/v1/msp-token-subscription/{id}/valid-until -
PUT /main/v1/msp-token-link/{id}/valid-until -
PUT /main/v1/msp-token-link/{id}/valid-from -
DELETE /main/v1/msp-token-link/{id}/valid-until -
DELETE /main/v1/msp-token-link/{id}/valid-from
Unknown fields
When the API receives JSON containing fields that are not recognized or used by
the endpoint, it will — on a best-effort basis — return an X-TD-Unknown-Fields
header. This header contains a comma-separated list of the unrecognized fields.
While receiving unknown fields is not considered an error, it may indicate a potential issue, such as incorrect or misspelled field names. Monitoring this header can help identify and correct such mistakes.
For example, if the header returns valid_fro,name, it suggests that the
valid_fro field was misspelled (it should be valid_from), and the field
name is not used by this endpoint.
Implicit fields
When the API does not receive a field which is documented to be optional, it will
use the default value for that field. If the default value of a field is undocumented,
then the default value of that field is null.
Some fields that are optional in the API should not have been optional to begin with as not specifying these fields can have unintended results. The most significant examples of this are fields on the “Replace or Create” endpoints. See Replace-Update endpoints for more details.
Fields can not be made required without breaking users that rely on the
fields being optional.
That is why we have added the X-TD-Implicit-Fields header.
This header is added to the response when a field was not provided even
though the field should be required (much like the X-TD-Unknown-Fields header).
Importantly, this allows users to detect potential issues without
breaking anyones implementation.
In the future, we intend to make these fields required from the start. Existing fields can be made required gradually when there is enough evidence that no user relies on the field being optional.
Advanced data corrections
The API provides powerful mechanisms to ensure data consistency, but not all actions automatically bring the complete system into a consistent state. For certain complex scenarios, the API offers advanced endpoints that provide enhanced consistency guarantees.
For example:
If you assigned a token to the wrong subscription, you would typically correct this by making two API requests: first, marking the incorrect token link as defunct, and then creating the correct token link. In these cases, the API will not automatically reprice the affected charge sessions after the first request, as it doesn’t know when you’ve completed the correction process.
For such advanced corrections, a special endpoint is available that allows you to complete the entire process in a single request. This endpoint not only performs the correction but also ensures that related data, such as pricing for charge sessions, is automatically updated for consistency.
It’s important to note that these automatic corrections are an advanced feature and are not available for all operations. For many tasks, you’ll need to follow the manual process to ensure data integrity.
Compatibility
RFC 1122/1123: “Adaptability to change must be designed into all levels of Internet host software.”
Our API follows a single versioning scheme. A new version is only introduced
when major, backwards-incompatible changes are necessary. The API version is
included in the URL path, so all paths in the specification are prefixed with
/main/v1.
API and product versioning
-
API Version:
The version number is part of the URL (e.g.,
/main/v1).To view the available API versions, use the
/main/versionendpoint. Note: This specific endpoint does not include the version prefix (/vN). -
Product Version:
To retrieve the current product version (distinct from the API version), check the
X-TD-Versioncustom header in responses.
Versioning Notifications
TandemDrive will notify customers in advance when a new API version is planned. During transition periods, TandemDrive supports two concurrent API versions to ensure a smooth migration.
Backwards-compatible changes
A change is considered backwards-compatible if it does not break or require modifications to existing client implementations that conform to previous API releases. As the API evolves to support new features and services, we strive to maintain compatibility with previous releases. The following changes are considered backwards-compatible:
-
New resources: Adding new API endpoints.
-
Optional parameters: Introducing new optional request parameters to existing API methods.
-
Response properties: Adding new properties to existing API responses.
-
Enumerated fields: Extending predefined sets of values in enumerated fields. API clients should handle new values gracefully.
-
Property order: The order of properties in API responses is arbitrary and should not be relied upon.
-
String formatting: Changes in string formatting that don’t alter meaning (e.g., changes in case-insensitive identifiers or optional separators).
-
Human-readable messages: Modifying error messages or other human-readable strings.
-
Webhooks: Adding new event types. Your webhook listener should handle unfamiliar event types without failure.
-
Unused endpoints: Modifying endpoints that have not yet been used in production allows for iterative improvements based on feedback.
-
Required fields: Changing previously required parameters or fields to optional, as long as it doesn’t compromise the integrity or functionality of the API.
Preview endpoints
New features and endpoints in TandemDrive often start in preview mode, clearly marked as such in the API specification. See Preview mode for what the implications of using such features and endpoints are.
Handling breaking changes
While we prioritize backwards compatibility, there are rare occasions where breaking changes are required to enhance the API or introduce significant new features without warranting a full version change. In these cases, we take a customer-first approach to minimize disruption:
-
Coordination and support: We work closely with each customer to coordinate the timing of the deployment, ensuring it aligns with their schedules and needs. Our team provides guidance and assistance to facilitate a smooth transition.
-
Transition period: When possible, we provide a transition period where both the old and new versions are supported, allowing customers to gradually adapt to the changes without immediate impact.
-
Testing and feedback: We encourage customers to test changes in their TandemDrive test instance before full deployment, and we remain open to feedback to address any concerns or issues that arise.
By taking this collaborative approach, we aim to ensure that breaking changes are handled as smoothly and efficiently as possible, minimizing impact on our customers’ operations.
Security
We take the security of our HTTP API seriously to ensure the protection of your data and operations.
Secure Communication
All API communications are secured using Transport Layer Security (TLS) to safeguard data in transit. We enforce TLS to prevent eavesdropping and tampering by third parties. Ensure that your HTTP client supports the latest TLS versions (TLS 1.2 or higher) and is updated regularly to stay protected against known vulnerabilities.
Authentication
Access to the API requires authentication through a dedicated API account. Each account is issued a pair of credentials, consisting of an API ID (similar to a username) and a secret key. It is crucial that the secret key remains confidential and is not exposed to unauthorized individuals or systems. We recommend storing the secret key securely, to avoid unintentional exposure. Never embed credentials directly in client-side code or public repositories.
These credentials must be included in each API request using the HTTP Basic Authentication scheme.
Authorization
Each API account is restricted to a specific set of functionalities and access to TandemDrive administrations. It’s recommended to setup your API accounts with the minimum necessary privileges to fulfill their designated tasks.
Server-to-Server Interaction
Our APIs are designed for server-to-server communication. It is recommended that API access is limited to trusted back-end systems and services. Avoid making direct API requests from client-facing applications like web browsers or mobile apps.
Incident Response and Vulnerability Reporting
If you suspect that your API key has been compromised or if there is any uncertainty about its security, contact us immediately. Quick action can help mitigate potential risks. For security incidents, vulnerability reports, or any concerns related to the security of the API, please reach out to our security team at security@tandemdrive.com.
By adhering to these practices, you can help maintain the integrity and security of your integration with our API.
HTTP and JSON
The API relies on HTTP for transport and uses JSON for data serialization.
JSON
All API messages must comply with RFC7493: “The I-JSON Message Format”. This RFC outlines key improvements for JSON interoperability. For reliable API usage, ensure that the JSON data you send adheres to this standard.
HTTP
We recommend using HTTP/1.1 or HTTP/2 for optimal performance. To improve efficiency, we encourage clients to maintain persistent connections between requests and limit the number of simultaneous open connections.
Key considerations for interacting with the API over HTTP:
- URL structure: Our API endpoints do not include a trailing slash (
/). - Case-insensitive headers: HTTP header names are case-insensitive, so clients should not rely on the case of header names.
- Standard HTTP requirements: Ensure proper encoding of query string parameters and compliance with standard HTTP protocols.
- Filtering results: When using query parameters to filter results, the
conditions are combined using a logical
ANDoperation.
HTTP Status Codes and Error Responses
Understanding the first digit of the HTTP status code is essential. Refer to RFC9110 for more detailed information.
Here are the most common status codes returned by the API:
200 - OK The request was successful.
400 - Bad Request The request is invalid, typically due to missing or
incorrect parameters, or referencing a non-existent
object. The response body will provide more details.
401 - Unauthorized No valid API key was provided.
403 - Forbidden The API key does not have sufficient permissions to
complete the request.
404 - Not Found The requested resource with that URI doesn't exist.
Because a non-existing URL pattern was used.
429 - Too Many Requests The client has sent too many requests in a short period.
We recommend implementing exponential backoff strategies.
500, 502, 503, 504 Server Errors: Something went wrong on TandemDrive's
side. The `503` code is returned during maintenance.
In all cases, the client can retry the request later.
For 4xx status codes, the API aims to return a detailed error response in JSON
format. We recommend that clients use the information in the response body,
rather than relying solely on the HTTP status code, to decide on the next course
of action. Check for the Content-Type: application/json header to determine if
a structured JSON error message is being returned.
Rate limits
To ensure system stability and protect against overloading, our API enforces
rate limits. If these limits are exceeded, the API will return an HTTP status
code 429 - Too Many Requests.
Rate-limited Actions
The following actions are subject to rate limiting:
- Simultaneous connections from the same IP address.
- Simultaneous connections from the same API account.
- Requests per second from the same IP address.
- Requests per second from the same API account.
- New connections per second from the same IP address.
Best practices
To avoid reaching the rate limits and to maximize performance:
- Minimize simultaneous connections: Typically, using more than a few concurrent connections will not significantly improve performance.
- Use persistent connections: Reuse existing connections for consecutive API requests to reduce overhead and improve efficiency.
Default rate limits (as of 2024-02-29)
- Requests per second: The default limit is 200 requests per second, with allowance for bursts of up to 500 requests per second.
- Simultaneous connections: The default maximum is 64 concurrent connections.
Need higher limits?
If your application requires higher rate limits, please contact us to discuss increasing your allocated resources.
App-API
Architecture
Each end user application must have its own dedicated back-end service. This is essential because the App-API does not provide direct user authentication. Instead, the app’s back-end is responsible for:
- Authenticating the end user.
- Interfacing with the TandemDrive App-API on behalf of the authenticated user.
By separating authentication from the API, the architecture allows each app to manage user sessions while the App-API handles data and actions related to those authenticated users. The app back-end can also provide services not offered by the TD App-API.
The API automatically restricts the data returned to only what the end user is authorized to access. If the user attempts to access or modify unauthorized data, the system will return an appropriate error (bad request).
Most endpoints require the end user identifier as part of the URL path to ensure that operations are tied to the correct user.
Offset Pagination
The app API uses offset-based pagination, in contrast to the
seek-based pagination used by the admin API. Offset-based
pagination is straightforward because the client does not need to track the ID
of the last item and can navigate to any page by specifying the page number.
Page numbering starts from 1.
Page Parameter
The optional page query parameter specifies the page to retrieve.
It defaults to the first page (page=1).
Page Size Parameter
The optional page_size query parameter adjusts the number of items returned
per page, with a default of 50. Check the specifications for the maximum
allowed value of this parameter.
Example Response
The following json data is an example of an offset-based pagination response:
{
"items": [...],
"page": 1,
"page_size": 50,
"has_next_page": true
}
items: Contains the list of items returned by the request, with a maximum number of items equal topage_size.page: Shows the current page number.page_size: Shows the number of items on the current page.has_next_page: Returnstrueif there is a page after the current one with at least one item, making it useful for “Next Page” buttons in user interfaces.
Preview Mode
Preview Mode
Our API offers preview endpoints and optional preview features within existing endpoints. These are new functionalities we believe are ready for initial use. Your feedback can help us refine the feature before we remove the preview mode. Note that backwards-incompatible changes may be introduced during the preview period.
Contact us
We highly recommend reaching out to us before using any preview endpoint or feature. Collaborating with us provides the following benefits:
- Insight into our plans: We’ll provide insights into the feature’s intended purpose and highlight any aspects still under consideration.
- Shaping the feature: Share your use case and requirements with us to help shape the endpoint’s final design and functionality, ensuring it better serves your needs.
Webhooks
Note: This is a “preview” feature and may be subject to change.
Introduction
Webhooks allow API users to receive timely updates without constant polling. You
can register a URL to receive a POST request whenever there are changes in the
data exposed by the API.
Message structure
Each message has a generic envelope that wraps the data of the event. The
data uses the same schema as the API’s endpoints. Here’s an example of a
customer update message:
{
"id": "5615894c-56ba-73ac-8fc8-4120262d7fd9",
"occurred_at": "2022-11-01T01:02:45.123Z",
"modification_kind": "update",
"kind": "customer",
"data": {
"id": "ca904ea7-4281-714e-a3cd-72df1d61907b",
"admin_id": "tdr-france",
"name": "TandemDrive BV",
"country": "NL",
"entity_type": "organisation",
"company_reg_num": "C123456",
"external_reference": "ABC-123",
"created_at": "2022-11-01T01:01:45.123Z",
"updated_at": "2022-11-01T01:02:45.123Z",
"billing_email": "info@tandemdrive.com",
"billing_phone": "+31123456789",
...
}
}
Object deletions, contain properties to identify the object that was deleted in data.
See Deletions below.
For the details about the structure of the envelope see the API specification.
Webhook registration
When subscribing to a webhook, you can specify which event kinds to receive.
If different endpoints need updates for different kinds, multiple webhook
subscriptions can be registered. While messages for each subscription are
guaranteed to be sequential, there is no guarantee of the order between
different subscriptions.
Messages may be sent concurrently to different webhook subscribers. However, for each individual subscriber, only one message is sent at a time to ensure that events are processed in the exact order they occurred.
Responses
The HTTP response code confirms the receipt of the update. Non-2xx responses
will trigger retries by TandemDrive.
As a webhook receiver, you should reject messages only in rare cases like
authentication failures or system errors. In all other situations, respond with
a 2xx status code to avoid disrupting the delivery of subsequent messages due
to a single rejection.
Webhook endpoints must support at least HTTP 1.1 and TLS 1.3. Accepting compressed HTTP requests is recommended for efficiency.
Authentication
To ensure security, you must verify that events originate from TandemDrive. This
can be done using configurable headers like X-Api-Key or Authorization. The
receiving URL should use https:// with a valid certificate.
Rapid Events
For rapid successive updates it may be possible that we either de-duplicate events of the same object as a single event, or send one event per update but each with the same state of the object.
Deletions
In deletion events, only the object identifier and external reference (if available) are included:
{
"id": "5615894c-56ba-43ac-8fc8-4120262d7fd9",
"occurred_at": "2022-11-01T01:02:45.123Z",
"modification_kind": "delete",
"kind": "customer",
"data": {
"id": "ca904ea7-4281-414e-a3cd-72df1d61907b",
"external_reference": "C0001234"
}
}
However this rule has some notable exceptions, for objects where there exists a
domain identifier that is used instead of the id generated by TandemDrive. The
list of object to which this exception applies:
| Object | Identifying property name |
|---|---|
msp-token | evco_id |
cpo-evse | evse_uid |
cpo-evse-location | location_id |
cpo-evse-connector | connector_id |
Performance
Prepare to handle potentially high volumes of messages. TandemDrive sends data
in batches (e.g., every 10 seconds) via a single TCP+TLS connection. Be sure to
quickly deliver a 2xx response to avoid timeouts.
Handling duplicate events
Our webhook system will retry delivery until your endpoint returns a successful
response. If we don’t fully process your response, the same message may be sent
more than once. To handle potential duplicates, ensure your event processing is
idempotent by tracking the ids of received events and skipping those already
processed.