---
title: "HTTP API"
description: "Authenticate, upload bytes, publish a version, and read shared history."
---

<!-- Generated from apps/cli/README.md and knowledge --help. -->

Use `Authorization: Bearer $KNOWLEDGE_TOKEN` against the same HTTPS origin that
issued the credential. HTTP is allowed only for loopback development. The CLI
refuses redirects, avoiding credential forwarding to a different destination.
Browser session authentication continues to work on these same resources.

| Request | Result |
| --- | --- |
| `GET /api/projects/:id/commits?at=` | `{version}` for current main, or null for an empty project |
| `GET /api/projects/:id/commits?at=VERSION` | Immutable snapshot, including files and parent ID |
| `GET /api/projects/:id/commits` | `{commits}` ordered by parent ancestry, newest first |
| `GET /api/projects/:id/files?at=VERSION&path=PATH` | Exact file bytes; query parameters must be URL encoded |
| `POST /api/projects/:id/files` | Upload raw bytes with URL-encoded `x-file-path` and `content-type`; returns path/hash/size/mediaType plus upload proof for bearer callers |
| `POST /api/projects/:id/commits` | Publish the JSON request below and return the immutable version receipt |
| `GET /api/projects/:id/events` | SSE current-head notifications; reconnect and reread after disconnect |
| `POST /api/projects/:id/access` | Browser-only, same-origin credential issuance; used by `/cli` |

```json
{
  "expectedHead": "VERSION_USED_AS_BASE",
  "operationId": "client-generated-unique-id",
  "summary": "Add research",
  "changes": [{
    "path": "notes.md",
    "hash": "SHA256_RETURNED_BY_UPLOAD",
    "size": 42,
    "mediaType": "text/plain",
    "proof": "PROOF_RETURNED_BY_UPLOAD"
  }],
  "removed": []
}
```

Use an empty expected head for an empty baseline. Reuse the exact operation ID
and intent after uncertain failure; a different intent or actor cannot reuse an
accepted operation. Upload proof is scoped to account/project/bytes, validated
before publication and excluded from stored manifests. Arbitrary account-wide
hashes are not sufficient. Author and account come from verified credentials.
For restore, send `restoreId` instead of `changes`; the expected head must match
exactly. Restore appends a version.

Errors carry an `error` message (coordination failures also include their Ablo
`code`). Status 400 is invalid input, 401 authentication required, 403 denied
upload proof/origin, 404 missing version/project/file, 409 conflict/ownership
failure, 413 oversized body and 503 unavailable or uncertain storage outcome.
Publication JSON is bounded to 2 MiB. An upload alone never advances main.
History is unpaginated in this initial bounded product; SSE is invalidation,
not an event-replay guarantee. Shared wire types live in `@knowledge/contracts`.
