When a driver taps an RFID card on a roaming charger, the CPO has a few hundred milliseconds to decide whether to start charging, and the card belongs to someone else's customer. The Tokens module is how the CPO knows about the eMSP's cards and app users, and how it asks the eMSP for a live decision when a cached answer is not enough.
This is part 5 of the OCPI 2.2.1 series. For the OCPP side of the same flow, see OCPP 1.6 Authorize and id tags and OCPP 2.0.1 authorization.
Who owns what
Tokens are owned by the eMSP. The eMSP implements the Tokens Sender interface (GET list and the real-time authorize endpoint); the CPO implements the Receiver interface, so the eMSP can push token changes to it. This is the reverse of Locations, and it is common to see both parties confused about it on day one.
The Token object
{
"country_code": "NL",
"party_id": "EXA",
"uid": "04A1B2C3D4E5F6",
"type": "RFID",
"contract_id": "NL-EXA-C12345678-X",
"visual_number": "EXA-1234-5678",
"issuer": "Example eMSP",
"group_id": "FLEET-ACME",
"valid": true,
"whitelist": "ALLOWED",
"language": "nl",
"last_updated": "2026-10-25T22:10:00Z"
}| Field | Notes |
|---|---|
uid | The identifier the charger sees: for RFID, the card UID; for app users, an ID the eMSP chooses. Up to 36 characters. |
type | TokenType: AD_HOC_USER, APP_USER, OTHER, RFID. |
contract_id | The eMA/contract ID that identifies the contract for billing. Appears again in CDRs. |
group_id | Groups tokens of the same account, e.g. several cards on one fleet contract. Lets the CPO treat them as one for sessions. |
valid | Whether the token may currently be used at all. |
whitelist | WhitelistType, see below. |
default_profile_type | Optional ProfileType (CHEAP, FAST, GREEN, REGULAR) for smart charging preferences. |
WhitelistType: the policy for offline decisions
The whitelist field is the eMSP telling the CPO how it may authorize this token:
| Value | Meaning | CPO behaviour |
|---|---|---|
ALWAYS | Token must always be whitelisted; real-time authorization is not possible or not allowed. | Decide locally using the token's valid flag. Never call authorize. |
ALLOWED | Whitelisting is allowed; real-time authorization is also allowed. | CPO may choose. Typically: local decision for speed, real-time when available or required. |
ALLOWED_OFFLINE | Use real-time authorization normally; whitelist only if the eMSP cannot be reached. | Call authorize; on timeout or connection failure, fall back to the local copy. |
NEVER | Whitelisting is forbidden. | Always call authorize. If the eMSP is unreachable, do not start charging. |
These map directly to OCPP concepts. Tokens with ALWAYS or ALLOWED are natural candidates for the charger's local authorization list (OCPP 1.6 SendLocalList), which lets the charger start sessions even when the CSMS is offline. NEVER tokens must not be sent to chargers at all. ALLOWED_OFFLINE sits in between: keep them in the CSMS's own store as a fallback, and decide per network whether they belong in chargers' local lists.
Getting tokens to the CPO
eMSP push
PUT /ocpi/cpo/2.2.1/tokens/NL/EXA/04A1B2C3D4E5F6?type=RFID HTTP/1.1
Host: cpo.example.com
Authorization: Token OWU4MGE1OWMtNmEyZi00YjVkLThlMWMtMmYzYTRiNWM2ZDdl
Content-Type: application/json
X-Request-ID: 5c4b3a29-1807-4f6e-8d5c-4b3a29180700
X-Correlation-ID: 5c4b3a29-1807-4f6e-8d5c-4b3a29180700Blocking a lost card is a PATCH to the same URL with {"valid": false, "last_updated": "..."}. The type query parameter matters: the same uid can theoretically exist as both an RFID and an APP_USER token, so the key is (country_code, party_id, uid, type). When type is omitted, RFID is assumed.
CPO pull
The CPO can also page through the eMSP's tokens with GET {tokens_url}?date_from=...&offset=...&limit=.... Large eMSPs have millions of tokens, so a full initial pull can take a long time; use date_from incrementally after that, and do not re-pull everything nightly.
Real-time authorization
When the CPO needs a live decision, it posts to the eMSP's Sender interface:
POST /ocpi/emsp/2.2.1/tokens/04A1B2C3D4E5F6/authorize?type=RFID HTTP/1.1
Host: emsp.example.com
Authorization: Token ZWJmM2IzOTktNzc5Zi00NDk3LTliOWQtYWM2YWQzY2M0NGQy
Content-Type: application/json
X-Request-ID: 0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d
X-Correlation-ID: 0a9b8c7d-6e5f-4a3b-8c2d-1e0f9a8b7c6d{
"location_id": "LOC-0042",
"evse_uids": ["3256"]
}The body is a LocationReferences object and is optional, but many eMSPs require it to apply location-specific rules. If the eMSP needs it and it is missing, it answers with status 2002. The response is an AuthorizationInfo:
{
"data": {
"allowed": "ALLOWED",
"token": {
"country_code": "NL",
"party_id": "EXA",
"uid": "04A1B2C3D4E5F6",
"type": "RFID",
"contract_id": "NL-EXA-C12345678-X",
"issuer": "Example eMSP",
"valid": true,
"whitelist": "ALLOWED",
"last_updated": "2026-10-25T22:10:00Z"
},
"authorization_reference": "AUTH-7f3c91",
"location": { "location_id": "LOC-0042", "evse_uids": ["3256"] }
},
"status_code": 1000,
"timestamp": "2026-10-26T10:14:58Z"
}allowed (AllowedType) | Meaning |
|---|---|
ALLOWED | Go ahead. |
BLOCKED | Token is blocked. |
EXPIRED | Token has expired. |
NO_CREDIT | Valid token, but the account has insufficient credit. |
NOT_ALLOWED | Valid token, but not allowed here or now (e.g. this location or EVSE is excluded). |
An unknown token is not an AllowedType: the eMSP returns status code 2004 (Unknown Token), typically with HTTP 404. Treat it as a rejection. An optional info field carries a DisplayText the CPO can show on the charger screen if it has one.
authorization_reference
If the eMSP returns an authorization_reference, the CPO must put it in the resulting Session and CDR. It is how the eMSP ties a session back to a specific authorization decision, and some eMSPs reject or dispute CDRs that lack it. OCPP 1.6 has no field to carry it to the charger, so the CSMS must remember the mapping from (EVSE, token, time) to reference itself. This is covered more in Part 10.
Latency budget
A charger waiting on OCPP Authorize has a timeout, and the driver is standing there. A real-time authorize call that goes CPO, hub, eMSP and back can easily take a second or more. Practical measures:
- Set a short, explicit timeout on the authorize call, and decide in advance what happens on timeout per
whitelistvalue. - For
ALWAYSandALLOWEDtokens, answer the charger from the local store first. - Keep HTTP connections to big partners warm (keep-alive, connection pooling).
- Record every authorize result with its correlation ID; disputes about "the card was rejected" are resolved from these logs.
Field gotchas
- RFID UID formatting. The same card can be read as
04A1B2C3D4E5F6,04a1b2c3d4e5f6, or with byte order reversed, depending on the reader. Normalise to the format the eMSP publishes (most use uppercase hex) before lookup, and if you see systematic misses from one charger model, check byte order. - Length mismatch with OCPP 1.6. OCPP 1.6
idTagis limited to 20 characters; OCPIuidallows 36. App-user UIDs longer than 20 characters cannot be passed to a 1.6 charger as-is, which matters for remote starts. - Stale `valid` flags. If the CPO only pulls tokens nightly, a card blocked at 10:00 keeps working until the next sync. Ask eMSPs to push, and pull with
date_fromfrequently. - Group IDs. If the eMSP sets
group_id, a driver may start with one card and stop with another from the same group. Your stop logic should accept that.
Key takeaways
- The eMSP owns tokens; the CPO receives them by push or pull, keyed on
country_code,party_id,uidandtype. WhitelistTypeis binding policy:ALWAYSlocal only,ALLOWEDeither,ALLOWED_OFFLINEreal-time with offline fallback,NEVERreal-time only.- Real-time authorization returns
AuthorizationInfowith anAllowedType; unknown tokens come back as status2004. - Carry
authorization_referenceinto sessions and CDRs.
Next: Part 6, Sessions and CDRs.

