OCPI MODULE · UPDATED 11 AUG 2026

Credentials: the registration handshake

Registration is where two companies stop being strangers. It is a three-token dance that every OCPI integration performs exactly once per partner — and that a surprising number of teams get wrong in the same specific way.

PART OF THE OCPI 2.2.1 COMPLETE GUIDE · 15 MODULE & COMMAND GUIDES

IN ONE PARAGRAPH

OCPI registration replaces a temporary token issued out of band with a permanent pair of tokens, one for each direction. Token A gets you in the door once. You use it to POST your own Token B to the partner. Their response hands you Token C, which is what you use from then on. Token A dies at that moment, and the mistake most teams make is continuing to use it.

What registration actually establishes

Two things. First, a mutual authentication credential — each party ends up holding a token it uses to call the other. OCPI connections are not symmetric with a shared secret; each direction has its own token, and either side can rotate its own without coordinating.

Second, an identity. The Credentials object carries the party's country_code and party_id, and in OCPI 2.2.1 a roles array describing every role that party plays. That identity is what appears in routing headers on every subsequent request, and it is what your authorisation logic keys on.

The three tokens

The naming is confusing because the letters describe sequence, not ownership. Read it as: A is temporary, B is mine, C is theirs.

TokenCreated byPurposeLifetime
Token AParty X (the inviter)Handed to Party Y out of band — email, onboarding portal, signed document. Y uses it for exactly two calls: read X's versions, then POST credentials.Dies the instant registration completes
Token BParty YSent in Y's credentials POST body. X uses it for every call it makes to Y, forever.Until rotated
Token CParty XReturned in the response to that POST. Y uses it for every call it makes to X, forever.Until rotated

The handshake, step by step

Assume a CPO (party X) is inviting an eMSP (party Y).

  1. Out of band: the CPO generates Token A and sends it to the eMSP with its versions URL. This is the only step that is not an API call.
  2. The eMSP reads the CPO's versions using Token A, picks a version, and fetches the endpoint directory to find the CPO's credentials URL.
  3. The eMSP POSTs its own Credentials object to that URL, authenticating with Token A. The body contains Token B and the eMSP's own versions URL.
  4. The CPO fetches the eMSP's versions using Token B, confirming the eMSP is reachable and speaks a compatible version.
  5. The CPO responds to the POST with its own Credentials object containing Token C. Token A is now invalid.

Step 4 is the one people skip. The CPO is expected to validate the eMSP before returning Token C. Skipping it means you can complete a registration with a partner whose endpoints do not work, and you will not discover it until the first live transaction.

The request and response

The eMSP's POST — note that roles is an array, which is what allows a single connection to represent a party operating as both CPO and eMSP:

POST /ocpi/2.2.1/credentials
Authorization: Token <base64 of Token A>
Content-Type: application/json

{
  "token": "9b8c1e44-3a7d-4f21-a0c5-77e2d6f1b930",
  "url": "https://emsp.example.com/ocpi/versions",
  "roles": [
    {
      "role": "EMSP",
      "party_id": "EMS",
      "country_code": "IN",
      "business_details": {
        "name": "Example Mobility Services",
        "website": "https://emsp.example.com",
        "logo": {
          "url": "https://emsp.example.com/logo.png",
          "category": "OPERATOR",
          "type": "png", "width": 512, "height": 512
        }
      }
    }
  ]
}

The CPO's response, carrying Token C:

HTTP/1.1 200 OK

{
  "data": {
    "token": "4d21f7a9-8e35-4c60-b1af-2ce9047ab5d1",
    "url": "https://cpo.example.com/ocpi/versions",
    "roles": [
      {
        "role": "CPO",
        "party_id": "EFI",
        "country_code": "IN",
        "business_details": { "name": "Example Charging Operator" }
      }
    ]
  },
  "status_code": 1000,
  "status_message": "Success",
  "timestamp": "2026-08-11T09:07:44Z"
}

Updating and revoking

The module supports four verbs on the same URL, and the distinction matters operationally:

VerbMeaningWhen you use it
GETReturn the credentials the other party is currently using for youDiagnostics — confirming which token a partner holds
POSTRegister for the first timeOnce per partner, with Token A
PUTReplace existing credentials — token rotationKey rotation, or after a suspected leak
DELETEUnregister; both tokens become invalidOffboarding a partner

PUT is the rotation mechanism and it is under-used. A credentials token that has never been rotated since onboarding is a standing security finding. PUT lets either side issue a fresh token without tearing down and rebuilding the registration, so rotation costs one call and no downtime.

DELETE is genuinely destructive and asymmetric in its consequences: in-flight sessions and undelivered CDRs will fail once the tokens are void. Settle outstanding CDRs before you unregister a partner.

Error handling

StatusMeaningTypical cause
2000Generic client errorMalformed credentials object
2001Invalid or missing parametersMissing roles, or party_id not exactly 3 characters
2002Not enough informationurl omitted, so the partner cannot call back
3001Unable to use the client's APIStep 4 failed — your versions endpoint was unreachable with Token B
3002Unsupported versionNo common version between the parties
HTTP 405Method not allowedPOST attempted on an already-registered connection — use PUT

The 3001 case is worth calling out. It means the partner tried to validate you and could not reach you. Nine times out of ten it is a firewall rule or an IP allowlist that was never opened in the outbound direction, and it is diagnosed from their logs, not yours.

Production lessons

  • Treat Token A as single-use and short-lived. Give it an expiry. A registration token sitting valid in an old onboarding email for a year is a credential leak waiting to be found in an audit.
  • Never store Token A after registration completes. It has no further purpose. Teams that keep it around inevitably have code that falls back to it.
  • Validate the partner in step 4 without exception. Fetching their versions endpoint before returning Token C is the only automated check that the connection works in both directions.
  • Rotate on a schedule using PUT. Build rotation into the platform from day one; retrofitting it across thirty live partner connections is far harder than designing it in.
  • Do not treat party_id as globally unique. It is only unique within a country_code. The primary key for a partner is the pair, and assuming otherwise breaks the first time you onboard across borders.
  • Log registration events immutably. When a partner disputes when a connection changed, an append-only registration log settles it. This is also what a compliance audit asks for first.

Frequently asked questions

What are Token A, B and C in OCPI?

Token A is a temporary registration token handed over out of band; it is used only to read the partner's versions and to POST credentials, then it expires. Token B is generated by the registering party and sent in that POST; the other side uses it thereafter. Token C is returned in the response and is what the registering party uses from then on.

Does Token A stay valid after OCPI registration?

No. Token A becomes invalid the moment registration completes successfully. It should be discarded, not stored. Any code path that falls back to Token A after registration is a defect.

How do you rotate an OCPI credentials token?

Send a PUT to the credentials endpoint with a new token. This replaces the existing credentials without tearing down the registration, so there is no downtime and no need to repeat the Token A handshake.

What does the roles array in OCPI 2.2.1 credentials do?

It lets a single connection declare every role a party plays — CPO, EMSP, HUB, NSP, SCSP or OTHER — each with its own party_id, country_code and business details. Earlier OCPI versions supported only one role per connection.

Why does OCPI registration fail with status 3001?

Status 3001 means the partner could not reach your API when validating you with Token B. It is nearly always an outbound firewall rule or IP allowlist that was never opened, and it has to be diagnosed from the partner's logs.