Metadata
Metadata lets your application keep its own business attributes on Ivora
objects: which site host owns a charger, a revenue share percentage, a
contract ID. Ivora stores the pairs and returns them on every read, webhook and
agent tool result; your systems keep the logic. Semantics follow Stripe's
metadata.
Diagram source
flowchart LR
App[Your application] -->|create or PATCH metadata| Station[Station or location]
Station -->|copied when it starts| Snapshot["Session, bill or transaction: station_metadata, location_metadata"]
App -->|filter by key and value| Lists[List filters]
Snapshot --> Lists
Station -.->|never| OCPI[OCPI roaming partners]Objects that carry metadata
| Object | Write it with | Scope |
|---|---|---|
| Station | POST /v1/tenants/{tenant_id}/stations, PATCH …/stations/{station_id} | stations:write |
| Location | POST /v1/tenants/{tenant_id}/locations, PATCH …/locations/{location_id} | stations:write |
| Project | POST /v1/teams/{team_id}/projects, PATCH /v1/tenants/{tenant_id} | the team's owners and admins (projects:manage) |
| Tariff | POST /v1/tenants/{tenant_id}/tariffs, PATCH …/tariffs/{tariff_id} | tariffs:write |
| RFID token | POST /v1/tenants/{tenant_id}/tokens, PATCH …/tokens/{token_id} | tokens:write |
| Bill | POST /v1/tenants/{tenant_id}/bills, PATCH …/bills/{bill_id} | billing:write |
| Charging session (application-managed) | POST /v1/tenants/{tenant_id}/charging-sessions, PATCH …/charging-sessions/{session_id} | billing:write |
A charging session is its bill: the session and its bill show the same
metadata, and changing one changes the other. Metadata extends
external_reference, which stays: use external_reference for the one
identifier you look an object up by, and metadata for everything else.
Transactions are the charger's own records and have no metadata of their own; they carry the snapshot below.
Set, merge and delete
metadata is an object of string keys and string values.
- Create sets the pairs given.
- PATCH merges: keys you send are set, keys you leave out keep their
values, and a key sent as
""ornullis deleted. Sending onlymetadatachanges nothing else on the object.
curl -X PATCH https://api.ivoracharge.com/v1/tenants/18/stations/1042 \
-H "Authorization: Bearer $IVORA_API_KEY" -H "Idempotency-Key: host-move-1042-01" \
-H "Content-Type: application/json" \
-d '{"metadata": {"host_id": "h-17", "revenue_share_pct": "30", "old_contract": null}}'
Every write is idempotent with its Idempotency-Key like the object's other
writes. Station edits answer 202 with an operation whose result is the
station; the other objects answer with the object itself.
Limits
| Rule | Limit | Error |
|---|---|---|
| Keys per object (after a merge) | 50 | 422 metadata_too_many_keys (details.limit, details.keys) |
| Key | 1–40 characters of A–Z a–z 0–9 _ . - | 422 metadata_key_invalid |
| Value | a string of at most 500 characters | 422 metadata_value_invalid |
Values are strings only: numbers, booleans and nested objects are refused, so
send "30", not 30. A refused request changes nothing. See
Errors and statuses.
Not for secrets or personal data. Metadata is returned to everyone who can read the object, delivered in webhooks and written to the project's audit log. Never store card data, credentials, driver names, emails or phone numbers.
Snapshots: attribution that survives edits
When a bill or a charging session is created, and when the charger starts a
transaction, Ivora copies the station's and its location's metadata into
station_metadata and location_metadata. The snapshot never changes
afterwards, so a monthly report by site host stays correct after a charger
changes hosts. The session's or bill's own metadata stays writable.
| Object | station_metadata, location_metadata taken |
|---|---|
| Bill, charging session | When it is created |
| Transaction | When the charging core records it (within seconds of the start). Transactions that started before snapshots existed have {} |
An object whose station or location had no metadata has {}. Deleting a
station or location deletes its own metadata but never a snapshot.
Filter lists
These lists accept metadata[key]=value query parameters: an exact match,
combined with AND across keys and with the list's other filters (at most 10
metadata filters). The filters run on indexed values, so per-host queries stay
fast, and pages stay full.
| List | metadata[key] matches | Snapshot filters |
|---|---|---|
GET …/stations | the station's metadata | — |
GET …/locations | the location's metadata | — |
GET …/bills | the bill's metadata | station_metadata[key], location_metadata[key] |
GET …/charging-sessions | the session's metadata | station_metadata[key], location_metadata[key] |
GET …/transactions | the station's metadata now | station_metadata[key], location_metadata[key] (at the start) |
# A site host's sessions in October, attributed as they were when each one started.
curl -G https://api.ivoracharge.com/v1/tenants/18/transactions \
-H "Authorization: Bearer $IVORA_API_KEY" \
--data-urlencode "station_metadata[host_id]=h-17" \
--data-urlencode "started_from=2026-10-01T00:00:00Z" \
--data-urlencode "started_to=2026-11-01T00:00:00Z"
A malformed filter (a key outside the rules, an empty value, the same key with
two values) answers 400 invalid_request. In the OpenAPI document the filters
are deepObject query parameters named metadata, station_metadata and
location_metadata; agent tools take them as objects.
Where metadata appears
- Reads and lists of every object above.
- Webhooks: events that carry an object carry its metadata:
token.*(token),tariff.updatedandtariff.archived(tariff; a metadata change is inchanges.metadata),bill.finalized(the bill with its snapshot),charging_session.status_changed(session) andoperation.completedof a station edit (result). - AI agents: the MCP tools return it, list tools take the filters as
objects, and
update_bill_metadataandupdate_external_session_metadatachange a bill's or session's metadata (Agent integration). - Audit log: every change records a
metadata.updatedentry in the project's audit log with the object, the keys set (values cut to 100 characters) and the keys deleted (Audit log).
Never published to roaming partners
OCPI partners never receive metadata: locations, EVSEs, tariffs, tokens, sessions and charge detail records are published without it, whatever its keys. Other projects never see it either: metadata belongs to its project and is read and written with the project's own access only.



