Docs

Requirements API

Query published utility requirements and migrate from the retired legacy read.

Requirements API

There are two requirement endpoints. Use the published one.

EndpointStatusReads
GET /api/v1/published-requirementsCurrentThe published requirement tier, minus utility-rejected rows
GET /api/v1/requirementsRetired — returns 410No corpus read; use the published endpoint

Query Published Requirements

GET /api/v1/published-requirements

Returns the published packet requirements for one utility — every published row except the ones a utility reviewer has explicitly rejected. This is the replacement for the legacy requirements read. Each returned row carries a reviewStatus of approved (utility reviewers signed off) or pending (awaiting a current utility decision), so you can tell the two apart without guessing.

This matches what a packet deficiency report can cite: reports draw from the same all-except-rejected set, so the requirements list shows what a report may raise. Pending rows are labelled honestly rather than hidden.

Coverage Policy

Everything published except rejected rows, with honest gaps. This surface never substitutes something adjacent for the thing you asked for:

  • Utility resolution is exact. The server matches on the utility name, then on its canonical slug ("Nashville Electric Service" → nashville-electric-service). There is no fuzzy match and no nearest-neighbour guess.
  • An unknown utility returns an empty result, not legacy rows and not a similar utility's rows.
  • An uncovered category returns an empty result. category=fees always returns an empty set — the published store has no fee role, and unrelated checks are never relabelled as fee requirements.
  • Rejected rows are omitted server-side. A row a utility reviewer rejected never appears in any response, in any count, on any surface.
  • Everything else is served, with its review status attached. An incomplete utility review no longer empties the response. Rows still awaiting a decision return with reviewStatus: "pending"; rows the utility signed off on return with reviewStatus: "approved".
  • A stale decision counts as pending re-review. When decision-relevant content changes after a verdict, the row reverts to reviewStatus: "pending" until a reviewer records a current decision. It is never silently dropped or hidden.
  • Rows whose published wording is still under review are withheld and counted. They appear in total and in filteredOutCount, never in requirements. This is a separate mechanism from reviewStatus.

Read message and summary before treating an empty page as "this utility has no requirements": they distinguish nothing is technically published, nothing in scope survived rejection, and published wording is still in review.

Query Params

  • utility (required): utility name or canonical slug
  • state (optional, exactly 2 characters): echoed in the response for context. No published rule is state-conditional any more (the state condition was retired on 2026-11-04), so it does not narrow the result. region is accepted as a compatibility alias for callers migrating off the legacy endpoint.
  • category (optional):
    • site_control
    • technical_specs
    • forms
    • fees (always empty — see coverage policy)
  • ixType (optional): T for transmission or D for distribution
  • limit (optional): 1-200, defaults to 50
  • offset (optional): defaults to 0

Example Request

curl "https://gridwonk.com/api/v1/published-requirements?utility=nashville-electric-service&state=TN&category=forms" \
  -H "Authorization: Bearer $GRIDBOOST_API_KEY"

Review-Incomplete Response

This is the durable response shape when the requested role has published rows whose utility review is still in progress. The rows are served — releaseStatus reports review progress, not whether anything came back. Counts depend on that utility's current review state.

{
  "utility": "nashville-electric-service",
  "state": "TN",
  "category": "forms",
  "count": 13,
  "total": 13,
  "source": "dynamic_extraction",
  "releaseStatus": "review_incomplete",
  "message": "13 published packet requirements for nashville-electric-service in this scope; 13 are awaiting a current utility review decision.",
  "summary": "13 published requirements; 13 awaiting utility review.",
  "filteredOutCount": 0,
  "dedupedCount": 0,
  "requirements": [
    {
      "id": "application-form:0:total_cost_missing",
      "requirementText": "Application Form — Distribution: Total system cost provided.",
      "validationStatus": "published",
      "extractedAt": "",
      "acceptedDocumentTypes": [],
      "timingRequirements": [],
      "citations": ["Total System Cost (Required)"],
      "reviewStatus": "pending",
      "sources": [
        {
          "title": "Utility application instructions",
          "documentType": "procedure",
          "pageRef": "2"
        }
      ]
    }
  ],
  "sources": [],
  "limit": 50,
  "offset": 0
}

As utility reviewers work through the scope, those rows flip to "reviewStatus": "approved" and releaseStatus advances toward released. Rows the utility rejects disappear from requirements entirely. Pending wording is withheld separately and counted in filteredOutCount, as described above.

Response Fields

