Test a list's write rules

Check what a list's write rules would do with a write, without making it. The rules run exactly as they would on a real request, including variable substitution and the list's JSON schema, and the response shows every part of every rule and what it evaluated to.

Send rules to try a rule set that hasn't been saved yet. Leave it out to test the list's own rules.

POST https://api.jsonpad.io/lists/{listId}/rules/test

This needs user auth mode, or a token with update permission on the list: a dry run with rule text of your own could read any field, including ones a guard index hides. It's metered like any other request — the command line tool can run the same checks offline for nothing.

Parameters

  • required
    listIdstringA list id or path name.

Request body

{
action: "update"
itemId: "current-game"
new: {
status: "started"
currentPlayerId: "4d1c…"
moves: [
0: {
playerId: "8f2a…"
column: 3
}
]
}
identityId: "8f2a…"
}
  • required
    actionstringcreate, update, delete or restore. A restore is checked against the update rules, as it is on a real write.
  • optional
    rulesnullable stringThe rules to test. Defaults to the list's saved rules, and testing a list with no rules is an error.
  • optional
    itemIdstringAn item id or alias to take the old data and metadata from. Its data is read in full, without guard indexes hiding anything, which is why this endpoint needs permission to edit the list.
  • optional
    oldanyThe item's data before the write, which overrides the item's own. Ignored for create.
  • optional
    newanyThe data after the write. Ignored for delete.
  • optional
    patcharrayA JSON Patch applied to the old data to make the new data, instead of sending new.
  • optional
    mergeanyA JSON merge patch applied to the old data, instead of sending new.
  • optional
    identityIdstringAn identity to make the write as, loaded from your account.
  • optional
    identitynullable objectAn identity object to make the write as, which doesn't have to exist. null means no identity.
  • optional
    tokenobjectThe token to make the write as, as { id, tags }. Useful for checking a rule that reads token.tags. Defaults to the calling token.
  • optional
    nowstringAn ISO-8601 date the rules see as now, for testing deadlines. Defaults to the current time.
  • optional
    pointernullable stringThe JSON pointer a partial-data write targets, which rules read as request.pointer.
  • optional
    substitutebooleanWhether to run $jsonpad-var substitution on the new data first, as a real write does. Defaults to true.
  • optional
    schemabooleanWhether to check the list's JSON schema between the two stages, as a real write does. Defaults to true.

Response body

200 OK

The response is 200 whatever the rules decided: allowed and status say what a real write would have got.

{
allowed: false
stage: "allow"
status: 403
code: "WRITE_RULE_DENIED"
reason: "no-allow-passed"
message: "Write denied by list rules"
statement: null
operation: "update"
action: "update"
engineVersion: "1.0.0"
languageVersion: 1
diagnostics: [
]
budget: {
used: 33
limit: 100000
}
statements: [
0: {
index: 0
kind: "allow"
label: "your turn"
line: 5
span: {
start: 87
end: 191
line: 5
column: 1
endLine: 7
endColumn: 41
}
operations: [
0: "update"
]
result: false
error: null
errorLine: null
trace: [
0: {
id: 20
span: {
start: 156
end: 190
line: 7
column: 6
endLine: 7
endColumn: 40
}
value: false
}
]
}
]
}
  • allowedbooleanWhether the write would go through.
  • stagestringWhich step decided: allow (nothing authorised it), schema (the list's JSON schema refused it), require (a check failed), or ok.
  • statusnumberThe status a real write would have got: 200, 400 or 403.
  • codenullable stringWRITE_RULE_DENIED, WRITE_RULE_FAILED or VALIDATION_ERROR, or null when the write is allowed.
  • messagenullable stringWhat a real write would have been told. For a failed require that's your else message.
  • statementnullable objectThe statement that decided, as { index, kind, label, line }. It's null when no allow passed, because no single statement is to blame.
  • diagnosticsarrayThe checker's warnings about the rules that were tested, each with a severity, a code, a message and a span.
  • budgetobjectHow much of the per-write evaluation budget these rules used, and the limit.
  • statementsarray

    Every statement that ran, in order, with its result and any error. Statements that don't cover the operation aren't included, and nothing after the decision runs.

    trace holds one entry per expression that was evaluated, with its span in the rule text and its value. Short-circuited expressions have no entry at all, so the trace is exactly what the engine looked at.

  • newanyThe data the rules saw, after variable substitution. Only included in user auth mode.

In token auth mode the trace keeps its shape and its boolean results, but every other value is left out and the entry is marked "hidden": true instead, so a token that can edit a list still can't read values out of it this way. The new field is left out for the same reason.

2026-09-23