PrecisionDocs

Developer Documentation

PrecisionDocs public API reference: authentication, project lifecycle, agent questions, knowledge-base search, reports, MCP connectors, errors, and rate limits.

API Overview

What the PrecisionDocs public API is, the base URL, and the three ways to connect.

What is the PrecisionDocs public API?

The PrecisionDocs public API lets your own tools, agents, and scripts drive the same project workflow the app uses: create a project for a site, wait for the knowledge base to build, ask the project agent cited questions, search indexed sources, generate reports, and download artifacts.

All REST endpoints live under https://api.precisiondocs.ai/v1.

Two access surfaces, one API:

SurfaceHow it connectsBest for
Remote MCPhttps://mcp.precisiondocs.ai/mcp - PrecisionDocs OAuth, no keyClaude Code, OpenAI Codex, Gemini CLI, Cursor, the Claude app, any MCP client
RESTHTTPS + Authorization: Bearer pd_live_... headerScripts, backends, CI, ChatGPT Actions

The machine-readable spec is at https://api.precisiondocs.ai/v1/openapi.json.

For copy-paste connector setup per client, use Account -> Integrations or the Help Center. This page is the endpoint-level reference.

Creating a project from an agent (wizard parity). A connected agent walks the same steps a person does in the app: POST /v1/parcels/resolve for each address or APN (candidates carry ll_uuid, address, size, confidence and GeoJSON geometry), POST /v1/parcels/preview with the chosen geometries to show the user the highlighted parcels, POST /v1/plan-check to learn which goal questions still need asking, then POST /v1/projects with dry_run: true and setup_mode: "wizard". The dry-run response is the pre-Create screen: estimate, prep_summary.sources (every governing source discovery found, with status and URL) and gaps - each source discovery could not settle, naming the request field that fills it (ordinance_url, county_ordinance_url, pending_gis_sources, or a skip_* flag the user must confirm). A confirmation_required response is the wizard asking a question; answer it by re-submitting with the field its options map to. Then create for real with an Idempotency-Key and poll GET /v1/projects/{id} until chat_ready.

Authentication & API Keys

Bearer pd_live keys, where to create them, scopes, and key-handling rules.

How do I authenticate REST requests?

Every REST request sends an organization API key as a Bearer token:

Authorization: Bearer pd_live_...

Create keys in Account -> API keys. Organization owners and active members can create keys; owner-created keys are org-wide, while member-created keys only authorize projects the member owns or is explicitly shared into. A connector (OAuth) session is always scoped to one person: it sees only projects that user owns or is shared into, whatever their role.

Key facts:

  • Keys are shown once at creation. Copy the full pd_live_ secret while it is visible — only a hashed form is stored.
  • Keys can carry an optional expiry and optional daily/monthly credit caps.
  • Rotate or revoke keys anytime from Account -> API keys.
  • Test a key with GET https://api.precisiondocs.ai/v1/wallet.

What scopes do API keys have?

Each key carries a scope list. Requests to an endpoint outside the key's scopes fail with 403 insufficient_scope.

