Skip to main content

Documents

Two documents are generated for an offer on demand: the grid operator's (Netzbetreiber) registration forms, and the string plan. Nothing is stored — each call renders from the offer as it stands, so a fresh call always reflects the current configuration.

Both need the read:documents scope.

The short version

The forms for an offer, with everything defaulted:

curl -X POST https://ohykhwowoowfruyzpgzb.supabase.co/functions/v1/api/v1/offers/812/network-carrier-documents -H "Authorization: Bearer mds_live_your_key" -H "Content-Type: application/json" -d '{}' --output documents.zip

With an empty body you get the grid operator set on the offer's project and all of that operator's forms, in the order the operator configured. That is the same set the planner produces by default, so most integrations never need to name an id at all.

Looking up the ids

Every id the request can carry is readable through the API.

The operators and their formsGET /v1/grid-operators:

curl https://ohykhwowoowfruyzpgzb.supabase.co/functions/v1/api/v1/grid-operators -H "Authorization: Bearer mds_live_your_key"
{
"data": [
{
"id": "7",
"name": "Stadtwerke München",
"documents": [
{ "id": "1", "name": "Anmeldung Erzeugungsanlage", "pass": "OFFER", "orderPriority": 1 },
{ "id": "4", "name": "Datenblatt Wechselrichter", "pass": "INVERTER", "orderPriority": 2 }
]
}
],
"total": 1,
"limit": 50,
"offset": 0
}

A document's pass says how often it is rendered: OFFER once per offer, INVERTER once per inverter on the offer, BATTERY once per battery.

The electrical contractors you can name on the forms — GET /v1/electrical-firms. Their ids go in electronicsFirmId.

Which operator a site belongs to — a project's networkCarrierId (GET /v1/projects/{id}). This is the operator a document request defaults to.

Everything for one offer at onceGET /v1/offers/{id}/network-carrier-documents/options:

{
"gridOperator": { "id": "7", "name": "Stadtwerke München", "documents": [] },
"documents": [{ "id": "1", "name": "Anmeldung Erzeugungsanlage", "pass": "OFFER", "orderPriority": 1 }],
"electricalFirms": [{ "id": "3", "name": "Elektro Huber GmbH", "firmNumber": "EL-2019-4471" }],
"moduleConfigurations": [
{ "id": "19", "roofName": "Süddach", "moduleName": "Vitovolt 300-DG M440HC", "selected": true, "roofActive": true }
],
"batteries": [{ "id": "88", "name": "Vitocharge VX3 8 kWh", "selected": true }]
}

selected marks what the planner UI preselects, so mirroring the planner's default output is a matter of passing the selected entries back. The module configuration ids are the only ones with no other route — they live inside the offer's roofs — so this is where moduleConfigurationIds comes from.

Choosing what goes on the forms

curl -X POST https://ohykhwowoowfruyzpgzb.supabase.co/functions/v1/api/v1/offers/812/network-carrier-documents -H "Authorization: Bearer mds_live_your_key" -H "Content-Type: application/json" -d '{
"networkCarrierId": "7",
"documentIds": ["1", "4"],
"plannedGoingLiveDate": "2026-09-01",
"electronicsFirmId": "3",
"moduleConfigurationIds": ["19"],
"batteryId": "88",
"includeCertificates": true,
"includeDatasheets": true,
"includeMergedDocument": true
}' --output documents.zip
  • documentIds may be any subset of the operator's forms. They always come out in the operator's configured order, whatever order you send.
  • moduleConfigurationIds restricts the technical data to those module configurations; omit it to include all of them.
  • batteryId picks which battery the BATTERY-pass forms describe. Omit it and the offer's selected battery is used when there is exactly one candidate; send null for none.
  • includeCertificates / includeDatasheets append the product PDFs, and includeMergedDocument adds one combined PDF alongside the individual files.

The response is the ZIP archive itself, not JSON.

Errors

StatusErrorWhat to do
400network_carrier_not_setThe project has no grid operator. Pass networkCarrierId, or set one on the project.
400no_documents_configuredThe operator has no forms configured (or is not yours). Check GET /v1/grid-operators.
400unknown_document_idsOne or more documentIds are not configured for that operator; the message lists the valid ones.
404not_foundThe offer does not exist in your company.
500generation_failedRendering failed — the message carries the generator's detail.

String plan

curl https://ohykhwowoowfruyzpgzb.supabase.co/functions/v1/api/v1/offers/812/string-plan -H "Authorization: Bearer mds_live_your_key" --output string-plan.pdf

No body and no options: it renders how the offer's modules are wired into the inverters' MPP trackers, as a PDF.