/api/wiki
Read and edit the open SynC Standards wiki. Reads are public — no API key needed. Writes require the relevant wiki permission.
GET /api/wiki/librariesList wiki standard libraries with standard count.
GET /api/wiki/libraries/:slugGet a wiki library by slug with its full category tree and standards list. Supports search.
| Name | In | Type | Description |
|---|---|---|---|
| slug | path | string | Library URL slug |
| search | query | string | Filter standards by title or description (ILIKE match) |
GET /api/wiki/standards/:librarySlug/:standardSlugGet a wiki standard by library and standard slug, including current revision content (markdown).
| Name | In | Type | Description |
|---|---|---|---|
| librarySlug | path | string | Library URL slug |
| standardSlug | path | string | Standard URL slug |
GET /api/wiki/standards/:id/revisionsList revision history for a wiki standard. Paginated.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| limit | query | integer | Results per page (default 50) |
| offset | query | integer | Offset for pagination (default 0) |
GET /api/wiki/standards/:id/revisions/:revIdGet a specific wiki standard revision with full markdown content.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| revId | path | string | Revision UUID |
GET /api/wiki/standards/:id/diff/:fromRev/:toRevCompare two revisions with computed diff. Returns both full documents, a structured hunks array (added/removed/unchanged lines), stats (added/removed counts), and a unified diff patch string.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| fromRev | path | string | Source revision UUID |
| toRev | path | string | Target revision UUID |
GET /api/wiki/standards/:id/citationsList the documents that cite this standard ("what cites this?"), from the compiled citation index. Rows carry citingEntityType: "wiki_standard" (a wiki document, addressed by librarySlug/slug) or "standard" (a main-site standard, whose citingEntityId is an INTEGER for /standard/:id). Paginated over DOCUMENTS. Returns two distinct counts: citingDocumentCount (distinct citing documents) and citationOccurrenceCount (individual references — one document may cite the standard many times). Both counts and the rows are viewer-scoped, so a caller who cannot read a citing document neither sees it nor sees it counted; the counts may therefore be lower than the unfiltered totals. A third count, privateCitationOccurrenceCount, aggregates the references made by main-site standards the caller may not read: it says such citers exist and never identifies them. A standard the caller cannot read returns 404, identically to one that does not exist.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID (the CITED standard) |
| limit | query | integer | Citing documents per page, 1-100 (default 25) |
| offset | query | integer | Offset for pagination (default 0) |
GET /api/wiki/standards/:id/adoptedList adopted (stable) revisions for a wiki standard. Revoked editions are omitted for everyone but a wiki moderator, who also sees revoked_at and revoked_reason_code on the row, plus an `effect` object on any row under an open revocation window (level, reason_code, revoked_at, replacement_available_at, replacement_adopted_revision_id, level2_at and the derived deadlines). `effect` is absent for every non-moderator caller.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
GET /api/wiki/standards/:id/adopted/:revNumGet a specific adopted revision by revision number. A revoked edition 404s for everyone but a wiki moderator.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| revNum | path | integer | Adopted revision number |
POST /api/wiki/standards/validateValidate in-flight standard markdown without saving. Returns blocking and advisory findings (each with a rule, severity, line, field, and message). Content that fails validation still returns 200 with hasBlocking:true — reporting failures is the job of this endpoint, not a 4xx. Cross-reference findings are advisory and appear only when categoryId or standardId is supplied; they never block. When standardId is supplied it also returns impact: the citation consequences of this edit, viewer-scoped and never blocking. impact.removedClauses lists each cited clause the edit disturbs with a disposition of removed (no successor could be traced — NOT a proven deletion), relocated (the requirement is now at impact.removedClauses[].movedTo), or reoccupied (the address survives but numbers a different requirement now). CONTRIBUTOR-GATED: wiki:standard:* permissions come from the SiteContributor role, which you are granted by accepting the Contributor License Agreement (CLA). The account owner accepts it signed in, from their profile or the wiki; an API key cannot accept it. Until the account (for an API key, its OWNER) has accepted, this endpoint returns 403 no matter what the key was granted, and /api/whoami lists the permission under `dormant` with that step as its `remedy`.
Required permission: wiki:standard:create
content — stringcategoryId — stringstandardId — stringcategoryId/standardId are used only to resolve the category path and run the DB-aware cross-reference advisory.
POST /api/wiki/standardsCreate a new wiki standard. Content is markdown, optionally with YAML frontmatter. If frontmatter is missing, it is auto-generated from the body params (title, description, category). Frontmatter is the source of truth for metadata — if present, it overrides body params. CONTRIBUTOR-GATED: wiki:standard:* permissions come from the SiteContributor role, which you are granted by accepting the Contributor License Agreement (CLA). The account owner accepts it signed in, from their profile or the wiki; an API key cannot accept it. Until the account (for an API key, its OWNER) has accepted, this endpoint returns 403 no matter what the key was granted, and /api/whoami lists the permission under `dormant` with that step as its `remedy`.
Required permission: wiki:standard:create
librarySlug — stringcategoryId — stringtitle — stringcontent — stringslug — stringdescription — stringeditSummary — stringtag_ids — array of tag UUIDs to attach to the new standard (must be an array; anything else is a 400)If content includes YAML frontmatter (---\ntitle: ...\n---), those values take precedence over body params for title, description, and category. If content has no frontmatter, one is prepended automatically from the body params. Every stored revision is guaranteed to have frontmatter.
PUT /api/wiki/standards/:idEdit a wiki standard. Uses baseRevisionId for conflict detection (returns 409 if outdated). CONTRIBUTOR-GATED: wiki:standard:* permissions come from the SiteContributor role, which you are granted by accepting the Contributor License Agreement (CLA). The account owner accepts it signed in, from their profile or the wiki; an API key cannot accept it. Until the account (for an API key, its OWNER) has accepted, this endpoint returns 403 no matter what the key was granted, and /api/whoami lists the permission under `dormant` with that step as its `remedy`.
Required permission: wiki:standard:edit
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
content — stringbaseRevisionId — stringeditSummary — stringtag_ids — array of tag UUIDs for the revision this edit creates (must be an array; anything else is a 400)baseRevisionId is required for conflict detection. If another user edited since your base, returns 409. Unknown fields are rejected — metadata changes go in the YAML frontmatter (title, description, category). tag_ids attaches tags to the NEW revision this edit creates, and it is the complete set for that revision — send the full list, not just the tags you are adding. Omitting tag_ids carries the base revision's tags forward unchanged; sending an empty array clears them. An edit that says nothing about tags therefore keeps them.
POST /api/wiki/standards/:id/revert/:revIdRevert a wiki standard to a previous revision. Creates a new revision carrying the old content AND the old tag set. CONTRIBUTOR-GATED: wiki:standard:* permissions come from the SiteContributor role, which you are granted by accepting the Contributor License Agreement (CLA). The account owner accepts it signed in, from their profile or the wiki; an API key cannot accept it. Until the account (for an API key, its OWNER) has accepted, this endpoint returns 403 no matter what the key was granted, and /api/whoami lists the permission under `dormant` with that step as its `remedy`.
Required permission: wiki:standard:edit
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| revId | path | string | Revision UUID to revert to |
GET /api/wiki/standards/search-usersSearch users for wiki role assignment. Returns masked emails for privacy. Moderators only.
Required permission: wiki:standard:moderate
| Name | In | Type | Description |
|---|---|---|---|
| query | query | string | Search by name or email (min 2 chars). Accepts q as well. Under 2 chars returns an empty list, not an error. |
GET /api/wiki/standards/:id/search-usersThe same masked user directory as /standards/search-users, gated per standard: the standard's own moderators pass as well as global moderators. Used by the roles page so a per-standard moderator can find a successor to appoint.
Required permission: wiki:standard:moderate
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| query | query | string | Search by name or email (min 2 chars). Accepts q as well. Under 2 chars returns an empty list, not an error. |
GET /api/wiki/standards/:id/rolesList wiki role assignments for a standard. The standard's own moderators pass as well as global moderators.
Required permission: wiki:standard:moderate
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
POST /api/wiki/standards/:id/rolesAssign a wiki role (adopter or moderator) to a user for a specific standard. Allowed for a moderator OF THIS STANDARD (a per-standard wiki role) or for a holder of global wiki:standard:moderate — so a standard’s own moderator can always appoint a successor.
Required permission: wiki:standard:moderate
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
userId — integerrole — stringValid roles: adopter, moderator. User must be approved.
DELETE /api/wiki/standards/:id/roles/:roleIdRemove a wiki role assignment. Allowed for a moderator of this standard or a holder of global wiki:standard:moderate. Removing the last moderator of a standard is refused with 409; a moderator may resign while another remains.
Required permission: wiki:standard:moderate
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| roleId | path | string | Role assignment UUID |
POST /api/wiki/standards/:id/preview-adoptionRead-only dry-run of an adoption. Reports whether the revision would parse and adopt cleanly (isParseable, errors, elementCount, datasheetCount, resolvedCategoryPath, categoryPathError, isNew) without performing any writes. A revision that fails to parse returns 200 with isParseable:false, not an error.
Required permission: wiki:standard:adopt
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
sourceRevisionId — stringsourceRevisionId is the revision being previewed; omit it to preview the latest revision (by revision_number).
POST /api/wiki/categoriesCreate a new wiki category in a library.
Required permission: wiki:category:create
librarySlug — stringname — stringparentId — stringslug — stringdescription — stringsortOrder — integerPUT /api/wiki/categories/:idUpdate a wiki category.
Required permission: wiki:category:update
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Category UUID |
name — stringslug — stringdescription — stringsortOrder — integerparentId — string — UUID of the category this one goes under; null makes it top-levelparentId re-parents the category. Omitting it leaves the current parent alone — it is not a reset, so an update that means to keep the nesting does not need to resend it.
DELETE /api/wiki/categories/:idDelete a wiki category. Standards filed directly in it become uncategorized. Sub-categories are NOT handled automatically — if the category has any, you must say what happens to them via childDisposition or the call is refused.
Required permission: wiki:category:delete
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Category UUID |
childDisposition — string — reassign | promote | cascade; REQUIRED when the category has sub-categorieschildReassignTo — string — top-level category UUID; REQUIRED when childDisposition is "reassign"A category with NO sub-categories deletes with an empty body. With sub-categories, childDisposition is REQUIRED: the call returns 400 with requiresChildDisposition:true and the list of children, and that listing is the read to make the choice from. "reassign" re-parents them under childReassignTo, which must be a TOP-LEVEL category in the SAME library; "promote" makes them top-level; "cascade" DELETES the child categories and leaves their standards uncategorized (it destroys categories, never standards). Any other value is a 400. Deletion is permanent — there is no undo.
GET /api/wiki/search-contentSearch the public wiki corpus and return matching clauses with their standard, address, and snippet. No auth, no project scope — the open-corpus twin of GET /api/standards/search-content. The response carries a search_event_id (uuid, or null when logging failed) purely so the browser can report a click back; API callers can ignore it.
| Name | In | Type | Description |
|---|---|---|---|
| query | query | string | Search text (required) |
| limit | query | integer | Max hits, 1-100 (default 20) |
GET /api/wiki/standards/:librarySlug/:standardSlug/elementsGet the standard's current revision as STRUCTURED elements (address / content / toc_content / depth / attributes) rather than raw markdown, by running the shared adoption parser over the stored content. Same content the markdown read and the public wiki page already expose — this is the parsed form, so a client does not have to reimplement the parser to address individual clauses.
| Name | In | Type | Description |
|---|---|---|---|
| librarySlug | path | string | Library URL slug |
| standardSlug | path | string | Standard URL slug |
GET /api/wiki/standards/:librarySlug/:standardSlug/titleGet just { title } for a wiki standard. A deliberately tiny, cacheable read for callers that need the display name without pulling the whole document.
GET /api/wiki/standards/:id/errataFull errata audit trail for a wiki standard — active AND withdrawn, with their affected adopted revisions. Public.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Wiki standard UUID |
GET /api/wiki/errata/:errataIdGet one erratum in full, including every adopted revision and element address it affects. Public.
GET /api/wiki/adopted-revisions/:adoptedRevisionId/errataACTIVE errata for a single adopted revision — the banner hot path. Ask this before treating an adopted revision as correct: an empty array is the only evidence that no defect has been announced against it.
GET /api/wiki/moderation-logSite-wide public moderation log, newest first. Each entry carries the action, the date, the reason category and — while the affected standard is still published — a link to it. The moderator, the free-text reason note and the removed content itself are never disclosed. Public, no auth.
| Name | In | Type | Description |
|---|---|---|---|
| action | query | string | Filter to one action: archive, restore, revision_hide, revision_restore, adoption_revoke, adoption_restore, adoption_level_change, adoption_element_revoke, adoption_element_restore, adoption_scope_narrow, adoption_replacement_change, display_name_reset, errata_issued, errata_withdrawn, errata_extended, errata_linked_correction |
| reason | query | string | Filter to one reason category: spam, vandalism, test_content, copyvio (legacy), copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other |
| limit | query | integer | Results per page, 1-200 (default 50) |
| offset | query | integer | Offset for pagination (default 0) |
GET /api/wiki/moderation-log/:idOne public moderation log entry, addressed by the entry id the feed and the log page already print. Carries everything a feed entry carries plus `related` — every other public entry belonging to the same moderation matter, oldest first and including this one — so a withholding and the restoration that ended it read as one record. `scope.kind` says what the action reached — `clauses` (with a count and their positional addresses), `edition` (one adopted edition; the standard stays published), `standard` (an archive or restore) or `revision` (an editorial revision hidden or restored) — and is null for an action that reached none of the four. The clause addresses are withheld once the standard is no longer published. Returns 404 both for an unknown id and for an entry the site-wide feed does not show, so enumerating ids discloses nothing the feed does not. Public, no auth.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | integer | Moderation log entry id |
GET /api/wiki/standards/:id/moderation-logPublic moderation log for one wiki standard. Returns 404 for any standard that is not publicly readable, including an archived one, so the endpoint cannot be used to confirm that a particular standard was removed; archived standards still appear in the site-wide feed with their identity withheld. Public, no auth.
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Wiki standard UUID |
| action | query | string | Filter to one action (see GET /moderation-log) |
| reason | query | string | Filter to one reason category |
| limit | query | integer | Results per page, 1-200 (default 50) |
| offset | query | integer | Offset for pagination (default 0) |
POST /api/wiki/standards/:id/direct-adoptAdopt a revision, publishing it as the adopted (citable) version of the standard.
Required permission: wiki:standard:adopt
sourceRevisionId — string — UUID of the wiki revision to adoptUse POST /standards/:id/preview-adoption first to confirm the revision parses. The response's adoption.syncStatus is 'queued': the standard's copy in the SynC library is updated in the background, usually within a few seconds, and retried if it fails. Requires the per-standard ADOPTER role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). A global permission grant alone is a 403.
POST /api/wiki/standards/:id/errataIssue an erratum against one or more ADOPTED revisions of a standard. An erratum is a sibling annotation: it never edits the adopted revision or changes its content hash. This is the only correct way to announce that published content is wrong.
Required permission: wiki:errata:issue
severity — stringdescription — string — what is wrongrecommended_action — stringaffected — array of { adopted_revision_id, element_address? }recommended_action_note — stringcorrected_by_revision_id — string — UUID of the revision that fixes it, if one exists yetOmitting element_address scopes the erratum to the whole adopted revision. Requires the per-standard ADOPTER role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). A global permission grant alone is a 403.
POST /api/wiki/standards/:id/errata/:errataId/revisionsExtend an existing erratum to additional adopted revisions once the defect is found to reach further back.
Required permission: wiki:errata:issue
affected — array of { adopted_revision_id, element_address? }Requires the per-standard ADOPTER role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). A global permission grant alone is a 403.
PATCH /api/wiki/standards/:id/errata/:errataId/correctionPoint an erratum at the revision that corrects it, or clear that pointer by sending null. State change only — it does not withdraw the erratum.
Required permission: wiki:errata:issue
corrected_by_revision_id — string | null — UUID of the correcting revision, or null to clearRequires the per-standard ADOPTER role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). A global permission grant alone is a 403.
POST /api/wiki/standards/:id/errata/:errataId/withdrawWithdraw an erratum that should not have been issued. A state change, not a delete: the erratum stays in the audit trail and stops appearing as an active banner.
Required permission: wiki:errata:withdraw
reason — string — why it is being withdrawnRequires the per-standard ADOPTER role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). A global permission grant alone is a 403.
POST /api/wiki/standards/:id/archiveArchive (soft-delete) a wiki standard. 409 if it is already archived.
Required permission: wiki:standard:archive
reason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"merge_target_standard_id — string — UUID; REQUIRED when reason_code is "merged_into"Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/restoreRestore an archived wiki standard. One permission gates both directions, and a reason is still recorded. 409 if the standard is not currently archived.
Required permission: wiki:standard:archive
reason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/title-resetReset a standard's title from a named source revision's frontmatter title, and record a title_reset entry in the public moderation log, which never repeats either title. 409 if the source revision is hidden or already carries the current title; 422 if it has no frontmatter title.
Required permission: wiki:standard:moderate
source_revision_id — string — UUID of a non-hidden revision of this standard whose title should be restoredreason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/slug/retireRetire a standard's current slug: move the standard to new_slug and reserve the old slug in its library. The old address answers 410 Gone and does not redirect. There is no undo. 409 if new_slug is taken or retired.
Required permission: wiki:standard:retire_slug
new_slug — string — lower-case letters and digits joined by single hyphensreason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Admin tier only (WikiAdmin, SiteAdmin). Unlike the other moderation actions, a per-standard moderator role does not open this endpoint.
POST /api/wiki/standards/:id/revisions/:revId/hideHide a single wiki revision (soft moderation) without touching the rest of the history. If the hidden revision was current, the standard is repointed to the highest non-hidden revision. 409 if it is already hidden, or if the hidden revision is the source of the standard's current title (reset the title first).
Required permission: wiki:revision:hide
reason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Returns { revision, currentRevisionRepointed, newCurrentRevisionId, activeAdoption, moderationLogEntry }. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/revisions/:revId/restoreUn-hide a hidden wiki revision, auto-promoting current_revision_id forward to the highest non-hidden revision.
Required permission: wiki:revision:hide
reason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/adopted/:adoptedRevId/revokeRevoke an adopted revision. The snapshot CONTENT and its content_hash are never touched — only the revoked state — so the immutability promise holds for anything already cited. If the revoked edition was the standard's current adopted revision, the pointer moves to the most recent surviving edition, or to null when none remain. 409 if it is already revoked or was never published (status review/cancelled); 404 if the adoption belongs to a different standard.
Required permission: wiki:adoption:revoke
reason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Returns { adoptedRevision, currentAdoptedRevisionRepointed, newCurrentAdoptedRevisionId, moderationLogEntry }. Revoking does NOT hide the source wiki revision — that is a separate action on a separate permission. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/adopted/:adoptedRevId/restoreRestore a revoked adopted revision. One permission gates both directions, and a reason is still recorded. The standard's adopted pointer is promoted FORWARD only, never backward. 409 if it is not currently revoked.
Required permission: wiki:adoption:revoke
reason_code — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.reason_note — string — REQUIRED when reason_code is "other"Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:id/adopted/:adoptedRevId/replacementMark the adopted edition that REPLACES a revoked one, starting the days-after-replacement clock on its open revocation window. The replacement is a moderator judgment and is never auto-detected: it may be an OLDER surviving edition, and re-marking overwrites the previous mark. 400 if the id is missing or names the revoked edition itself; 404 if either id is unknown or belongs to another standard, or if that edition has no open revocation window; 409 if the proposed replacement was never published (status review/cancelled) or is itself revoked.
Required permission: wiki:adoption:revoke
replacement_adopted_revision_id — string (UUID) — a published adopted revision of the SAME standardReturns { effect, deadlines, eventType }. `deadlines` are derived from config/moderationPolicy.js on every read, so changing a policy number moves every open deadline. Marking notifies the project:admin holders of affected projects that an upgrade now clears the restriction, and starts the clock after which level 2 applies. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
GET /api/wiki/moderation/worklistOpen moderation matters, flagged items first and the oldest matter leading each tier. Each row carries the standard, the scope, the address of the held clause, how long it has been open, and which signals have fired: `changed` (the held text moved since a moderator last looked), `unlocatable` (the clause could not be found in the saved text) and `escalated` (the matter passed the 90-day mark and calls for an explicit lift or affirm). A signal is advisory: it never permits an adoption and never lifts a hold. Carries NO clause text. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| state | query | string | 'open' (default), 'resolved' or 'all' |
| standardId | query | string | Restrict to one standard (UUID) |
| limit | query | integer | Page size, 1-500 (default 100) |
| offset | query | integer | Rows to skip (default 0) |
GET /api/wiki/moderation/worklist/:itemIdOne moderation matter, whole: the worklist row as the list serves it, the hold it hangs off with its standard, adopted edition and noticed revision, every clause identity the hold covers with whether each has been cleared and where its hold stops (the release point, when a moderator has recorded one), the hold's own 30/90 window measured from the day it opened, the adopted editions a release point could name, a record of which parties were notified and when (counts only, never a recipient), and the public-register rows this hold produced. While the `changed` signal stands it also carries `texts`: the clause text a moderator last reviewed beside the text now at that slot. THIS IS THE ONLY ENDPOINT THAT SERVES HELD CLAUSE TEXT, and it serves it only to a moderator of that standard — deciding whether held text has been rewritten is not possible without both. `texts` is null on any matter whose held text has not moved. 404 when no such item exists. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| itemId | path | integer | Worklist item ID |
GET /api/wiki/moderation/revoked-editionsThe revision-level revocation register: adopted editions revoked outright, oldest first, each with the reason, the level in force, the 30/90 window running toward level 2, the replacement edition if a moderator has marked one, and the restore date if it was restored. `state=open` (default) lists editions still revoked; `state=all` includes restored ones. DISTINCT FROM THE WORKLIST: a revoked edition is withdrawn whole, while a hold withholds named clauses without revoking anything and has no window at all. Carries NO clause text. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| state | query | string | 'open' (default) or 'all' |
| limit | query | integer | Page size, 1-500 (default 100) |
| offset | query | integer | Rows to skip (default 0) |
GET /api/wiki/moderation/registerEvery row of the wiki moderation log, newest first — the moderator's own view of the table the public register projects. It carries the three things the public register withholds: the real actor (the moderator who performed the action, resolved to a name), the private reason note, and the target even where the standard is no longer published. Rows the public register never shows are listed here too, marked `isPublic: false`, because a row the projection drops must not vanish from the one place a moderator can read it. A reader-filed `attribution_report` row names NO filer: its actor is never resolved, on any row, to anyone. `entryUrl` is the public permalink and is null on a row the public register does not carry; `identityWithheldPublicly` says the register shows the row without naming its standard. Served alongside the rows: `total`, `notPublic` and `withNote` over the whole filtered set, plus the two filter vocabularies (`actions`, `standards`). NOT the public register — that is GET /api/wiki/moderation-log, which is unauthenticated and carries none of these fields. The path is `/moderation/register` rather than `/moderation/log` because tool names flatten punctuation, and two endpoints with opposite disclosure rules must not derive one name. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| action | query | string | Restrict to one wiki_moderation_action value. Accepts every value the enum carries, including `attribution_report`, which the public register excludes. |
| standardId | query | string | Restrict to one standard (UUID) |
| limit | query | integer | Page size, 1-100 (default 25) |
| offset | query | integer | Rows to skip (default 0) |
POST /api/wiki/moderation/log/:id/statementPublish SynC's own statement on a public moderation log entry, or revise the one that stands. Body: `{ text }`. The statement appears on the public entry page under SynC's name, is optional and never expected, and may be revised or withdrawn later; every version is kept, so the statement table is its own change record and no row is written to the moderation log for a statement change. NOT the private reason note: that column is written for moderators and is never published at any level, and a statement that merely repeats it is refused. 400 when the text is blank, over 2000 characters, or repeats the note; 404 when no such entry exists; 409 when the entry is not one the public register carries, so there is no page for a statement to appear on. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| id | path | integer | Moderation log entry ID |
| text | body | string | The statement, 1-2000 characters |
DELETE /api/wiki/moderation/log/:id/statementWithdraw the statement that stands on a moderation log entry. The text comes off the public entry page and is not shown again; the page then records that SynC published a statement and withdrew it, with both dates. Nothing is deleted — a statement that stood in public and then vanished without trace is the silent edit the version history exists to prevent. 404 when no such entry exists; 409 when no statement stands to withdraw. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| id | path | integer | Moderation log entry ID |
POST /api/wiki/moderation/worklist/:itemId/resolveResolve one open matter, by one of three moves. `lift` clears the hold on that clause — the ONLY thing that lifts a hold; editing the clause does not — resolves the item, and ends the whole action when it was the last clause held, at which point adoption of the standard is permitted again. `affirm` says the hold stands: it advances the changed-text reference point so the same words never flag twice, clears the signal, and LEAVES THE ITEM OPEN, because an affirmed hold is still an open matter. `release` records that the clause is clear FROM A GIVEN ADOPTED EDITION ONWARD while earlier editions stay withheld: that edition and every later one render it, the wiki head renders it, a project holding a released edition may publish again, and the upgrade panel offers it as the way out. It leaves the item OPEN for the same reason `affirm` does — earlier editions are still withheld and the matter is still open. Sending `release` with no `releasedFromAdoptedRevisionId` WITHDRAWS the release point, so every edition carrying the clause is withheld again; that is the path for a later edition that reintroduces the noticed text. 404 no such item, or no such adopted revision; 409 the item is already resolved, the hold has already ended, the clause was already lifted, the edition belongs to another standard, or the release point would sit at or before the edition the withheld text first appeared in — which is a lift, and must go through one.
Required permission: wiki:adoption:revoke
move — string — 'lift', 'affirm' or 'release'note — string (max 2000) — the moderator's note, recorded on the item and, for a lift, on the cleared clausereleasedFromAdoptedRevisionId — string (UUID) — 'release' only: the first adopted edition the clause is clear in. Omit or send null to withdraw an existing release point.Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/moderation/worklist/:itemId/counter-noticeRecord a §512(g)(2) counter-notice on one moderation matter and start the statutory window. The moderator transcribes it from the designated agent’s mailbox: there is no public counter-notice form. The window is counted from RECEIPT, never from the forwarding, so a slow forwarding cannot extend the period the material stays withheld; the server computes business day 10 (`restore_due_at`, when the sweep restores) and business day 14 (`restore_deadline_at`, the outer bound) and STAMPS them, and they are never recomputed afterwards. Business days are Monday to Friday with no holiday calendar. The counter-notice’s text and the sender’s name are NOT stored — only the receipt date and the identifier the e-mail is filed under. Nothing here is published: the public register carries the restoration when one happens and nothing about the counter-notice itself. 400 a future date, a blank or over-long reference, or a forwarding dated before the receipt; 404 no such item; 409 a counter-notice window is already running on this matter, or the hold has already ended. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| itemId | path | integer | Worklist item ID |
receivedAt — string (YYYY-MM-DD) — when the designated agent received it. Not in the future.reference — string (max 255) — the identifier the e-mail is filed under. NOT the sender’s name and NOT the text of the counter-notice.forwardedAt — string (YYYY-MM-DD) — when it was forwarded to the complainant, if that has already happened. Omit and record it later from the matter.Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/moderation/worklist/:itemId/counter-notice/forwardedRecord that the counter-notice was forwarded to the complainant, as §512(g)(2)(B) requires. An unrecorded forwarding is indistinguishable from none, which is why it is a stored date rather than an inference. It does NOT move the window, which is counted from receipt. Recorded once and not overwritten: the date is evidence about a statutory obligation. Still available after a court action has stopped the window, because that obligation does not lapse because the complainant filed. 400 a future date, or one before the receipt; 404 no counter-notice on this matter; 409 the forwarding is already recorded, or the window ended in a restoration. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| itemId | path | integer | Worklist item ID |
forwardedAt — string (YYYY-MM-DD) — when it was forwarded. Not in the future.Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/moderation/worklist/:itemId/counter-notice/court-actionRecord that the complainant has told SynC’s designated agent an action has been filed seeking to restrain the material. THE ONLY THING THAT STOPS THE COUNTER-NOTICE WINDOW: the sweep does not restore, and the hold stands. It does NOT end the matter — affirm, release, lift and restore all stay available afterwards — and nothing about the court action is ever published. Only the court and docket, or the identifier the notice is filed under, is stored. 400 a future date, a blank reference, or a date before the receipt; 404 no counter-notice on this matter; 409 a court action is already recorded, or the window ended in a restoration. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| itemId | path | integer | Worklist item ID |
recordedAt — string (YYYY-MM-DD) — when notice of the action was received. Not in the future.reference — string (max 255) — court and docket, or the identifier the notice is filed under.Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/moderation/worklist/:itemId/counter-notice/restoreRestore the held material before the counter-notice window runs out. The same whole-action restore the nightly sweep performs at the end of the window, taken earlier by a moderator: every held clause is restored, the hold ends, every open matter on it resolves, and the release notice goes to the parties the hold notice reached. Filed in the public register under `counter_notice`, which the projection labels "Counter-notification received" — the only indication a public reader gets of why the material came back. Refused when a court action stopped the window: restoring anyway is the ordinary POST /moderation/element-actions/:actionId/restore, which says in the register that a moderator lifted the hold rather than that a statutory window ran out. 404 no counter-notice on this matter; 409 a court action stopped the window, the window has already ended, or the hold has already been restored. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| itemId | path | integer | Worklist item ID |
note — string (max 2000) — the moderator’s note, recorded on the cleared clauses and the resolved items.Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
GET /api/wiki/standards/:id/adopted/:revNum/elementsThe selectable clauses of one adopted edition: each clause identity and the address it sits at, in document order. Carries NO clause text — the text is read from GET /standards/:id/adopted/:revNum; this index exists to tie a rendered clause to the identity a hold is keyed on. 404 when the edition is unknown or has no synced SynC revision (adoption syncs best-effort, so an unsynced edition is a real state in which no clause can be identified). Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
Required permission: wiki:adoption:revoke
| Name | In | Type | Description |
|---|---|---|---|
| id | path | string | Standard UUID |
| revNum | path | integer | Adopted revision number |
POST /api/wiki/moderation/element-actionsOpen a hold on one or more clauses of an adopted edition, or on a whole standard. Records what the NOTICE identifies — SynC follows a notice and neither assigns blame nor judges merits — so `noticedRevisionId` is required for every held matter and is never defaulted, and `scope` is required and explicit: an empty clause list is never read as a whole-standard hold. The hold is keyed on the clause identity and follows that slot forward into every later edition until a moderator lifts it. 404 an unknown edition, or a clause absent from the named edition; 409 an edition that was never published, or one already revoked at revision level and named without `narrowFrom`.
Required permission: wiki:adoption:revoke
standardId — string (UUID)adoptedRevisionId — string (UUID) — the edition the notice namedscope — string — 'element' (the notice named clauses) or 'standard' (it named none)reasonCode — string — one of: spam, vandalism, test_content, copyright_notice, suspected_copy, counter_notice, legal_compulsion, out_of_scope, merged_into, other. Any other value is a 400. `copyvio` is still accepted but is LEGACY (migration 03_161 split it into copyright_notice, a third party asserted infringement, and suspected_copy, SynC's own editorial finding); do not use it in new integrations. `counter_notice` is the basis for a restore that follows a counter-notification, and `legal_compulsion` covers a court order or statutory erasure — that one publishes a deliberately minimal notice carrying no date, ordinal or per-standard link.noticedRevisionId — string (UUID), or an object mapping each clause identity to a revision UUID — the wiki revision the notice identifies. Required for every held matter and never derived.identityUuids — array of clause identity UUIDs — required at scope 'element', rejected at scope 'standard'reasonNote — stringlevel — number — 1 or 2widen — boolean — also admit editions where the clause is present but its text differs. Those members are recorded under SynC's own editorial basis, not under the notice.narrowFrom — number — the open revision-level revocation this hold narrows. Restoring the edition and holding the clause happen as ONE action; the register records a narrowing, which is not a restoration.Returns { action, identities, members, worklist, logEntry, narrowedFromEffectId }. Every open hold is on the moderator worklist from the moment it opens. Requires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/moderation/element-actions/:actionId/restoreLift a hold: one restore, the whole set, both scopes. The action ends, every clause still held is cleared and every open worklist item is resolved, in one transaction. The evidentiary member rows are kept — they record what was noticed where, which a later dispute needs whether or not the hold still stands. 404 an unknown action; 409 one already restored.
Required permission: wiki:adoption:revoke
note — string — the moderator's note, recorded on each cleared clause and each resolved worklist itemreason_note — string — free text for the public moderation-log rowRequires the per-standard MODERATOR role in addition to the permission (SiteAdmin bypasses via wiki:standard:moderate). Every moderation action is written to the moderation log with its reason — there is no silent form of this call.
POST /api/wiki/standards/:standardId/revisions/:revId/tag-only-revisionChange a standard's tags by appending a NEW revision that carries the parent's content and content hash unchanged, with the new tag set attached. The wiki has no working-copy concept, so this is the ONLY correct way to change tags: writing tags onto an existing revision would edit a record that has already been read and cited. CONTRIBUTOR-GATED: wiki:standard:* permissions come from the SiteContributor role, which you are granted by accepting the Contributor License Agreement (CLA). The account owner accepts it signed in, from their profile or the wiki; an API key cannot accept it. Until the account (for an API key, its OWNER) has accepted, this endpoint returns 403 no matter what the key was granted, and /api/whoami lists the permission under `dormant` with that step as its `remedy`.
Required permission: wiki:standard:edit
tag_ids — array of tag UUIDs — the COMPLETE new set; anything other than an array is a 400The array REPLACES the tag set rather than adding to it, so send every tag the revision should end up with, including ones it already has. An empty array clears all tags. :revId is the PARENT revision the new revision descends from — normally the standard's current revision.
POST /api/wiki/revisions/:revisionId/report-attributionReport a contribution-attribution problem on a wiki revision (impersonation, a wrong employer byline, an undisclosed conflict). Any authenticated reader may report. A hidden or non-existent revision returns 404 rather than confirming it exists.
reason_note — string — non-empty; an empty note is a 400POST /api/wiki/standards/:librarySlug/:standardSlug/import-to-new-projectCreate a placeholder project from a wiki standard in one atomic call: mirrors the standard's category into project scope, attaches the ADOPTED revision to the new project's draft, and returns the deep link to its datasheet. The signed-in half of the wiki "use this standard" call to action. Any signed-in account may call it, company or no company: the company is required at first publish or first distribution, not at import.
idempotency_key — string — UUID; a repeat with the same key returns the first result, not a second projectsource — string — provenance label, e.g. "wiki_import"Returns { success, projectId, standardId, datasheetUrl }. Generate a fresh idempotency_key per intended import and REUSE it on retry — that is what stops a network retry from creating a duplicate project.