Developers

API reference

Groundbook API

Every route the website uses, open to your own code. Requests act as your account with exactly your permissions. Each endpoint shows curl and the gb command line; assistants reach the same routes through the tools named on it.

Authentication

Create an API key in Connectors → API keys and send it as a bearer token. A read-only key can call GET routes only. Keys never carry admin powers, and you can revoke one at any time.

curl
curl "https://app.groundbook.ai/api/auth/me" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"

Assistants use OAuth 2.1 instead: authorization code with PKCE, dynamic client registration, and rotating refresh tokens. Discovery is at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource; the MCP endpoint is https://app.groundbook.ai/api/mcp. An OAuth access token works on every route below in the same way as a key.

The base URL is https://app.groundbook.ai. Bodies are JSON; ids are UUIDs; times are ISO 8601 in UTC.

Errors

Every error is JSON with one plain-English error message you can show to a person.

json
{ "error": "Wait for every file to finish analyzing before payment." }
StatusTypeMeaning
400Bad requestA field is missing or out of range. The message says which.
401UnauthorizedNo key, a revoked key, or an expired OAuth token. Refresh the token or create a new key.
403ForbiddenA read-only key on a write route, or an action only the owner can take.
404Not foundThe id does not exist or your account cannot open it. We do not say which.
409ConflictThe check is past editing, payment is in progress, or someone needs project access first (code: "project_access_required").
429Too many requestsOnly the OAuth token route and anonymous uploads send this. Wait a minute and retry.

Account

Who the key or token belongs to.

GET/api/auth/me

The signed-in account: email, organization, and credit balance.

Assistant tools: get_me

Request
curl "https://app.groundbook.ai/api/auth/me" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb whoami
Response
{
  "user": {
    "id": "An9o6aC0tnYvuUqnsIwEp1HLjuk2",
    "email": "you@example.com",
    "displayName": "Your Name",
    "isSuperAdmin": false,
    "organization": {
      "id": "6c93e7de-cbd9-44c8-a5dd-edafca9138a0",
      "name": "Example Engineering",
      "role": "admin",
      "billingMode": "account"
    },
    "billing": {
      "scope": "account",
      "balanceCents": 12000
    }
  }
}

Projects

A project holds one set of drawings and its latest check. Every project you can open on the site is here, with the same permissions.

GET/api/projects

Projects this account can open, newest first.

Assistant tools: list_projects