FieldTypeDescription
utilitystringThe utility name as provided in the query.
statestring | nullThe two-character state code echoed from the query, or null. It no longer narrows rows.
categorystring | nullRequirement category filter applied, or null if not filtered.
countnumberNumber of requirements returned on this page.
totalnumberEvery served rule in scope, including rows beyond this page and rows withheld for pending wording. Rows awaiting a utility decision are included; only utility-rejected rows are excluded.
sourcestring"dynamic_extraction" for a live read of the published store.
releaseStatusstringTyped utility-review progress: released, partially_released, review_incomplete, or not_published. It is not a serving gate — review_incomplete still returns rows. Use it to describe review state, and per-row reviewStatus to judge an individual rule.
messagestringHuman-readable outcome — what was served, how much is awaiting a utility decision, wording still in review, or no technical publication.
summarystringOne-line count and review-progress summary suitable for display.
filteredOutCountnumberServed rows withheld because their published wording is still under review. Separate from reviewStatus, which marks rows that are served.
dedupedCountnumberAlways 0 today; the published reader performs no duplicate collapse. Present for contract compatibility.
requirementsarrayRequirement objects for this page (see below).
sourcesarrayDeduplicated source list covering the returned page only.
limitnumberPage size used for this response.
offsetnumberOffset used for this response.

Requirement Object Fields

FieldTypeDescription
idstringIdentifier of the rule within the current published set (role:index:findingId). Not guaranteed stable across republishes — do not key long-lived state on it.
requirementTextstringReviewed customer-facing wording, prefixed by its group heading ("<Role> — <Variant>: <rule>").
validationStatusstringAlways "published" for wire compatibility. Read reviewStatus for the utility-review state of the row.
extractedAtstringAlways "". This surface does not expose an extraction timestamp; read reviewStatus for utility approval.
acceptedDocumentTypesstring[]Always empty on this surface.
timingRequirementsstring[]Always empty on this surface.
depositInLieuAllowedbooleanOptional; present only when the published rule records it.
citationsstring[]Quoted citation text supporting the rule.
reviewStatusstring"approved" — utility reviewers signed off on this row; or "pending" — awaiting a current utility decision, including a decision made stale by a later change. Rejected rows are never returned, so there is no third value. Absent on the deprecated legacy read.
sourcesarraySource documents for this rule: title, documentType: "procedure", and optional url / pageRef.

Rate Limits

Standard API-key per-minute and daily limits apply, plus the daily unique-utility cap shared with utility matching. Exceeding the cap returns RATE_LIMITED; you can still re-query utilities you have already accessed that day. Unlike the legacy endpoint, this read emits no webhook event — a read is not an extraction.

Other Bindings

SurfaceBinding
SDKclient.queryPublishedRequirements(options) — strict QueryPublishedRequirementsResult type
CLIgridwonk requirements published --utility <name>
MCPquery_published_requirements tool, and the gridwonk://utilities/{utility}/requirements resource

The SDK and CLI default to the canonical https://gridwonk.com API origin; use a base URL override only for an approved alternate environment.

The SDK method calls this endpoint directly. The CLI wraps that SDK method and deliberately does not pre-resolve the utility name — resolution stays exact and server-side, so an unknown utility comes back as an honest empty answer rather than a guessed neighbour.

The MCP tool takes the same arguments and, in stateful sessions, inherits utility and state from the active work item when they are omitted. See MCP API.


Query Requirements (legacy — retired)

GET /api/v1/requirements

Retired. Production no longer reads the frozen legacy ix_requirements corpus through this route. Use GET /api/v1/published-requirements instead.

Authenticated calls now return typed 410 Gone / ENDPOINT_GONE with the replacement path.

Deprecation Signals (historical notice)

Before retirement, handler-produced responses carried these migration signals. The current 410 keeps the two headers and the replacement body field:

SignalValue
Deprecation headertrue
Link header</api/v1/published-requirements>; rel="successor-version"
replacement body field"/api/v1/published-requirements"

The replacement field mirrors the headers for clients that only parse bodies. Authentication and per-minute rate-limit rejections can still occur in the API-key wrapper before the retired handler, so those wrapper responses do not carry the retirement headers. The RFC 8594 Sunset value was announced as 2026-08-11T19:00:00Z and remains on the handler's 410 response.

Retirement Stages

StageState todayWhat changes
1. Deprecation signalsCompleteDeprecation: true, Link rel="successor-version", and the replacement body field announced the replacement before retirement.
2. Sunset announcementCompleteThe retirement instant was 2026-08-11T19:00:00Z.
3. 410 GoneLive since 2026-08-11The endpoint no longer reads the legacy corpus and returns typed 410.

The retirement stages are complete. The response is now:

{
  "error": {
    "code": "ENDPOINT_GONE",
    "message": "This endpoint has been retired. Use GET /api/v1/published-requirements for published Packet Intake requirements."
  },
  "replacement": "/api/v1/published-requirements",
  "requestId": "req_xxx"
}

The legacy corpus is never read on this route and the requirements.extracted webhook event stops firing for this endpoint. See ENDPOINT_GONE for the error contract and a handling snippet.

Migration notes:

  • region becomes state on the replacement (the replacement also accepts region as an alias).
  • limit rises from a maximum of 50 to a maximum of 200.
  • The replacement emits no requirements.extracted webhook event.
  • The response shape changes: see Response Fields above.

Historical Current Procedure Enforcement

This section records how the legacy endpoint behaved before retirement. No current route can restore this behavior. The published endpoint has no shadow/enforcement mode: it serves the Packet Intake store and exposes each row's utility-review state as approved or pending.

