Skip to main content
OCPI is symmetric: you are a server to your roaming partners and a client to them, often within the same request. EVPanda records both sides.
Everything shared across protocols — installing the SDK, getting an API key, configuration, monitoring, shutdown — is in Getting started. This page is the OCPI-specific part.

The model

The direction is never something you pass. It follows from the method you call.
In both directions, identity is the partner on the other side of the exchange — never your own platform. When Acme pushes you a CDR, the identity is Acme. When you pull Acme’s locations, the identity is still Acme.

Where identity comes from

Your OCPI server already knows who is calling: you looked the partner up by their Token A or Token B to decide whether to serve the request at all. That lookup is your identity source. The order matters. Adapters resolve identity once per request, so they must run after whatever authenticates the partner. Mounted the other way around, no request has an identity yet, every message is dropped, and you see nothing in the dashboard.
A request with no resolvable identity is served exactly as it would have been. The adapters never block, alter, or fail a request on EVPanda’s account — they simply don’t capture it.

What ships per language

The Go middleware is the stdlib shape, so it drops into net/http, chi, and gorilla/mux directly and into echo and gin through their wrappers. The Node express adapter is typed against node:http, so it works on connect too, with no express dependency. For anything else — koa, hono, fastify, FastAPI, Django — skip the adapter and call the capture methods yourself. It is a few lines, and you hand the identity over directly instead of resolving it.

Integration steps

1

Start one client at boot

The client needs a roaming network API key. A charger network’s key is rejected.
2

Teach the SDK how to find the partner

Each SDK has its own hook into your existing partner lookup.
Returning nothing — or an identity that fails validation, such as an empty platform name — means the exchange is not captured. The request itself is never blocked.
3

Capture inbound traffic

Mount capture after your auth layer.
The Go middleware records the request body as your handler reads it, tees the response on its way out, and ships one message when the handler returns — including when the handler panics, so the exchange that broke your server is still recorded.
4

Capture outbound traffic

Instrument the HTTP client your OCPI calls actually go through.
Close your response bodies. Outbound capture completes when the body is read to the end or closed, so a leaked body is also a message that never ships. A call that fails at the transport layer — DNS, connection refused, timeout — captures nothing: there was no exchange to record.

A complete example

server.go
Custom resolvers. If identity lives somewhere the default resolver can’t see — a client certificate, a path prefix, a subdomain — pass your own. Returning false means “don’t capture this one”.
Other routers. The middleware is the stdlib shape, so it composes with everything:

Per-SDK notes

The default resolver reads the request context first, then falls back to X-EVPanda-* headers. The context is the better hook: it keeps identity out of the HTTP layer entirely and can’t be spoofed by a partner who guessed the header names.The round tripper’s only change to your request is stripping those four headers before dispatch, so a partner never receives them — your tenant names stay internal.
The express adapter reads the request body from req.body rather than teeing the raw stream: a data listener would flip the stream to flowing mode and could starve your own parser. Without a parser, exchanges are captured with no request body.express.raw() gives you the exact bytes off the wire; express.json() gives a re-serialized form, which is usually fine and occasionally differs in key order or whitespace.
In Node, axios goes through node:http and never fetch. Wrapping one captures nothing from the other. Instrument whichever client your OCPI calls actually use, or both.
Build the HttpExchange yourself, as shown above. The capture methods are a stable API; the adapters, when they land, will be a convenience on top of them.For httpx, an event hook on response also works, as long as you call await response.aread() first. A wrapper function is easier to reason about and gives you one obvious place to attach identity.
Every SDK’s default resolver understands the same four headers, matched case-insensitively — they exist so a host can stamp identity in middleware it already has, without writing a resolver.Partners never send them and never receive them: the outbound adapters strip all four before dispatch.

Capturing without the adapters

When no adapter fits — koa, hono, fastify, a message queue, a replay tool — build the exchange yourself. Both methods take an identity and an HTTP exchange; the method name sets the direction.
captureOutboundMessage is the same call for the other direction. Both are non-blocking and never raise back at you.
Go: use SetRequestBody / SetResponseBody rather than assigning the fields. They copy, so you can reuse a pooled buffer the moment the call returns. Assigning directly hands the SDK memory you might overwrite before the next flush.
Always set a status code. The SDKs let you omit it, but the ingestion API requires a value between 100 and 599 and rejects the message without one — after a successful delivery, so nothing in the SDK reports the loss. For an exchange that never produced a response, skip the capture instead.

Capture extra headers

OCPI headers pass through an allowlist, so Authorization, Cookie, and everything else unlisted is dropped at capture. The list can be extended, never shrunk.
Only add headers you are certain carry no secret — an allowlisted header is stored and displayed exactly as it arrived. See What gets captured for the defaults.

Common mistakes

Next

What gets captured

The exchange shape, the header allowlist, and credentials masking.

Getting started

Configuration, monitoring, shutdown, and troubleshooting.