ParameterTypeDescription
q
query
stringText to match against the project address or file names.
status
query
draft | processing | readyRepeat to allow several statuses.
page
query
integerPage number, starting at 1. 20 projects per page.
owner
query
stringOwner user id; repeat for several. Only useful inside an organization.
Request
curl "https://app.groundbook.ai/api/projects?status=ready" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb list_projects --status ready
Response
{
  "projects": [
    {
      "id": "8c0d0499-7e63-40f6-812f-0b080be21449",
      "address": "2465 Semlin Drive, Vancouver, BC",
      "isOwner": true,
      "ownerEmail": "you@example.com",
      "accessSource": "owner",
      "checkId": "7100d123-0aac-41eb-898d-98679e1f9694",
      "checkStatus": "complete",
      "customerStatus": "ready",
      "documentCount": 2,
      "issueCount": 42,
      "primaryDocumentName": "B&S_-_Structural_Dwgs.pdf",
      "createdAt": "2026-08-21T01:40:12.000Z",
      "updatedAt": "2026-08-21T01:52:23.506Z"
    }
  ],
  "owners": [
    {
      "id": "An9o6aC0tnYvuUqnsIwEp1HLjuk2",
      "email": "you@example.com",
      "displayName": "Your Name"
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalPages": 1,
    "totalCount": 1
  }
}

POST/api/projects201

Creates a draft project with its first check. Name it and add files next.

Assistant tools: create_project

Request
curl -X POST "https://app.groundbook.ai/api/projects" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb create_project --address "12 Main St" --review-instructions "Check the framing plan against the foundation plan."
Response
{
  "projectId": "0b1b0ef3-9f2d-4c1a-8d0e-3f2a5a6b7c8d",
  "checkId": "fed043d1-a9c8-4eab-bd26-047af9d6d34b",
  "address": null,
  "reviewInstructions": "Find conflicts, inconsistencies, and missing information across the drawings.",
  "reviewInstructionsAutoGenerated": true,
  "reviewInstructionsTitle": null,
  "codeCheckEnabled": true,
  "priceCents": 10000
}

GET/api/projects/{projectId}

One project with its latest check's settings and totals.

Assistant tools: get_project, update_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
Request
curl "https://app.groundbook.ai/api/projects/PROJECT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb get_project --project-id PROJECT_ID
Response
{
  "project": {
    "id": "8c0d0499-7e63-40f6-812f-0b080be21449",
    "address": "2465 Semlin Drive, Vancouver, BC",
    "organizationAccessEnabled": false,
    "projectDisciplines": [],
    "projectIssueStatuses": [
      "Needs RFI"
    ],
    "isOwner": true,
    "accessSource": "owner",
    "ownerEmail": "you@example.com",
    "ownerDisplayName": "Your Name",
    "updatedAt": "2026-08-21T01:52:23.506Z",
    "checkId": "7100d123-0aac-41eb-898d-98679e1f9694",
    "checkStatus": "complete",
    "reviewInstructions": "Review these plans for coordination conflicts.",
    "reviewInstructionsAutoGenerated": false,
    "reviewInstructionsTitle": "Coordination",
    "codeCheckEnabled": true,
    "priceCents": 10000,
    "paymentMethod": "credits",
    "creditsAppliedCents": 10000,
    "paidAt": "2026-08-21T01:52:23.506Z",
    "documentCount": 2,
    "pageCount": 59,
    "codeCount": 0,
    "primaryDocumentName": "B&S_-_Structural_Dwgs.pdf"
  }
}

PATCH/api/projects/{projectId}

Renames a project.

Assistant tools: create_project, update_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
address
body
string | null requiredThe new address or name, up to 500 characters. null clears it.
Request
curl -X PATCH "https://app.groundbook.ai/api/projects/PROJECT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"address":"12 Main St"}'
gb update_project --project-id PROJECT_ID --address "12 Main St"
Response
{
  "address": "12 Main St"
}

DELETE/api/projects/{projectId}204

Permanently deletes a project with its files and issues. Owner only. This cannot be undone.

Assistant tools: delete_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
Request
curl -X DELETE "https://app.groundbook.ai/api/projects/PROJECT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb delete_project --project-id PROJECT_ID --yes
Response
No body.
  • 409 when a payment for the project is still being completed.

GET/api/projects/{projectId}/shares

Who can open the project: editors, pending invitations, and organization access.

Assistant tools: get_project, share_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
Request
curl "https://app.groundbook.ai/api/projects/PROJECT_ID/shares" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb api GET /api/projects/PROJECT_ID/shares
Response
{
  "owner": {
    "id": "An9o6aC0tnYvuUqnsIwEp1HLjuk2",
    "email": "you@example.com",
    "displayName": "Your Name"
  },
  "editors": [
    {
      "id": "u2",
      "email": "eng@example.com",
      "displayName": "Eng",
      "sharedAt": "2026-09-01T00:00:00.000Z",
      "source": "direct"
    }
  ],
  "invitations": [
    {
      "id": "c79a143e-9ac5-48b4-86c3-67e352e8a05f",
      "email": "new@example.com",
      "createdAt": "2026-09-14T00:00:00.000Z"
    }
  ],
  "organization": {
    "id": "6c93e7de-cbd9-44c8-a5dd-edafca9138a0",
    "name": "Example Engineering"
  },
  "organizationAccessEnabled": false,
  "canChangeOrganizationAccess": true
}

POST/api/projects/{projectId}/shares201

Invites someone by email. An existing account gets access at once; a new one gets an invitation.

Assistant tools: share_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
email
body
string requiredThe person to invite.
Request
curl -X POST "https://app.groundbook.ai/api/projects/PROJECT_ID/shares" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"eng@example.com"}'
gb share_project --project-id PROJECT_ID --email eng@example.com
Response
{
  "editor": {
    "id": "u2",
    "email": "eng@example.com",
    "displayName": "Eng"
  },
  "delivered": true
}
  • For an email without an account the response is { invitation, delivered } instead.

PATCH/api/projects/{projectId}/shares

Lets everyone in the owner's organization open the project, or stops that.

Assistant tools: share_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
organizationAccessEnabled
body
boolean requiredtrue to open the project to the organization.
Request
curl -X PATCH "https://app.groundbook.ai/api/projects/PROJECT_ID/shares" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"organizationAccessEnabled":true}'
gb share_project --project-id PROJECT_ID --organization-access true
Response
{
  "organizationAccessEnabled": true
}

