Projects API
List, read, create, and update developer projects.
capacityMw remains MW on the API. The approval-gated precision migration widens storage to six decimal places, preserving 11.4 kW as 0.0114 MW. Extraction reads the utility's registered field mapping and keeps AC and DC capacity separate; existing values are not silently corrected.
Projects API
The Projects API manages the same developer project records shown in the GridWonk console. Every read and write is limited to the developer organization that owns the API key. Utility and platform organizations cannot use these customer endpoints.
Authentication uses the authority of the organization API key or its short-lived legacy-audience
JWT. Fine-grained projects:* OAuth grants are not advertised or enforced yet.
Use packet intake to add documents after creating a project. Submission and resubmission remain console workflows while their full package-integrity transaction is extracted for headless use; the API does not bypass that safety boundary.
You can create a private project by default and review its packet for a utility with a released, complete packet setup, even if that utility has not joined the platform. Creating or reviewing a project does not send the packet to the utility. Formal submission is available only after GridWonk and the utility have accepted submissions for the selected state and program and an active utility reviewer is available. Private Review remains available after that activation; there is no automatic submission. Explicit Submit shares the frozen documents and the one packet report pinned to the package, not other unpinned private reviews or private document-review history. Project creation does not require a receiving utility organization or an environment flag.
Project create JSON bodies use the general 1 MiB API cap. Project PATCH bodies use a stricter
16 KiB cap because updates contain only a few fields. A malformed or oversized update is rejected
before any project lookup or write occurs.
List projects
GET /api/v1/projects
Optional query parameters: utilityName, state, limit (1–100), and offset.
curl "https://gridwonk.com/api/v1/projects?state=TN&limit=20" \
-H "Authorization: Bearer $GRIDBOOST_API_KEY"Get a project
GET /api/v1/projects/{projectId}
An unknown project, a project in another organization, and an archived synthetic demo project all
return 404.
Create a project
POST /api/v1/projects
{
"utilityName": "nashville-electric-service",
"projectName": "River Solar",
"capacityMw": 12.5,
"state": "TN",
"interconnectionLevel": "distribution"
}Creation requires the exact canonical utility record and an available packet-review lane for the
requested state and connection type. It does not require a receiving utility organization.
A missing utility returns 404; demo project markers and synthetic applicant organizations return
403. Missing state or an unavailable review lane returns 400 INVALID_INPUT. Creation stores
the utility's canonical EIA identifier so readiness and automatic-review workflows use the same
utility identity as the console. Formal submission separately checks the receiving utility's
acceptance and reviewer setup.
Program declaration
capacityMw remains megawatts on the API; no API unit selector is introduced. Console input may use
kW and converts before calling the server. Database precision is six decimal places after the
approved precision migration, preserving 11.4 kW as 0.0114 MW. AC and DC application ratings remain
distinct; a mismatch is surfaced rather than silently overwriting the stored project capacity.
Some utilities publish more than one interconnection program for the same track — for example
Maryland Level 2, Level 3, and Level 4 for distribution. The console's project dialog asks for the
program in that case, and the API enforces the same rule through the optional declaredTierSlug
field:
- The utility publishes two or more programs for
interconnectionLevel:declaredTierSlugis required and must be the slug of one of them. - The utility publishes zero or one program:
declaredTierSlugmust be omitted.
A missing, unpublished, or unexpected declaredTierSlug returns 400 INVALID_INPUT. The message
lists the valid programs as slug (label), and error.details.programs carries the same list as
{ slug, label } objects, so a client can discover the programs from the refusal and retry. There is
no separate list-programs endpoint yet; this payload is the discovery mechanism.
{
"error": {
"code": "INVALID_INPUT",
"message": "This utility publishes 3 interconnection programs for the distribution track; declaredTierSlug is required. Valid programs: maryland-level-2 (Maryland Level 2), maryland-level-3 (Maryland Level 3), maryland-level-4 (Maryland Level 4).",
"details": {
"programs": [
{ "slug": "maryland-level-2", "label": "Maryland Level 2" },
{ "slug": "maryland-level-3", "label": "Maryland Level 3" },
{ "slug": "maryland-level-4", "label": "Maryland Level 4" }
]
}
},
"requestId": "req_a1b2c3d4e5f6g7h8"
}Resend with the chosen program:
{
"utilityName": "delmarva-power",
"projectName": "Asilomar II",
"capacityMw": 0.25,
"state": "MD",
"interconnectionLevel": "distribution",
"declaredTierSlug": "maryland-level-2"
}The accepted program is stored on the project, returned as declaredTierSlug on every project read
(null for an undeclared project), and selects the rulebook the review runs against. A stored
declaration that cannot be read returns 409 rather than reporting the project as undeclared. It cannot be
changed through PATCH. If the utility's published programs cannot be read, or the publication is
misconfigured, the API returns 503 SERVICE_UNAVAILABLE and creates nothing — a project is never
created without the declaration its utility requires. error.reason says which case it is:
program_lookup_failed is transient and carries retryAfter plus a Retry-After header;
program_publication_invalid means the utility's publication needs correction and retrying will not
help. The SDK exposes both as reason and retryAfter on the thrown error, and the CLI includes
them in --json output.
Update a project
PATCH /api/v1/projects/{projectId}
You may change projectName, capacityMw, state, or interconnectionLevel while the project is an
editable draft. After authorization, a project that is no longer an editable draft returns 409
before checking the proposed field changes.
The utility and the declared program cannot be changed, and a track change has to leave the project
in a state creation would have accepted: moving a project that declared a program
to another track returns 409 INVALID_STATE, and moving an undeclared project onto a track where the
utility publishes two or more programs returns 400 INVALID_INPUT with the valid programs in
error.details.programs — create a new project with declaredTierSlug instead. A state or track
change must also land on a released packet-review lane within the utility's service states, as
creation requires; otherwise it returns 400 INVALID_INPUT. A draft created before state was
required can still change only its track under the original program rule; add state before
submitting. The update and stage
check happen in one database statement, so a concurrent submission cannot be overwritten by a stale
edit.
SDK and CLI
const created = await client.createProject({
utilityName: 'nashville-electric-service',
projectName: 'River Solar',
capacityMw: 12.5,
state: 'TN',
interconnectionLevel: 'distribution',
// declaredTierSlug: 'maryland-level-2', // only when the utility publishes 2+ programs
});When the API refuses an undeclared or mismatched program, the SDK throws a ValidationError whose
details is a ProjectProgramDeclarationDetails — programs lists the valid { slug, label }
options. Every SDK error also exposes reason and retryAfter, so a 503 can be told apart as
program_lookup_failed (wait retryAfter seconds, then retry) or program_publication_invalid
(do not retry) without parsing the message.
gridwonk projects list --state TN --json
gridwonk projects create \
--utility nashville-electric-service \
--name "River Solar" \
--capacity-mw 12.5 \
--state TN \
--interconnection-level distribution \
--yes
# Multi-program utilities: add --declared-tier-slug <slug>. A refusal lists the
# valid slugs, and --json output includes them under error.details.programs.