Custom verbs
Writ ships with a fixed set of built-in verbs (refund, admit,
prescribe, provision, verify, verify_human, …). They cover common
ground, but they are not your domain language. Custom verbs let you name
the consequential actions your agents actually take — phi.export,
trade.settle, patient.discharge — and put Writ policy on them, with the
same checks, grants, receipts, and audit chain as everything else.
Verbs attach to principals: a check always runs as a principal’s key, and every receipt snapshots the principal. See Principals.
Defining a verb
POST /v1/verbs with a name and a description:
Names are lowercase domain.action style: 1–64 characters of letters,
digits, dots, and underscores, with at least one dot — bare words are
reserved for built-in verbs. A name may not collide with a built-in verb
(case-insensitive) or an existing custom verb. A verb is usable in policies
the moment it is created. The response is the verb record:
Tenants are capped at 200 verbs (including deprecated ones).
Setting policy on custom verbs
Custom verbs are ordinary verbs everywhere else: PUT /v1/policy (and the
per-environment overrides) accepts them the moment they exist, and
GET /v1/policy lists the live ones in its customVerbs field. Unknown
verbs still fail closed to require_grant — defining the verb is what
promotes it from “deny by default” to a first-class policy target.
Rename is safe
Language changes. PATCH /v1/verbs/{name} renames a live verb (and/or
updates its description), and the rename cannot silently drop protection:
- The old name becomes a deprecated alias — checks that name it still resolve, and evaluate against the current verb’s policy. Old code and old receipts keep their meaning.
- Policies that used the old name are migrated to the new name — in the
tenant default and in every environment override — so a rename can never
silently drop a
deny. Each touched policy’s version bumps by one. - The old name is rejected in new policies, with the offending verbs listed in the 400 response.
The receipt for a check that resolved an alias carries resolvedVerb: the
verb whose policy actually decided. verb always echoes exactly what the
caller sent. See the data dictionary.
Retire, don’t delete
DELETE /v1/verbs/{name} retires a verb: it becomes deprecated, checks
that name it still resolve (its own policy still applies), and the name can
never be reused or re-created — so old receipts stay interpretable. Retired
verbs are rejected in new policies. Only live verbs can be retired.
Fetch the full vocabulary — built-ins plus custom, each with its status and
rename history — with GET /v1/verbs. The scanner reads this manifest to
map its findings onto your vocabulary; see
Scanner.