DELETE/api/projects/{projectId}/shares/{userId}204

Removes a person's access.

Assistant tools: remove_project_share

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
userId
path
string requiredThe editor's user id, from the shares list.
Request
curl -X DELETE "https://app.groundbook.ai/api/projects/PROJECT_ID/shares/USER_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb remove_project_share --project-id PROJECT_ID --user-id USER_ID --yes
Response
No body.

DELETE/api/projects/{projectId}/invitations/{invitationId}204

Cancels a pending invitation.

Assistant tools: remove_project_share

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
invitationId
path
uuid requiredThe invitation id, from the shares list.
Request
curl -X DELETE "https://app.groundbook.ai/api/projects/PROJECT_ID/invitations/INVITATION_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb remove_project_share --project-id PROJECT_ID --invitation-id INVITATION_ID --yes
Response
No body.

POST/api/projects/{projectId}/statuses201

Adds a custom issue status to the project, next to open, reviewed, resolved, and dismissed.

Assistant tools: update_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
status
body
string requiredThe status name, up to 80 characters.
Request
curl -X POST "https://app.groundbook.ai/api/projects/PROJECT_ID/statuses" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"Needs RFI"}'
gb update_project --project-id PROJECT_ID --add-issue-status "Needs RFI"
Response
{
  "status": "Needs RFI"
}

POST/api/projects/{projectId}/disciplines201

Adds a discipline the project can tag issues with.

Assistant tools: update_project

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
discipline
body
string requiredThe discipline name, up to 80 characters.
Request
curl -X POST "https://app.groundbook.ai/api/projects/PROJECT_ID/disciplines" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"discipline":"Fire protection"}'
gb update_project --project-id PROJECT_ID --add-discipline "Fire protection"
Response
{
  "discipline": "Fire protection"
}

Checks

A check is one review run over a project's files. Set what it should focus on, add codes, then pay and start it.

GET/api/checks/{checkId}

Where the check is: draft, waiting for payment, a percentage while it runs, complete, or failed.

progressPercent weighs every stage the same: a started stage counts half, a finished one counts in full. It is 100 once the check is complete.

Assistant tools: get_project

ParameterTypeDescription
checkId
path
uuid requiredThe check id, from the project.
Request
curl "https://app.groundbook.ai/api/checks/CHECK_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb get_project --project-id PROJECT_ID
Response
{
  "check": {
    "id": "7100d123-0aac-41eb-898d-98679e1f9694",
    "projectId": "8c0d0499-7e63-40f6-812f-0b080be21449",
    "status": "processing",
    "progressPercent": 43,
    "documentCount": 2,
    "pageCount": 59,
    "issueCount": 0,
    "priceCents": 10000,
    "paidAt": "2026-08-21T01:52:23.506Z",
    "createdAt": "2026-08-21T01:40:12.000Z",
    "updatedAt": "2026-08-21T02:10:00.000Z",
    "url": "https://app.groundbook.ai/projects/8c0d0499-7e63-40f6-812f-0b080be21449"
  }
}

GET/api/checks/{checkId}/review-instructions

What the check focuses on, and the instructions you used on recent projects.

Assistant tools: create_project, set_review_instructions

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
Request
curl "https://app.groundbook.ai/api/checks/CHECK_ID/review-instructions" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb api GET /api/checks/CHECK_ID/review-instructions
Response
{
  "reviewInstructions": "Check the framing plan against the foundation plan for grid mismatches.",
  "reviewInstructionsAutoGenerated": false,
  "reviewInstructionsTitle": "Framing vs foundation",
  "codeCheckEnabled": true,
  "recentReviewInstructions": []
}

PATCH/api/checks/{checkId}/review-instructions

Sets what the check focuses on and whether it checks against building codes. Draft checks only.

Assistant tools: create_project, set_review_instructions

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
reviewInstructions
body
string required20 to 4000 characters.
reviewInstructionsTitle
body
string | null requiredA short title, up to 80 characters, or null.
autoGenerated
body
boolean requiredfalse when a person wrote the instructions.
codeCheckEnabled
body
boolean requiredWhether to check against building codes.
Request
curl -X PATCH "https://app.groundbook.ai/api/checks/CHECK_ID/review-instructions" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reviewInstructions":"Check the framing plan against the foundation plan for grid mismatches.","reviewInstructionsTitle":"Framing vs foundation","autoGenerated":false,"codeCheckEnabled":true}'
gb set_review_instructions --project-id PROJECT_ID --review-instructions "Check the framing plan against the foundation plan for grid mismatches."
Response
{
  "reviewInstructions": "Check the framing plan against the foundation plan for grid mismatches.",
  "reviewInstructionsTitle": "Framing vs foundation",
  "codeCheckEnabled": true
}
  • 409 once payment has started; the check is no longer editable.

