To test OCPI code, an eMSP needs a CPO on the other end that hands out credentials, answers like a real one and has a charger behind it. SimItNow can be that counterpart. It plays the CPO and your platform plays the eMSP. The connection is direct, there is no roaming hub, and the protocol version is OCPI 2.2.1.

This guide follows the order a developer would work in: set up a chargepoint, get Token A, run the credentials handshake, read locations and tariffs, push a token, start and stop a session, then check what arrived. It is a test environment, not a live roaming network. The locations are generated, there is one tariff, and no money changes hands.

What you need first

1. Set up a chargepoint

OCPI locations come from the chargepoints registered on your private hosted CSMS. A chargepoint that connects to an open CSMS has no registration, so it cannot appear as a location. Switch your CSMS to private first. The hosted CSMS guide explains both modes.

curl -X PUT https://api.simitnow.com/v2/csms \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"public":false}'

Then register a chargepoint. The response contains a password that is shown once.

curl -X PUT https://api.simitnow.com/v2/csms/cp/CP-001 \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"displayName":"OCPI test chargepoint"}'

A real OCPP 1.6 charger gets the connection URL from that response. Without one, create a simulated station that dials in to your hosted CSMS with the same id and the password:

curl -X PUT https://api.simitnow.com/v2/cs/CP-001 \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "csmsUrl": "wss://csms.simitnow.com/ocpp/AbCdEf0123456789",
  "protocol": "ocpp1.6",
  "security": {
    "basicAuth": {
      "password": "password-from-the-registration"
    }
  }
}'

Put your own csmsId in the URL. It is in the response of the first call. The chargepoint id, CP-001 here, becomes the OCPI location id, and a chargepoint id longer than 34 characters is left out of the location list.

2. Get Token A and the versions URL

Open the OCPI tab on the dashboard. It shows the versions URL, https://ocpi.simitnow.com/ocpi/versions, and the country code and party ID SimItNow uses as the CPO. Choose Generate Token A and copy it. It is shown once, because only a hash is stored, and generating a new one invalidates the old one.

Token A is for the handshake. It can read the versions routes, and a successful credentials call spends it.

3. Run the credentials handshake

Your eMSP does this itself. The curl calls below show what it sends, so you can also try the handshake by hand. Authenticate with Authorization: Token <Token A>. A base64 encoded token, as OCPI 2.2.1 sends it, works too. First the versions list and the 2.2.1 endpoints:

curl https://ocpi.simitnow.com/ocpi/versions \
  -H "Authorization: Token $TOKEN_A"
curl https://ocpi.simitnow.com/ocpi/2.2.1 \
  -H "Authorization: Token $TOKEN_A"

Then post your credentials: a token of your own (Token C), your versions URL and your role. SimItNow sends Token C back when it calls you, base64 encoded as OCPI 2.2.1 does, so your eMSP has to decode it before it compares.

curl -X POST https://ocpi.simitnow.com/ocpi/2.2.1/credentials \
  -H "Authorization: Token $TOKEN_A" \
  -H "Content-Type: application/json" \
  -d '{
  "token": "your-token-c",
  "url": "https://emsp.example.com/ocpi/versions",
  "roles": [
    {
      "role": "EMSP",
      "country_code": "DE",
      "party_id": "ABC",
      "business_details": {
        "name": "Example eMSP"
      }
    }
  ]
}'

Before it accepts, SimItNow calls your versions URL with Token C, finds the 2.2.1 entry, reads your endpoints and checks that each one it uses is a public https address. The reply carries Token B. Your eMSP uses Token B for every later call, and Token A stops working. Token B is shown once in this reply, and SimItNow stores only a hash of it.

A handshake that SimItNow refuses or cannot complete does not spend Token A, so you can fix the problem and send the same call again. To receive sessions and CDRs later, your 2.2.1 module list must include the sessions and cdrs receiver endpoints.

4. Read locations and tariffs

From here on, send Token B.

curl https://ocpi.simitnow.com/ocpi/2.2.1/locations \
  -H "Authorization: Token $TOKEN_B"
