Endpoints: article edits
On this page
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 expectedVersionfrom the article you read, so an edit made on stale text is refused with 409article_changedinstead of overwriting.Unit numbers ncome fromparagraphs[].nand shift after a structure change. Read the article again after one.
POST/api/v1/articles/{id}/claims/{claimId}/sources
- Scopeedit
- Credits free
- Role edit
Path
| Name | Type | Description |
|---|---|---|
id | string | |
claimId | string | id of a claim. |
Body
| Name | Type | Description |
|---|---|---|
urlrequired | string | https:// only. |
Response
200Source added.
{
"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
| Status | Code | When |
|---|---|---|
| 400 | invalid_url | https:// address. |
| 400 | unsafe_url | |
| 422 | source_unreadable | |
| 422 | source_does_not_support | |
| 503 | grouping_unavailable | |
| 409 | article_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"
}'POST/api/v1/articles/{id}/paragraphs/{n}/regenerate
- Scopeedit
- Credits free
- Role edit
Path
| Name | Type | Description |
|---|---|---|
id | string | |
n | integer | n of a paragraphs[] entry. Headings, list items and table rows are numbered too. |
Body
| Name | Type | Description |
|---|---|---|
moderequired | string | shorter, longer or tone. |
tone | string | mode: "tone", the tone to write in. |
Response
200
Errors
| Status | Code | When |
|---|---|---|
| 422 | rewrite_added_claims | |
| 422 | block_not_rewritable | |
| 409 | article_generating |
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"
}'PUT/api/v1/articles/{id}/units/{n}
- Scopeedit
- Credits free
- Role edit
text, a table row with cells. A table header cannot be edited.
Path
| Name | Type | Description |
|---|---|---|
id | string | |
n | integer | n of a paragraphs[] entry. Headings, list items and table rows are numbered too. |
Body
| Name | Type | Description |
|---|---|---|
text | string | |
cells | string[] | |
expectedVersion | integer |
Response
200
Errors
| Status | Code | When |
|---|---|---|
| 409 | article_changed | expectedVersion is out of date. |
| 422 | unsourced_statement | |
| 422 | wrong_unit_kind | text for a table row, cells for text, or edited a table header. |
| 400 | text_too_long | |
| 409 | article_generating |
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
}'PUT/api/v1/articles/{id}/title
- Scopeedit
- Credits free
- Role edit
Path
| Name | Type | Description |
|---|---|---|
id | string |
Body
| Name | Type | Description |
|---|---|---|
textrequired | string | |
expectedVersion | integer |
Response
200
Errors
| Status | Code | When |
|---|---|---|
| 422 | unsourced_statement | |
| 422 | sources_not_checked |
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"
}'PUT/api/v1/articles/{id}/deck
- Scopeedit
- Credits free
- Role edit
text removes it.
Path
| Name | Type | Description |
|---|---|---|
id | string |
Body
| Name | Type | Description |
|---|---|---|
textrequired | string |
Response
200
Errors
| Status | Code | When |
|---|---|---|
| 422 | unsourced_statement |
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."
}'DELETE/api/v1/articles/{id}/links/{linkId}
- Scopeedit
- Credits free
- Role edit
internalLinks of the article. The words stay; the link goes.
Path
| Name | Type | Description |
|---|---|---|
id | string | |
linkId | string | id of an entry in internalLinks. |
Response
200Link removed.
Errors
| Status | Code | When |
|---|---|---|
| 404 | not_found |
curl -X DELETE "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/links/$LINK_ID" \
-H "Authorization: Bearer $BRENZURI_KEY"POST/api/v1/articles/{id}/structure
- Scopeedit
- Credits free
- Role edit
Path
| Name | Type | Description |
|---|---|---|
id | string |
Body
| Name | Type | Description |
|---|---|---|
oprequired | string | insertParagraph, insertHeading, insertItem, insertRow, remove or move. |
after | integer | |
n | integer | remove and move: the unit. |
text | string | |
cells | string[] | insertRow. |
level | integer | insertHeading: 2 or 3. |
direction | string | move: up or down. |
expectedVersion | integer |
Response
200
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_structure_edit | op is unknown or a field it needs is missing. |
| 409 | flagged_removal_needs_person | |
| 422 | paragraph_first | |
| 422 | last_paragraph | |
| 422 | cells_mismatch |
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
}'POST/api/v1/articles/{id}/archive
- Scopeedit
- Credits free
- Role edit
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
| Name | Type | Description |
|---|---|---|
id | string |
Response
204
Errors
| Status | Code | When |
|---|---|---|
| 409 | article_generating |
curl -X POST "$BRENZURI_URL/api/v1/articles/$ARTICLE_ID/archive" \
-H "Authorization: Bearer $BRENZURI_KEY"