POST/api/checks/{checkId}/code-sources

Adds a building code by public URL, or marks one of the check's files as the code book.

Assistant tools: add_code_source

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
codeName
body
string requiredThe code's name, up to 300 characters.
sourceUrl
body
stringA public URL of the code. Give this or checkDocumentId.
checkDocumentId
body
uuidOne of the check's files that is the code book.
Request
curl -X POST "https://app.groundbook.ai/api/checks/CHECK_ID/code-sources" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"codeName":"2022 California Building Code","sourceUrl":"https://example.com/cbc-2022.pdf"}'
gb add_code_source --project-id PROJECT_ID --code-name "2022 California Building Code" --source-url https://example.com/cbc-2022.pdf
Response
{
  "source": {
    "codeName": "2022 California Building Code",
    "sourceType": "url",
    "sourceStatus": "queued",
    "sourceUrl": "https://example.com/cbc-2022.pdf"
  },
  "priceCents": 12000
}

DELETE/api/checks/{checkId}/code-sources

Removes a building code from the check.

Assistant tools: remove_code_source

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
codeName
query
string requiredThe code's name.
Request
curl -X DELETE "https://app.groundbook.ai/api/checks/CHECK_ID/code-sources?codeName=2022+California+Building+Code" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb remove_code_source --project-id PROJECT_ID --code-name "2022 California Building Code" --yes
Response
{
  "priceCents": 10000
}

POST/api/checks/{checkId}/checkout

Pays for and starts the check. Credits or the free first project are used when available; otherwise you get a payment link.

Assistant tools: start_check

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
Request
curl -X POST "https://app.groundbook.ai/api/checks/CHECK_ID/checkout" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
gb start_check --project-id PROJECT_ID
Response
{
  "paid": true,
  "paymentMethod": "credits",
  "projectId": "8c0d0499-7e63-40f6-812f-0b080be21449"
}
  • When payment is needed the response is { paid: false, url } and the check starts as soon as the payment completes.
  • 409 with a plain-English reason when a file is still analyzing, no file is marked checked, the instructions are missing, or the check is already paid.

Files

PDFs in a check, and PDFs saved for reuse. Uploads go straight to storage through a signed URL.

GET/api/checks/{checkId}/documents

The check's files with their analysis status, plus its code sources and price.

Assistant tools: get_project, add_saved_files_to_project

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
Request
curl "https://app.groundbook.ai/api/checks/CHECK_ID/documents" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb get_project --project-id PROJECT_ID
Response
{
  "documents": [
    {
      "id": "73808823-a773-4917-a5a6-24168948f498",
      "checkDocumentId": "0dcf2ea3-e845-417a-9aba-1b1ce9ea092c",
      "name": "B&S_-_Structural_Dwgs.pdf",
      "sizeBytes": "48213377",
      "pages": 42,
      "kind": "drawing",
      "saved": false,
      "reviewRole": "checked",
      "processingStatus": "complete",
      "codeMatchingStatus": null
    }
  ],
  "codeSources": [],
  "projectAddress": "2465 Semlin Drive, Vancouver, BC",
  "codeCheckEnabled": true,
  "priceCents": 10000
}

POST/api/uploads201

Step 1 of an upload: reserves a file and returns a signed URL to PUT the PDF to.

PUT the bytes to uploadUrl with the returned headers, then call the complete route. The CLI's gb upload does all three steps.

ParameterTypeDescription
checkId
body
uuid requiredA draft check.
fileName
body
string requiredMust end in .pdf.
sizeBytes
body
integer requiredUp to 1 GB.
Request
curl -X POST "https://app.groundbook.ai/api/uploads" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"checkId":"fed043d1-a9c8-4eab-bd26-047af9d6d34b","fileName":"plans.pdf","sizeBytes":1048576}'
gb upload plans.pdf --project PROJECT_ID
Response
{
  "documentId": "6459b03a-9122-45f3-a915-7949704fdb2d",
  "uploadUrl": "https://…signed…",
  "headers": {
    "Content-Type": "application/pdf"
  }
}

