processd Operator Guide

Operator guide for processd, the process decision engine: NB Anmeldung straight-through processing, LF and MSB answers, role-gated binaries and Cedar ABAC.

On this page 14 sections

processd is the process decision engine. It receives the market messages its operator must answer, walks the published EBD (Entscheidungsbaum) for the message's PID, dispatches the answer through makod before the Frist, and queues whatever the tree cannot decide for an operator. Terms are defined in the glossary; which PID belongs to which process is the process map.

graph TB
    marktd["marktd :8180<br/>fan-out"]
    processd["processd :8580<br/>(this service)"]
    makod["makod :8080"]
    pg["PostgreSQL<br/>anmeldung_decisions · abmeldeanfragen<br/>approval_queue · eog_activations<br/>neuanlage_faelle"]

    marktd -->|"de.mako.process.initiated<br/>de.markt.versorgung.gap-detected<br/>HMAC POST /webhook"| processd

    subgraph NB ["NB module (--features nb-only)"]
        NC["mako-pruefung<br/>NB: E_0622 · E_0623 · E_0607 · E_0608 · G_0011 · G_0007<br/>LF: E_0609 · E_0624 · E_0614 · E_0615<br/>STP target ≥ 95%"]
        EOG["EoG gap closure<br/>§36/§38 EnWG · §38 timer"]
        NC --> pg
        EOG --> pg
    end

    subgraph LF ["LF module (--features lf-only)"]
        LFA["LF answers · Strom 55007 / 55010 / 55013 / 55016 / 55607<br/>Gas 44007 / 44010 / 44013 / 44016"]
        LFA --> pg
    end

    processd --> NB
    processd --> LF
    NB -->|"gpke.lieferbeginn.*<br/>gpke.lieferende.*<br/>gpke.eog.anmelden<br/>POST /api/v1/commands"| makod
    LF -->|"gpke.nb-lieferende.bestaetigen/ablehnen<br/>geli.lieferbeginn.anmelden<br/>POST /api/v1/commands"| makod
    NB & LF -->|"GET /api/v1/versorgung<br/>GET /api/v1/malos/{id}/grid"| marktd

Role isolation

Feature flags select which decision paths compile in, so a deployment carries only the answers its Marktrolle gives and an nb-only binary cannot answer as an LF:

[features]
role-lf-strom  = ["mako-pruefung/role-lf"]  # LF answers 55007/55010/55013/55016/55607
role-lf-gas    = ["mako-pruefung/role-lf"]  # LF Gas 44007 / 44010 / 44013 / 44016
role-nb-strom  = ["mako-pruefung/role-nb"]  # GPKE STP (55001, 55077, 55004, 55600/55601), EoG closure
role-nb-gas    = ["mako-pruefung/role-nb"]  # GeLi Gas An-/Abmeldung STP (44001, 44004)
role-msb       = ["mako-pruefung/role-msb"]   # REQOTE→QUOTES, §14a ORDRSP, MSB-answered MSB-Wechsel,
                                             # ESA Wertebestellung (35003/17007/17008/39002)

lf-only    = ["role-lf-strom", "role-lf-gas"]
nb-only    = ["role-nb-strom", "role-nb-gas"]
msb-only   = ["role-msb"]
integrated = ["role-lf-strom", "role-lf-gas", "role-nb-strom", "role-nb-gas", "role-msb"]

The flags gate handlers, not linked code: mako-gpke, mako-geli-gas and mako-wim are unconditional dependencies, so a role-gated build still links their catalogues. The legal separation of §§ 6a/7/7a EnWG happens at the level of the legal entity and its operations; the build only makes the operator's obligations readable off the binary.

PID 55016 „Kündigung" is an LF process. The Anwendungsübersicht der Prüfidentifikatoren 4.0 (lfd. Nr. 20030) has it going LFN → LFA, answered 55017/55018 by the Altlieferant under E_0614, so it belongs to role-lf-strom.


Routes and authorization

Every REST route takes the Claims extractor and evaluates one Cedar action against services/processd/policies/processd.cedar. There is no global auth middleware; tests/it/authorization_guard.rs checks every mounted route for a Claims extractor and an authorization call, and holds the action sets of code and policy to each other in both directions.

RouteCedar actionGranted to
POST /webhook— (marktd HMAC signature + timestamp)marktd
GET /api/v1/decisionsread-decisionsany caller of the tenant
GET /api/v1/queueread-queueany caller of the tenant
GET /api/v1/eogread-eogany caller of the tenant
GET /api/v1/neuanlageread-neuanlageany caller of the tenant
POST /api/v1/queue/{id}/approve · /rejectdecide-queueNB, LF or MSB
PUT /api/v1/neuanlage/{id}/identifikationidentify-neuanlageNB
POST /api/v1/start-supply[-gas] · end-supply[-gas]initiate-supplyLF
POST/GET /mcpuse-mcp, then a per-tool actionany caller of the tenant

Roles come from the mako_roles JWT claim, the tenant from mako_tenant, which must equal the deployment's own. The runner adds /health/live, /health/ready and /metrics.

processd refuses to start without [oidc] and without [webhook] inbound_secret; the refusal names each missing one. allow_insecure_no_auth = true accepts both, for local development (just dev processd reads dev/config/processd.toml).


NB module — Anmeldung STP

Decision pipeline (nb_module.rs)

de.mako.process.initiated
  ├─ Anmeldung (55001 verb. MaLo / 55077 erz. MaLo / 44001 Gas)
  │    → GET marktd /api/v1/versorgung/{malo_id}       → VersorgungsStatus
  │    → GET marktd /api/v1/malos/{malo_id}/grid       → MaloGridRecord
  │    → GET marktd /api/v1/partners/{lf_mp_id}        → partner_known
  │    → mako_pruefung::evaluate(…)             ← E_0622 / E_3005, the Vorprüfung
  │    → mako_pruefung::evaluate_lieferbeginn(…) ← E_0623 / E_3007, what is answered
  └─ Abmeldung (55004 Strom / 44004 Gas)
       → GET marktd /api/v1/versorgung/{malo_id}       → VersorgungsStatus
       → mako_pruefung::evaluate_abmeldung(…)

  Accept   → anmeldung_decisions(Accept, Zustimmungscode)
             [if auto_accept] → makod …bestaetigen (antwort_code, antwort_ebd)
             else             → approval_queue
  Reject   → anmeldung_decisions(Reject, Antwortcode)
             → makod …ablehnen (antwort_code, antwort_ebd, bemerkung)
  Escalate → anmeldung_decisions(Escalate) → approval_queue
  AnfrageErforderlich
           → INSERT abmeldeanfragen (the waiting AnmeldungAnfrage)
           → makod gpke.beendigung-zuordnung.anfragen  → UTILMD 55010 to the LFA
             …09:00 Uhr des 1. WT, or the LFA answers 55011/55012…
