The review API
What a connection carries, what comes back, what a key can and cannot reach, and the limits. Written to be pasted into an integration ticket or a supplier assessment.
What you send, and what comes back
Blocks of text, each with a role and your own address for it — and findings back carrying that address unchanged, so a result lands on the exact node the sentence came from.
{
"workspaceId": "…",
"blocks": [{
"role": "artwork",
"location": "Frame 3 · title",
"text": "A cure for chronic fatigue.",
"ref": { "node": "12:445" }
}]
}{
"findings": [{
"statement": "Never claim a cure.",
"severity": "block",
"excerpt": "cure",
"replacement": "helps manage",
"span": { "start": 2, "end": 6 },
"ref": { "node": "12:445" },
"tier": "verified"
}],
"steeredRuleIds": ["substantiation"],
"review": { "requested": false, "ran": false }
}The six roles
Every block declares one of six roles, and the list is closed: artwork, body, caption, metadata, alt_text, notes.
Not a slide, a paragraph or a cell among them — so one rule reaches a slide headline, a document heading and a frame title alike, and the engine never learns what any of those is.
One engine, not one per surface
The endpoint imports the checker the editor runs. The same function, not a port of it and not a reimplementation kept in step by discipline.
Two implementations of “is this allowed” eventually disagree, and the day they do you have content one surface cleared and another flagged. Nothing recovers a compliance tool’s credibility after that conversation.
So the API cannot be more permissive than the studio. Rules set on the account hold over it, house rules included, and there is no mode that checks content against rules supplied in the request — a verdict is only meaningful against a real rule set a real account owns.
Three things get called “accepting a finding”
They mean opposite things about the rule, and every other tool in this category files them as one. Keeping them apart is what turns a pile of dismissals into something a compliance lead can act on.
false_positiveThe rule is wrong here- It matched something it should never match. Evidence about the rule.
out_of_scopeThe rule doesn’t apply here- Correct in general, not applicable to this content. Evidence about its scope.
accepted_riskShipping anyway- The rule is right, it applies, and someone decided to publish. A record of a risk — never evidence against the rule.
Limits
- 500
- blocks in one request
- 20,000
- characters in any one block
- 200,000
- characters across the request
- 120
- calls a minute per key
Over any of them the request is refused rather than truncated. A checker that quietly skipped the last of your copy and reported no findings would be worse than one that said no.
Keys, and what they cannot do
A key is a standing grant of access to your rules, so everything about it is narrower than you would expect.
Who can issue one. An account owner, never an editor. Issuing a key is a governance decision rather than an editing one.
What it can reach. Checking content, and recording an override. It cannot read your work, change a rule, or export — the widest key is narrower than any human role.
Where the secret lives. Nowhere. Only a SHA-256 is stored; the key is shown once and nobody, including us, can read it back. Revocation is immediate and permanent.
How tenancy is enforced. In the database, not in the application. A key resolving to one account cannot reach another’s workspace, and the refusal is identical whether that workspace belongs to someone else or does not exist.
What the rate limit is for. A ceiling on damage rather than a commercial one: a leaked key should cost an attacker time, and a plugin looping on an error should not be able to empty an account’s allowance before anyone notices.