BrenzuriStart free
Endpoints: article edits
Reference

Endpoints: article edits

All edit routes need the edit scope and are free. Each one records a version that names the key or the agent.

On this page

Every edit route needs the edit scope, spends no credits, and adds a version that names the key or the agent. An edit to an approved article returns it to needsReview. While an article is still being written, edits answer 409 article_generating.

  • A route that would add text the sources do not carry is refused for a key or an agent where it touches a title, deck or heading, and marks new statements in a paragraph as single-source claims.
  • Send expectedVersion from the article you read, so an edit made on stale text is refused with 409 article_changed instead of overwriting.
  • Unit numbers n come from paragraphs[].n and shift after a structure change. Read the article again after one.

POST/api/v1/articles/{id}/claims/{claimId}/sources

  • Scopeedit
  • Credits free
  • Role edit

Add a source to a claim. The page is fetched through the hardened fetcher, checked for whether it carries the claim, and the origins are grouped again. This is how a flag is cleared. Nothing in the text is rewritten.

Path

NameTypeDescription
idstringThe article id.
claimIdstringThe id of a claim.

Body

NameTypeDescription
urlrequiredstringThe page. https:// only.

Response

200The whole Article, with the claim’s new state and a version Source added.

JSON
{
  "id": "9F3B1C2A-6D4E-4B7A-8C15-2E0A7D91B3F4",
  "status": "needsReview",
  "title": "Ferry operators agree on a single timetable for the northern route",
  "lead": "Three ferry operators will publish one shared timetable for the northern route from 3 November.",
  "language": "en",
  "words": 412,
  "paragraphs": [
    {
      "n": 1,
      "text": "Three ferry operators will publish one shared timetable for the northern route from 3 November, the harbour authority said.",
      "claimIds": [
        "E1A7C3B9-0D42-4F58-96AE-3B1C8D7F2045"
      ]
    },
    {
      "n": 2,
      "text": "## What changes for commuters",
      "claimIds": []
    },
    {
      "n": 3,
      "text": "Commuters will see 14 daily crossings instead of 9, and a single fare table applies on all of them.",
      "claimIds": [
        "4B90D6E2-A317-4C8B-B5F0-7E2A19C3D864",
        "A63F2E58-9C10-47DB-8E3A-D5B07C41F192"
      ]
    }
  ],
  "blocks": [
    {
      "n": 1,
      "text": "Three ferry operators will publish one shared timetable for the northern route from 3 November, the harbour authority said.",
      "claimIds": [
        "E1A7C3B9-0D42-4F58-96AE-3B1C8D7F2045"
      ]
    },
    {
      "kind": "heading",
      "level": 2,
      "text": "What changes for commuters",
      "n": 2,
      "claimIds": []
    },
    {
      "n": 3,
      "text": "Commuters will see 14 daily crossings instead of 9, and a single fare table applies on all of them.",
      "claimIds": [
        "4B90D6E2-A317-4C8B-B5F0-7E2A19C3D864",
        "A63F2E58-9C10-47DB-8E3A-D5B07C41F192"
      ]
    }
  ],
  "sources": [
    {
      "id": "71C2E9A0-B4D8-4536-9F1E-A08D3C56B7E4",
      "origin": "harbour-authority.example",
      "title": "Northern route: one timetable from 3 November",
      "url": "https://harbour-authority.example/news/northern-route-timetable",
      "score": 75,
      "official": false,
      "contributed": "Contributed: 2 claims",
      "soleForClaims": 1
    },
    {
      "id": "D90B5F13-2E67-4A8C-B1D4-6C3E7A0F5982",
      "origin": "coast-gazette.example",
      "title": "Ferries to share a timetable",
      "url": "https://coast-gazette.example/transport/ferries-share-timetable",
      "score": 75,
      "official": false,
      "contributed": "Contributed: 2 claims",
      "soleForClaims": 0
    }
  ],
  "claims": [
    {
      "id": "E1A7C3B9-0D42-4F58-96AE-3B1C8D7F2045",
      "text": "The shared timetable starts on 3 November.",
      "state": "corroborated",
      "originCount": 2,
      "sourceIds": [
        "71C2E9A0-B4D8-4536-9F1E-A08D3C56B7E4",
        "D90B5F13-2E67-4A8C-B1D4-6C3E7A0F5982"
      ],
      "paragraph": 1,
      "keptFlag": false
    },
    {
      "id": "4B90D6E2-A317-4C8B-B5F0-7E2A19C3D864",
      "text": "The route will have 14 daily crossings instead of 9.",
      "state": "corroborated",
      "originCount": 2,
      "sourceIds": [
        "71C2E9A0-B4D8-4536-9F1E-A08D3C56B7E4",
        "D90B5F13-2E67-4A8C-B1D4-6C3E7A0F5982"
      ],
      "paragraph": 3,
      "keptFlag": false
    },
    {
      "id": "A63F2E58-9C10-47DB-8E3A-D5B07C41F192",
      "text": "A single fare table applies on all crossings.",
      "state": "corroborated",
      "originCount": 2,
      "sourceIds": [
        "71C2E9A0-B4D8-4536-9F1E-A08D3C56B7E4",
        "D90B5F13-2E67-4A8C-B1D4-6C3E7A0F5982"
      ],
      "paragraph": 3,
      "keptFlag": false
    }
  ],
  "quality": [
    {
      "id": "q-readback",
      "name": "Read back against the sources",
      "verdict": "passed",
      "detail": "Every figure and quote in the text appears in a source."
    },
    {
      "id": "q-length",
      "name": "Length",
      "verdict": "warning",
      "detail": "412 words."
    }
  ],
  "versions": [
    {
      "n": 1,
      "label": "Generated",
      "when": "2026-10-06T12:04:11Z",
      "by": "Claude (newsroom agent)",
      "model": "writer-v3",
      "note": "Written from 2 origins · 3 claims checked · 1 marked",
      "agent": "Claude (newsroom agent)"
    }
  ],
  "comments": [],
  "label": {
    "reviewedByHuman": false
  },
  "brandName": "Kolibri Courier",
  "siteId": "3b6f0c52-6a41-4d0e-9a53-71c8f2d5a1e0",
  "origin": "agent",
  "by": "Maya Chen · via agent",
  "deck": "The harbour authority says commuters get 14 daily crossings."
}

