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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
utility | string | No | — | Filter applications by utility name. Supports * wildcards (for example duke*). Does not filter submissions. |
status | string | No | — | Filter applications by session status: active, completed, error, timeout. |
limit | integer | No | 50 | Results per list (1–1,000), applied independently to applications and submissions. |
offset | integer | No | 0 | Non-negative pagination offset, applied independently to each list. |
includeReports | string | No | false | Set to "true" to include deficiency reports for returned applications. |
changed_since | string | No | — | ISO 8601 timestamp. Filters the supplemental submissions payload for incremental sync. |
external_source | string | No | — | Filters the supplemental submissions payload by external platform, such as powerclerk. |
external_id | string | No | — | Filters the supplemental submissions payload by external platform record ID. |
intake_method | string | No | — | 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.
| Pattern | Matches |
|---|---|
duke* | duke-energy, duke-energy-carolinas, duke-energy-indiana |
*electric* | pacific-gas-electric, nashville-electric-service |
con-ed | Exact 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
}| Field | Type | Description |
|---|---|---|
submissions | array | Submission records that match the external-sync filters. |
submissionsTotal | integer | Total 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:
- On first sync, provide an external-sync filter such as
external_source=powerclerk; omitchanged_sinceonly if you want all matching supplemental submissions. - Compare
submissionsTotalwith 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. - 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. - 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 plusupdatedAt. - 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:
applicationsreflects the legacy agent-session viewsubmissionsexposes 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.