POST/api/uploads/{documentId}/complete201

Step 3 of an upload: tells us the PDF is in storage so analysis starts.

ParameterTypeDescription
documentId
path
uuid requiredFrom the create-upload response.
checkId
body
uuid requiredThe same draft check.
originalName
body
string requiredThe file name to show.
sizeBytes
body
integer requiredThe size you uploaded.
codeName
body
stringName the file as a code book instead of a drawing set.
Request
curl -X POST "https://app.groundbook.ai/api/uploads/DOCUMENT_ID/complete" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"checkId":"fed043d1-a9c8-4eab-bd26-047af9d6d34b","originalName":"plans.pdf","sizeBytes":1048576}'
gb upload plans.pdf --project PROJECT_ID
Response
{
  "checkDocumentId": "0dcf2ea3-e845-417a-9aba-1b1ce9ea092c",
  "runId": "5925f763-677f-4b04-84ea-9ccd12995f82",
  "runStatus": "queued"
}

POST/api/checks/{checkId}/documents201

Adds saved PDFs to a draft check without uploading them again.

Assistant tools: add_saved_files_to_project

ParameterTypeDescription
checkId
path
uuid requiredA draft check.
documentIds
body
uuid[] required1 to 50 saved file ids.
Request
curl -X POST "https://app.groundbook.ai/api/checks/CHECK_ID/documents" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"documentIds":["73808823-a773-4917-a5a6-24168948f498"]}'
gb add_saved_files_to_project --project-id PROJECT_ID --document-ids DOC_ID_1,DOC_ID_2
Response
{
  "documents": [
    {
      "checkDocumentId": "0dcf2ea3-e845-417a-9aba-1b1ce9ea092c",
      "documentId": "73808823-a773-4917-a5a6-24168948f498",
      "name": "B&S_-_Structural_Dwgs.pdf"
    }
  ]
}

PATCH/api/check-documents/{checkDocumentId}

Marks a file as checked or reference, or sets its file type. Draft checks only.

Assistant tools: set_file_role

ParameterTypeDescription
checkDocumentId
path
uuid requiredThe file's id within the check, from the files list.
reviewRole
body
checked | referencechecked files are reviewed for issues; reference files are context only.
fileType
body
stringA file type such as drawing, specification, or code.
Request
curl -X PATCH "https://app.groundbook.ai/api/check-documents/CHECK_DOCUMENT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reviewRole":"checked"}'
gb set_file_role --check-document-id CHECK_DOCUMENT_ID --role checked
Response
{
  "fileType": null,
  "reviewRole": "checked",
  "codeMatchingStatus": null
}

DELETE/api/check-documents/{checkDocumentId}204

Removes a file from a draft check. An upload is deleted; a saved file stays in the library.

Assistant tools: remove_file

ParameterTypeDescription
checkDocumentId
path
uuid requiredThe file's id within the check.
Request
curl -X DELETE "https://app.groundbook.ai/api/check-documents/CHECK_DOCUMENT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb remove_file --check-document-id CHECK_DOCUMENT_ID --yes
Response
No body.

GET/api/saved-documents

PDFs saved by you or your organization, with the projects that use each one.

Assistant tools: list_saved_files

Request
curl "https://app.groundbook.ai/api/saved-documents" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb list_saved_files
Response
{
  "organization": {
    "id": "6c93e7de-cbd9-44c8-a5dd-edafca9138a0",
    "name": "Example Engineering"
  },
  "documents": [
    {
      "id": "73808823-a773-4917-a5a6-24168948f498",
      "name": "B&S_-_Structural_Dwgs.pdf",
      "pages": 42,
      "sizeBytes": "48213377",
      "scope": "organization",
      "projects": [
        {
          "id": "8c0d0499-7e63-40f6-812f-0b080be21449",
          "address": "2465 Semlin Drive, Vancouver, BC"
        }
      ]
    }
  ]
}

PATCH/api/documents/{documentId}

Saves a file for reuse, renames it, or changes whether the organization can use it.

ParameterTypeDescription
documentId
path
uuid requiredThe document id.
saved
body
booleantrue keeps the file after its project; false removes it from the library.
name
body
stringA new name.
scope
body
personal | organizationWho may reuse it. Only the person who saved it can change this.
Request
curl -X PATCH "https://app.groundbook.ai/api/documents/DOCUMENT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"saved":true,"scope":"organization"}'
gb api PATCH /api/documents/DOCUMENT_ID --data '{"saved":true,"scope":"organization"}'
Response
{
  "saved": true,
  "removed": false,
  "scope": "organization"
}

