Tidenda

Guides

API overview

Authentication, scopes, rate limits, contract scope. Start here for any integration.

API overview

Tidenda exposes a small public HTTP surface for the two integration patterns most organisations need:

  • Push documents in. External feeds, importers, news agents, and scrapers post documents to POST /v1/documents.
  • Receive signed events out. Outbound webhooks fire on workflow transitions and carry signed snapshots. See Receiving webhooks.

The pages under Reference carry the per-endpoint shapes, request and response examples, and error codes. This page orients you to the authentication model and stable contract.

Stable contract

Four things are stable and the integration target:

  • POST /v1/documents — push documents in.
  • GET /v1/documents — read documents out (query by type, status and updated-since; cursor-paginated).
  • GET /api/keys — fetch the trusted public-key list for verifying snapshot signatures.
  • The webhook delivery payload — described in Receiving webhooks.

The /v1/ prefix is a version marker. Breaking changes to any of these arrive under a new prefix (/v2/); the old prefix continues to serve the old shape.

Anything outside the surfaces above is internal and may change without notice.

Authentication

The public surface uses per-tenant API keys. A key looks like vk_<prefix>_<secret>. The prefix is a short visible identifier that maps to a tenant; the secret is a long random suffix. The full key is shown to the operator exactly once at creation. Only the hash is stored. Lost keys must be reissued.

To use a key:

Authorization: Bearer vk_abc123_J8YOBfm5LDpFwQ...

Tenant identity is taken from the key — don't include a tenant id in the body, it would be ignored.

Scopes

Each key declares a set of scopes. The current scope set:

  • documents:createPOST /v1/documents.
  • publisher:readGET /v1/documents, and the publisher-facing reads under /v1/publisher/* used by custom sites.
  • phrases:readGET /v1/phrases.
  • phrases:writePUT /v1/phrases, POST /v1/phrases/import, DELETE /v1/phrases/:id.

New endpoints declare their own scopes; an existing key keeps working until you choose to grant it the new scope.

Where keys come from

A tenant's system-admin creates a key in the Settings → API keys view. The key value is copied off the success screen at creation; the platform never stores or returns the plaintext again.

Pushing documents in

POST /v1/documents
Authorization: Bearer vk_abc123_J8YOBfm5LDpFwQ...
Content-Type: application/json

{
  "version": 1,
  "payload": {
    "type": "core/newswire",
    "title": "Cabinet reshuffle expected ahead of Thursday vote",
    "content": {
      "text": "..."
    },
    "meta": {
      "source": "your-importer"
    }
  }
}

The payload shape matches the document type's schema (see Document formats). The platform fills in id, meta.status (always draft on intake), meta.workflowId, meta.workflowVersion, and any other fields the workflow runtime owns.

The full request, response, and error reference is in API reference.

Receiving published content

Tidenda does not expose a polled fetch on the public surface. Snapshots arrive at a registered endpoint as signed webhooks when a document reaches a snapshotted status (usable or withheld). The payload includes the full snapshot JSON inline, an Ed25519 signature covering it, and an HMAC over the request body.

See Receiving webhooks for the receiver contract and Signing and signatures for the verification recipe.

Signing keys

Public, unauthenticated:

GET /api/keys

Returns the current active key id and a list of trusted public keys (active plus historical). Cache the response for around an hour; the keys change rarely. The keyId on each delivered snapshot signature picks the right key from the list.

See Signing and signatures for the full verification flow, key rotation behaviour, and sample code.

Rate limits

Two ceilings apply:

  • Per-IP volumetric ceiling — applied before key validation to shed pre-auth load.
  • Per-key budget — per-tenant ceiling on writes.

When a limit is hit the response is 429 Too Many Requests with a Retry-After header (seconds) and an envelope describing which limit was hit. The exact ceilings are operator-tunable per deployment.

Where to go next