de.mako.abmeldeanfrage.beantwortet
           → take abmeldeanfragen → mako_pruefung::evaluate_lieferbeginn(…)
           → the Accept / Reject / Escalate paths above

Two trees, and on an assigned Marktlokation two phases. E_0622 is a Vorprüfung: surviving it means only that the Anmeldung is not directly refusable. If the Marktlokation is assigned at the Zuordnungsbeginn, the NB owes the incumbent LFA an Anfrage zur Beendigung der Zuordnung (55010) before answering the LFN, because E_0623 Prüfschritte 20–50 read that answer (GPKE Teil 2 § 2.1.2 SD Lieferbeginn Nr. 1 Prüfschritt 4, Nr. 3).

The waiting AnmeldungAnfrage is written to abmeldeanfragen before the Anfrage goes out, so a fast loopback answer always finds it; phase two replays the same pure evaluation with one more fact. Resolution is a single UPDATE … WHERE resolved_at IS NULL, so of the LFA's answer and the 09:00 lapse exactly one answers the LFN.

Silence releases the Marktlokation. „Verstreicht die Frist, ohne dass eine Antwort beim NB eingeht, gilt dies als Bestätigung nach Fall a)." A Widerspruch other than A30 / A41 refuses the Anmeldung with E_0623 A50 / A57 (Z35 in Gas). Those two codes need a second SG4 STS+Z35 restating the LFA's E_0624 code (GPKE Teil 2 § 2.1.2 Nr. 6); processd puts it on the ablehnen command and makod refuses to render the message without it.

Geschäftsvorfall. On 55077/55078 SG4 STS+7 DE 9013 element 3 carries ZW0 (ganze Marktlokation), ZW1 (bestehende Tranche) or ZW2 (neu zu bildende Tranche); E_0622 Prüfschritte 300/310 branch on it, and a message carrying none escalates. Geschäftsvorfall 3 asks all Tranchen-LFA and decides on arithmetic (Prüfschritte 500–540): at least one release (A53), enough percentage (A54), and whether a residual share would stay in the NB's own Bilanzkreis on a direktvermarktungspflichtige Marktlokation (A55 / A56).

Geschäftsvorfall-3 detailBehaviour
Direktvermarktungspflicht (Prüfschritt 540)From einsd — installed capacity against the 100-kW ceiling of §21 Abs. 1 Satz 1 Nr. 1 EEG; §20 Satz 1 Nr. 3 EEG pays the Marktprämie only from a pure EE-Bilanzkreis. No register answer → escalate
The registered shareSG8 Produkt-Code 9991000002090, Produkteigenschaft „prozentuale Aufteilung" (Muss on GV 3). An Aufteilungsfaktor or Aufteilung auf Technische Ressourcen escalates
AnswersOne per Tranchen-LFA before deciding; a lapsed 09:00 Frist resolves whatever is outstanding as Zustimmung; an LFA code E_0624 does not publish escalates

Three trees, three alphabets. Strom splits into two branches that share no Antwortcode, and Gas answers from a different Codeliste:

AnwendungsfallTree„andere Anmeldung in Bearbeitung"FristüberschreitungZustimmung
Strom, verbrauchende / ruhende MaLoE_0622 15–70A06A07A51 (E_0623)
Strom, erzeugende MaLo / TrancheE_0622 220–830A45A34/A28/A29/A30/A32/A35/A44A58 (E_0623)
GasE_3005 / G_0011ZC5E17E15 (G_0012)
Abmeldung Strom, verbrauchende / ruhende MaLoE_0607 10–140—A02A11
Abmeldung Strom, erzeugende MaLo / TrancheE_0607 500–620—A22 (A21 Datum)A27
Abmeldung GasE_3019 / G_0007—E17E15

Every code is resolved through mako_pruefung::codes against the tree that publishes it; RejectReason can only be built from a resolved entry, and makod re-checks it at the command boundary, where the code's Cluster decides the response PID. A Bestätigung carries a code too (SG4 STS+E01 is Muss), so NbEntscheidung::Accept carries A51 / A58 / E15.

mako_pruefung::nb::evaluate — the Anmeldung

Strom, verbrauchende / ruhende Marktlokation (E_0622 Prüfschritte 15–70):

PrüfschrittRuleOn failure
—MaloGridRecord exists for the MaLoEscalate (a data gap, not a ground to refuse)
15Vorlauffrist — one full Werktag between receipt and Zuordnungsbeginn (LFW24, all Transaktionsgründe)Reject A07
30MaLo participates in MaKoReject A02
60Zuordnungsermächtigung: Bilanzierungsgebiet matches, LF in the partner directoryReject A05
70No other Anmeldung in BearbeitungReject A06

A ruhende Marktlokation is not refused: Prüfschritt 30's Hinweis names only stillgelegte Marktlokationen and the Modell-2-Zuordnung.

Strom, erzeugende Marktlokation (Prüfschritte 220–830) uses A45 / A25 for the same two questions, then picks one of the six Vorlauffristen of GPKE Teil 2 § 2.1.1, keyed on (Geschäftsvorfall, bestehende, angemeldete Veräußerungsform). The angemeldete one is SG10 CCI+Z22; the bestehende one and the Ausfallvergütung flag come from einsd (GET /api/v1/anlagen/by-malo/{malo_id}/veraeusserungsform, [nb] einsd_url), because wire code Z90 covers both Einspeisevergütung and Ausfallvergütung, whose Fristen differ. A missing fact escalates; a CCI+Z22 code outside the published four is dropped at ingest with a warning. The Monatserster rule rests on § 21b Abs. 1 Satz 2 EEG 2023.

Gas (G_0011) runs the A03/A04/A16/A17 identification checks first, then E17 Fristüberschreitung, E13 Bilanzierungsproblem and ZC5 / Z08 for a conflicting or duplicate Anmeldung.

mako_pruefung::nb::evaluate_neuanlage — the Neuanlage, E_0608

Inbound 55600 / 55601 register a Marktlokation commissioned for the first time. Prüfschritte 110 / 590 loop: a Marktlokation the NB cannot yet identify is re-checked daily for 60 Werktage and only then refused A07 / A16. processd keeps the case log neuanlage_faelle and a daily worker:

