U3b · September 2026 · Developers

API reference

Request contracts for the U4 tract API and integration rules for the existing application.

Scope and compatibility

The detailed contract below covers the three U4 tract endpoints. The generated OpenAPI 3.1 document covers these endpoints, not a claim that every registered application operation is enabled in the pilot. The separate route inventory lists the product-composition registry and capability requirements for discovery.

All three endpoints are GET requests under /api/v1, require a valid application session and evidence:read authorization for the organization/project, and use the lookout.api.v1 envelope. Select response behavior by its contract field, not by guessing from a URL or an array length.

The release adds fields and routes to existing U3a behavior. Do not treat frozen fixture examples as a guarantee that optional fields never appear; parsers should validate known fields while accommodating additive changes.

Authentication and request safety

For the pilot, sign in through the application-owned login page. The server issues a Secure, HttpOnly, SameSite=Strict cookie. Browser code uses same-origin credentials; never extract that cookie for a pasted example or store it in localStorage.

The protected API is not a public API-key service. Enterprise OIDC and optional integration surfaces require their own explicit configuration and acceptance. An entry in the registry is not a grant to call it.

Mutating /api/v1 routes require the configured Origin, matching Host, and X-Lookout-Request: lookout-v1; cross-site requests are denied. JSON bodies need the application/json content type and route-specific size limits. The read-only U4 examples below do not perform writes.

Login/logout have their own same-origin checks and error responses. Do not assume authentication errors use the same JSON envelope as /api/v1.

// Run inside an authorized same-origin application context.
// Use IDs supplied by your session/project selection.
async function readDefinitions(organizationId, projectId, signal) {
  const query = new URLSearchParams({ organizationId, projectId });
  const response = await fetch(`/api/v1/tract-measures/definitions?${query}`, {
    credentials: 'same-origin', signal, headers: { Accept: 'application/json' }
  });
  const body = await response.json();
  if (!response.ok) throw new Error(`${body.error}; request ${body.requestId || 'unknown'}`);
  if (body.contract !== 'lookout.tract-measures.definitions.v1') throw new Error('Unsupported contract');
  return body;
}

Common parameters and limits

Common parameters and limits
Parameter/ruleContract
organizationId, projectIdRequired on all three endpoints. Identifier pattern: ^[A-Za-z0-9][A-Za-z0-9_-]{2,127}$. Use the authorized project; U4 data is admitted to the Thurston pilot.
Query allow-listUnknown keys, repeated keys, empty values and values longer than 256 characters are rejected.
countyFipsFive decimal digits. Matrix accepts 1–5 distinct comma-separated county codes; profile uses one path segment.
measureIdMatrix only: 1–5 distinct comma-separated IDs matching ^[a-z][a-z0-9_]{2,63}$.
levelMatrix only: tract (default) or county. County-only resilience requested at tract level is unavailable.
geoidProfile path only: 11 decimal digits; it must belong to the requested county.
Request scopeFilter to counties advertised by the current capability/definitions. One unadmitted county refuses the whole matrix request.

GET /tract-measures/definitions

Required query: organizationId and projectId. Returns contract lookout.tract-measures.definitions.v1, release identity, counties, 59 measures, groups, source citations, value-column meanings, tuple order, code dictionaries and profile metadata.

Each definition supplies measureId/key, label, family/group/sourceKey, unit, decimals, levels, period, status, interval kind, publisher fields and the applicable overlay rule. Read the current status: the shipped dictionary is a release snapshot and does not override a subsequent revocation.

GET /api/v1/tract-measures/definitions?organizationId=YOUR_ORG_ID&projectId=YOUR_PROJECT_ID

GET /tract-measures

Required query adds countyFips and measureId; optional level defaults to tract. Returns contract lookout.tract-measures.matrix.v1, level, complete/partial status, county metadata, tract lists, measure columns, source-use identifiers, citations and legend context.

At tract level, align every measure’s county column by the corresponding county’s geoids array. At county level, tracts is null and each county column contains one value. Do not join arrays across counties or separate responses by a shared numeric index.