GET/api/documents/{documentId}/content

The PDF itself. Supports Range requests; add ?download=1 for an attachment.

ParameterTypeDescription
documentId
path
uuid requiredThe document id.
download
query
1Send as an attachment instead of inline.
Request
curl "https://app.groundbook.ai/api/documents/DOCUMENT_ID/content" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb api GET /api/documents/DOCUMENT_ID/content
Response
The PDF bytes with Content-Type: application/pdf.

Issues

What a check found. Each issue cites the sheets it comes from and carries a status, severity, labels, assignees, and comments.

GET/api/checks/{checkId}/issues

Every issue on a check, most severe first, with references, assignees, and your own feedback.

Assistant tools: list_issues

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
Request
curl "https://app.groundbook.ai/api/checks/CHECK_ID/issues" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb list_issues --project-id PROJECT_ID --severity High --status open
Response
{
  "issues": [
    {
      "id": "1f78a5ca-1640-4075-998e-b1495295dcda",
      "displayNumber": 1,
      "title": "Governing SW3 schedule is unclear between S42 and S43",
      "description": "Sheet S42 and sheet S43 both carry a schedule for shear wall SW3 with different bar sizes…",
      "severityLabel": "High",
      "severityRank": 100,
      "disciplines": [
        "Structural"
      ],
      "labels": [],
      "impact": "The contractor cannot tell which reinforcement to build.",
      "recommendation": "Remove one schedule or reference the governing sheet.",
      "status": "open",
      "assignees": [],
      "commentCount": 0,
      "feedback": {
        "accuracy": null,
        "usefulness": null,
        "reasons": []
      },
      "references": [
        {
          "id": "4d9dabbe-40c4-4e7f-b8b2-e0b4daf32e45",
          "documentId": "73808823-a773-4917-a5a6-24168948f498",
          "documentName": "B&S_-_Structural_Dwgs.pdf",
          "pageIndex": 41,
          "pageLabel": "42",
          "sheetNumber": "S42",
          "referenceLabel": "SW3 schedule",
          "citedText": "SW3 … 15M @ 300"
        }
      ]
    }
  ]
}

GET/api/issues/{issueId}

One issue in full, with its project.

Assistant tools: get_issue, update_issue, assign_issue

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
Request
curl "https://app.groundbook.ai/api/issues/ISSUE_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb get_issue --issue-id ISSUE_ID
Response
{
  "issue": {
    "id": "1f78a5ca-1640-4075-998e-b1495295dcda",
    "displayNumber": 1,
    "title": "Governing SW3 schedule is unclear between S42 and S43",
    "description": "Sheet S42 and sheet S43 both carry a schedule for shear wall SW3 with different bar sizes…",
    "severityLabel": "High",
    "severityRank": 100,
    "disciplines": [
      "Structural"
    ],
    "labels": [],
    "impact": "The contractor cannot tell which reinforcement to build.",
    "recommendation": "Remove one schedule or reference the governing sheet.",
    "status": "open",
    "assignees": [],
    "commentCount": 0,
    "feedback": {
      "accuracy": null,
      "usefulness": null,
      "reasons": []
    },
    "references": [
      {
        "id": "4d9dabbe-40c4-4e7f-b8b2-e0b4daf32e45",
        "documentId": "73808823-a773-4917-a5a6-24168948f498",
        "documentName": "B&S_-_Structural_Dwgs.pdf",
        "pageIndex": 41,
        "pageLabel": "42",
        "sheetNumber": "S42",
        "referenceLabel": "SW3 schedule",
        "citedText": "SW3 … 15M @ 300"
      }
    ],
    "checkId": "7100d123-0aac-41eb-898d-98679e1f9694",
    "projectId": "8c0d0499-7e63-40f6-812f-0b080be21449",
    "projectLabel": "2465 Semlin Drive, Vancouver, BC"
  }
}

PATCH/api/issues/{issueId}

Changes severity, status, or labels.