curl https://ocpi.simitnow.com/ocpi/2.2.1/tariffs \
  -H "Authorization: Token $TOKEN_B"

Each registered chargepoint is one location with the chargepoint id as its id, one EVSE with the uid CP-001-1, and one connector with id 1, a Type 2 socket on three-phase AC at 400 V and 32 A. The EVSE is AVAILABLE while the chargepoint is connected. The address is an invented site in Istanbul, with coordinates derived from the id.

There is one tariff for everyone, SIMITNOW-DEFAULT: EUR 0.30 per kWh of energy and nothing else. Lists accept offset, limit, date_from and date_to, return X-Total-Count, and default to 50 items per page with a maximum of 100.

5. Push a token

SimItNow is the receiver for tokens, so your eMSP pushes them with PUT or PATCH. The country code and party ID in the path must be the ones you registered in the handshake, and the uid can have at most 36 characters.

curl -X PUT https://ocpi.simitnow.com/ocpi/2.2.1/tokens/DE/ABC/TOKEN-0001 \
  -H "Authorization: Token $TOKEN_B" \
  -H "Content-Type: application/json" \
  -d '{
  "country_code": "DE",
  "party_id": "ABC",
  "uid": "TOKEN-0001",
  "type": "RFID",
  "contract_id": "DE-ABC-C00000001-X",
  "issuer": "Example eMSP",
  "valid": true,
  "whitelist": "ALLOWED",
  "last_updated": "2026-10-30T10:00:00Z"
}'

All nine fields are required. A token with a missing or malformed field is refused with OCPI status 2001 and a message that names the fields without quoting their values. A push is not strictly needed before a start, because START_SESSION carries its own token. When you did push the token, SimItNow judges the pushed copy. Either way the token must be valid and its whitelist must not be NEVER.

6. Start a session

curl -X POST https://ocpi.simitnow.com/ocpi/2.2.1/commands/START_SESSION \
  -H "Authorization: Token $TOKEN_B" \
  -H "Content-Type: application/json" \
  -d '{
  "response_url": "https://emsp.example.com/ocpi/emsp/2.2.1/commands/START_SESSION/1",
  "token": {
    "country_code": "DE",
    "party_id": "ABC",
    "uid": "TOKEN-0001",
    "type": "RFID",
    "contract_id": "DE-ABC-C00000001-X",
    "issuer": "Example eMSP",
    "valid": true,
    "whitelist": "ALLOWED",
    "last_updated": "2026-10-30T10:00:00Z"
  },
  "location_id": "CP-001",
  "authorization_reference": "AUTH-0001"
}'

The location_id is the chargepoint id. The response_url must be on the same origin (scheme, host and port) as your versions URL or one of your endpoints, because SimItNow sends Token C to it. Otherwise the call fails with 2001 and nothing runs.

The answer comes at once and only promises an attempt:

{"data":{"result":"ACCEPTED","timeout":30},"status_code":1000,"status_message":"Success","timestamp":"..."}

The result arrives later, as a CommandResult that SimItNow posts to your response_url within that timeout of 30 seconds. SimItNow asks the station to start remotely and reports what the station said:

Some starts are answered REJECTED immediately instead: no registered eMSP, a location that is not one of your chargepoints, a token that is not valid or is whitelisted NEVER, a session already pending or active on that chargepoint, or an authorization_reference that was used before. A reference is spent even by a start that failed, so use a new one for each attempt.

An accepted start is not yet a session. The station accepts the remote start at once, but the session only begins when an EV connects. Create a virtual EV and connect it to the chargepoint within 5 minutes. A session that has not begun by then is marked INVALID.

curl -X PUT https://api.simitnow.com/v2/ev/EV-01 \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"batteryCapacity":60}'
curl -X POST https://api.simitnow.com/v2/ev/EV-01/connection \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cbId":"CP-001","connectorId":1}'

SimItNow pushes the session to your sessions endpoint while it is still PENDING, and again each time it changes: ACTIVE once the EV is connected, then updates about every meter sample (30 seconds by default on a simulated station), then COMPLETED, or INVALID if no EV came. Each push is a PUT to /{country_code}/{party_id}/{session_id} under your sessions endpoint, where the country code and party ID are SimItNow's CPO identity from the OCPI tab, not yours. Your eMSP has to handle all four statuses. You can also read sessions yourself, and date_from is required for that call:

