Skip to content

API reference

Authenticate, create a project, track its reports, pull results, and embed the authenticated status viewer — with copy-paste cURL and TypeScript examples.

This is the single reference for building against the AI PreCheck API. It covers authentication, creating a project, tracking its reports to completion, pulling results, and embedding the authenticated status viewer in your own UI. The three integration patterns (Embedded, Backend-only, Standalone) all compose from the same calls documented here.

Every request authenticates with an API key sent in the x-api-key header.

As a vendor you create keys per city: a key is scoped to a single city you have access to, and carries that city’s permissions. Requests also take a cityKey parameter identifying the city the call operates on. Keep keys server-side — they are never exposed to the browser or embedded in client code.

The API is region- and environment-specific. The host follows the pattern https://echeck-api.<env>.<region>.archistar.<tld>:

EnvironmentRegionBase URL
ProductionCanadahttps://echeck-api.prod.ca.archistar.ai
DevelopmentAustraliahttps://echeck-api.dev.au.archistar.io

Your Archistar representative confirms the correct base URL for your city. The examples below use these placeholders:

Environment
BASE_URL="https://echeck-api.prod.ca.archistar.ai"
API_KEY="sk_live_..." # per-city key, kept server-side
CITY_KEY="fairview" # the city this call operates on

A minimal authenticated request:

Terminal window
curl "$BASE_URL/cities/$CITY_KEY" \
-H "x-api-key: $API_KEY" \
-H "Accept: application/json"
GET /cities · GET /cities/:cityKey
curl "$BASE_URL/cities" -H "x-api-key: $API_KEY" # cities your key can access
curl "$BASE_URL/cities/$CITY_KEY" -H "x-api-key: $API_KEY" # a city's permit types + reports

The city config tells you the valid permitType values and which reports each one runs. You pass a single permitType when creating or processing a project — you don’t choose individual reports.

There are three ways to create a project, depending on where the applicant experiences PreCheck.

Use this when the drawings are already at a URL Archistar can fetch (an S3 link, a signed download URL from your DMS, etc.). Processing starts immediately.

POST /projects
curl -X POST "$BASE_URL/projects" \
-H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
-d '{
"permitType": "single-family",
"cityKey": "fairview",
"address": "25 Maple Street, Fairview, 90210",
"longitude": -118.32, "latitude": 34.09,
"userEmail": "applicant@example.com",
"userName": "Jordan Rivera",
"files": [
{ "type": "Architectural Drawings", "label": "drawings.pdf",
"fileLink": "https://files.example.gov/25-maple/drawings.pdf" }
],
"metadata": { "permitId": "PR-2026-000123" }
}'

The metadata object is echoed back to you unchanged (including on the completion webhook) — use it to correlate the project with your own permit record.

Use this when the applicant uploads files to you and you don’t have a public URL. Create a draft project, get a pre-signed upload URL, PUT the bytes, then start processing.

  1. Create a draft project (no permit type or files yet). Returns the projectId.

    POST /projects
    curl -X POST "$BASE_URL/projects" \
    -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
    -d '{
    "cityKey": "fairview",
    "address": "25 Maple Street, Fairview, 90210",
    "longitude": -118.32, "latitude": 34.09,
    "userEmail": "applicant@example.com",
    "userName": "Jordan Rivera"
    }'
  2. Ask for a pre-signed upload URL for each file.

    GET /projects/:projectId/upload-file-url
    curl "$BASE_URL/projects/$PROJECT_ID/upload-file-url?filename=drawings.pdf" \
    -H "x-api-key: $API_KEY"
  3. PUT the file bytes straight to the returned URL (storage, not the API — no x-api-key).

    PUT <upload url>
    curl -X PUT --upload-file ./drawings.pdf "$UPLOAD_URL"
  4. Start processing, naming the permit type and the uploaded files.

    POST /projects/:projectId/process
    curl -X POST "$BASE_URL/projects/$PROJECT_ID/process" \
    -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
    -d '{
    "permitType": "single-family",
    "files": [
    { "type": "Architectural Drawings", "label": "Drawings", "filename": "drawings.pdf" }
    ]
    }'
// 1. create draft
const { id: projectId } = await api("/projects", {
method: "POST",
body: JSON.stringify({
cityKey: CITY_KEY, address: "25 Maple Street, Fairview, 90210",
longitude: -118.32, latitude: 34.09,
userEmail: "applicant@example.com", userName: "Jordan Rivera",
}),
});
// 2. + 3. upload each file
async function upload(file: { name: string; blob: Blob }) {
const { url } = await api(
`/projects/${projectId}/upload-file-url?filename=${encodeURIComponent(file.name)}`,
);
await fetch(url, { method: "PUT", body: file.blob }); // straight to storage
}
await upload(primary);
// 4. process
await api(`/projects/${projectId}/process`, {
method: "POST",
body: JSON.stringify({
permitType: "single-family",
files: [{ type: "Architectural Drawings", label: "Drawings", filename: "drawings.pdf" }],
}),
});

Option C — complete in Archistar (hand-off)

Section titled “Option C — complete in Archistar (hand-off)”

Use this for embedded intake: create a draft project, then send the applicant to a completion link where they finish the question set and uploads inside Archistar’s UI.

