Cheqq Docs
Developers

REST API reference

Every endpoint of the Cheqq REST API, with the scope it needs, its parameters, errors and paging.

The basics

  • Base URL: https://cheqq.ai/api/platform/v1
  • Authentication: Authorization: Bearer cqk_… with an API key, or an access token from a connected app.
  • Requests: GET and DELETE take query parameters; POST, PUT and PATCH take a JSON body (Content-Type: application/json). Ids in the path always win over the same name in the body.
  • Responses: JSON. Creating something answers 201, starting an App publish answers 202, everything else 200.
  • Who it acts as: you, in the key's workspace, with your role in each Wapp. Writes appear in each Wapp's history with "(API)" at the end, and can be undone there. An archived Wapp can be read but not changed.

Every operation here is also an MCP tool; its name is under each table.

Errors

Every error has the same shape:

{
  "error": {
    "code": "INSUFFICIENT_SCOPE",
    "message": "This key isn't allowed to add and change records. Create a key with the \"records:write\" scope.",
    "details": { "requiredScope": "records:write" }
  }
}

message is written to be shown to a person. details is null or an object with more to go on.

StatusCodes you'll seeWhat to do
400VALIDATION_ERROR, INVALID_JSON, INVALID_FIELD, NOTHING_TO_CHANGEFix the request. VALIDATION_ERROR lists each problem in details, with details.issues naming where each one is (operations.0.font); INVALID_FIELD lists the fields you could have meant.
401UNAUTHORIZED, INVALID_TOKENSend a valid key.
403INSUFFICIENT_SCOPE, FORBIDDEN, WORKSPACE_ACCESS_LOST, ACCOUNT_SUSPENDED, NOT_YOUR_DECISIONThe key, or you, may not do this.
404NOT_FOUND and specific ones such as APP_NOT_FOUNDIt doesn't exist in this workspace, or you can't see it.
409DESTRUCTIVE_SCHEMA_CHANGE, DOCUMENT_VERSION_CONFLICT, WORKFLOW_CHANGED, WORKFLOW_DRAFT_CONFLICT, APP_ACCESS_WIDENING_UNCONFIRMED, AMBIGUOUS_WAPPSomething changed or needs confirming; the message says what.
413FILE_TOO_LARGE, FILES_TOO_LARGE, ARTIFACT_FILE_TOO_LARGESend less at once. A document's HTML files are 2 MB each at most; move styles, scripts or data into their own files.
422REQUEST_REJECTEDCheqq understood the request but couldn't do it; the message says why.
429RATE_LIMITEDWait the Retry-After seconds, then retry. See Rate limits.

A 401 carries a WWW-Authenticate header, and a 403 INSUFFICIENT_SCOPE names the missing scope in it as well.

Paging

Lists that can be long page in one of two ways, and say so in their answer:

  • Records and runs return nextCursor. Pass it back as cursor to get the next page; null means there are no more.
  • Documents take page (from 1) and pageSize, and return total.

Rate-limit headers

Every answer after authentication carries:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997

See Rate limits.

You

MethodPathScopeWhat it does
GET/meanyThe key's workspace, your role, its scopes and its requests per minute.

MCP: cheqq_whoami.

Wapps

MethodPathScopeWhat it does
GET/wappsstructure:readThe Wapps installed in the workspace that you can open, most recently used first, with each one's install id, slug and your role.
GET/wapps/{idOrSlug}structure:readOne Wapp, by install id, Wapp id or slug, with its collections.
POST/wappsstructure:writeCreate an empty Wapp: name, and optionally description, icon. If you can already edit a Wapp of that name, you get it back with alreadyExisted: true; send allowDuplicate: true for a second one.
DELETE/wapps/{idOrSlug}structure:writeDelete a Wapp you made, in two steps (see below).

A Wapp is named everywhere by its install id, its Wapp id or its slug (such as content-studio). If two Wapps in the workspace share a slug, use the install id.

Deleting a Wapp. The first DELETE deletes nothing: it answers needsConfirmation: true with what goes with it (its records, collections and automations, and how many other people lose it). Check with the person, then send it again with { "confirmed": true }. The Wapp moves to Trash and can be restored for 30 days with its records, automations and inbox addresses; its Apps are deleted for good. If automations of yours outside the Wapp still use it, the answer is 409 WAPP_DELETE_BLOCKED naming them in details.workflows: send trashWorkflowIds with their ids to move them to Trash with it.

MCP: cheqq_list_wapps, cheqq_get_wapp, cheqq_create_wapp, cheqq_delete_wapp.

Collections and fields

