Keep Digital Human

API documentation

Register works, check hashes, and list registrations programmatically. Everything the web app can do, the API can do too.

Authentication

Most endpoints require an API key. Generate one from your dashboard. Send it as a Bearer token in the Authorization header:

Authorization: Bearer kdh_your_api_key_here

Keys are shown once on creation — store them securely. You can revoke a key at any time from the dashboard.

Base URL

https://keepdigitalhuman.com/api/v1

Rate limiting

API requests are limited to 60 per minuteper API key. If you exceed this, you'll receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait.

Endpoints

POST/api/v1/registerAuth required

Register a new work. Hash your content client-side and send the hash — we never see the original content.

Request body (JSON)
{
  "title": "My blog post",
  "hash": "a1b2c3d4...64-char SHA-256 hex string",
  "contentType": "text",
  "wordCount": 1200,
  "fileName": null,
  "fileSize": null,
  "mimeType": null,
  "parentId": null
}
Response (201)
{
  "id": "uuid-of-registration",
  "hash": "a1b2c3d4...",
  "title": "My blog post",
  "verifyUrl": "https://keepdigitalhuman.com/verify/uuid"
}
Example (curl)
curl -X POST https://keepdigitalhuman.com/api/v1/register \
  -H "Authorization: Bearer kdh_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My blog post",
    "hash": "a1b2c3d4e5f6...",
    "contentType": "text",
    "wordCount": 1200
  }'
Versioning:To register an updated version of an existing work, pass the original registration's ID as parentId. The new registration will be linked to the original, and both will show a version history on their verification pages.
Hashing tip: Use SHA-256 on the raw text content. In JavaScript: const hash = Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text)))).map(b => b.toString(16).padStart(2, "0")).join("")
POST/api/v1/register/batchAuth required

Register up to 50 works in a single request. Ideal for back-catalogues.

Request body (JSON)
{
  "items": [
    {
      "title": "Chapter 1",
      "hash": "a1b2c3d4...",
      "contentType": "text"
    },
    {
      "title": "Chapter 2",
      "hash": "e5f6a7b8...",
      "contentType": "text"
    }
  ]
}
Response (200)
{
  "total": 2,
  "registered": 2,
  "failed": 0,
  "results": [
    {
      "index": 0,
      "id": "uuid-1",
      "hash": "a1b2c3d4...",
      "title": "Chapter 1",
      "verifyUrl": "https://keepdigitalhuman.com/verify/uuid-1"
    },
    {
      "index": 1,
      "id": "uuid-2",
      "hash": "e5f6a7b8...",
      "title": "Chapter 2",
      "verifyUrl": "https://keepdigitalhuman.com/verify/uuid-2"
    }
  ]
}
Each item accepts the same fields as the single register endpoint (title, hash, contentType, fileName, fileSize, mimeType, parentId). Items with missing title or hash are skipped and reported in the results array with an error field.
GET/api/v1/check/:hash

Check whether a SHA-256 hash matches any registered work. This endpoint is public — no API key needed.

Response (200)
{
  "hash": "a1b2c3d4...",
  "found": true,
  "matches": [
    {
      "id": "uuid",
      "title": "My blog post",
      "contentType": "text",
      "registeredAt": "2026-04-13T16:46:13.000Z",
      "verifyUrl": "https://keepdigitalhuman.com/verify/uuid"
    }
  ]
}
Example (curl)
curl https://keepdigitalhuman.com/api/v1/check/a1b2c3d4e5f6...
GET/api/v1/registrationsAuth required

List your registrations. Supports pagination with limit and offset query parameters.

ParameterDefaultDescription
limit50Number of results (max 100)
offset0Number of results to skip
Response (200)
{
  "registrations": [ ... ],
  "total": 42,
  "limit": 50,
  "offset": 0
}
GET/api/v1/registrations/:idAuth required

Get the full details of a specific registration you own.

Response (200)
{
  "id": "uuid",
  "user_id": "clerk_user_id",
  "title": "My blog post",
  "content_hash": "a1b2c3d4...",
  "content_type": "text",
  "word_count": 1200,
  "file_name": null,
  "file_size": null,
  "mime_type": null,
  "thumbnail_url": null,
  "registered_at": "2026-04-13T16:46:13.000Z"
}

Open standard endpoints

These endpoints support the KDH Provenance Record standard. They're designed for AI platforms and tools that want to check content provenance at scale.

POST/api/v1/verify-bulk

Check up to 100 SHA-256 hashes against the registry in a single request. Public — no auth required.

Request body (JSON)
{
  "hashes": [
    "a1b2c3d4...",
    "e5f6a7b8...",
    "c9d0e1f2..."
  ]
}
Response (200)
{
  "results": {
    "a1b2c3d4...": { "found": true, "matches": [...] },
    "e5f6a7b8...": { "found": false, "matches": [] }
  },
  "checked": 3,
  "matched": 1
}
GET/api/v1/provenance/:id

Get the full machine-readable provenance record for a registration. Returns application/kdh-provenance+json.

Response
{
  "@context": "https://keepdigitalhuman.com/standard/v1",
  "@type": "ProvenanceRecord",
  "id": "urn:kdh:uuid",
  "contentHash": { "algorithm": "SHA-256", "value": "..." },
  "title": "...",
  "registeredAt": "2026-04-13T16:46:13.000Z",
  "registry": {
    "name": "Keep Digital Human",
    "verifyUrl": "https://keepdigitalhuman.com/verify/uuid"
  }
}

Error responses

All errors return a JSON object with an error field:

{
  "error": "Description of what went wrong"
}
StatusMeaning
400Bad request (missing or invalid parameters)
401Unauthorised (missing or invalid API key)
404Resource not found
429Rate limit exceeded
500Server error

Browser extension

The Chrome extension uses this API under the hood. Install it to register works with a right-click, check pages against the registry, and get prompted when publishing on Medium, WordPress, or Ghost.

To install: open chrome://extensions, enable Developer mode, click "Load unpacked", and select the extension/ folder from the GitHub repository.

Need help?

Open an issue on GitHub or get in touch.