Skip to main content
The pipeline is split by scope: the customer only ever touches the ingest endpoint (scope external); everything downstream runs behind internal endpoints (scope process), normally driven by the orchestrator.

The steps

Ingest — POST /v1/forecasts (external)

Accepts production/consumption series per control area — or per market location (MaLo) with a malo_mapping table. Validation:
  • correct interval count for the delivery day (96/92/100, DST-aware),
  • every series complete (no gaps — a short series is rejected),
  • only active control areas (ACTIVE_CONTROL_AREAS); a subset is fine, missing areas are treated as zero.
The forecast is persisted with a content hash; nothing is processed inline. Re-posting a corrected forecast just updates the stored input — the orchestrator notices the changed hash on its next run.

Aggregate — POST /v1/process/delivery-days/{day}/aggregate

Pure functions compute net, open_position and total_open (algorithm) and persist them.

Publish — POST /v1/process/delivery-days/{day}/publish-open-position

The total open position is rendered as a CSV and written to the trader’s sFTP drop directory (atomically: temp name, then rename). This is the volume the trader executes on the EPEX day-ahead auction.

Submit — POST /v1/process/delivery-days/{day}/submit-schedules

Builds one schedule per balancing group — production forecast, consumption forecast, and all trades aggregated per direction and counterparty — and submits them to Engrate, one call per TSO per delivery date (per Engrate’s API model). The executed EPEX volume can be injected in the request body; it defaults to the requested open position.

Orchestrate — POST /v1/process/orchestrate

Called regularly by a timer (a Container Apps cron job in Azure). For every known delivery day that is not in the past, it compares the current input hash with the hash each step last ran on, and re-runs exactly the stale steps in order. No change → no action; the call is idempotent and cheap, so a tight schedule is fine.

State machine per delivery day

Everything lives in the audit trail (state.json per day): A step is stale iff its hash differs from input_hash. Manual step calls update the same state, so manual and orchestrated operation mix safely.