Assistant tools: update_issue

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
severityLabel
body
High | Medium | LowThe new severity.
status
body
stringopen, reviewed, resolved, dismissed, or a status saved on the project.
labels
body
string[]Replaces the labels. Up to 20, each up to 40 characters.
Request
curl -X PATCH "https://app.groundbook.ai/api/issues/ISSUE_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"resolved","labels":["grid","coordination"]}'
gb update_issue --issue-id ISSUE_ID --status resolved --labels grid,coordination
Response
{
  "issue": {
    "severityLabel": "High",
    "severityRank": 100,
    "status": "resolved",
    "labels": [
      "grid",
      "coordination"
    ]
  }
}

GET/api/issues/{issueId}/comments

The issue's comments, oldest first, with attachments.

Assistant tools: get_issue

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
Request
curl "https://app.groundbook.ai/api/issues/ISSUE_ID/comments" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb get_issue --issue-id ISSUE_ID
Response
{
  "comments": [
    {
      "id": "c1",
      "body": "Fixed in revision B.",
      "author": {
        "id": "u2",
        "email": "eng@example.com",
        "displayName": "Eng"
      },
      "mentions": [],
      "attachments": [],
      "createdAt": "2026-09-14T00:00:00.000Z",
      "updatedAt": "2026-09-14T00:00:00.000Z"
    }
  ]
}

POST/api/issues/{issueId}/comments201

Adds a comment. Mentioned people are emailed; someone outside the project is invited only when you say so.

Assistant tools: comment_on_issue

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
body
body
string requiredUp to 4000 characters.
mentions
body
{ email, displayLabel }[]People to notify, up to 20.
grantProjectAccessEmails
body
string[]Mentioned emails to invite to the project if they cannot open it.
attachmentIds
body
uuid[]Attachments uploaded through the site.
Request
curl -X POST "https://app.groundbook.ai/api/issues/ISSUE_ID/comments" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"Fixed in revision B.","mentions":[{"email":"eng@example.com","displayLabel":"eng@example.com"}],"grantProjectAccessEmails":[],"attachmentIds":[]}'
gb comment_on_issue --issue-id ISSUE_ID --body "Fixed in revision B." --mentions eng@example.com
Response
{
  "comment": {
    "id": "c2",
    "body": "Fixed in revision B.",
    "author": {
      "id": "u1",
      "email": "you@example.com",
      "displayName": "Your Name"
    },
    "mentions": [
      {
        "email": "eng@example.com",
        "displayLabel": "eng@example.com"
      }
    ],
    "attachments": [],
    "createdAt": "2026-09-14T00:00:00.000Z"
  }
}
  • 409 { code: "project_access_required", people } when a mentioned person cannot open the project and grantProjectAccessEmails does not include them.

POST/api/issues/{issueId}/assignees201

Assigns the issue to someone by email.

Assistant tools: assign_issue

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
email
body
string requiredWho to assign.
grantProjectAccess
body
booleanInvite them to the project if they cannot open it. Default false.
Request
curl -X POST "https://app.groundbook.ai/api/issues/ISSUE_ID/assignees" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"eng@example.com","grantProjectAccess":false}'
gb assign_issue --issue-id ISSUE_ID --email eng@example.com
Response
{
  "assignee": {
    "id": "a1",
    "userId": "u2",
    "email": "eng@example.com",
    "displayName": "Eng",
    "pending": false,
    "notificationDelivered": true
  },
  "notificationDelivered": true
}
  • 409 { code: "project_access_required", people } when the person cannot open the project; call again with grantProjectAccess: true to invite them.

DELETE/api/issues/{issueId}/assignees/{assigneeId}204

Removes an assignee.

Assistant tools: unassign_issue

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
assigneeId
path
uuid requiredFrom the issue's assignees.
Request
curl -X DELETE "https://app.groundbook.ai/api/issues/ISSUE_ID/assignees/ASSIGNEE_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb unassign_issue --issue-id ISSUE_ID --assignee-id ASSIGNEE_ID --yes
Response
No body.

PATCH/api/issues/{issueId}/feedback

Records whether the issue is correct and useful. One answer per person per issue.

Assistant tools: give_issue_feedback

ParameterTypeDescription
issueId
path
uuid requiredThe issue id.
accuracy
body
correct | incorrect | null requiredWhether the finding is right.
usefulness
body
useful | not_useful | null requiredWhether it helps.
reasons
body
string[] requiredAny of important, low_importance, strong_evidence, wrong_or_missing_evidence, duplicate, unclear, related_issue.
Request
curl -X PATCH "https://app.groundbook.ai/api/issues/ISSUE_ID/feedback" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"accuracy":"correct","usefulness":"useful","reasons":["important"]}'
gb give_issue_feedback --issue-id ISSUE_ID --accuracy correct --usefulness useful --reasons important
Response
{
  "feedback": {
    "accuracy": "correct",
    "usefulness": "useful",
    "reasons": [
      "important"
    ]
  }
}