curl "https://ocpi.simitnow.com/ocpi/2.2.1/sessions?date_from=2026-10-30T00:00:00Z" \
  -H "Authorization: Token $TOKEN_B"

7. Stop the session and get the CDR

Take the session id from the session you received and send STOP_SESSION. Only an ACTIVE session can be stopped. For anything else the answer is UNKNOWN_SESSION.

curl -X POST https://ocpi.simitnow.com/ocpi/2.2.1/commands/STOP_SESSION \
  -H "Authorization: Token $TOKEN_B" \
  -H "Content-Type: application/json" \
  -d '{
  "response_url": "https://emsp.example.com/ocpi/emsp/2.2.1/commands/STOP_SESSION/1",
  "session_id": "the-session-id"
}'

The CommandResult arrives the same way as the start result. The session becomes COMPLETED, and the CDR is posted to your cdrs endpoint, priced as the energy delivered at the default tariff. You can read it as well. A session that ends INVALID produces no CDR.

SimItNow counts a session or CDR push as delivered only when your endpoint answers HTTP 2xx with an OCPI status of 1000, or with no OCPI status at all. Anything else is retried with a growing delay, and each retry counts as a message. A CommandResult is posted once and not retried.

curl https://ocpi.simitnow.com/ocpi/2.2.1/cdrs \
  -H "Authorization: Token $TOKEN_B"

8. Read the checklist

The OCPI tab turns your calls into a readiness checklist of ten steps: Credentials handshake, Versions, Locations, Tariffs, Token push, Start a session, Command result delivered, Session visible, Stop the session and CDR delivered. Each is marked Passed, Failed or Not run.

Below the checklist, the tab lists your last 100 calls, in both directions, with method, path, HTTP and OCPI status and a note. Calls with a wrong or already used token are refused and not listed, because SimItNow cannot tell which account they belong to.

When something goes wrong

What you seeLikely cause
Handshake answers 401Token A was wrong or already spent. Generate a new one.
Handshake answers 502 with status 3001SimItNow could not use your API: your versions URL or details did not answer, or had no 2.2.1 entry.
Handshake answers 400 with status 2001One of your URLs is not a public https address, or the credentials body is malformed. The message names what is wrong.
Handshake answers 405You are already registered. Send a PUT with Token B to update the credentials.
Locations is emptyThe CSMS is open, the chargepoint is not registered, or its id is longer than 34 characters.
START_SESSION answers 2001The response_url is not on your registered origin.
CommandResult is FAILEDThe chargepoint is not connected. Check the station before your eMSP.
Session is INVALIDNo EV connected within 5 minutes, or the station refused the start.
Sessions and CDRs are not pushedYour module list lacks the sessions or cdrs receiver endpoint. They stay available to read.

The daily allowance

Every authenticated OCPI call counts as one message, in either direction, and OCPI shares one daily counter with your OCPP messages. That includes the command results and each session update SimItNow sends you, and the station's heartbeats count too. The free plan allows 500 messages a day for its one chargepoint. A short test session fits: SimItNow's own end-to-end test runs this whole sequence under 500 messages, heartbeats included. A station left connected all day does not fit. The hosted CSMS asks for a heartbeat every 5 minutes by default, and each heartbeat is two messages, so heartbeats alone come to 576 a day. While a session charges, meter values and session updates add more, about every 30 seconds by default.

A call past the allowance and its 10% grace is refused with HTTP 429, and going over also closes your OCPP chargepoints with WebSocket close code 4429. The Solo plan is the plan for real testing: 5 chargepoints and 2,500 messages a day.

If the allowance runs out during a session, the station is closed and cannot take STOP_SESSION. The command is accepted, its CommandResult is FAILED, and the session stays ACTIVE until it is marked INVALID after 6 hours, without a CDR.

What it does not do

The reference is in the OCPI section of the API docs. Create a free account to get your Token A.