EndpointPurpose
GET /api/v1/neuanlage?status=offenThe Prüflauf view, with letzter_pruefungstag (a refusal is admissible only after it) and the pruefungen count
PUT /api/v1/neuanlage/{id}/identifikationThe MaLo-ID the NB matched from its NIS/GIS; the next pass walks the tree with it

The branches share no code (A01–A09 verbrauchend, A10–A19 erzeugend). The Vorlauffrist is measured against the Übertragungstag, so a re-evaluation on day 40 reaches the verdict of day one.

mako_pruefung::nb::evaluate_abmeldung — the Abmeldung, E_0607 / E_3019

Inbound 55004 / 44004, answered 55005/55006 (44005/44006). Strom splits into two branches that share no Antwortcode:

PrüfschrittRuleverbrauchend / ruhenderzeugend
—MaLo known to this NB, and the LF holds an assignment or a Lieferende at this date is already settledEscalateEscalate
50 / 500+520Vorlauffrist — verbrauchend: one full Werktag; erzeugend: Monatserster (A21) one month ahead (A22), § 21b Abs. 1 EEG 2023A02A21 / A22
90 / 570Aufhebung einer zukünftigen Zuordnung (ZH2) nennt den bestätigten ZeitpunktA06A23
80Z41 „Ende der ESV ohne Folgelieferung" setzt eine E/G voraus, die innerhalb von 3 Monaten vor dem Endezeitpunkt begannA05—
100–130 / 580–610Kein bereits bestätigtes Lieferende zum selben DatumA09A25
140 / 620—Zustimmung A11Zustimmung A27

Gas (G_0007) has one code space: E17 Vorlauffrist, Z08 bereits bestätigt, E15 Zustimmung.

Not decided automatically: Prüfschritte 10–30 (Kundenanlagen-Herauslösung, § 20 Abs. 1d EnWG / § 10c EEG) need a fact the projection does not record, so A01 is unreachable; Prüfschritt 130 / 610 asks about an earlier message's Transaktionsgrund and escalates. Escalation is the § 20 Abs. 1 Satz 1 EnWG-safe direction: an unfounded Ablehnung binds a customer to a supplier they have left.

STP rate and [nb] auto_accept

