Skip to content
Talk to our solutions team

Sweeps

A sweep runs a static scope over every record its over matches, a page at a time, in the background. It is evaluate for a whole entity: dry by default, so you can see what the rules would do to every record before you let them, and applied when you ask.

Sweeps are off until an operator turns them on (rules.sweeps.enabled), and need a system user (Configuration).

RouteDoesNeeds
POST /rules/sweepsstarts a sweep: 202 with the sweepevaluate; apply too with apply: true
GET /rules/sweepsthe sweeps you started; every sweep of the tenant with manageread
GET /rules/sweeps/{id}one sweep: its state and countsread for your own, manage for anyone’s
GET /rules/sweeps/{id}/items?kind=…&limit=…what it recordedas above
POST /rules/sweeps/{id}/cancelstops it between pagesas above
{ "scope": "invoice-routing", "where": "total > 100", "apply": false }
KeyMeaning
scopea static scope with data.over
wherenarrows the scope’s own over.where (the two are combined with and), in the where language
applywrite what the rules propose
{
"id": "swp_e6220dcab707874e61914c1e",
"scope": "invoice-routing",
"stamp": "sha256:84853064e9fe118a5c7c4882e0dc5fa321305e2b941d6304cbf1642b3db8efb8",
"apply": false,
"where": "total > 100",
"status": "queued",
"startedby": "u-shop-owner",
"startedbytype": "user",
"counts": { "seen": 0, "matched": 0, "changed": 0, "partial": 0, "conflicts": 0, "errors": 0 },
"createdon": "2026-10-17T11:15:26.269112Z"
}
{
"id": "swp_e6220dcab707874e61914c1e",
"scope": "invoice-routing",
"stamp": "sha256:84853064e9fe118a5c7c4882e0dc5fa321305e2b941d6304cbf1642b3db8efb8",
"apply": false,
"where": "total > 100",
"status": "done",
"startedby": "u-shop-owner",
"startedbytype": "user",
"principal": "u-rules-system",
"counts": { "seen": 2, "matched": 2, "changed": 0, "partial": 0, "conflicts": 0, "errors": 0 },
"createdon": "2026-10-17T11:15:26.269112Z",
"finishedon": "2026-10-17T11:15:26.285998Z"
}
FieldWhat it is
statusqueued, running, done, failed or cancelled
stampthe rule text the sweep runs, fixed when it started
startedby, startedbytypewho started it
principalwho it runs as: the system user
counts.seenrecords read
counts.matchedrecords whose rules proposed something
counts.changedrecords written (with apply)
counts.partial, counts.conflicts, counts.errorsrecords that ended so, as in evaluate
errorwhy a failed sweep stopped
finishedonwhen it ended

GET /rules/sweeps/{id}/items lists what the sweep kept about each record, filtered by kind:

kindKept for
actionevery proposal in a dry sweep (the preview), every applied write in an applied one
findingevery finding a rule raised
errorevery record that failed
conflictevery record that conflicted twice
partialevery record applied in part
{
"items": [
{
"id": "swi_1bd44e848c0884544ce5dab3",
"kind": "action",
"record": "inv-big",
"rule": "Senior",
"op": "set",
"detail": { "applied": false, "field": "review_tier", "value": "senior" },
"createdon": "2026-10-17T11:15:26.278888Z"
},
{
"id": "swi_7b51332f35ecaca7c3ec8c71",
"kind": "action",
"record": "inv-big",
"rule": "Senior",
"source": "shop",
"entity": "task",
"op": "create",
"detail": { "applied": false, "fields": { "id": "t-inv-big", "subject": "review inv-big for gold" } },
"createdon": "2026-10-17T11:15:26.278888Z"
}
]
}

Finished sweeps and their items are kept for rules.sweeps.retention (30 days by default).

Starting a sweep is the caller’s: it needs evaluate, and apply for an applied one. Once it starts, the sweep acts as the system user, whose rights are checked again before every page: revoke them and the sweep stops, failed, saying why. Every record it reads and every write it makes goes through the source as the system user, so data.svc records the system user as the writer.

  • The rule text is pinned. A sweep runs the rule text it started with (its stamp). An edit to the scope mid-sweep does not change it. A replica that takes over a sweep and holds other rule text for the scope fails it, asking you to start it again.
  • Each page is kept whole. After every page, the sweep keeps that page’s items, its cursor and its counts together. A replica that dies mid-page leaves the sweep at the last page it kept; when its lease lapses, a worker on any replica resumes it after that page.
  • Re-applying is safe. A page applied twice writes nothing the second time: a field set to what it holds is dropped, and each update carries its version.
  • A source that cannot be reached pauses it. The sweep waits and resumes from its last page; it does not fail.
SettingDefaultEffect
rules.sweeps.pertenant1sweeps a tenant may have queued or running; one more answers 429 rules-sweep-limit
rules.sweeps.pagesize500records per page
rules.sweeps.maxerrors100a sweep stops, failed, after this many records failed
rules.sweeps.retention720hhow long finished sweeps are kept

A cancel is honoured between pages: the page in hand finishes and is kept.

StatusCodeWhy
409rules-sweeps-disabledsweeps are off, or no system user is configured
429rules-sweep-limitthe tenant already has pertenant sweeps queued or running
400rules-facts-invalida scope without data.over, or a where that does not parse
403rules-access-deniedthe caller may not evaluate, or not apply
404rules-scope-not-foundno static scope of that name

An unknown sweep, or one you may not see, answers 404 rules-sweep-not-found.