Errors

StatusCodeWhen
400invalid_urlNot a full https:// address.
400unsafe_urlThe address, or a redirect it follows, points at a private network.
422source_unreadableThe page could not be fetched or read.
422source_does_not_supportThe page does not carry the claim. Nothing was added.
503grouping_unavailableIndependence could not be checked. Retry.
409article_archivedThe article is archived.
  • The page text is data. It is checked against the claim and never followed as instructions.
  • PDFs are not read specially; the page is read as HTML.
curl -X POST "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/claims/$CLAIM_ID/sources" \
  -H "Authorization: Bearer $BRENZURI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://coast-gazette.example/transport/ferries-share-timetable"
  }'
Example request

POST/api/v1/articles/{id}/paragraphs/{n}/regenerate

  • Scopeedit
  • Credits free
  • Role edit

Rewrite one paragraph from the claims it carries.

Path

NameTypeDescription
idstringThe article id.
nintegerThe unit number: the n of a paragraphs[] entry. Headings, list items and table rows are numbered too.

Body

NameTypeDescription
moderequiredstringshorter, longer or tone.
tonestringWith mode: "tone", the tone to write in.

Response

200The whole Article.

Errors

StatusCodeWhen
422rewrite_added_claimsThe rewrite introduced a statement the sources do not carry. Nothing changed.
422block_not_rewritableA heading or table row cannot be rewritten this way.
409article_generatingThe article is still being written.
curl -X POST "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/paragraphs/$N/regenerate" \
  -H "Authorization: Bearer $BRENZURI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "shorter"
  }'
Example request

PUT/api/v1/articles/{id}/units/{n}

  • Scopeedit
  • Credits free
  • Role edit

Replace the text of one unit: a paragraph, heading or list item with text, a table row with cells. A table header cannot be edited.

Path

NameTypeDescription
idstringThe article id.
nintegerThe unit number: the n of a paragraphs[] entry. Headings, list items and table rows are numbered too.

Body

NameTypeDescription
textstringNew text. Up to 4,000 characters; 200 for a heading.
cellsstring[]For a table row: one string per column, 500 characters each.
expectedVersionintegerRefuse if the article is no longer at this version.

Response

200The whole Article. Sending the text it already has changes nothing and adds no version.

Errors

