Pushing documents in
Push a wire with one request, read the response, retry safely and fix the common errors.
On this page
Send wires, planning items and articles into Tidenda from your own systems with one HTTP request each. This guide is for developers building an importer or feed connector.
curl -s -X POST "https://<public-api-host>/v1/documents" \
-H "Authorization: Bearer $TIDENDA_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "3b2c1a40-f128-4cb6-b8c2-19a4d8d7e2f5",
"type": "core/newswire",
"title": "Cabinet reshuffle expected ahead of Thursday vote",
"meta": {
"priority": 3,
"origin": "wire",
"source": "acme-wire",
"sourceItemId": "item-20260706-0142"
},
"content": { "text": "The prime minister is expected to name three new ministers." },
"links": []
}'
Get a key
Ask an owner or admin of the organisation to create a key in
Settings → API keys with the documents:create scope and nothing
else. Copy it when it is shown; it is not shown again. Keep it in a
secret store and pass it to the importer as an environment variable,
here TIDENDA_KEY.
Push a wire
The request above creates a wire. Every field in it is required:
id: a UUID you generate, such ascrypto.randomUUID(). Store it with the source item so a retry sends the same one.type:core/newswire.title: a string. Tidenda stores it as one paragraph of rich text.meta.priority: 1 to 6, 1 most urgent.meta.origin:wire,pressreleaseormedia.meta.source: a stable name for the feed, such as the agency.meta.sourceItemId: the feed's own id for this item.content.text: the body, as a string or a rich-text node array. A string becomes a single paragraph; send nodes to keep paragraphs apart. The node format is in the Document reference.links: an empty array is fine.
Optional, when the feed has them: meta.sourceUrl (the link to the
original), meta.byline (the author line) and content.attachments
(files as links). The Document reference
has their shapes.
Leave out meta.status and the other workflow fields. Tidenda sets
them: every pushed document enters its workflow at draft. A body
with any other meta.status is refused with 400 INVALID_STATUS, so
a push can never publish on its own.
Send only the fields the Document reference
defines. A field Tidenda does not know, such as a misspelt
meta.sorce, is refused with 400 UNKNOWN_FIELDS and listed in the
error's details.
Push a planning item or an article
The same endpoint accepts core/planning-item and core/article,
shaped as in the Document reference. Each type's
rules apply: a planning item needs a startdate and an enddate in
meta.time; an article needs a target or slot time and links to
its planning item and assignment. Both stay at draft until an
editor moves them on. No other type can be pushed; any other type
is refused with 400 UNSUPPORTED_TYPE.
Read the response
{ "ok": true, "data": { "id": "3b2c1a40-f128-4cb6-b8c2-19a4d8d7e2f5" } }
201 Created means the wire is stored. In the same request Tidenda
moved it from draft to usable: it appears in the Wires view, a
signed snapshot is made, and any webhook watching wires fires. See
Wires for what editors do with it next.
Idempotency
Retries are safe, and there are two guards.
A repeated source item. If a wire with the same meta.source and
meta.sourceItemId exists, the response is 200 with the existing
wire's id, and nothing is created:
{ "ok": true, "data": { "id": "3b2c1a40-f128-4cb6-b8c2-19a4d8d7e2f5", "idempotent": true } }
This holds even when the retry carries a new id, so a feed that
cannot remember its UUIDs still does not create duplicates. Treat
200 and 201 alike: the wire is there. Wires are kept and cannot
be deleted, so a source item that arrived once is always found.
A repeated id. Any other create with an id already in use
returns 409 CONFLICT and changes nothing. If the document with that
id was deleted, the answer is 409 DOCUMENT_DELETED: deleted ids
are never reused, so send the item under a new id.
Common errors
| Response | Usual cause | Fix |
|---|---|---|
400 INVALID_PAYLOAD |
A missing or wrong field. The message names it, for example meta.sourceItemId: Required. |
Correct the body. Retrying unchanged fails again. |
400 UNKNOWN_FIELDS |
A field the type does not define, often a misspelling. details lists each one. |
Rename or remove the fields. |
400 UNSUPPORTED_TYPE |
A type other than core/newswire, core/planning-item or core/article. |
Push only those types. |
400 INVALID_STATUS |
meta.status set to anything other than draft. |
Leave meta.status out. |
401 AUTH_ERROR |
No Authorization header, or a revoked or mistyped key. |
Check the header and the key. |
403 FORBIDDEN |
The key lacks documents:create. |
Ask for a key with the scope. |
409 CONFLICT |
The id is taken by a different item. |
Generate a new id for it. |
409 DOCUMENT_DELETED |
The id belonged to a deleted document. |
Send the item under a new id. |
429 RATE_LIMITED |
Too many requests from the key or the address. | Wait Retry-After seconds. |
5xx |
A failure on Tidenda's side. | Retry with the same body and backoff. |
Retry only 429 and 5xx. Every status, limit and header is in the
API reference.
Read it back
GET /v1/documents serves published articles, images, audio, tags and
site configuration, so a wire itself is not on it. Use a webhook on
core/newswire to receive each wire as it publishes, or look for it in
the Wires view.
When an editor turns the wire into an article and publishes it, read
the article with a key holding publisher:read:
curl -s "https://<public-api-host>/v1/documents?type=core/article&updatedSince=2026-07-06T00:00:00Z" \
-H "Authorization: Bearer $TIDENDA_READ_KEY"
Articles written from a wire carry a link back to it, with
"type": "core/newswire" and "rel": "source".