Docs
API Reference

Applications API

Query interconnection applications scoped to your organization.

Applications API

Query Applications

GET /api/v1/applications/query

Returns interconnection applications scoped to the calling API key's organization. The primary applications array still comes from agentSessions, ordered by creation date, newest first.

When you provide a non-empty external-sync filter (changed_since, external_source, external_id, or intake_method), the response also includes a supplemental submissions payload from application_submissions.

Archived synthetic demo submissions remain audit history and are not returned as active sync data.

Authentication

Bearer token required. See Authentication.

curl https://gridwonk.com/api/v1/applications/query \
  -H "Authorization: Bearer $GRIDBOOST_API_KEY"

Query Parameters

ParameterTypeRequiredDefaultDescription
utilitystringNo—Filter applications by utility name. Supports * wildcards (for example duke*). Does not filter submissions.
statusstringNo—Filter applications by session status: active, completed, error, timeout.
limitintegerNo50Results per list (1–1,000), applied independently to applications and submissions.
offsetintegerNo0Non-negative pagination offset, applied independently to each list.
includeReportsstringNofalseSet to "true" to include deficiency reports for returned applications.
changed_sincestringNo—ISO 8601 timestamp. Filters the supplemental submissions payload for incremental sync.
external_sourcestringNo—Filters the supplemental submissions payload by external platform, such as powerclerk.
external_idstringNo—Filters the supplemental submissions payload by external platform record ID.
intake_methodstringNo—Filters the supplemental submissions payload by intake method: batch_upload, platform, api, or powerclerk_sync.

Invalid parameter values return HTTP 400 with error code INVALID_INPUT.

Response Shape

{
  "applications": [
    {
      "id": "uuid",
      "utility": "duke-energy",
      "status": "completed",
      "source": "api",
      "createdAt": "2026-02-15T12:00:00.000Z",
      "updatedAt": "2026-02-15T12:05:00.000Z",
      "completedAt": "2026-02-15T12:05:00.000Z"
    }
  ],
  "reports": [
    {
      "id": "uuid",
      "sessionId": "uuid",
      "createdAt": "2026-02-15T12:05:00.000Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

The reports field is only present when includeReports=true and at least one application is returned. Each report references its parent application through sessionId.

Wildcard Filtering

The utility parameter supports * as a wildcard character. Internally, * is converted to a SQL ILIKE pattern (%), so matching is case-insensitive.

PatternMatches
duke*duke-energy, duke-energy-carolinas, duke-energy-indiana
*electric*pacific-gas-electric, nashville-electric-service
con-edExact match only (case-insensitive)

Example: Paginated Query with Filters

curl "https://gridwonk.com/api/v1/applications/query?utility=duke*&status=completed&limit=10&offset=0&includeReports=true" \
  -H "Authorization: Bearer $GRIDBOOST_API_KEY"

Supplemental Submissions Payload

When any non-empty external-sync filter is provided (changed_since, external_source, external_id, or intake_method), the response includes additional fields alongside the standard applications array, even when no submissions match:

{
  "applications": [],
  "submissions": [
    {
      "id": "uuid",
      "applicationNumber": "APP-2026-001",
      "projectName": "Sunrise Solar",
      "screeningStatus": "screened",
      "externalSource": "powerclerk",
      "externalId": "pc-12345",
      "intakeMethod": "powerclerk_sync",
      "createdAt": "2026-02-15T12:00:00.000Z",
      "updatedAt": "2026-02-15T12:05:00.000Z"
    }
  ],
  "submissionsTotal": 1,
  "total": 0,
  "limit": 50,
  "offset": 0
}
FieldTypeDescription
submissionsarraySubmission records that match the external-sync filters.
submissionsTotalintegerTotal count of matching submission rows.

The two lists are independent. utility and status filter only applications; the external-sync filters affect only submissions. total counts matching applications, while submissionsTotal counts matching submissions. The same limit and offset apply separately to both lists, so limit=50 can return up to 50 rows in each. Submissions are ordered by updatedAt, newest first.

Submission projectName values have internal demo identity markers removed. The field is null if no displayable name remains; stored names are unchanged.

Incremental Sync Pattern

Use the changed_since parameter for delta syncs of the supplemental submissions payload. The current route uses inclusive timestamp filtering and offset pagination; it does not expose a snapshot cursor or upper-bound filter. Do not advance a checkpoint when a sync window is larger than one response page, because concurrent writes can shift offsets and make a multi-page pass incomplete.

The following illustrative pattern advances only to the newest server-returned updatedAt value returned by a complete one-page window. It uses idempotent upserts (inserts or updates that are safe to repeat) and stops without advancing the checkpoint when the window exceeds one page:

async function incrementalSync(apiKey: string, lastSyncedAt?: string) {
  if (lastSyncedAt && Number.isNaN(Date.parse(lastSyncedAt))) {
    throw new Error('Stored sync checkpoint is not a valid ISO 8601 timestamp');
  }

  const url = new URL('https://gridwonk.com/api/v1/applications/query');
  url.searchParams.set('external_source', 'powerclerk');
  url.searchParams.set('limit', '1000');
  if (lastSyncedAt) {
    url.searchParams.set('changed_since', lastSyncedAt);
  }

  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${apiKey}` },
  });

  if (!res.ok) throw new Error(`Sync failed: ${res.status}`);
  const data = await res.json();
  const submissions = data.submissions ?? [];

  if ((data.submissionsTotal ?? 0) > submissions.length) {
    throw new Error(
      'Sync window exceeds one page; checkpoint was not advanced. ' +
      'Run more frequently or request a cursor-capable export path.'
    );
  }

  let nextCheckpoint = lastSyncedAt;
  for (const submission of submissions) {
    const updatedAtMs = Date.parse(submission.updatedAt);
    if (Number.isNaN(updatedAtMs)) {
      throw new Error('Response contains an invalid submission.updatedAt value');
    }

    await upsertSubmission(submission);
    if (!nextCheckpoint || updatedAtMs > Date.parse(nextCheckpoint)) {
      nextCheckpoint = submission.updatedAt;
    }
  }

  if (nextCheckpoint) {
    await saveSyncCheckpoint(nextCheckpoint);
  }

  return { synced: submissions.length, checkpoint: nextCheckpoint ?? null };
}

Recommended pattern:

  1. On first sync, provide an external-sync filter such as external_source=powerclerk; omit changed_since only if you want all matching supplemental submissions.
  2. Compare submissionsTotal with the returned row count. If the window exceeds the page limit, do not advance the checkpoint; the current offset contract is not snapshot-safe under concurrent writes.
  3. Commit every returned row with an idempotent upsert. Advance only to the maximum server-returned updatedAt; when the response is empty, retain the previous checkpoint.
  4. On subsequent syncs, keep the external-sync filter and pass the stored timestamp as changed_since. Because the filter is inclusive, deduplicate by submission ID plus updatedAt.
  5. Do not build a lossless multi-page integration on this offset route. That requires a server-generated snapshot upper bound and a stable (updatedAt, id) cursor, which this endpoint does not currently expose.

Access Scope

The applications array is tenant-scoped to the calling API key's organization. Applications belonging to other organizations are never returned.

The supplemental submissions payload is visible to both the owning organization and the applicant organization: a submission is returned when the calling API key's organization matches either the submission's owning org or its applicantOrgId. This lets a developer (applicant) org sync submissions it filed even when another organization owns the record.

This endpoint is still hybrid today:

  • applications reflects the legacy agent-session view
  • submissions exposes submission rows only when external-sync filters are used

The shared submission architecture is real in the database, but the public route has not fully switched to a submission-first response contract yet.

On this page