MethodPathScopeWhat it does
GET/collections?wapp=structure:readCollections with their fields, in one Wapp or in every Wapp.
POST/collectionsstructure:writeCreate a collection: wapp, name, slug, fields.
GET/collections/{collectionId}structure:readOne collection and every field in schemaDef.
PATCH/collections/{collectionId}structure:writeRename it or change its slug: name, slug.
DELETE/collections/{collectionId}structure:writeMove it and its records to the Wapp's Trash.
POST/collections/{collectionId}/fieldsstructure:writeAdd a field: name, type, and what the type needs.
PATCH/collections/{collectionId}/fields/{fieldKey}structure:writeChange a field's settings: field, confirmDestructive.
DELETE/collections/{collectionId}/fields/{fieldKey}structure:writeRemove a field and its values. Needs ?confirmDestructive=true.

Fields. A field's slug is its key: record values, filters and sorts use it, and renaming the field never changes it. Where a path takes {fieldKey}, the field's label works too if only one field has it.

Field types: text, long_text, number, duration, checkbox, date, url, email, select, multi-select, json, relation, file, user, lookup, rollup, formula. Select fields take options (labels); a relation takes targetCollectionSlug and mode (single or multiple); a lookup takes relationFieldSlug and targetFieldSlug; rollups and formulas take a formula.

Changes that lose data (retyping a field, making it required, removing it) answer 409 DESTRUCTIVE_SCHEMA_CHANGE until you send confirmDestructive: true. Every one can be undone from the Wapp's history.

curl -X POST https://cheqq.ai/api/platform/v1/collections/COLLECTION_ID/fields \
  -H "Authorization: Bearer $CHEQQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Channel", "type": "select", "options": ["Blog", "LinkedIn", "Newsletter"] }'

MCP: cheqq_list_collections, cheqq_create_collection, cheqq_get_collection, cheqq_update_collection, cheqq_delete_collection, cheqq_add_field, cheqq_update_field, cheqq_remove_field.

Views

MethodPathScopeWhat it does
GET/collections/{collectionId}/viewsstructure:readA collection's saved views, in order, with their filters, sorts, grouping and visible fields.
POST/collections/{collectionId}/viewsstructure:writeAdd a view for everyone: name, viewType (table, board, calendar, gallery, list…), config.
PATCH/collections/{collectionId}/views/{viewId}structure:writeChange a view's name, type, config, default flag or position.
DELETE/collections/{collectionId}/views/{viewId}structure:writeDelete a view. Its records are untouched; a collection keeps at least one view.

config replaces the whole config, so read the view, change what you need and send it all back. A locked view refuses changes to its name, type and config unless you're an admin of the Wapp. MCP: cheqq_list_views, cheqq_create_view, cheqq_update_view, cheqq_delete_view.

Dashboards

MethodPathScopeWhat it does
GET/dashboards?wapp=structure:readDashboards and their tiles, in one Wapp or every Wapp. Query: dashboardId for just one, detail=summary for each tile's id, kind and title only.
POST/dashboardsstructure:writeAdd a dashboard to a Wapp: wapp, name, and optionally config.tiles.
PATCH/dashboards/{dashboardId}structure:writeRename or move it, or replace its whole config.
PATCH/dashboards/{dashboardId}/configstructure:writeChange some tiles and keep the rest: upsertTiles, removeTileIds.
DELETE/dashboards/{dashboardId}structure:writeDelete a dashboard. Its records are untouched.

Each tile has a uuid id, a grid layout (x, y, w, h) and content of kind chart, table or timeline. A tile's source is all of a collection's records ({ "mode": "all" }), a saved view's ({ "mode": "view", "viewId" }) or a filter's ({ "mode": "filter", "filter" }, in the filter language records use). Tiles are checked before they're saved: a tile over a collection of another Wapp, or a filter on a field the collection doesn't have, answers 422 naming the tile and the field keys there are. Read an existing dashboard to copy the shape. MCP: cheqq_list_dashboards, cheqq_create_dashboard, cheqq_update_dashboard, cheqq_update_dashboard_tiles, cheqq_delete_dashboard.

Records

MethodPathScopeWhat it does
GET/collections/{collectionId}/recordsrecords:readA page of records, newest first. Query: filter (a filter group as JSON, see below), sortBy (createdAt or data.<field key>), sortOrder (asc, desc), cursor, limit (up to 200, default 50).
GET/collections/{collectionId}/records/{recordId}records:readOne record.
POST/collections/{collectionId}/records/countrecords:readHow many records the collection has, or with filter how many match.
POST/collections/{collectionId}/records/aggregaterecords:readSums, averages, counts per option and more, without reading every record: widgets.
POST/collections/{collectionId}/recordsrecords:writeAdd a record: data.
PATCH/collections/{collectionId}/records/{recordId}records:writeChange some fields of a record: data. Fields you leave out keep their values.
POST/collections/{collectionId}/records/batch-createrecords:writeAdd up to 200 records: records (each its field values). All are created or none is.
POST/collections/{collectionId}/records/batch-updaterecords:writeSet the same values on up to 200 records: recordIds, data.
DELETE/collections/{collectionId}/records/{recordId}records:deleteMove a record to the Wapp's Trash.
POST/collections/{collectionId}/records/batch-deleterecords:deleteMove up to 200 records to the Trash: recordIds.

