Skip to main content
The checks engine groups the comments your agents write into checks. Use the Checks APIs to read a project’s checks, its comments and its score, and to resolve, reject, reopen, exclude, assign and attach.

Concepts

Project

A project is one Velt document of one organization: the pair organizationId and documentId. Every Checks endpoint takes both, and both are required. The same documentId in two organizations is two projects.

Check

A check is one kind of problem, named by its ruleKey, for example link-broken (a link that returns an error) or image-broken. Rule keys are lowercase letters, digits, : and -, at most 120 characters. A custom agent’s checks have keys like custom:{agentId}:{type}. Each check has a title, a description, a severity, a responsibility (content, design or dev), an owner, the number of open comments and pages, its impact on the score, and a state:

Comment

A comment is one problem at one spot. It is a regular Velt comment annotation, written by the agent, with the engine’s metadata in checkInfo. A finding seen again on a later run updates the same comment: it never writes a duplicate.
  • Page comment: a problem on one page (scope: "page").
  • Site comment: the same problem at the same spot on two or more pages, for example a broken link in a shared footer. It is one comment for every page (scope: "site"), with its pages and its true pageCount. A site comment is stored on the velt-site location with isDocumentLevel: true, so Velt lists it on every page of the document and draws its pin only where its target is found.
  • Attached comment: a comment a person wrote and then attached to a check. It counts in the check’s open count and pages, lists under the check, and resolves and verifies with it.
Each comment has a location type: element (pinned to an element on the page), source (a line in the page’s stored HTML) or none (the evidence only).

Statuses

Verified means Resolved by a scan. Only a scan verifies: verifiedAt is set by a scan and nothing else. Rejected is not a status. A person who disagrees with the agent rejects its comment, in the same shape as the Velt SDK’s rejectSuggestion(): suggestion.status becomes rejected, suggestion.rejectReason holds one of the four reasons below, the person’s own words, excluded (an excluded check) or nothing, and the comment keeps its status (an Open comment stays Open with its pin). A rejected comment counts nowhere (open counts, failing checks, the score), and found again it stays rejected as it is. When a person or an MCP client resolves a comment, it is Resolved and waits for the scan that verifies it (pendingVerify: true). The resolve queues a verify rescan:
  • The problem is gone: the comment is verified, with one reply such as “Verified. Fixed on all 12 pages. Time to resolve: 2 hours 14 minutes.”
  • The problem is still there: the comment is reopened, with one reply such as “Still on 3 pages”.
  • The rescan could not check its pages: it is tried again, up to 3 times, at no extra charge. After that the comment gets the reply “Could not verify yet.” and stays Resolved and unverified until the next scan of its pages.
A scan also resolves on its own: a comment its agent no longer finds on a page it checked is Resolved and verified by that scan.

Severities

The severity is also stored as the comment’s priority. Checks are listed most severe first.

Score

Each page has a score from 0 to 100: the weighted share of the checks that apply to the page and pass on it. A check applies to a page when one of its agents checked the page in the last 30 days. The site score is the mean over the pages checked in the last 30 days. An excluded check is left out of the score.

Reject reasons

Rejecting a comment and excluding a check take one of the same four reasons. excluded is the engine’s own reason for an excluded check’s comments and is refused as input:

Resolves and credits

A resolve through Resolve Comment or Resolve Check starts a verify rescan, which uses AI credits at the flat rescan rate. Resolves on one project within 15 seconds share one rescan per agent. Resolve is idempotent: resolving a comment that already waits for its rescan starts nothing and charges nothing. When the agent is busy or the workspace has no credits, no rescan runs: the comment stays Resolved and the next scan of its pages verifies or reopens it. Resolve takes by: "user" for a person, "mcp" for an MCP client. Nothing else is accepted.

Status changes made in a Velt client

A status a person changes in any Velt client (the Velt comment UI, or your own UI calling the SDK) reaches the checks engine on its own, about 10 seconds later:
  • Resolved: resolved by that user, then verified by a scan. A rejected comment takes the status only, with no verify.
  • Back to Open from Resolved: reopened. A rejected comment stays rejected.
  • A suggestion accepted in a Velt UI: resolved by suggestion, then verified by a scan.
  • A suggestion rejected in a Velt UI: the SDK’s own reject. The engine records it (the agent’s feedback, the counts) without changing the reject.
A status changed with the Update Comment Annotations API does not reach the engine. Use Resolve Comment, Reject Comment and Reopen Comment instead.

Requests

Every Checks endpoint is a POST with the x-velt-api-key and x-velt-auth-token headers and a { "data": { ... } } body. Every body takes organizationId and documentId (both required, 1 to 512 characters each). Every body uses .strict(): a field that is not on the endpoint’s page is rejected with INVALID_ARGUMENT, never ignored. The decisions record the userId you send as the person who made them. The auth token proves the caller is the workspace, so send the id of the person acting. The same endpoints are also served under /v1/agents/checks/... with the same body and the same response.

Workspaces without a store database

The checks engine needs the workspace’s Firestore store database. Without one, the reads and the check-level decisions return NOT_FOUND with the reason CHECKS_WORKSPACE_NOT_FOUND. Resolve, reject and reopen still change the comment, with no verify and no scan.

Errors

A body outside the schema returns INVALID_ARGUMENT, with every failing field in details.issues. Every other refusal carries its reason in details.reason. Branch on the reason, never on the message.
The engine also checks rule keys, cursors, filters, reasons and settings itself (CHECKS_INVALID_RULE_KEY, CHECKS_INVALID_CURSOR, CHECKS_INVALID_VERSION_FILTER, CHECKS_INVALID_REJECT_REASON, CHECKS_INVALID_RESOLVER, CHECKS_INVALID_SETTINGS, CHECKS_INVALID_PAGE_SIZE), but over REST the schema refuses these values first, with INVALID_ARGUMENT and details.issues.

Endpoints