Skip to main content
A custom journey is a charging journey described as JSON: the same thing the Charging Journeys builder produces, written down so a human or an agent can version it, diff it, and replay it from CI. Import one and it opens ready to run: a template-shaped file opens with every knob restored, and a file with its own hand-authored action list opens with that list shown, its values editable in the builder (more on the difference below). Either way it streams evidence and lands a report exactly like a catalogue journey. The fastest way to get a valid file is Download sample on the Charging Journeys page. It exports a ready-to-run custom journey for your selected target. Edit that, or write one from the shape below.

The shape

The knobs that matter

  • journey: the template the custom journey starts from (e.g. first-successful-charge). It selects the story; an unknown id falls back to the recommended journey.
  • ocpiVersion and targetId are advisory in the file. On import the builder runs the journey against whichever target you have selected, so a sample from one target still runs against another, and you do not need a real targetId to share a file.
  • clock.mode: accelerated compresses a session into seconds; real runs it at wall-clock cadence for long-session testing.
  • session.cadenceSeconds and jitterSeconds: how often meter updates are pushed, and how irregular they are (jitterSeconds above 0 means irregular).
  • session.updates: which fields each update carries (kwh, total_cost, status, charging_periods).
  • disturbances: faults injected mid-journey to prove the endpoint fails safely. The full set is under Disturbances below.
  • coverageDepth: happy-path, edge-cases, or adversarial.
  • seed: makes a run reproducible; keep it stable to replay the same story.
Unknown or malformed fields fall back to sensible defaults, so a partial file still imports and uses the defaults for anything you left out.

How do I write my own timeline?

There is one exception to that. A file that spells out its own timed actions list carries those hand-authored actions into the builder, as the timeline you wrote rather than the knob editor above. The Configure step then shows each action’s time, kind, and values, in file order. Click any value to change it in place. Where an OCPI version defines the field, the builder names the spec file it comes from. Timing comes from the file. You can still change the endpoint, the clock, and the seed, and the run streams and grades exactly like any other custom journey. To change an action’s kind, its time, or the order of the list, edit the JSON and import it again. One thing to watch: the run executes each action at its own atSeconds, not in list order. Keep the list chronological if you want to read it the way it will run. A file that says journey: 'custom' but lists no actions is refused with a message, rather than imported and quietly emptied. Asking for a custom journey is not something the importer should discard on your behalf.

Journeys, packs and what grades them

Three ids in the file name what you are running rather than what it does.

What can I put in journey?

journey picks the story to start from. An unknown id falls back to the recommended journey rather than failing. first-successful-charge, rough-session, charger-reset, partner-connection, catalogue-trust, remote-start-stop, live-price-parking, reservation, cdr-desk, offline-catch-up, hub-routed-charge, book-arrive-charge, direct-terminal-payment, smart-charging-control, hub-connection-status, invalid-charging-data, and custom for a timeline you write yourself.

What does journeyPack group?

journeyPack groups journeys by the part of OCPI they exercise: core-charging, foundation, catalogue, commands, billing, recovery, hub, booking, direct-payment, charging-profiles, hub-client-info, negative.

What does oracleIds do?

oracleIds on an action names the checks that judge it. An oracle is the thing that decides whether what came back was right, so this is how a hand-authored action earns a verdict instead of merely happening: ocpi_response_success, session_identity, session_progress, session_completed, cdr_identity, session_cdr_reconciliation, catalogue_identity, command_response, command_result, billing_reconciliation, pull_window, hub_routing, booking_lifecycle, payment_reconciliation, charging_profile_result, hub_client_status. An action may carry up to 12, and the field is optional: leave it out and the action runs without a check of its own.

The actions you can write

An action is one thing the runner does at one moment. Every entry needs three things: what to do, when, and which scenarios it provides evidence for.

Why is scenarioIds required?