Writing values. data names fields by key or by label. A select takes the option's label. A name no field answers to is refused with 400 INVALID_FIELD, listing the fields you could have meant. Automations that run on new or changed records run for API writes as they do for yours.

Reading values. data is keyed by field key. A select value reads as { "id": "…", "label": "Live" } and a multi-select as a list of them, so you can show the label and send either one back.

Filtering records

Listing, counting and dashboard tiles take one filter language, the one saved views use: a group of rules joined by and (the default) or or, where a rule can itself be a group.

{
  "glue": "and",
  "rules": [
    { "field": "status", "filter": "contains", "includes": ["Live"] },
    { "field": "amount", "filter": "greater", "value": 100 },
    { "glue": "or", "rules": [
      { "field": "title", "filter": "contains", "value": "launch" },
      { "field": "owner", "filter": "isEmpty" }
    ] }
  ]
}

Rules name fields by key. Text takes contains, notContains, equal, notEqual, beginsWith and endsWith; numbers and dates equal, notEqual, greater, greaterOrEqual, less and lessOrEqual; any field isEmpty and isNotEmpty. Select, relation and person fields use contains or notContains with includes: option labels or ids, record ids or member ids. A rule on a field the collection doesn't have answers 422 with the field keys it does have. On a GET, send the group as JSON in ?filter=. The older equality shorthand filter.<field key>=value still works, but not together with filter.

Summaries. Each widget is { "id": "<a uuid you choose>", "aggregation": { "type": "sum", "sourceFieldId": "amount" } }. Types: count, sum, avg, min, max, median, empty, unique, earliest, latest, option-counts, group-by. Results come back keyed by your widget id.

MCP: cheqq_list_records, cheqq_get_record, cheqq_count_records, cheqq_aggregate_records, cheqq_create_record, cheqq_update_record, cheqq_create_records, cheqq_update_records, cheqq_delete_record, cheqq_delete_records.

Automations

MethodPathScopeWhat it does
GET/workflows?wapp=workflows:readYour automations, most recently changed first: whether each is on, and how many steps and triggers it has.
GET/workflows/{workflowId}workflows:readOne automation: every step and trigger, and the values each trigger passes on.
PUT/workflows/{workflowId}/definitionworkflows:writeReplace its whole definition: definition, and optionally triggers, name, description, expectedUpdatedAt.
DELETE/workflows/{workflowId}workflows:writeMove it to the Trash. Its triggers stop.
GET/workflow-actionsworkflows:readThe actions a step can be built from: connected apps, saved API endpoints, and your Wapps' record actions. Query: q, wapp, source, limit.

There's no endpoint to create an automation from scratch yet: build new ones over MCP, or in Cheqq. See Automations. MCP: cheqq_list_workflows, cheqq_get_workflow, cheqq_replace_workflow, cheqq_delete_workflow, cheqq_list_workflow_actions.

Runs

MethodPathScopeWhat it does
POST/workflows/{workflowId}/runsworkflows:runStart a run: mode (execute, test, dry-run), input, wait, timeoutMs (up to 60000).
GET/workflows/{workflowId}/runsworkflows:readAn automation's runs, newest first. Query: status (active or failed), cursor, limit (up to 100, default 20).
GET/runs/{runId}workflows:readOne run: every step's output and errors, and, if it waits at an approval, who it asks and whether you may answer. Query: detail=summary leaves step outputs out and gives each one's size; step=<nodeId> returns one step.
POST/runs/{runId}/cancelworkflows:runStop a run. What it already did stays done.
POST/runs/{runId}/respondworkflows:approveAnswer a waiting run: approved, and optionally stepId, resumeData, stopAsking.

MCP: cheqq_run_workflow, cheqq_list_runs, cheqq_get_run, cheqq_stop_run, cheqq_respond_to_run.

Documents