The target is ≥ 95 % straight-through processing on a verbrauchende Anmeldung. A missing malo_grid record escalates; provision grid records (the NB's own topology, not MaStR) via marktd's NB-role PUT /api/v1/malos/{malo_id}/grid. An erzeugende Marktlokation additionally needs [nb] einsd_url; without it every 55077 escalates. Monitor via GET /api/v1/decisions or get_stp_rate.

Keep auto_accept = false (the default; PROCESSD_NB__AUTO_ACCEPT) until grid records cover the portfolio, the partner directory holds every expected LF MP-ID, and one manual review cycle confirmed the codes.

Affiliate guard — § 20 Abs. 1 Satz 1 EnWG

In a vertically integrated NB+LF utility, auto-acceptance is always blocked for An- and Abmeldungen from the operator's own LF:

initiating LF MP-ID == [identity] own_mp_id  →  initiator_is_affiliate = true
                                                auto_accept overridden to false
                                                → approval_queue (operator review)

[identity] own_mp_id must match makod's primary party. Startup logs the coding authority derived from it (293 = BDEW, 332 = DVGW) and warns on a GS1 GLN. The flag lands on every anmeldung_decisions row and on obsd's ProcessProjection; see the obsd Gleichbehandlung parity report.

Selbstzahler — the Lieferantenwechsel carve-out

GPKE Teil 1 (BK6-24-174 Anlage 1a), Vorbemerkung: „Ist der Letztverbraucher selbst Netznutzer, so tritt er in die Rolle des Lieferanten i.S. dieser Prozessbeschreibung […]. Eine Ausnahme bilden die Meldungen des Lieferanten im Rahmen des Lieferantenwechsels."

processd reads the Netznutzungsvertrag in force the day before the requested Zuordnungsbeginn (GET /api/v1/nb-contracts/by-malo/{malo_id}?on=) and holds the decision for the operator when it displaces a Selbstzahler:

transaktionsgrund == "E03" (Wechsel)
  ∧ incumbent netznutzer_typ == LETZTVERBRAUCHER
  → approval_queue (operator review), Antwortfrist attached

An Einzug (E01, E02) stays automated and skips the lookup.


NB module — EoG gap closure (§36/§38 EnWG)

Every consuming Marktlokation must be assigned to a Bilanzkreis at all times (GPKE Teil 2 Kap. 2.3). The EoG module closes supply gaps:

de.markt.versorgung.gap-detected           (marktd: an interval no supplier
  │                                         covers — a Lieferende the successor
  │                                         does not follow on, or a Fall-b
  │                                         Bestätigung 55002/55078/44002)
  ├─ record case in eog_activations         (idempotent per MaLo)
  ├─ [eog.auto_activate] GET /api/v1/grundversorger/{nb_mp_id}?sparte=…
  │    └─ found → Strom: gpke.eog.anmelden → UTILMD 55013;
  │             Gas:   geli.eog.anmelden → UTILMD G 44013 → makod → E/G
  │       (Zuordnungsbeginn = day after Lieferende — retroactive allowed)
  │    └─ missing → case stays `detected`; operator provisions the
  │       §36 Abs. 2 Feststellung and re-triggers
  └─ de.markt.versorgung.eog-begonnen → case `active`
       (eog_art = Ersatz-/Grundversorgung as classified by the E/G in 55014;
        eog_seit = Zuordnungsbeginn)
stateDiagram-v2
    [*] --> detected: gap-detected (uncovered interval)
    detected --> detected: Grundversorger missing (operator provisions §36 Abs. 2)
    detected --> angemeldet: eog.anmelden → UTILMD 55013 / G 44013
    angemeldet --> active: E/G Bestätigung 55014 (eog-begonnen)
    angemeldet --> detected: E/G Ablehnung 55015 (EBD E_0615)
    active --> expiring: §38 timer, warn_days_before_expiry ahead
    expiring --> expired: three-month maximum reached (§38 Abs. 4 S. 1)
    active --> [*]: Grundversorgung (no statutory maximum)
    expired --> [*]: operator follow-up (Grundversorgung regime / vertragliche Ersatzbelieferung / Anschlussunterbrechung)

A daily worker enforces the three-month maximum (§38 Abs. 4 S. 1 EnWG, calendar months from eog_seit): warn_days_before_expiry ahead the case turns expiring, at expiry expired; both emit de.markt.versorgung.ersatz-auslaufend to [eog] notify_webhook_url. After expiry, Haushaltskunden pass into Grundversorgung without a market message; otherwise the NB secures the Bilanzkreis (vertragliche Ersatzbelieferung, STS E06) or interrupts the Anschlussnutzung. Cases are listed by GET /api/v1/eog?status=detected|angemeldet|active|expiring|expired.


LF module — answering what the market asks a supplier

SparteInboundProcessEBDAnswersFrist
Strom55007Lieferende von NB an LFE_060955008 / 5500905:00 Uhr des 1. WT nach dem ÜT
Strom55010Beendigung der ZuordnungE_062455011 / 5501209:00 Uhr des 1. WT nach dem ÜT
Strom55013Anmeldung E/G (§ 36 / § 38 EnWG)E_061555014 / 5501515:00 Uhr am ÜT, sonst 15:00 Uhr des 1. WT
Strom55016Kündigung (LFN → LFA)E_061455017 / 55018Ablauf des 1. WT nach dem ÜT
Strom55607Ankündigung Zuordnung LF (erz. MaLo / Tranche)E_0603–E_060655608 / 5560915:00 Uhr am ÜT, sonst 15:00 Uhr des 1. WT
Gas44007Lieferende von NB an LFE_300244008 / 44009Ablauf des 3. WT
Gas44010Beendigung der ZuordnungE_302044011 / 44012Ablauf des 3. WT
Gas44013Anmeldung E/GE_300844014 / 44015Ablauf des 2. WT
Gas44016Kündigung beim AltlieferantenE_300144017 / 44018Ablauf des 3. WT

For the two „am ÜT" rows GPKE Teil 2 (§ 2.3.2.2 resp. § 2.4.2.2) states two windows depending on whether the Zuordnungsbeginn lies in the future. mako-fristen publishes the same-day instant, rolling to 15:00 on the next Werktag when that instant is already past, after the cut-off or on a non-Werktag.

The same process carries the same command name in both Sparten — {gpke,geli}.nb-lieferende.*, {gpke,geli}.beendigung-zuordnung.*, {gpke,geli}.kuendigung.*.

How a decision is made

de.mako.process.initiated (an answerable PID)
  → LfAnfrage from the CloudEvent — Transaktionsgrund and its Ergänzung
    (ZW3/ZW4/ZW5/ZAP, which selects the tree's branch), Termin, ÜT
  → LfVertragslage from marktd (supply state) + vertragd (contract state)
  → pruefe_*(…)
      Antwort    → makod command carrying the Antwortcode and its EBD
      Eskalation → approval_queue, naming the Prüfschritt it stopped at

The Antwortcode's Cluster selects the answer PID: A36 „Vertragsverhältnis wurde beendet" rides 55011, A35 „Es besteht eine Vertragsbindung" 55012.

Two facts come from the message itself:

  • Object of the Vorgang — SG4 STS+7 DE 9013 element 3: ZW3 erzeugende, ZW4 verbrauchende, ZW5 Tranche, ZAP ruhende Marktlokation. The halves of E_0609 and E_0624 answer from different code ranges, so a missing Ergänzung escalates. The same element carries ZW0–ZW2 on a 55077 and ZW8–ZX1 on a 55607; the PID decides which code space applies.
  • Vorlauffrist (E_0609 Prüfschritt 40, E_3002 E17) — mako_fristen::abmeldung per GPKE Teil 2 § 2.5.2 Nr. 1: a calendar month for EEG-Marktlokationen, the day before the last Werktag otherwise. An erzeugende Marktlokation escalates unless the deployment can say which applies.

55607: silence is not a lapse. GPKE Teil 2 § 2.4.2.2 Nr. 3 has the NB assign the supplier „aufgrund fehlender Antwort" with the Bilanzkreis on file. The substance of the answer is that Bilanzkreis (A01 / A99 are the only codes). UTILMD AHB Strom 2.2 Bedingungen [161]–[164] map ZW8–ZX1 onto E_0603–E_0606. The BK rides SG8 SEQ+Z79, Produkt-Code 9991000002082, Merkmalswert in SG10 CAV+ZV4 (the AHB admits SG4 FTX+ACB on the 55609 only, Bedingung [48]). [[lf.bilanzkreise]] is keyed on the Bilanzierungsgebiet because MaBiS § 10.2.1 grants the Zuordnungsermächtigung „je ZRT, BG, BK und LF"; several BKs for one regime are an operator choice.

Contract facts

PrüfschrittQuestionSource
E_0624 5Ging die Anfrage bis 07:00 Uhr des nächsten WT ein?SG4 DTM+154 on the message
E_0624 20Besteht zum Folgetag noch eine Zuordnung?marktd
E_0624 50Ist der Kunde aus der Anfrage identisch mit dem Kunden beim LFA?SG12 NAD+Z09, compared by vertragd
E_0624 60Hat der LFA Informationen, dass sein Kunde nicht ausgezogen ist?vertragd
E_0624 90 / 220Bleibt das Vertragsverhältnis zum Folgetag bestehen?vertragd — kuendigung_zum and vertragsende, whichever cuts first
E_0614 40 / 50 / 80Wurde der Vertrag bereits gekündigt, und zu welchem Datum?vertragd — kuendigung_zum only
E_0614 70 / 580Ist der Vertrag zum Kündigungstermin kündbar?vertragd — naechstmoeglicher_kuendigungstermin ≤ Termin
E_0614 500Liegt zu dem genannten Objekt ein Vertrag vor?vertragd — a contract row
E_0609 40Wurde die Vorlauffrist eingehalten?mako-fristen
E_0615 20Liegt die MaLo im Grundversorgungsgebiet des Empfängers?[lf] grundversorgungs_netzgebiete

Prüfschritt 50 compares names, not strings: processd forwards NAD+Z09 as ?kunde= and vertragd matches token sets; a shared Nachname alone is Unklar and escalates (see vertragd). Without [vertragd] url every contract fact is Bekannt::Unbekannt and a decision reaching one escalates. auto_respond = false means an operator decides: the walk still runs and its outcome is queued with the Antwortfrist.

The E/G Anmeldung has its own switch

E_0615 / E_3008 is the only Anmeldung whose Zustimmung accepts a statutory duty (§ 36 / § 38 EnWG), so dispatch needs [lf] eog_auto_respond = true on top of auto_respond. An empty grundversorgungs_netzgebiete escalates every 55013 / 44013. A Zustimmung also needs the Versorgungsart (SG10 CCI+Z36: ZC9, ZD0, ZE3) from [lf] eog_versorgungsart and the Bilanzkreis from [[lf.bilanzkreise]]; missing either, the answer is queued with the field named.

Approval queue

The queue is shared by every compiled role. Each entry stores the makod command for approve and for reject, resolved at enqueue, and the bodies:

EntryApprove sendsReject sends
A held decision (auto_accept / auto_respond off, a § 20 EnWG affiliate, a Selbstzahler)the answer the automatic path would have sent, frozen at enqueue (answer_payload)the other command, with the operator's Antwortcode
An escalationthe operator's answerthe operator's answer

What an answer is addressed by (the MeLo of a WiM order, the tx_id of a § 14a Steuerungsauftrag, the location and Messprodukt of an ESA order) is frozen on both (answer_payload, reject_payload). The operator states the rest:

{ "antwort_code": "A01", "antwort_tree": "E_0622", "antwort_codeliste": "E_0622", "bemerkung": "…" }

The body is optional and refuses unknown keys. A field the frozen answer already holds cannot be replaced (422): an approval sends the answer that was shown. Every queueable command has an answer contract (ANSWER_CONTRACTS in src/pg/approval.rs) naming the fields makod needs; a decision missing one is 422 with the claim released, before anything is sent. cargo xtask check-approval-commands refuses an enqueue site naming a command without a contract. The ESA Werteanfrage dispatches its refusal only; its Angebot needs prices and OBIS-Kennzahlen (QUOTES AHB 1.1a §4.3) and goes through the makod command API.

expires_at is the business Frist from mako_fristen::antwort less one hour:

TriggerFristSource
GPKE Anmeldung 55001 / 5507711:00 Uhr des 1. WT nach dem ÜTBK6-24-174 Teil 2, SD Lieferbeginn 5/6
GPKE Abmeldung 5500406:00 Uhr des 1. WT nach dem ÜTBK6-24-174 Teil 2, SD Lieferende von LF an NB 2/3
GPKE LF answer 5500705:00 Uhr des 1. WT nach dem ÜTBK6-24-174 Teil 2, SD Lieferende von NB an LF 2
GPKE LFA answer 5501009:00 Uhr des 1. WT nach dem ÜTBK6-24-174 Teil 2, SD Lieferbeginn 4
GPKE LFA answer 55016Ablauf des 1. WT nach dem ÜTBK6-24-174 Teil 2, SD Kündigung 2
GeLi Gas LF answer 44007Ablauf des 3. WTAWH GeLi Gas 2.0 Kap. 2.3.2 Nr. 2
GeLi Gas LFA answer 44010Ablauf des 3. WTAWH GeLi Gas 2.0 Kap. 2.5.2 Nr. 4
GeLi Gas LFA answer 44016Ablauf des 3. WTGeLi Gas 3.0 Kap. 3.1
GeLi Gas Anmeldung 44001Ablauf des 4. WT nach EingangBK7-24-01-009 Kap. 3.2.3
GeLi Gas Abmeldung 44004Ablauf des 3. WT nach EingangBK7-24-01-009 Kap. 3.2.2
WiM MSB-Wechsel 55039 / 55042 / 55051 / 551683 / 5 / 7 / 1 WTWiM Strom Teil 1
GPKE Neuanlage 55600 / 5560100:00 Uhr des 61. WT nach dem ÜTBK6-24-174 Teil 2 § 2.2.2 (60 WT täglicher Prüflauf, E_0608)
GPKE Sperr-/Entsperrauftrag 17115 / 17117 / 39000spätester ÜT ist der 1. WT nach dem ÜTBK6-24-174 Teil 2 §§ 3.5.1.2 / 3.5.2.2 / 3.5.3.2 Nr. 2
GPKE Anfrage Sperrung an den MSB 171163. WT nach dem ÜT — Fristverstreichen gilt als ZustimmungBK6-24-174 Teil 2 § 3.5.1.2 Nr. 4
GPKE Teil 4 Stammdaten-Rückmeldung 55109 / 55230 / 55557 / 55639–55643 / 556932. WT nach dem ÜTBK6-22-024 Anlage 1d (GPKE Teil 4) §§ 1.4.3 / 1.4.4 Nr. 2
GPKE Teil 2 Bearbeitungsstand Abrechnungsdaten 55156 / 55220 / 556732. WT nach dem ÜTBK6-24-174 Teil 2 §§ 3.1.1.2 / 3.1.2.2 / 3.1.3.2

The 45-minute APERAK window is a separate clock, owned by makod.

GET  /api/v1/queue                → list Pending entries (review before expires_at)
POST /api/v1/queue/{id}/approve   → dispatch the approve command via makod, mark Approved
POST /api/v1/queue/{id}/reject    → dispatch the reject command via makod, mark Rejected

The handlers claim the entry (status = 'Pending' guard) before dispatching and release the claim if the body is incomplete or the dispatch fails, so a decided entry never re-sends and two operators cannot send both answers. decided_by records the deciding principal's sub. A task running every 60 s marks stale entries Expired; expired entries log a WARN and are reconciled manually.


LF module — LFN bootstrap

Strom: POST /api/v1/start-supply

Initiates a GPKE Lieferbeginn (UTILMD 55001) with LFW24 Vorlauffrist validation.

curl -X POST http://processd:8580/api/v1/start-supply \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"malo_id": "10001234558", "lieferbeginn_datum": "2026-10-01",
       "bilanzkreis": "11XBK-STD-----9"}'
FieldRequiredNotes
malo_id✓11-digit Strom Marktlokations-ID
lieferbeginn_datum✓ISO-8601 date (YYYY-MM-DD)
bilanzkreis✓*SG8 SEQ+Z79 Produktpaket, Produkt-Code 9991000002082

* Optional only where [[lf.bilanzkreise]] declares exactly one standard entry. UTILMD AHB Strom 2.2 Kap. 5.3 makes the Produktpaket Muss on a 55001, so a request resolving to none is 422 MISSING_BILANZKREIS.

Vorlauffrist (BK6-24-174 GPKE Teil 2, SD Lieferbeginn Prozessschritt 1): „Spätester ÜT ist der Tag vor dem letzten WT vor dem Zuordnungsbeginn" — day-granular, no time-of-day cutoff. The earliest Lieferbeginn is the calendar day after the next Werktag after today (Berlin date); a date before today is RETROACTIVE_DATE. The response carries earliest_lieferbeginn and berlin_date_at_submission.

Gas: POST /api/v1/start-supply-gas

Initiates a GeLi Gas Lieferbeginn (UTILMD 44001).

curl -X POST http://processd:8580/api/v1/start-supply-gas \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "malo_id":    "10001234558",
    "zaehlpunkt": "DE00123456789012345678901234567890",
    "process_date": "20261001",
    "bilanzkreis": "9870000000006"
  }'
