OCPP server
OCPI server
Supported languages
@evpanda/sdk; the API documented here is 0.1.0. Until it ships, the OCPP session handle and the OCPI adapters are not available — the flat capture calls are.The Python SDK is not on PyPI yet. Install it from source, and note it has no HTTP adapters:Source and API reference
Optional compression dependency
Compression defaults to zstd, which needs one optional package in Node and Python. Without it the SDK falls back to gzip silently.What the SDK does
The SDK is passive. It observes traffic your application already handles — it never sits in the request path, never blocks on the network, and never fails your process if EVPanda is unreachable.Capture never blocks your application
Capture never blocks your application
Memory is capped, and the SDK drops its own data first
Memory is capped, and the SDK drops its own data first
Secrets never leave your process
Secrets never leave your process
Authorization, Cookie, and API keys are dropped at capture. Tokens in OCPI /credentials bodies are masked. See What gets captured.A bad config can't crash your boot
A bad config can't crash your boot
Get an API key
A network is the container for everything EVPanda records about one of your systems, and an API key is scoped to exactly one network.Create a network
Generate a key
evp_sk_… — is shown once. EVPanda stores only a hash of it.Put it in the environment
EVPANDA_API_KEY when no key is passed in code, which keeps the key out of your source.Start a client
The protocol is the client.OCPI and OCPP have separate client types, separate configs, and separate API keys. Start one at boot and share it across your process — each client runs its own background worker and its own buffer.
OCPP for OCPI if that’s what your service speaks. Both clients are safe for concurrent use.
Configuration
endpoint and apiKey are hard-required. Every other option is a tunable: an out-of-range value falls back to its default and says so in the logs, rather than failing.
- Go
- Node
- Python
time.Duration. The buffer is capped by bytes, so the number you set is the memory footprint, not an estimate of it.LogMode is LogModeSilent, LogModeErrors (default), or LogModeDebug. The EVPANDA_LOG environment variable sets it without a code change; an explicit config value wins.Sizing the buffer
The buffer absorbs a delivery stall. Size it for how long you want to survive one:message rate × average size × seconds.
At 400 messages/second and 500 bytes each — roughly a 10,000-charger CSMS — one minute of stalled delivery is about 12 MB. The 32 MiB Go default covers about two and a half minutes of that: enough for a blip, small enough for an ordinary container limit.
How delivery works
One background worker per client owns delivery. It flushes when 1,000 messages are waiting or the flush interval elapses, whichever comes first, compresses the batch, and POSTs it tohttps://ingest.evpanda.io.
Where data can be lost
Five places, all counted:Shut down cleanly
Stop accepting traffic first, then drain. Draining while chargers or partners are still sending is a race you’ll lose.flush() forces an immediate delivery and waits for it — useful in tests and at shutdown, never on a request path.
Check that it’s working
Confirm the client is live, not inert
Start* is nil. In Node and Python, debug is on and no config error appeared at startup.Drive real traffic
Wait one flush interval
flush().Check the dashboard
Check both directions
Counters and logs
Go exposes the delivery counters directly, on any client — including an inert or closed one:debug to get the same picture in your logs, and use the dashboard’s message volume to confirm an integration is healthy.What to alert on
Troubleshooting
Nothing arrives, and Captured is 0
Nothing arrives, and Captured is 0
debug (Node, Python). Confirm EVPANDA_API_KEY is set in the environment your service actually runs in — set in your shell, absent from the container is the classic miss — and that endpoint is https://ingest.evpanda.io with no path.Nothing arrives, and DroppedInvalid is climbing
Nothing arrives, and DroppedInvalid is climbing
Nothing arrives, and DroppedUndeliverable is climbing
Nothing arrives, and DroppedUndeliverable is climbing
ingest.evpanda.io on port 443. If it succeeds, suspect the key: a revoked or wrong-protocol key returns 401, which is permanent and never retried. An OCPP client needs a charger network’s key; an OCPI client needs a roaming network’s key.Messages deliver but don't show up
Messages deliver but don't show up
{"captured": N, "failed": M}. Messages counted in failed were rejected after a successful delivery.Usual causes, in order:- An OCPP message with no tenant pair. The API currently requires
tenant_idandtenant_nameon OCPP messages even though the SDKs treat them as optional. - An OCPI exchange with no status code. It must be between 100 and 599; there is no null form.
- An identity field over its limit. Tenant name is 32 characters; the rest are 64.
Only half the traffic appears
Only half the traffic appears
Bodies are missing or truncated
Bodies are missing or truncated
maxCaptureBytes (64 KiB by default) drops the whole message rather than storing a truncated one. Raise the cap — 256 KiB is reasonable for a roaming server — and raise the buffer ceiling with it.Memory grew after adding the SDK
Memory grew after adding the SDK
No SDK for your language?
The ingestion API is a small HTTP contract: two routes, one auth header, one response shape.POST /v1/ocpp and POST /v1/ocpi each take up to 1,000 messages per batch, optionally gzip- or zstd-compressed, capped at 5 MB compressed and 20 MB decompressed. Mail support@evpanda.io for the full message schemas.
If you implement your own client, copy the behaviors that make an SDK safe on a hot path: never block the caller, bound your memory, batch, retry only 5xx and network errors, redact before you buffer, and count what you drop.