scenarioIds is required on every action, between 1 and 12 of them, each a capital letter followed by two digits (S01, G13). They are what tie an action to the scenarios your report is graded against: an action with no scenario would run and prove nothing, so the schema does not accept one. A file missing them is refused on import rather than run. atSeconds counts from the start of the run, up to 48 hours. Actions execute at their own time, not in list order, so keep the list chronological if you want to read it the way it will run. Most kinds are named module_verb and send that request to that module, so tariff_put puts a Tariff and session_get reads a Session back. Once you know the pattern you can mostly guess the name you want: The handshake and discovery versions_get, version_details_get, credentials_get A charging session, start to finish prepare_token, session_put, session_patch, session_complete, session_get The catalogue your partner reads location_get, location_put, location_patch, tariff_get, tariff_put, tariff_delete Billing cdr_post, cdr_get Remote control, and the callback it answers with command_post, await_callback, charging_profile_get, charging_profile_put, charging_profile_delete Bookings (OCPI 2.3.0) booking_get, booking_put, booking_patch, booking_post Direct payment (the Payments extension) payment_get, payment_post, payment_put, payment_patch Hubs hub_client_info_get, hub_client_info_put Waiting for your system to call us expect_inbound, await_inbound Timing and transport wait, transport_cut, transport_restore, transport_latency, sandbox_ptp_step Some of these change state on your side and some only read, which is worth knowing before you point a list at a live endpoint: a put or a post writes a real object there.

Disturbances

A disturbance is a fault the runner injects on purpose, to see whether your endpoint survives something real rather than only the happy path. Adversity is opt-in: a journey runs clean unless you ask for it.
The full set, by what each one disrupts:
  • Session updates: skip_intermediate_update, duplicate_full_put, zero_energy_pause, non_monotonic_meter, patch_before_put, pending_start, session_never_closes, session_invalid_close, reboot_energy_gap, offline_replay_burst
  • Charge records: cdr_before_terminal_session, delay_cdr, early_cdr, duplicate_cdr, credit_cdr, corrected_cdr
  • Pricing and parking: tariff_boundary, parking_period
  • Callbacks: callback_timeout, callback_duplicate, callback_out_of_order
  • Transport: transport_outage
Toggle one in the builder and the Configure step describes what it will do before you run it. An id the runner does not know is refused on import rather than ignored, so a typo tells you at once.

The values an action carries

Each hand-authored action can carry a bag of named values, in the file or added in the builder:
On the Configure step every value is a control. Click one to edit it in place, or use + Add a value on the action to add a name the file did not carry. Enter saves, Escape puts it back.

What can a value be?

A value is text, a number, true, false, or null. A list or an object is refused in the builder, because a structure belongs in the JSON file rather than in a single-line editor. Types survive the round trip: 16 comes back as the number your file held, not as the string "16". An action can hold up to 40 values, and a name can be up to 64 characters. Six names belong to the server and an action cannot set them: url, headers, authorization, token, template, and expression. These carry the request itself, so a scenario that could overwrite them could point a run at somewhere you did not choose.

Which keys does the runner check?

Most keys are yours to name. A few are read by the runner, which means it also checks them. Being on this list is not the same as being in the specification, and the two are marked separately below because they are different kinds of thing: ocpp_status is the case worth understanding. No OCPI version names it, and we do not pretend otherwise: the editor labels it a partner extension exactly as it labels a name you invent yourself. The runner reads it only so that a value you did supply is checked rather than silently shipped empty, and so a PATCH can carry the CSMS status your platform reports alongside the OCPI one. Nothing about it being on this list makes it standard. Where a key is one of these, the editor says so while you type, and an empty value is refused rather than saved into a run that Preview would reject.

Can I send a field OCPI does not define?

Any other name is yours. The editor labels it a partner extension and tells you it goes on the wire exactly as you write it, so you can model something your platform sends that the specification never named. This is deliberate on our side and allowed on the receiving side. OCPI 2.3.0 puts it plainly: “An OCPI Platform SHALL NOT reject request or response payloads based on the presence of JSON object field names that are not documented in this specification.” A partner who refuses your extra field is the one out of line, and a run that sends one is a fair way to find that out before production does. Two things worth knowing. An extension is not graded, because there is no clause to grade it against. And a key the runner does not read may be left empty, where a key it does read may not.

Import, run, and save

  1. On Charging Journeys, choose Import scenario JSON and pick your file, or Download sample to start from a working one.
  2. The custom journey opens in the builder on the Configure step with your knobs applied. Adjust anything, then Preview to compile the plan.
  3. Start the run. It streams live and lands a graded report in Runs, stored on your account, replayable and runnable from CI with the same JSON.
You can annotate a working copy with // comments and trailing commas; the importer strips them. The API and CI expect strict JSON. See also Run a charging journey for the run, follow, and recovery flow.