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
| Parameter/rule | Contract |
|---|---|
| organizationId, projectId | Required 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-list | Unknown keys, repeated keys, empty values and values longer than 256 characters are rejected. |
| countyFips | Five decimal digits. Matrix accepts 1–5 distinct comma-separated county codes; profile uses one path segment. |
| measureId | Matrix only: 1–5 distinct comma-separated IDs matching ^[a-z][a-z0-9_]{2,63}$. |
| level | Matrix only: tract (default) or county. County-only resilience requested at tract level is unavailable. |
| geoid | Profile path only: 11 decimal digits; it must belong to the requested county. |
| Request scope | Filter 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_IDSelected 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 | Error/meaning | Client response |
|---|---|---|
| 400 | tract_measures_request_invalid | Correct malformed, repeated, unknown or missing parameters. Retrying unchanged will not help. |
| 401 | authentication_required | Prompt for sign-in; do not retain protected data from the prior session. |
| 403 | origin_denied on protected writes | Check the same-origin integration and required write header; never loosen the server policy. |
| 404 | resource_unavailable | Access, source/project scope, county, measure or tract is not available. Do not use this response to enumerate hidden evidence. |
| 429 | rate_limited | Back off and deduplicate. Do not fan out retries or add extra processes to evade a subject limit. |
| 499 | request_cancelled | The request was aborted; do not present partial data as a complete response. |
| 503 | tract_measures_service_unavailable | Configured provider/runtime is unavailable. Preserve the request ID and retry only with a bounded policy. |
| 504 | request_timeout | The 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.mjsserver/app/application.mjsserver/auth/soldierDataAuth.mjsserver/app/subjectRateLimiter.mjsdocs/implementation-plans/execution/evidence/U4/api-contract.mddocs/implementation-plans/execution/evidence/U3A/api-contract.md