FieldRequiredNotes
malo_id✓Gas-MaLo-ID — rendered into SG5 LOC+Z16; supplied by the ERP
zaehlpunkt✓Zählpunktbezeichnung (RFF+Z13)
process_date✓Lieferbeginn date (YYYYMMDD, CET/CEST)
bilanzkreis✓*SG10 CCI+Z19 DE 7037 (UTILMD AHB Gas 1.2, Muss on 44001) — Gas has no Produktpaket
transaktionsgrund—E03 Wechsel (default), E01/E02 Einzug

* Same fallback as Strom. A Lieferantenwechsel needs 10 WT Mindestvorlauf (AWH GeLi Gas 2.0 Kap. 2.5.2 Nr. 1), enforced here; an Einzug passes with a vorlauffrist_hinweis, because the NB corrects the date rather than rejecting (Kap. 4). The GNB answers 44002 / 44003 by Ablauf des 4. WT (Kap. 3.2.3).

POST /api/v1/end-supply / end-supply-gas take malo_id and lieferende_datum and forward gpke.lieferende.anmelden / geli.lieferende.anmelden; the notice period is vertragd's to enforce.

Gas Datenabruf

Abrechnungsbrennwert and Zustandszahl are requested through makod's command API (geli.datenabruf.anfragen, ORDERS 17103, payload malo_id and the required zeitspanne_bis): the GNB answers within ten Werktage, but not before the 10. Werktag of the month after the Zeitspanne ends (GeLi Gas 3.0 Kap. 4.2). The answer is MSCONS 13007 or ORDRSP 19103; a delivery updates edmd meter_billing_periods via record_gas_quality.