Numeric v, lo, hi, m and s values are decimal strings at the definition’s precision, or null. Preserve their precision; the overlap implementation compares integer unit steps without re-rounding. Rating r, reason n and reliability q carry code strings or null.

A revoked measure appears unavailable without values. A source unavailable for a county returns that column as source_unavailable. Complete/partial is not an HTTP authorization decision.

GET /api/v1/tract-measures?organizationId=YOUR_ORG_ID&projectId=YOUR_PROJECT_ID&countyFips=53067&measureId=adult_diabetes,households_no_vehicle_share&level=tract

GET /tract-measures/{countyFips}/{geoid}

Required query: organizationId and projectId. Returns contract lookout.tract-measures.profile.v1 and a compact profile for the tract. The m map is keyed by measureId; each tuple follows definitions.tupleOrder: [v, lo, hi, r, n, q].

Other profile blocks contain admitted source context, NRI components, ACS tables and PLACES population context. Use definitions.profile for field order and labels rather than displaying raw keys.

A revocation can add a withheld block and remove reconstructive ACS tables or NRI fields. The tenure/cost-burden linked group becomes unavailable together. Render Withheld distinctly from a publisher-missing dash; do not reconstruct a removed measure from remaining fields.

GET /api/v1/tract-measures/53067/53067010100?organizationId=YOUR_ORG_ID&projectId=YOUR_PROJECT_ID

Selected fields from the released profile fixture, not a complete response

{
  "m": {
    "adult_diabetes": ["8.7", "7.5", "9.9", null, null, "P"],
    "fema_avalanche_risk_score": [null, null, null, "NA", "NA", "N"]
  }
}

Status and error handling

Status and error handling
StatusError/meaningClient response
400tract_measures_request_invalidCorrect malformed, repeated, unknown or missing parameters. Retrying unchanged will not help.
401authentication_requiredPrompt for sign-in; do not retain protected data from the prior session.
403origin_denied on protected writesCheck the same-origin integration and required write header; never loosen the server policy.
404resource_unavailableAccess, source/project scope, county, measure or tract is not available. Do not use this response to enumerate hidden evidence.
429rate_limitedBack off and deduplicate. Do not fan out retries or add extra processes to evade a subject limit.
499request_cancelledThe request was aborted; do not present partial data as a complete response.
503tract_measures_service_unavailableConfigured provider/runtime is unavailable. Preserve the request ID and retry only with a bounded policy.
504request_timeoutThe application time budget expired. Report repeated failures with request IDs.

Illustrative error envelope; requestId is generated by the server

{"error":"resource_unavailable","schemaVersion":"lookout.api.v1","requestId":"example-request-id"}

Pagination, caching and request budget

The bounded U4 matrix does not paginate: it accepts up to five counties and five measures. Definitions and tract-profile contracts are capped at 64 KiB; the worst matrix must remain within 160 KiB in the release checks (raised from 96 KiB so that a county the size of King, 494 tracts, fits in one response).

Existing spatial and facility routes paginate using their own continuation fields. Follow the returned cursor and inspect coverage gaps; a short or empty page with a next cursor is not necessarily the end. Do not invent cursors or assume a record count proves complete source coverage.

Protected responses use Cache-Control: private, no-store. In-memory UI caches must be scoped to the current account/project and cleared on logout or scope change. Deduplicate in-flight requests and debounce rapid changes.

Per subject, per route family and per API process, token buckets have capacities of 30 per 10 seconds and 600 per 10 minutes, replenished continuously. The tract family is independent of facility-records, detail-records and hazard-events. An exact request-31 test must avoid enough elapsed time for refill; it is not a sliding-window promise.

Implementation references for this manual

Repository paths reviewed for this U3b documentation. Optional code is not a claim of live availability.

  • server/evidence/tractMeasuresRoutes.mjs
  • server/app/application.mjs
  • server/auth/soldierDataAuth.mjs
  • server/app/subjectRateLimiter.mjs
  • docs/implementation-plans/execution/evidence/U4/api-contract.md
  • docs/implementation-plans/execution/evidence/U3A/api-contract.md