Model
The direction follows from the method you call, so there is no argument to get backwards.Adapters
The adapters assemble the exchange for you: collect headers and bodies, resolve the partner, call the right method. A request with no resolvable identity is served exactly as it would have been, just not captured.- Go
- Node
- Python
Telling the adapters who the partner is
Your OCPI server already knows: you looked the partner up by their Token A or Token B to decide whether to serve the request. Stamp the identity there. Each SDK reads three carriers in order, and falls back to theX-EVPanda-* headers last.
- Go
- Node
- Python
Stamp the request context in the middleware that authenticates the partner.Outbound, stamp the context on the request you are about to send:
The X-EVPanda headers
If nothing else carries the identity, every adapter reads these, matched case insensitively.
Partners never send them and never receive them: the outbound adapters strip all four before dispatch.
Custom resolvers
If identity lives somewhere else entirely, a client certificate or a path prefix, pass your own resolver. Returning nothing means the exchange is not captured, and the request is never blocked or altered.Complete example
- Go
- Node
- Python
server.go
Per-SDK notes
Go: auth must wrap capture
Go: auth must wrap capture
The middleware resolves identity up front, before the handler runs, so the auth layer that stamps the context has to be outside it. Node and Python read their carrier when the response finishes, which is why mount order is free there.
Node: mount a body parser first
Node: mount a body parser first
The express adapter reads the request body from
req.body rather than teeing the raw stream, since 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 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.Node: axios and fetch are separate worlds
Node: axios and fetch are separate worlds
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.Python: the WSGI adapter reads the body up front
Python: the WSGI adapter reads the body up front
WSGI gives no way to observe a body the handler never reads, so the WSGI middleware takes it at the start of the request. That is a copy per in-flight request, bounded by
max_capture_bytes. The ASGI adapter does not pay it.Close your response bodies
Close your response bodies
Outbound capture completes when the response body is read to the end or closed. A leaked body is a message that never ships. A call that fails at the transport layer captures nothing, because there was no exchange.
Capturing without an adapter
When no adapter fits, build the exchange yourself.captureOutboundMessage is the same call for the other direction. Both are non-blocking and never raise.
In Go, use SetRequestBody and SetResponseBody rather than assigning the fields: they copy, so you can reuse a pooled buffer as soon as the call returns.
Capture extra headers
OCPI headers pass through an allowlist, soAuthorization and everything else unlisted is dropped at capture. ocpiAllowedHeaders extends the list and can never shrink it.
Common mistakes
Next
Data flow
Exchange shape, the header allowlist, credentials masking.
Getting started
Config, stats, shutdown, troubleshooting.