Gleichbehandlung evidence

Every anmeldung_decisions row carries initiator_is_affiliate (TRUE when lf_mp_id == own_mp_id) — the evidence behind the § 7a Abs. 5 EnWG Gleichbehandlungsbericht that obsd assembles. The duty it evidences is § 20 Abs. 1 Satz 1 EnWG: a systematically faster decision for affiliates is a violation.


Configuration reference

processd reads a TOML file (PROCESSD_CONFIG, default ./processd.toml); "env:VAR_NAME" values defer secrets to the environment, and any key can be overridden by PROCESSD_<SECTION>__<KEY> (e.g. PROCESSD_DATABASE__URL). Unknown keys are refused, and a config error names every offending field. cargo test -p processd --test it -- published_config parses the block below.

Full processd.toml reference

[http]
addr = "0.0.0.0:8580"          # default

[database]
url       = "env:DATABASE_URL"  # required
pool_size = 10                  # default

[identity]
own_mp_id = "9900357000004"     # required — must match makod.toml [[party]] primary
tenant    = ""                  # optional; defaults to own_mp_id

[makod]
url     = "http://makod:8080"   # required
api_key = "env:MAKOD_API_KEY"   # required

[marktd]
url     = "http://marktd:8180"  # required
api_key = "env:MARKTD_API_KEY"  # required

# Optional. The LF and MSB modules read contract facts from it; without it
# every contract fact is `Unbekannt` and the decisions asking one escalate.
[vertragd]
url     = "http://vertragd:9780"
api_key = "env:VERTRAGD_API_KEY"

[webhook]
inbound_secret = "env:INBOUND_WEBHOOK_SECRET"   # required unless allow_insecure_no_auth

[subscription]
# Self-registers with marktd on startup (PUT /api/v1/subscriptions/{subscriber_id},
# retried with backoff up to 30 s).
webhook_url   = "http://processd:8580/webhook"  # optional; omit to skip registration
subscriber_id = "processd"                       # default
# Comma-separated. The default is de.mako.process.initiated +
# de.markt.versorgung.{gap-detected,eog-begonnen,changed}; leaving one out
# silently disables the module that consumes it.
# event_types = "de.mako.process.initiated,de.markt.versorgung.gap-detected,de.markt.versorgung.eog-begonnen,de.markt.versorgung.changed"

[nb]
auto_accept              = false  # true → dispatch bestaetigen automatically on Accept
gas_bearbeitungsfrist_wt = 3      # AWH GeLi Gas 2.0 Kap. 2.2
einsd_url                = ""     # EEG-/KWKG-Register; without it every 55077 escalates
einsd_api_key            = ""

[lf]
auto_respond = true   # false → every inbound LF process routed to approval_queue
# NB MP-IDs whose area this supplier is Grund-/Ersatzversorger in (E_0615
# Prüfschritt 20). Empty → every 55013 / 44013 escalates.
grundversorgungs_netzgebiete = ["9900000000001"]
eog_auto_respond = false          # E_0615 / E_3008 dispatch, on top of auto_respond
# SG10 CCI+Z36 on a 55014: ZC9 §38 Ersatzversorgung, ZD0 §36 Grundversorgung,
# ZE3 vertragliche Ersatzbelieferung. Unset → an automatic Zustimmung is queued.
eog_versorgungsart = "ZD0"

# The Bilanzkreise a 55607 Zustimmung (and a start-supply default) may name,
# by Bilanzierungsgebiet and regime. Omit `bilanzierungsgebiet` for the
# fallback row; no table → every 55607 escalates.
[[lf.bilanzkreise]]
bilanzierungsgebiet = "11YN-BG-EON---X"
eeg      = ["11XBK-EEG-----1"]
kwkg     = ["11XBK-KWKG----5"]
standard = ["11XBK-STD-----9"]

[msb]
auto_accept       = false   # true → dispatch the MSB-Wechsel Bestätigung
auto_preisanfrage = true    # false → the REQOTE goes to the approval queue

[esa]                                 # WiM Teil 2 Kap. 4 — the MSB's answers to an ESA
auto_accept                = false    # true → dispatch the E_0256/E_0257/E_0254 Zustimmungscode
auto_reject                = false    # true → dispatch a deterministic Ablehnungscode
accept_after_bindungsfrist = false    # E_0256 Prüfschritt 2 — a commercial decision