POST /projects → GET /projects/:projectId/complete-link
# 1. create a draft project (as in Option B step 1)
# 2. get a link to hand the applicant off to
curl "$BASE_URL/projects/$PROJECT_ID/complete-link" -H "x-api-key: $API_KEY"

Redirect the applicant to the returned URL (or embed it). When they finish, processing starts and you receive the webhook like any other project.

A project’s reports each move through five processing stages — Preparing → Analysing → Categorising → Assessing → Summarising — then land on a terminal status of Complete with a result (Passed / Failed) and a score.

GET /projects/:projectId · GET /projects/:projectId/reports
curl "$BASE_URL/projects/$PROJECT_ID" -H "x-api-key: $API_KEY" # project + overall status
curl "$BASE_URL/projects/$PROJECT_ID/reports" -H "x-api-key: $API_KEY" # per-report status & scores
Reports response
[
{ "id": "R_BPIC-21", "name": "Fairview – Building Permit Intake Completeness",
"status": "Complete", "result": "Passed", "score": 85, "modelVersion": "0.15" },
{ "id": "R_SF3-84", "name": "Fairview – SF-3 Single Family Zoning Compliance",
"status": "Complete", "result": "Passed", "score": 82, "modelVersion": "0.8" }
]

You can either poll these endpoints, or — preferred for production — register a webhook and be pushed each result.

When a report finishes, Archistar POSTs to your configured endpoint with type=project_report_completed. It fires once per report, and carries your original metadata so you can look up the matching permit record without a polling loop.

POST → your webhook (type=project_report_completed)
{
"projectId": "P_7A3F21C9",
"reportId": "R_BPIC-21",
"status": "Complete",
"result": "Passed",
"score": 85,
"metadata": { "permitId": "PR-2026-000123" },
"documents": [
{ "documentName": "AI PreCheck Report.pdf", "documentType": "report",
"documentURL": "https://echeck-api.prod.ca.archistar.ai/Documents/R_BPIC-21_report" }
]
}

Once a report’s status is Complete:

You want…Call
A project’s reports + scoresGET /projects/:projectId/reports
One report’s detailGET /projects/:projectId/reports/:reportId
A report’s uploaded files or PDFGET /projects/:projectId/reports/:reportId/document?type=uploaded · ?type=report
All of a project’s documentsGET /projects/:projectId/document?type=uploaded · ?type=report
Filter / paginate projectsGET /projects?cityKey=…&permitType=…&take=20&skip=0&orderBy=last_activity
One report's detail + its PDF
curl "$BASE_URL/projects/$PROJECT_ID/reports/R_BPIC-21" -H "x-api-key: $API_KEY"
curl "$BASE_URL/projects/$PROJECT_ID/reports/R_BPIC-21/document?type=report" -H "x-api-key: $API_KEY"

The viewer is the project page — the same view every stakeholder sees, rendered by Archistar and dropped into your UI as an <iframe>. It shows live processing progress, every report with its score, and the project’s documents.

There are two link types from GET /projects/:projectId/viewer-link:

  • Signed (?signed=1&generatedFor=<email>) — a time-limited (48 h), read-only URL for embedding as an iframe. No login required; anyone with the record sees the same view. Best for surfacing status on a permit record.
  • Unsigned — the standard viewer, which requires Archistar authentication and gives full reviewing capability.
  1. Fetch a signed link from your backend. generatedFor records who it’s for.

    GET /projects/:projectId/viewer-link
    curl "$BASE_URL/projects/$PROJECT_ID/viewer-link?signed=1&generatedFor=reviewer@fairview.gov" \
    -H "x-api-key: $API_KEY"
    Response
    { "url": "https://viewer.archistar.ai/p/P_7A3F21C9?token=..." }
  2. Embed the returned URL in your application record view.

    In your permit record page
    <iframe
    src="https://viewer.archistar.ai/p/P_7A3F21C9?token=..."
    title="AI PreCheck — project status"
    style="width:100%; height:900px; border:0; border-radius:12px;"
    loading="lazy"
    referrerpolicy="no-referrer"
    ></iframe>

Putting it together server-side — mint the link on demand so it’s always freshly signed:

// GET /permits/:id/precheck-viewer → { viewerUrl }
export async function getViewerUrl(projectId: string, viewerEmail: string) {
const { url } = await api(
`/projects/${projectId}/viewer-link?signed=1&generatedFor=${encodeURIComponent(viewerEmail)}`,
);
return url; // hand to the frontend to set as the iframe src
}

The same calls compose into four integration methods. The Sandbox lets you step through each one.

MethodHow it runsCalls it uses
Gated PreCheckPreCheck is the last step before submit; a minimum score is required, and applicants can re-upload and re-runCreate draft → upload → process → poll reports / webhook → embed viewer; block submit until scores clear the bar
Ungated PreCheckSame in-flow PreCheck, but no minimum — results are informationalCreate draft → upload → process → poll reports / webhook → embed viewer; submit always allowed
Behind the scenesApplicant submits your form; you create the project server-side and surface the viewer link on the permit recordCreate (Option A, file links) at submit → webhook → store viewer link on the record
StandaloneApplicant runs PreCheck separately; you adopt an existing project by its IDGET /projects/:projectId + reports + document + viewer-link; attach files & report to the record