MethodPathScopeWhat it does
GET/documentsdocuments:readDocuments in the workspace, most recently opened first. Query: q, kind, scope (mine, shared, all), sort (viewed, updated, title), page, pageSize (up to 100, default 30).
GET/documents/{documentId}documents:readA document, its currentVersionId and, for pages, documents, decks and designs, the text of its files. editor: "blocks" means it's written in blocks: use the block endpoints below. Query: versionId, content=false.
GET/documents/{documentId}/files?path=documents:readOne file whole, for a file that didn't come with the document (too large, or an image or font, which reads as base64). Query: path, versionId.
POST/documentsdocuments:writeCreate a document: format (page, document, deck, design), title, files (path, content, and encoding: "base64" for an image or font), summary. For a document written in blocks, send markdown and/or outline instead of files, and a page (font, width, pageSize, orientation, margins, pageNumbers) to start it with page settings of its own.
PATCH/documents/{documentId}documents:writeSave an edit as a new version: baseVersionId, summary, and files, deletePaths or title.
POST/documents/{documentId}/patchdocuments:writeSmall edits without resending files: baseVersionId, summary, operations, path.
GET/documents/{documentId}/blocksdocuments:readRead a document written in blocks; the outline includes its page settings. Query: mode (outline, blocks, section, tab, search, changes), blockIds (comma-separated), headingId, tabId, query, sinceRevision, cursor.
POST/documents/{documentId}/blocksdocuments:writeChange a document written in blocks: operations, summary, and baseRevision or a hash on each operation. The operations also add, rename, move, copy and delete its tabs, setPage changes its look, width, paper, orientation, margins and page numbers, colorWords colours words, and the table operations change a table's rows, columns, cell colours and widths in place. A stale one is 409 DOCUMENT_BLOCK_STALE.
POST/documents/{documentId}/exportsdocuments:readMake a file from a document: format (pdf or docx for a document, pptx or pptx-editable for a deck, png or jpg for a design), and tabId for one tab of a document written in blocks. A pending answer is asked again with its exportId. With documents:write, a document people are typing in is saved as a version first; with only documents:read, the file is of the last saved version.
GET/documents/{documentId}/exports/{exportId}documents:readThe finished file, from the downloadUrl a ready export gave you.
GET/documents/{documentId}/versionsdocuments:readIts version history, newest first.
POST/documents/{documentId}/restoredocuments:writeBring back an earlier version as a new one: baseVersionId, restoreVersionId.
DELETE/documents/{documentId}documents:deleteMove a document you own to Trash, where you can restore it in Cheqq for 30 days. A published one's link stops working.

See Documents and Apps for how versions keep two people's edits apart. MCP: cheqq_list_documents, cheqq_get_document, cheqq_read_document_file, cheqq_create_document, cheqq_update_document, cheqq_patch_document, cheqq_read_document_blocks, cheqq_edit_document_blocks, cheqq_export_document, cheqq_list_document_versions, cheqq_restore_document_version, cheqq_delete_document.

Apps

MethodPathScopeWhat it does
GET/appsapps:readThe Apps you made in the workspace: audience, whether they're live, their addresses and their App Builder link.
GET/apps/{appId}apps:readOne App: its versions, its newest deploy, and the data it may use.
GET/apps/{appId}/deployments/{deploymentId}apps:readHow a publish is going: building, deployed or failed.
GET/apps/{appId}/filesapps:readEvery file in the App's source.
GET/apps/{appId}/files/content?path=apps:readOne file, or with offset and length a part of it, up to 1 MB at a time: content, its encoding (utf8 for text, base64 for an image or other binary file), the file's size in bytes, and nextOffset to read on from (null at the end).
PUT/apps/{appId}/filesapps:writeCreate or replace up to 50 files (2 MB in all): files (path, content, and encoding: "base64" for a binary file), label. Returns the checkpoint taken first.
DELETE/apps/{appId}/files/content?path=apps:writeDelete one file. Query: path, label.
POST/apps/{appId}/dependenciesapps:writeAdd or remove npm packages: add (name to version), remove.
GET/apps/{appId}/checkpointsapps:readSnapshots of the source, newest first.
POST/apps/{appId}/checkpoints/restoreapps:writePut the source back as it was at a checkpoint: hash.
POST/apps/{appId}/checkapps:writeTypecheck and build the draft without publishing.
POST/apps/{appId}/verifyapps:writeOpen the draft in a browser and report errors, what rendered and screenshots: path, viewport, steps.
POST/apps/{appId}/publishapps:publishStart publishing the draft. Answers 202 with a deploymentId.
POST/apps/{appId}/unpublishapps:publishTake the App offline. Everything is kept.
POST/apps/{appId}/rollbackapps:publishPut an earlier deployed version back live: versionNumber.

Source files you can write are under src/, public/ and index.html; the rest of the project is managed by Cheqq. See Documents and Apps. MCP: cheqq_list_apps, cheqq_get_app, cheqq_get_app_deployment, cheqq_list_app_files, cheqq_read_app_file, cheqq_write_app_files, cheqq_delete_app_file, cheqq_update_app_dependencies, cheqq_list_app_checkpoints, cheqq_restore_app_checkpoint, cheqq_check_app, cheqq_verify_app, cheqq_publish_app, cheqq_unpublish_app, cheqq_rollback_app.

Was this page helpful?