[eog]                                     # §36/§38 EnWG gap closure (NB role)
auto_activate             = false         # true → dispatch gpke.eog.anmelden on gap-detected
default_transaktionsgrund = "ZT6"         # SG4 STS DE9013 for automatic Anmeldungen
warn_days_before_expiry   = 14            # §38 Abs. 4 3-month warning lead
# notify_webhook_url      = "https://erp.example/hooks/eog"   # ersatz-auslaufend CloudEvents
# notify_webhook_secret   = "env:EOG_WEBHOOK_SECRET"

# Required unless allow_insecure_no_auth = true.
[oidc]
issuer   = "https://login.microsoftonline.com/{tenant-id}/v2.0"
audience = "api://mako-processd"
# jwks_refresh_secs = 300       # default

# [mcp]                 # optional /mcp bearer keys beside OIDC
# api_key = "env:PROCESSD_MCP_API_KEY"
# [[mcp.named_keys]]    # one named key per agent, so audit logs say which
# name    = "agentd"
# api_key = "env:AGENTD_MCP_KEY"
Flag / Env varDefaultDescription
--check—Probe the running instance's /health/ready on loopback and exit 0/non-zero (container HEALTHCHECK)
PROCESSD_CONFIGprocessd.tomlPath to the config file
RUST_LOG / LOG_LEVELinfoLog level
OTEL_EXPORTER_OTLP_ENDPOINT—OTLP endpoint; unset disables tracing export

MCP tools

/mcp (Streamable HTTP) takes an OIDC token or an [mcp] API key. The transport checks use-mcp, then each tools/call against the tool's own Cedar action; a tool with no mapped action is refused 403. approve_queue_entry / reject_queue_entry stamp the verified caller as decided_by (an unattributable decision is refused) and call processd's own POST /api/v1/queue/{id}/approve / /reject over the loopback, so the REST handler's decide-queue check runs as well.

ToolCedar actionDescription
list_decisionsread-decisionsLast N Anmeldung decisions with ERC codes and affiliate flag
get_decisionread-decisionsSingle Anmeldung decision by process_id (UUID)
get_stp_rateread-decisionsSTP rate over last N days vs. 95 % target
get_stp_breakdown_by_ercread-decisionsRejection breakdown by ERC code
list_affiliate_decisionsread-decisionsDecisions involving affiliated suppliers (Gleichbehandlung evidence)
list_pending_approvalsread-queuePending approval queue entries (most urgent first)
get_queue_entryread-queueSingle queue entry by UUID
approve_queue_entrydecide-queueDispatch the entry's answer; takes antwort_code / antwort_tree / antwort_codeliste / bemerkung for an escalation
reject_queue_entrydecide-queueDispatch the refusal with the operator's antwort_code, or bemerkung as the Begründung

Four guided prompts ship alongside them — triage-nb-rejection, investigate-stp-drop, triage-msb-wechsel and trigger-lieferbeginn.


Monitoring

MetricTypeDescription
processd_decisions_total{decision,pid}counterNB STP decisions by outcome (Accept/Reject/Escalate) and PID (55001/55077/55004/44001/44004)
processd_approval_queue_pendinggaugeProcesses waiting for an operator decision
processd_approval_queue_overduegaugePending entries past expires_at — the answer deadline has been missed
processd_eog_opengaugeErsatz-/Grundversorgung cases not yet closed (§ 36/§ 38 EnWG)
AlertConditionUsual cause
STP rateAccept / total < 90 % (target ≥ 95 %)missing malo_grid records
Escalation shareEscalate / total > 5 %grid or register coverage
Overdueprocessd_approval_queue_overdue > 0a business answer Frist was missed
Decision latency> 10 smarktd connectivity

MSB module — WiM MSB-Wechsel STP

An NB build (role-nb-strom / role-nb-gas) answers 55042/44042 (Anmeldung MSB) and 55051/44051 (Ende MSB); role-msb answers 55039/44039 (Kündigung MSB) and 55168/44168 (Verpflichtungsanfrage). One handler serves both Sparten because AWH WiM Gas 2.0 restates WiM Strom Teil 1 use-case for use-case. STP target: ≥ 80 %. The Prüfschritte are mako_pruefung::msb.