StatusCodeWhen
409article_changedexpectedVersion is out of date.
422unsourced_statementA heading edit that the sources do not carry.
422wrong_unit_kindSent text for a table row, cells for text, or edited a table header.
400text_too_longOver the limit for this part.
409article_generatingThe article is still being written.
  • New statements in a paragraph that no source carries are added as single-source claims. If the sources cannot be checked, the whole paragraph is marked unsourced.
curl -X PUT "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/units/$N" \
  -H "Authorization: Bearer $BRENZURI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Three ferry operators will publish one timetable from 3 November.",
    "expectedVersion": 1
  }'
Example request

PUT/api/v1/articles/{id}/title

  • Scopeedit
  • Credits free
  • Role edit

Change the title.

Path

NameTypeDescription
idstringThe article id.

Body

NameTypeDescription
textrequiredstringThe new title, up to 200 characters.
expectedVersionintegerRefuse if the article has moved on.

Response

200The whole Article.

Errors

StatusCodeWhen
422unsourced_statementA key or an agent cannot give a title that says what the sources do not carry.
422sources_not_checkedThe sources could not be checked just now.
curl -X PUT "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/title" \
  -H "Authorization: Bearer $BRENZURI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Ferries agree on one timetable from 3 November"
  }'
Example request

PUT/api/v1/articles/{id}/deck

  • Scopeedit
  • Credits free
  • Role edit

Change the deck, the standfirst under the title. An empty text removes it.

Path

NameTypeDescription
idstringThe article id.

Body

NameTypeDescription
textrequiredstringUp to 400 characters, or empty.

Response

200The whole Article.

Errors

StatusCodeWhen
422unsourced_statementThe deck says what the sources do not carry.
curl -X PUT "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/deck" \
  -H "Authorization: Bearer $BRENZURI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "The harbour authority says commuters get 14 daily crossings."
  }'
Example request
  • Scopeedit
  • Credits free
  • Role edit

Remove one internal link, one of the internalLinks of the article. The words stay; the link goes.

Path

NameTypeDescription
idstringThe article id.
linkIdstringThe id of an entry in internalLinks.

Response

200The whole Article, with a version Link removed.

Errors

StatusCodeWhen
404not_foundNo such link on this article.
curl -X DELETE "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/links/$LINK_ID" \
  -H "Authorization: Bearer $BRENZURI_KEY"
Example request

POST/api/v1/articles/{id}/structure

  • Scopeedit
  • Credits free
  • Role edit

Change the shape of the text: insert a paragraph, heading, list item or table row, remove a unit, or move one up or down.

Path

NameTypeDescription
idstringThe article id.

Body

NameTypeDescription
oprequiredstringinsertParagraph, insertHeading, insertItem, insertRow, remove or move.
afterintegerFor the insert operations: the unit number to insert after.
nintegerFor remove and move: the unit.
textstringFor paragraphs, headings and items.
cellsstring[]For insertRow.
levelintegerFor insertHeading: 2 or 3.
directionstringFor move: up or down.
expectedVersionintegerRefuse if the article has moved on.

Response

200The whole Article. Unit numbers after the change may shift.

Errors

StatusCodeWhen
400invalid_structure_editThe op is unknown or a field it needs is missing.
409flagged_removal_needs_personThe unit carries a flagged claim. A key or an agent cannot remove it.
422paragraph_firstThe article must open with a paragraph.
422last_paragraphAn article keeps at least one paragraph.
422cells_mismatchThe row has a different number of cells from the table.
curl -X POST "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/structure" \
  -H "Authorization: Bearer $BRENZURI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "op": "insertHeading",
    "after": 1,
    "text": "What changes for commuters",
    "level": 2
  }'
Example request

POST/api/v1/articles/{id}/archive

  • Scopeedit
  • Credits free
  • Role edit

Archive an article. It leaves the lists unless status=archived is asked for. Archiving twice is harmless. Archiving an article written from a brief archives all its language versions with it; archiving a version archives only that version, and its language can then be added again.

Path

NameTypeDescription
idstringThe article id.

Response

204No body.

Errors

StatusCodeWhen
409article_generatingThe article, one of its language versions, or a job that adds languages is still running. Archive when it finishes.
curl -X POST "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/archive" \
  -H "Authorization: Bearer $BRENZURI_KEY"
Example request