The ENFORCE_CURRENT_PROCEDURE_REQUIREMENTS flag controls whether legacy customer-facing reads filter to only current usable requirements:

ModeBehavior
Shadow mode (false)Returns the existing displayable extracted corpus so rollout does not hide utilities before review cleanup is complete. The message field may include shadow diagnostics, such as how many returned matches would remain customer-usable after enforcement.
Enforcement mode (true)Returns only requirements that pass the current-procedure usability check: extractionOutcome: "requirements_found", validationStatus: "validated", linked documentId, linked procedure document with approved/manual-override discovery and completed digitization, and matching source hash when both hashes exist.

Deprecated, rejected, pending-review, missing-source, and source-hash-mismatched requirements can still appear in admin review or migration tooling, but they are not customer-usable API results when enforcement is on.

Historical Query Params

These parameters describe the removed success handler. The permanent 410 tombstone does not parse them.

  • utility (required)
  • region (optional, 2-char)
  • category (optional):
    • site_control
    • technical_specs
    • forms
    • fees
  • ixType (optional): T for transmission or D for distribution
  • limit (optional): 1-50, defaults to 50
  • offset (optional): defaults to 0

Example Request (retired path)

curl "https://gridwonk.com/api/v1/requirements?utility=duke-energy&region=NC&category=site_control" \
  -H "Authorization: Bearer $GRIDBOOST_API_KEY"

Historical Success Response

{
  "utility": "duke-energy",
  "state": "NC",
  "category": "site_control",
  "requirements": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "category": "site_control",
      "key": "lease_agreement",
      "value": {
        "description": "Executed lease or ownership documentation for the project site",
        "required": true,
        "stage": "application"
      },
      "spec": {
        "site_control_requirements": {
          "interpreted_summary": "Executed lease or ownership documentation for the project site"
        }
      },
      "requirementText": "Executed lease or ownership documentation for the project site",
      "ixType": "D",
      "extractedAt": "2026-02-15T08:30:00.000Z",
      "validationStatus": "validated"
    },
    {
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "category": "site_control",
      "key": "proof_of_ownership",
      "value": {
        "description": "Deed or title documentation proving site ownership or control",
        "required": true,
        "stage": "application"
      },
      "spec": null,
      "requirementText": "Deed or title documentation proving site ownership or control",
      "ixType": "D",
      "extractedAt": "2026-02-15T08:30:00.000Z",
      "validationStatus": "validated"
    }
  ],
  "count": 2,
  "total": 12,
  "limit": 2,
  "offset": 0,
  "source": "dynamic_extraction",
  "message": "Successfully loaded 2 of 12 requirement(s) from IX Requirements Agent database."
}

Response Fields (legacy)

FieldTypeDescription
utilitystringThe utility name as provided in the query.
statestring | nullTwo-character state/region code, or null if not filtered.
categorystring | nullRequirement category filter applied, or null if not filtered.
requirementsarrayArray of matching requirement objects (see below).
countnumberNumber of requirements returned in this page.
totalnumberTotal matching rows before pagination.
limitnumberPage size used for this response.
offsetnumberOffset used for this response.
sourcestringData source: "dynamic_extraction" when requirements are found in the database, "fallback_static" when no results are available.
messagestringHuman-readable summary of the query result.

Requirement Object Fields (legacy)

Each entry in the requirements array contains:

FieldTypeDescription
idstringUUID of the requirement record.
categorystringRequirement category (site_control, technical_specs, forms, fees).
keystringMachine-readable requirement identifier (e.g., lease_agreement).
valueobjectStructured data extracted from the source document. Contents vary by requirement type.
specobject | nullFull rule-pack style requirement object when available, including site-control detail.
requirementTextstring | nullHuman-readable requirement text derived from spec or value.
ixType"T" | "D" | nullTransmission/distribution applicability when known.
extractedAtstringISO 8601 timestamp of when the requirement was extracted.
validationStatusstringCurrent validation state, usually "pending", "validated", or "disputed". Customer-facing enforcement requires "validated".

SDK and CLI Retirement

The TypeScript SDK client.queryRequirements() method and the CLI requirements query / requirements list aliases have been removed. Use queryPublishedRequirements() and gridwonk requirements published.

Notes (legacy)

  • The retired route emits no requirements.extracted webhook event. The published endpoint also has no read-side webhook.
  • The former success handler shared the daily unique-utility cap used by utility matching. The 410 guard runs before that check.
  • Results reflect the frozen extracted requirement corpus. The utility name is normalized to slug format (e.g., "Duke Energy" becomes duke-energy) with fuzzy matching as a fallback. The published endpoint removes that fuzzy fallback deliberately.
  • When no requirements are found, the response returns count: 0, source: "fallback_static", and an empty requirements array.
  • In shadow mode, source: "dynamic_extraction" can include pending, deprecated-source, or missing-source rows from the existing corpus. Treat the shadow diagnostic as the customer-usable count forecast, not as the returned count.

On this page