de.mako.process.initiated (PID 55039 / 55042 / 55051 / 55168)
  → GET marktd /api/v1/melos/{melo_id}          ← MeLo known?
  → GET marktd /api/v1/partners/{msb_mp_id}     ← Rahmenvertrag § 9 Abs. 1 Nr. 3 MsbG?
  → mako_pruefung::msb::pruefe_{anmeldung,abmeldung,kuendigung}
      Accept   → wim.geraetewechsel.bestaetigen (antwortcode) [if auto_accept]
                 else approval_queue with the WiM Frist
      Reject   → wim.geraetewechsel.ablehnen (antwortcode from the process's EBD)
      Escalate → approval_queue with the WiM Frist

The Frist runs from the Übertragungstag in the CloudEvent's time, so a redelivery does not restart it.

Anmeldung MSB (55042), E_0201, answered by the NB (WiM Strom Teil 1 Kap. 2.3.2 Nr. 2):

CheckOutcome on failure
Messlokation known to this NBZC9
Versicherung über die Beauftragung durch den AN vorhandenZB6
Mindestvorlaufzeit eingehalten — 15 WT, 7 WT bei erstmaliger EinrichtungE17, naming the earliest reachable Zuordnungsbeginn
Vertrag nach § 9 Abs. 1 Nr. 3 MsbG mit dem MSBNEscalate — E_0201 publishes no code

The metering technology is not a ground: §5 MsbG gives the Anschlussnutzer a free choice of MSB, §14 MsbG the right to switch.

Ende MSB (55051), E_0202, answered by the NB. A Zuordnungsende inside the 20-Werktage Mindestvorlauffrist is set to the nächstmögliche Zuordnungsende and confirmed Z01 (Kap. 2.4.2 Nr. 2). An Außerbetriebnahme has no lead time.

Kündigung MSB (55039), E_0200, answered by the MSBA. Every Prüfschritt is about the MSBA's Messstellenbetriebsvertrag in vertragd (GET /api/v1/messstellenvertraege/{melo_id}/{msb_mp_id}):

Contract stateOutcome
vertragd unreachable or unconfiguredEscalate
404 — this MSB holds no contract hereZC9
beendet_am setZ29
kuendigung_zum setthe Kap. 2.2.3 table — E15, Z01 to an earlier date, or Z34
Live, requested date on or after naechstmoeglichE15
Live, requested date inside the bindingZ12, naming naechstmoeglich
Live, no naechstmoeglich recordedEscalate
„Nächstmöglicher Termin" requested (DTM+471)Z01, naming the date

vertragd derives naechstmoeglich from the notice period, capped by § 309 Nr. 9 lit. c BGB.

Verpflichtungsanfrage (55168), E_0240. Kap. 2.4.2 Nr. 4 leaves the answer „nach eigenem Ermessen" to the gMSB, so it escalates with its 1-Werktag window. Escalated decisions still write an anmeldung_decisions row.


MSB module — REQOTE auto-response

For PIDs 35001, 35002, 35004 and 35005 (REQOTE Preisanfrage) processd answers from the active PreisblattMessung in marktd:

de.mako.process.initiated (PIDs 35001/35002/35004/35005, REQOTE)
  → GET marktd /api/v1/preisblaetter-messung/{own_mp_id}  ← PreisblattMessung current?
      Found   → wim.preisanfrage.angebot-senden
                 (includes preisblatt_gueltigkeit in payload for makod QUOTES build)
      Not found → approval_queue with the REQOTE Frist — an operator quotes

[msb] auto_preisanfrage = false (default true) routes every REQOTE to the approval queue, e.g. while a Preisblatt is being updated.


MSB module — ESA Wertebestellung (WiM Teil 2 Kap. 4)

Serving an Energieserviceanbieter is a mandatory Zusatzleistung (§34 Abs. 2 S. 2 Nr. 10 MsbG), so role-msb always carries:

Inbound PIDProcessAnswered withFristEBD
35003Werteanfrage (REQOTE)QUOTES 150035 WTE_0252
17007Bestellung von WertenORDRSP 19011/190122 WTE_0256
39002Stornierung der BestellungORDRSP 19013/190142 WTE_0257
17008Abbestellung von WertenORDRSP 19011/190122 WTE_0254
de.mako.process.initiated (PID 35003 / 17007 / 17008 / 39002)
  → GET marktd /api/v1/esa/framework/{msb}/{esa}   ← Rahmenvertrag established?   (E_0256 Nr. 6)
  → GET marktd /api/v1/esa/consent-check           ← Einwilligung still valid?    (E_0256 Nr. 8)
  → GET marktd /api/v1/melos/{melo}/msb?at=        ← MSB assigned for the period? (E_0256 Nr. 7)
  → GET marktd /api/v1/malos/{malo}/buendel?at=    ← one MSB across the bundle?  (E_0256 Nr. 11)
  → mako_pruefung::esa::wertebestellung::pruefe_{anfrage,bestellung,stornierung,beendigung}
      Accept   → wim.wertebestellung.*-beantworten (Zustimmungscode) [if auto_accept]
                 else approval_queue with the WiM Frist
      Reject   → the same command with the tree's Ablehnungscode      [if auto_reject]
      Escalate → approval_queue with the WiM Frist

The code's Cluster picks the answer PID, so Accept and Reject share one command. [esa] auto_accept and auto_reject both default to false and are separate: a wrong Bestätigung commits the MSB to a delivery, a wrong Ablehnung denies a mandated Zusatzleistung.

The Werteanfrage is E_0252 („Anfrage prüfen", refusals A02–A07, EBD 4.3 Kap. 8.25.1), the MSB's tree. E_0253 and E_0258 are the ESA's checks of the offer and the ORDRSP. A refusal goes out as wim.wertebestellung.anfrage-ablehnen with the code's wording in FTX+ACB (the 15003 has no AJT). A surviving Anfrage queues with its 5-WT window: the Angebot's Bindungsfrist, start, prices and OBIS registers are commercial terms. A02–A07 mean different things in E_0252 and E_0256.

The Messprodukt-Katalog. Whether this MSB offers an optional Kapitel-4.6 product is stated in marktd's esa_messprodukt_katalog (PUT /api/v1/esa/messprodukte/{msb_mp_id}, dated, with separate flags for the two Abo modes: A04 declined Abo, A05 declined one-shot). An absent row escalates; only a catalogue that omits the product yields A02/A04/A05. The seven Pflichtprodukte (BNetzA Mitteilung Nr. 3) are served whatever the catalogue holds, from their dated cut-over (9991 00000 077 1 and 078 9: „Optional ab 01.10.2023, Pflicht ab 06.08.2024").

Gerätetechnik (E_0252 Nr. 6, E_0256 Nr. 9) is judged on the union of the Messlokation's registers:

RuleSource
A Kapitel-4.6.2 product without an iMS cannot be servedUC 4.1.1 Vorbedingung: „Bei Übermittlung von Werten aus dem iMS: Alle … benötigten Messlokationen sind mit einem iMS ausgestattet."
A direction with no registerZaehlwerk.richtung — AUSSP Verbrauch, EINSP Erzeugung
Blindarbeit with no Blindarbeit registerthe OBIS C group: 1/2 Wirkarbeit, 3–8 Blindarbeit

Still escalated: a ¼h Lastgang on a non-iMS meter (mako holds the Zaehlertyp, not the RLM capability); no meter or no registers on record; an unknown Abo start on a 17008 (E_0254 Prüfschritt 2 compares against the Bestellung's DTM+203, carried as abo_beginn).


MSB module — §14a Steuerungsauftrag auto-ORDRSP

On a WiM Steuerungsauftrag (makoworkflow = wim-steuerungsauftrag) processd confirms when the SteuerbareRessource has istFernschaltbar = true and the produktcode is in its contracted konfigurationsprodukte (GPKE Teil 3 Kap. 1.3); an uncontracted produktcode is refused (wim.steuerungsauftrag.ablehnen).

de.mako.process.initiated (wim-steuerungsauftrag)
  → [parallel]
      GET marktd /api/v1/steuerbare-ressourcen/{sr_id}                 ← istFernschaltbar?
      GET marktd /api/v1/steuerbare-ressourcen/{sr_id}/konfigurationsprodukte  ← contracted?
  → istFernschaltbar=true + produktcode contracted  → bestaetigen
  → istFernschaltbar=true + produktcode NOT contracted  → ablehnen (GPKE Teil 3 Kap. 1.3)
  → istFernschaltbar=false  → Escalate (manual ORDRSP required)
  → SR not found  → Escalate

Escalations queue with the 5-Werktage Steuerungsauftrag window less an hour. Register contracted products via marktd's PUT /api/v1/steuerbare-ressourcen/{sr_id}/konfigurationsprodukte.

Edit this page ↗