ScopeGrantsDefault
project:createCreate projects (POST /v1/projects) and run plan checks (POST /v1/plan-check)Yes
project:readList/poll projects, site knowledge, messages, reports, searchYes
agent:askAsk the project agent (POST /v1/projects/{id}/messages)Yes
report:writeGenerate reports (POST /v1/projects/{id}/reports)Yes
source:writeAdd URL and GIS sources and upload files (POST .../sources, POST .../gis-sources, POST /v1/uploads/*)Yes
artifact:readList artifacts and mint one-time download linksYes
wallet:readRead the organization credit balanceYes
gis:exportGIS export artifacts (POST .../gis-exports) and map/aerial rendering (POST .../maps)Yes
siteplan:readSite Plan Studio design data: concept geometry, design criteria, yield scenarios, CAD exports, proposalsYes
siteplan:writeApprove the development program, propose and decide concept proposals (POST .../site-plan/program/approve, POST .../site-plan/proposals, POST .../site-plan/proposals/{id}/approve or /reject). Also needs Site Plan Studio enabled for the organizationYes

There is no custom-scope picker: every new key carries the full set above, and a connector (OAuth) token carries the same set. A credential keeps the scopes it was minted with - they are never backfilled - so a key or connected app created before siteplan:write existed (September 2026) does not have it: mint a new key, or reconnect the app from its client, to pick it up.

Security rules:

  • Never paste a pd_live_ key into a shared document, prompt, chat message, screenshot, or support ticket.
  • Put keys only in server-side config, sent as the Authorization header. Never in a URL, and never in an MCP client config - MCP clients sign in with OAuth and hold no key.
  • Rotate a key immediately if it may have been exposed.

Projects Lifecycle

Create a project, poll readiness, and verify parcel zoning over REST.

How do I create and manage projects?

EndpointWhat it doesRate limit
POST /v1/projectsCreate a project (returns 202 — bootstrap runs async)8/min
GET /v1/projectsList your projects, a page at a time: limit (1-100) and cursor, answered as data and next_cursor (null on the last page)60/min
GET /v1/projects/{project_id}Poll readiness + knowledge-base summary60/min
PATCH /v1/projects/{project_id}/parcels/{parcel_index}/zoningVerify or correct a parcel zoning designation30/min

Creating a project (POST /v1/projects):

  • dry_run: true returns a setup and cost estimate without creating anything.
  • setup_mode: "wizard" (default) runs the same source-discovery gates as the in-app New Project wizard; setup_mode: "provided" preserves caller-supplied parcels and source decisions.
  • When the platform needs a decision (ambiguous parcels, source choices, duplicates), the API responds 422 with confirmation_required and the choices to present. Re-submit with the selections applied.
  • Send an Idempotency-Key header so a retried create never makes a duplicate project.

Polling readiness (GET /v1/projects/{project_id}):

  • Poll every 5–15 seconds until chat_ready is true.
  • The response carries a readiness block with per-target ordinance status, GIS layer states, supplemental source lanes, and advisories - use it to see exactly which lane is still open.
  • A ready bootstrap can still hold chat_ready: false with zoning_verification_required until every parcel's zoning is verified — confirm or correct each parcel via the zoning PATCH, then re-poll.

Plan Check, Sources & Uploads

Classify a goal before creation, then attach URL sources, GIS sources, and file uploads to a project.

How do I classify a goal and attach sources to a project?

All five endpoints require the source:write scope except plan check, which uses project:create. Source and upload writes accept an Idempotency-Key header so a retry never double-ingests.

EndpointWhat it doesScopeRate limit
POST /v1/plan-checkClassifies goal text into a typology and reports which follow-up values the goal already answersproject:create20/min
POST /v1/projects/{project_id}/sourcesAdds a URL source; the response names the lane that accepted it (document, supplemental ordinance, or GIS)source:write10/min
POST /v1/projects/{project_id}/gis-sourcesRegisters a user-verified GIS source by authority scopesource:write10/min
POST /v1/uploads/initiateStarts a file-upload job for a local documentsource:write30/min
POST /v1/uploads/{job_id}/finalizeFinalizes an initiated upload into the project knowledge basesource:write30/min

Plan check (POST /v1/plan-check): send { goal_text, primary_typology?, has_existing_structure? }. The verdicts in the response tell your agent which follow-up values the goal already settled and which come back as ask. Ask the user only the ask items, then pass the settled answers as project_goal_context on POST /v1/projects.

URL sources (POST .../sources): send { url, title?, category? }. The response reports which ingest lane accepted the URL - a project document, a supplemental ordinance corpus, or a GIS source - so your client can say what will happen next.

GIS sources (POST .../gis-sources): send { url, scope, jurisdiction_name? } where scope is municipality, county, or state_reference. Use it for a viewer or ArcGIS REST endpoint the jurisdiction publishes that discovery did not find.

File uploads: local files never travel as URLs. Call POST /v1/uploads/initiate, PUT the raw bytes to the returned upload_url with the file's Content-Type, then call POST /v1/uploads/{job_id}/finalize. The upload_url is a one-time PrecisionDocs link on api.precisiondocs.ai (it works once and expires after 15 minutes; the storage URL behind it never leaves the server); if the PUT fails or finalize answers upload_object_missing, initiate again with the same body and Idempotency-Key for a fresh link to the same job, PUT the bytes, then finalize. Over MCP the same two steps are precisiondocs_start_upload and precisiondocs_finish_upload, which need a client that can read the file and make an HTTP request (Claude Code, Codex).

Sources at creation: POST /v1/projects also accepts pending_supplemental_sources (max 10: url, title, category, authority_scope) and pending_gis_sources (max 6: url, scope, jurisdiction_name) so a connector can hand over everything the user confirmed in one create call.

Ask the Agent

Agent questions with model choice, citations and recoverable turn history.

How do I ask the project agent a question?

POST /v1/projects/{project_id}/messages runs the same ChatAgent the app uses. Send wait: false to receive a pollable job handle, or use the default wait: true for a synchronous answer with citations, tool usage and generated artifacts.

Limits and behavior:

  • Rate limit: 2/min per key, with a concurrency limit of 2 in-flight questions.
  • Send an Idempotency-Key header — a retried request returns the original turn instead of re-running (and re-billing) the agent.
  • Before the project is ready you get 409 with bootstrap_not_ready; when parcels still need zoning confirmation you get 409 with zoning_verification_required. Poll GET /v1/projects/{project_id} and retry.

Model selection: Read GET /v1/models for the same approved models, Auto default and estimated credit rates the app offers (requires agent:ask; no inference or charge). Pass an exact returned model_id with your message, or omit it, send null, or send auto to use Auto. Each API request defaults to Auto on its own.

A rejected, unavailable or disabled explicit selection fails before the turn starts. The answering model can be selected; report generation, classifiers and other tools retain their own task-specific models. Finished responses, and a turn read back by id, expose model_selection with requested/resolved IDs and observed served identity when available.

Reading turns back:

EndpointWhat it returns
GET /v1/projects/{project_id}/messages/{turn_id}One prior turn (question, answer, citations, artifacts)
GET /v1/projects/{project_id}/messagesTurn list for the project - API-surface turns only, not in-app chat history; data a page at a time (limit, cursor, next_cursor) plus running_turns

Site Plan & CAD Handoff

Read Site Plan Studio design data, compile a concept from the interview, and export a concept picture or geometry for Civil 3D.

How do I read site-plan design data and export concept geometry?

All four endpoints require the siteplan:read scope. Every key and connector token carries the full scope set by default, so no separate grant step exists.

EndpointWhat it doesRate limit
GET /v1/projects/{project_id}/site-planLatest compiled concept: object_counts (every object counted by type), metrics and scenario, plus WGS84-only GeoJSON features by default; include_geometry=false returns an empty FeatureCollection60/min
GET /v1/projects/{project_id}/design-criteriaResolved zoning standards per district with per-field provenance60/min
GET /v1/projects/{project_id}/yield-scenarioSettled yield scenarios per district plus snapshot summaries60/min
POST /v1/projects/{project_id}/site-plan-exportsRenders the concept to a picture (concept_png), LandXML, DXF, or GeoJSON and returns a one-time download link10/min

Concept geometry: until a concept compiles in Site Plan Studio, the site-plan route returns has_concept: false with an empty feature collection. Features carry object_type, label, layer, status, editable, and measurements; projected coordinates are stripped, WGS84 only. measurements holds site numbers only - solver bookkeeping (planner strategy, fit ratios, discarded-geometry counters) is excluded, so nothing there should be read as an area or dimension on the ground.

For a summary, pass include_geometry=false: has_concept, revision, object_counts, metrics and scenario still describe the plan, while feature_count is 0 and truncated is false. An empty FeatureCollection in that response does not mean the concept is absent. object_counts counts every plan object, including those beyond the geometry response cap, in descending count order.

Design criteria: absent values are never bare nulls. A field reads Not provided in ordinance only when the extraction corroborated the absence; otherwise it reads Not located in the indexed ordinance, which claims nothing about the law. user_provided_fields lists the values the user themselves confirmed. citations are readable references such as Tangipahoa Parish Code § 36-91; retrieval chunk identifiers are never returned as citations. stormwater_context splits into known and not_in_code, so a requirement the code is silent on is named rather than read as a zero. governing_district is the district the concept is compiled and checked against, with its basis (current, or rezone_target once the user confirmed a rezone in Site Plan Studio) and the parcel district as current_zoning_code beside it; a rezone target is also listed in districts, so never take the parcel district for the governing one.

Yield scenarios: values the user settled under one district are inherited into another district's partition when missing, with the borrowing stated per key in inherited.

CAD handoff: landxml is the native Civil 3D import format; dxf covers generic CAD; concept_png is a picture of the concept plan for people, not CAD. The response is artifact_id, filename, mime_type, download_url, and expires_at (15 minutes); geometry travels as a file, never inline. Send an Idempotency-Key header so a retried request replays the original artifact. With no compiled concept the endpoint returns 404 (no_site_plan_concept).

Concept picture disclosures: the drawing carries recorded wetland exclusion, constraint override and floodplain retention caveats, including suspended decisions. When space is limited, optional notes give way first and a long stated override reason may be shortened with a marker pointing to the full project record; the caveats remain whole. If required disclosures still cannot fit, export returns 400 (invalid_site_plan_export_request) with the refusal message. Retrying the same revision cannot resolve that refusal; transient worker failures remain 502 (export_render_failed).

How do I walk the guided site-planning interview from my own agent?

Three endpoints walk the same interview the PrecisionDocs app walks. The engine is deterministic: it picks the next question and already knows what the project settled, so you never decide what to ask next.

EndpointWhat it doesRate limit
GET /v1/projects/{project_id}/interview/workflowsGuided workflows this project can walk, which one it is on, and how far in (siteplan:read)60/min
GET /v1/projects/{project_id}/interview/stepThe next 1-3 questions as JSON Schema, plus what is already answered or inferred (siteplan:read)60/min
POST /v1/projects/{project_id}/interview/answersSubmit typed answers, get the next step back (project:create)30/min

Both reads are free. No LLM call and no credit spend, so poll them rather than guessing what comes next. Only the answer endpoint writes.

Questions arrive as JSON Schema mapped from the same control the in-app card renders: types, ranges, units, and options with their labels. A default on a field is the engine's recommendation, and recommended.reason says why - offer it as a suggestion, never as a requirement.

Nothing is asked twice. answered lists what the project settled and inferred lists what the engine derived, each with the statement behind it. remaining_question_ids is the rest of the walk; complete: true with no questions means the scripted interview is finished, and completeness.missing_required names anything a concept still needs.

A rejected batch records nothing. Values are validated against the same schema server-side; a 400 invalid_interview_answer names every bad field under errors, so fix those and resend the same batch. What is recorded is the user's own decision, on the same ledger the in-app card writes - so an interview can be started in the app and finished over the API, or the other way round.

Read standing, not the workflow rows. standing always describes the walk the project is on. selected_workflow_id is null both before a workflow is chosen and for a use that has no template lane (commercial, hotel), so a null there never means the interview has not started.

How do I compile the concept plan once the interview is complete?

When the interview returns complete: true with completeness.ready: true, four endpoints turn the settled program into a drawn concept. They call the same functions the in-app Site Plan Studio calls - one engine, no second compile path - so a walk can start in the app and finish here or the other way round.

EndpointWhat it doesRate limit
POST /v1/projects/{project_id}/site-plan/program/approveApprove the development program and advance the session to Generate Concept; idempotent once approved (siteplan:write)30/min
POST /v1/projects/{project_id}/site-plan/proposalsCreate the generate_concept proposal (or a one-zone regeneration with zone_id); returns notes, recap and conflicts, compiles nothing yet (siteplan:write)10/min
GET /v1/projects/{project_id}/site-plan/proposalsProposals on the active planning session, newest first, with limit/offset and status_filter (siteplan:read)60/min
POST /v1/projects/{project_id}/site-plan/proposals/{proposal_id}/approveApprove = compile. Returns the applied revision and the plan's scalar metrics (siteplan:write)10/min
POST /v1/projects/{project_id}/site-plan/proposals/{proposal_id}/rejectReject, recording the optional reason (siteplan:write)10/min

Every write is fenced on the session. Pass the planning_session_id the interview responses carry; a write against a session that is no longer active is 409 planning_session_conflict. state_version is optional and, when given, is a compare-and-swap token against the last response you saw. An accepted proposal write consumes that token even if the later compile blocks or fails, and the proposal-create and decision responses return the session's new state_version - send that one on the next call, and re-read the interview state after a conflict. An already-decided proposal replay consumes nothing. Send an Idempotency-Key on each write so a retry replays rather than repeats.

The writes are gated; the reads are not. Besides siteplan:write, every write requires Site Plan Studio for the organization, which comes with every paid plan (a free workspace gets it by buying credits): without it every write answers 403 site_plan_studio_not_enabled while every read in this section keeps answering 200. Single-family and subdivision plans compile through their own Studio lanes and answer 409 typology_not_supported_via_api.

A flagged proposal needs the user's reason. conflicts on a proposal names the rules the plan would break. Approving one without a reason is 422 proposal_override_reason_required; the reason is recorded in the plan's decision ledger beside the overridden findings. It is the user's own words - a client must never supply it for them.

The compile runs inline. The engine is deterministic and a 16-acre multifamily concept compiles in seconds, so approve answers with applied_revision_id, zone_failures and metrics in one call, and GET .../site-plan reports has_concept: true right after. 409 proposal_expired means the program or plan changed since the proposal - propose again. 409 proposal_blocked names a prerequisite to fix first and leaves the proposal retryable; 409 proposal_failed is a domain rejection of the compile. Re-approving an applied proposal replays (replayed: true) and never compiles twice.

Reports & Artifacts

Generate due-diligence and feasibility reports, then download artifacts through one-time links.

How do I generate and download reports?

EndpointWhat it doesRate limit
POST /v1/projects/{project_id}/reportsStart a report job (202 with a job_id)5/min
GET /v1/projects/{project_id}/reports/{job_id}Poll job status; completed jobs include artifact references60/min
GET /v1/projects/{project_id}/reportsList report jobs for the project60/min
GET /v1/projects/{project_id}/artifactsList downloadable artifacts (reports, map bundles)60/min
GET /v1/projects/{project_id}/artifacts/{artifact_id}Mint a fresh one-time download link60/min
GET /v1/downloads/{token}Stream the file a one-time link names; the link is the credential60/min per IP

Report generation:

  • report_type is due_diligence or feasibility.
  • Generation is asynchronous — poll the job until it is terminal.
  • If a matching report job is already queued or running, the API attaches to the in-flight job instead of dispatching (and billing) a duplicate.

Artifacts:

  • Every download_url is a one-time PrecisionDocs link on api.precisiondocs.ai: it works once and expires after 15 minutes, and the file streams from our server, so no storage URL, bucket, path or token ever reaches your client. Re-request the artifact endpoint any time you need a fresh link; reminting is free.
  • Hand your user only these PrecisionDocs links. If your own code downloads the file, mint a fresh link for the user, because the one you used is spent.

Wallet & Billing

How API usage debits the organization credit wallet, and per-key and per-connection caps.

How is API usage billed?

GET /v1/wallet returns the organization's credit balance so your integration can check affordability before metered work.

The credit model:

  • Reserved then settled: project creation (POST /v1/projects) holds an estimate up front, then debits what bootstrap actually used and releases the remainder.
  • Metered: agent turns (POST .../messages), summaries (POST .../summaries), and report generation (POST .../reports) debit credits based on the underlying work, exactly like in-app usage.
  • Flat: knowledge-base search (POST .../search) costs 2 credits per call.
  • Free: reads — project polling, site knowledge, message history, report status, artifact listing, wallet checks.

Caps:

  • Keys can carry optional daily and monthly credit caps (never mandatory). When a cap or the wallet balance is exhausted, metered endpoints return 402 (api_key_credit_cap_exceeded or insufficient_credits).
  • A chat-agent (MCP) connection can carry the optional daily and monthly credit caps the person chose on the consent page; past one, metered endpoints return 402 connection_credit_cap_exceeded, and the way past it is reconnecting with a higher cap.
  • A key's or connection's caps are read before every operation that spends credits, and count what that key or connection charged to the wallet.
  • Owners top up or configure auto-refill from Account -> Billing.

Connector spend lands in the same workspace wallet as in-app work and is labelled API or MCP in usage views. For the customer-facing explanation of what spends credits and why amounts vary, see the Credits & Billing help articles.

Errors & Rate Limits

The error envelope, common error types, per-route limits, and the 202-then-poll pattern.

What does an error response look like?

Every non-2xx response is one RFC 9457 problem document, Content-Type: application/problem+json, whatever refused the call - a route, the scope check, request validation, the rate limiter or an unknown path:

{
  "type": "https://precisiondocs.ai/developers/errors#bootstrap_not_ready",
  "title": "Conflict",
  "status": 409,
  "code": "bootstrap_not_ready",
  "detail": "Project knowledge base is still building.",
  "request_id": "5d0c1c9e-7f1b-4f5e-9a57-2f6d3c1b8e44",
  "retry_after_seconds": 15,
  "poll": "/v1/projects/{project_id}"
}

Branch on code. detail is human copy, request_id matches the X-Request-ID response header (quote it to support), and recovery.action (with recovery.url where there is one) says what to do next when the error has a known recovery. The context of each error rides beside them: required_scope, required_credits and available_credits, period and credit_cap, gaps, and for a request the schema refused, errors[] with each bad field, its code and a message.

A 429 from the per-route rate limiter is the same shape with code: "rate_limited", retry_after_seconds and a Retry-After header. It is always transient - wait the named seconds and retry the same call.

Common error codes:

CodeStatusMeaning
not_found404Unknown resource, or a project outside your organization
insufficient_scope403Key lacks the scope for this endpoint
bootstrap_not_ready409Knowledge base still building — poll and retry
zoning_verification_required409Parcels need zoning confirmation before agent work
goal_version_conflict409The project goal moved since the user_goal_revision / user_goal_hash you read - re-read GET /v1/projects/{id}, confirm the wording with the user, and send again
goal_version_required428PATCH /v1/projects/{id} was sent without expected_goal_revision or expected_goal_hash; a goal is replaced whole, never overwritten blind
invalid_interview_answer400One or more interview answers were rejected; nothing was recorded, and errors names each bad field
site_plan_studio_not_enabled403Site Plan Studio is not enabled for the organization (it comes with every paid plan); the compile-lane writes are refused while every read still works
planning_session_conflict409The planning_session_id is no longer the active session, or the state_version is stale - re-read and retry
program_incomplete409The program still has required gaps; gaps names them per zone
invalid_program422The program cannot be approved as it stands - a staged parking draft the studio could not confirm
program_not_approved409A concept was proposed before the program was approved
concept_proposal_rejected409The proposal could not be built as asked - an unknown zone_id, a zone with no sketched boundary, or no existing concept for a zone_id regeneration to start from
typology_not_supported_via_api409Single-family and subdivision plans compile only in the in-app Site Plan Studio
proposal_override_reason_required422The proposal carries conflicts; approving it needs the user's written reason
proposal_expired409The program or plan changed since the proposal was made - propose again
proposal_blocked409A prerequisite the compile needs is missing (block says which); the proposal stays retryable
proposal_failed409The compile was refused on a domain rule (repair gate, conflict); the proposal is marked failed
proposal_decided409The proposal was already approved, rejected or expired
proposal_execution_failed500The compile faulted rather than refusing; the proposal is left retryable, so retry the same approve
idempotency_key_reused409Same Idempotency-Key sent with a different payload
idempotency_in_progress409A request with this Idempotency-Key is still running — retry
concurrency_limit_exceeded429Too many concurrent agent turns for this key (limit 2) - retry_after_seconds in the body and a Retry-After header
house_paid_daily_limit429This key or connection added 50 URL documents and GIS services today, the daily limit for sources PrecisionDocs processes at its own cost - Retry-After names the wait until 00:00 UTC; supplemental ordinance sources and duplicates are not counted
insufficient_credits402Organization wallet is empty
api_key_credit_cap_exceeded402This key hit its daily or monthly credit cap
connection_credit_cap_exceeded402This chat-agent connection hit a daily or monthly credit cap chosen when it was connected - reconnect with a higher cap
free_tier_limit402A free-plan limit: the feature (own documents, sources, GIS registration) is not on the free plan, or its allowance is used up - limit names which; add credits
search_unavailable503Search backend temporarily unavailable — retry with backoff

What are the rate limits?

Limits are per API key. Exceeding one returns 429 with a Retry-After header and a typed body naming the wait (type: "rate_limited" plus retry_after_seconds) - wait and retry the same call. The MCP client puts the wait in the error text as "Retry after Ns".

RouteLimit
POST /v1/projects8/min
POST /v1/plan-check20/min
POST .../sources, POST .../gis-sources10/min
POST /v1/uploads/initiate, POST /v1/uploads/{job_id}/finalize30/min
GET /v1/projects, GET /v1/projects/{id}60/min
PATCH .../parcels/{i}/zoning30/min
PATCH /v1/projects/{id}30/min
GET .../site-knowledge60/min
POST .../messages2/min (concurrency 2)
GET .../messages, GET .../messages/{turn_id}60/min
POST .../search30/min
POST .../summaries5/min
POST .../gis-exports10/min
POST .../maps10/min
POST /v1/parcels/resolve20/min
POST /v1/parcels/preview10/min
GET .../site-plan, GET .../design-criteria, GET .../yield-scenario60/min
POST .../site-plan-exports10/min
GET .../interview/workflows, GET .../interview/step60/min
POST .../interview/answers30/min
POST .../site-plan/program/approve30/min
POST .../site-plan/proposals, POST .../site-plan/proposals/{id}/approve, POST .../site-plan/proposals/{id}/reject10/min
GET .../site-plan/proposals60/min
GET .../gis-exports/{artifact_id}30/min
POST .../reports5/min
GET .../reports, GET .../reports/{job_id}, GET .../artifacts60/min
GET .../artifacts/{artifact_id}30/min
GET /v1/wallet30/min

The 202-then-poll pattern: long-running work (project creation, report generation) returns 202 immediately. Poll the corresponding read endpoint every 5–15 seconds until the resource is terminal — never spin faster than the read limits.

MCP & Connectors

Remote and local MCP servers, and how MCP tools map to REST operations.

How do I connect over MCP?

One MCP endpoint wraps the whole API: https://mcp.precisiondocs.ai/mcp, Streamable HTTP with OAuth 2.1 (dynamic client registration, PKCE S256, refresh-token rotation). The client opens your browser, you approve on the PrecisionDocs consent page, and the client holds a token scoped to your user and organization. No pd_live_ key is involved; the endpoint rejects one.

ClientSetup
Claude Codeclaude mcp add --scope user --transport http precisiondocs https://mcp.precisiondocs.ai/mcp, then /mcp -> precisiondocs -> Authenticate
OpenAI Codexcodex mcp add precisiondocs --url https://mcp.precisiondocs.ai/mcp, then codex mcp login precisiondocs
Gemini CLIgemini mcp add --scope user --transport http precisiondocs https://mcp.precisiondocs.ai/mcp, then /mcp auth precisiondocs (the command writes {"url": "https://mcp.precisiondocs.ai/mcp", "type": "http"} under mcpServers.precisiondocs in settings.json)
Cursor{"mcpServers":{"precisiondocs":{"url":"https://mcp.precisiondocs.ai/mcp"}}} in mcp.json, then sign in when Cursor asks
Claude app (claude.ai, Claude Desktop)Customize -> Connectors -> Add custom connector -> paste the URL -> Add -> Connect (Team and Enterprise: an Owner adds it in Organization settings -> Connectors first)
ChatGPTPlugins -> + -> Add custom MCP server -> paste the URL -> OAuth -> Create as a plugin (no such option: Developer mode under Settings -> Security and login)
Any other MCP clientThe URL alone. A client that only runs local stdio servers: npx -y mcp-remote https://mcp.precisiondocs.ai/mcp (the standard mcp-remote bridge, same browser sign-in)

Discovery for a custom client: /.well-known/oauth-protected-resource/mcp names the authorization server, /.well-known/oauth-authorization-server lists authorization_endpoint, token_endpoint, registration_endpoint and revocation_endpoint. Disconnect a client any time from Account -> Integrations -> Connected apps; that revokes its token and refresh token immediately.

A long agent turn never times out. precisiondocs_ask_agent submits the turn asynchronously (wait: false): the API answers 202 with a job_id, the turn runs on a worker, and the tool polls GET /v1/projects/{id}/messages/{job_id} for up to 45 seconds before handing the model that handle to keep polling - always under the proxy read timeout, so no more opaque 524s. REST callers get the same contract by sending wait: false; wait: true still runs the turn synchronously in one request. If a transport error ever still says the turn may be running, do not re-ask (a second ask would charge a second turn): list the project messages and read the newest turn.

Switch models by asking: Tell your connected assistant "use Claude Sonnet 5.5" or "switch to Auto". It reads precisiondocs_get_models and passes the approved model_id on subsequent precisiondocs_ask_agent calls. Auto is the default whenever no explicit choice is sent. A preference change itself does not run a paid turn; the assistant keeps it within your current conversation.

MCP tools and their REST equivalents:

MCP toolREST operation
precisiondocs_plan_checkPOST /v1/plan-check
precisiondocs_resolve_parcelsPOST /v1/parcels/resolve
precisiondocs_preview_parcelsPOST /v1/parcels/preview (returned to the client as an MCP image block)
precisiondocs_create_projectPOST /v1/projects
precisiondocs_add_sourcePOST /v1/projects/{id}/sources, or POST /v1/projects/{id}/gis-sources when scope is given
precisiondocs_start_uploadPOST /v1/uploads/initiate
precisiondocs_finish_uploadPOST /v1/uploads/{job_id}/finalize
precisiondocs_dismiss_advisoryDELETE /v1/projects/{id}/advisories/{advisory_id}
precisiondocs_list_projectsGET /v1/projects
precisiondocs_get_projectGET /v1/projects/{id}
precisiondocs_update_project_goalPATCH /v1/projects/{id}
precisiondocs_verify_zoningPATCH /v1/projects/{id}/parcels/{i}/zoning
precisiondocs_get_modelsGET /v1/models
precisiondocs_ask_agentPOST /v1/projects/{id}/messages
precisiondocs_get_messageGET /v1/projects/{id}/messages/{turn_id}
precisiondocs_list_messagesGET /v1/projects/{id}/messages
precisiondocs_get_site_knowledgeGET /v1/projects/{id}/site-knowledge
precisiondocs_get_study_areaGET /v1/projects/{id}/study-area
precisiondocs_get_parcel_geometryGET /v1/projects/{id}/parcels/geometry
precisiondocs_set_study_areaPOST /v1/projects/{id}/study-area
precisiondocs_clear_study_areaDELETE /v1/projects/{id}/study-area
precisiondocs_reapply_study_areaPOST /v1/projects/{id}/study-area/reapply
precisiondocs_export_gisPOST /v1/projects/{id}/gis-exports
precisiondocs_create_mapPOST /v1/projects/{id}/maps
precisiondocs_get_site_planGET /v1/projects/{id}/site-plan
precisiondocs_get_design_criteriaGET /v1/projects/{id}/design-criteria
precisiondocs_get_yield_scenarioGET /v1/projects/{id}/yield-scenario
precisiondocs_export_site_planPOST /v1/projects/{id}/site-plan-exports
precisiondocs_list_workflowsGET /v1/projects/{id}/interview/workflows
precisiondocs_get_interview_stepGET /v1/projects/{id}/interview/step
precisiondocs_answer_interviewPOST /v1/projects/{id}/interview/answers
precisiondocs_approve_programPOST /v1/projects/{id}/site-plan/program/approve
precisiondocs_propose_conceptPOST /v1/projects/{id}/site-plan/proposals
precisiondocs_list_proposalsGET /v1/projects/{id}/site-plan/proposals
precisiondocs_decide_proposalPOST /v1/projects/{id}/site-plan/proposals/{proposal_id}/approve or /reject
precisiondocs_search_projectPOST /v1/projects/{id}/search
precisiondocs_generate_reportPOST /v1/projects/{id}/reports
precisiondocs_get_reportGET /v1/projects/{id}/reports/{job_id}
precisiondocs_list_artifactsGET /v1/projects/{id}/artifacts
precisiondocs_get_artifactGET /v1/projects/{id}/artifacts/{artifact_id}
precisiondocs_get_walletGET /v1/wallet
precisiondocs_routePOST /v1/route: names the workflow a request asks for; the tool returns its tools in order, an argument sketch, the confirmations and the guide
precisiondocs_get_docsNo REST call for a workflow guide (section set to a guide id such as maps-and-exports); otherwise the public OpenAPI spec (below), served as an index of every operation with the tool that calls it, its scope and its cost, or as one operation with its schemas resolved

REST-only, each for a stated reason precisiondocs_get_docs repeats: PATCH /v1/projects/{id}/documents/{document_id} (no MCP read lists uploaded-document ids, and a display rename changes nothing an answer reads), GET /v1/projects/{id}/gis-exports/{artifact_id} (precisiondocs_get_artifact re-mints the same file), POST /v1/projects/{id}/summaries (the connected model summarizes itself, so a second model would spend credits for the same text) and GET /v1/projects/{id}/reports (precisiondocs_get_report and precisiondocs_list_artifacts cover it, and re-sending a report request attaches to the running job).

For step-by-step setup with copyable configs, use the in-app Account -> Integrations wizard or the Help Center connector guides.

How do I install the PrecisionDocs skill for Claude?

The PrecisionDocs Agent Skill teaches Claude the workflows behind the MCP tools: which tool answers what and in what order, what needs your confirmation, what spends credits, and every map and export PrecisionDocs makes, with how to put maps into a document. It is generated from the same source as the MCP server's own instructions, so the two never disagree, and it drives the connector above, which must be connected too.

ClientInstall
Claude Code (macOS, Linux, Git Bash)curl -fsSL https://precisiondocs.ai/skills/precisiondocs.zip -o precisiondocs-skill.zip && unzip -o precisiondocs-skill.zip -d ~/.claude/skills && rm precisiondocs-skill.zip
Claude Code (Windows PowerShell)Invoke-WebRequest https://precisiondocs.ai/skills/precisiondocs.zip -OutFile precisiondocs-skill.zip; Expand-Archive -Force precisiondocs-skill.zip "$HOME/.claude/skills"; Remove-Item precisiondocs-skill.zip
Claude app (claude.ai, Claude Desktop)Download precisiondocs.zip, then open Settings, find Skills, choose Upload skill and pick the zip

Claude loads the skill when a request fits it. Without the skill the same guides are one call away inside MCP: precisiondocs_get_docs with section set to a guide id (for example maps-and-exports), or with no section for the list of guides and the full API index.

Can I address PrecisionDocs by my agent name in Claude?

Yes. Name your agent under Account -> Profile -> Agent personality, the name the in-app chat already answers to. Every MCP session you connect opens its server instructions with that name (for example: The user calls PrecisionDocs "Atlas". A request to Atlas is a request to use these tools.) and names it on precisiondocs_ask_agent, so asking Claude "Atlas, what are the setbacks on my Maple Street project?" reaches PrecisionDocs instead of being answered from Claude's general knowledge.

The name is kept to plain name characters, up to 50. A name that already means Claude or any assistant (Claude, ChatGPT, Gemini, Copilot, Assistant, AI, Agent, Bot and similar) is not used for routing, because it would send your ordinary requests into paid agent turns; pick a distinct name such as Atlas. Claude reads the instructions when it connects, so after a rename start a new Claude session to pick it up.

OpenAPI Spec

The machine-readable schema and importing it into ChatGPT Actions.

Where is the OpenAPI spec?

The public OpenAPI 3.1 schema lives at https://api.precisiondocs.ai/v1/openapi.json. It is pruned to the public /v1 surface and always matches production.

ChatGPT Actions: in your Custom GPT, open Configure -> Actions -> Create new action, click Import from URL, and paste the spec URL. Do not paste the URL into the large Schema editor — use the Import dialog. Then set Authentication -> API Key -> Bearer with your pd_live_ key.

Any OpenAPI-aware client generator (openapi-typescript, openapi-python-client, Postman/Insomnia import) works against the same URL.

Also useful