Exports

A PDF report, marked-up drawings, a Bluebeam FDF, or a CSV of a check's issues. Exports build in the background.

POST/api/checks/{checkId}/exports202

Starts an export. Poll it until it is ready, then download the file.

Assistant tools: export_issues

ParameterTypeDescription
checkId
path
uuid requiredThe check id.
format
body
report | markup | fdf | csv requiredWhat to build.
issueIds
body
uuid[] | nullOnly these issues, in this order. Otherwise every open issue.
Request
curl -X POST "https://app.groundbook.ai/api/checks/CHECK_ID/exports" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format":"report"}'
gb export_issues --project-id PROJECT_ID --format report
Response
{
  "export": {
    "id": "faaef1d5-91cc-4585-9edb-83b5977af77a",
    "status": "queued",
    "format": "report",
    "fileName": "2465 Semlin Drive issues.pdf",
    "progressDone": 0,
    "progressTotal": null,
    "sizeBytes": null,
    "error": null
  }
}

GET/api/exports/{exportId}

Progress of an export you started.

Assistant tools: get_export

ParameterTypeDescription
exportId
path
uuid requiredThe export id.
Request
curl "https://app.groundbook.ai/api/exports/EXPORT_ID" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb get_export --export-id EXPORT_ID
Response
{
  "export": {
    "id": "faaef1d5-91cc-4585-9edb-83b5977af77a",
    "status": "ready",
    "format": "report",
    "fileName": "2465 Semlin Drive issues.pdf",
    "progressDone": 12,
    "progressTotal": 12,
    "sizeBytes": 2481920,
    "error": null
  }
}

GET/api/exports/{exportId}/file

The built file. 409 until the export is ready. Files are kept for seven days.

Assistant tools: get_export

ParameterTypeDescription
exportId
path
uuid requiredThe export id.
Request
curl "https://app.groundbook.ai/api/exports/EXPORT_ID/file" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY"
gb download-export EXPORT_ID --out issues.pdf
Response
The file bytes, with Content-Type and Content-Disposition set for the format.

Ask

The project assistant as one call: a question in, an answer with references out. It reads the sheets, issues, and schedules the same way the chat on the site does, and uses the project's chat credits.

POST/api/projects/{projectId}/ask

Asks a question about a project and waits for the answer.

When the answer takes longer than waitSeconds, status is running: call again with the conversationId and no question to collect it. Use the same conversationId to keep asking in context.

Assistant tools: ask

ParameterTypeDescription
projectId
path
uuid requiredThe project id.
question
body
stringUp to 8000 characters. Omit it to collect a pending answer.
conversationId
body
uuidContinue an earlier conversation.
waitSeconds
body
integer0 to 55. Default 45.
Request
curl -X POST "https://app.groundbook.ai/api/projects/PROJECT_ID/ask" \
  -H "Authorization: Bearer $GROUNDBOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Which sheets have unresolved conflicts?"}'
gb ask --project-id PROJECT_ID --question "Which sheets have unresolved conflicts?"
Response
{
  "conversationId": "28eb6c74-3b02-44d5-b7bf-3f967820e7e9",
  "turnId": "36e92139-a0fd-41bd-b911-6ecf03b024cf",
  "status": "complete",
  "answer": "Three sheets still have open conflicts: S42 and S43 disagree on the SW3 schedule (#1), and S10 conflicts with M-200 on the P2 floor elevation (#6)…",
  "message": null,
  "links": [
    {
      "label": "Issue #1",
      "href": "https://app.groundbook.ai/projects/8c0d0499-7e63-40f6-812f-0b080be21449/documents/73808823-a773-4917-a5a6-24168948f498?issue=1f78a5ca-1640-4075-998e-b1495295dcda&reference=4d9dabbe-40c4-4e7f-b8b2-e0b4daf32e45"
    }
  ],
  "url": "https://app.groundbook.ai/projects/8c0d0499-7e63-40f6-812f-0b080be21449?chat=panel&conversation=28eb6c74-3b02-44d5-b7bf-3f967820e7e9"
}
  • status is locked, with a message, when the project's free chat allowance is used up and the account has no credits.