Silo HTTP API: REST endpoints for content and media
Complete REST API reference for Silo. JSON everywhere, clean routes under /api, no URL versioning. Authenticate with Authorization: Bearer or X-Api-Key.
Use this file to discover all available pages before exploring further.
Silo speaks JSON everywhere. All routes live under /api, CORS is enabled for /api/*, and there is no URL versioning — a breaking change is a release-note event and the data format carries its own version. Present an API key as Authorization: Bearer <key> or X-Api-Key: <key>.
Rename a project, with claim rewrite (?dry_run=true)
DELETE
/api/projects/{project}
Delete a project, its environments, and their collections (?force=true)
GET / POST
/api/projects/{project}/envs
List environments / create {id}
PATCH
/api/projects/{project}/envs/{env}
Rename an environment (?dry_run=true)
DELETE
/api/projects/{project}/envs/{env}
Delete an environment and its collections (?force=true)
POST
/api/projects/{project}/envs/{env}/copy
Copy another environment of this instance into it
/environments is accepted anywhere /envs appears. Collection, entry, and environment-copy routes are scoped to a (project, environment) pair. Everything else is instance-wide.
Pass ?variables=raw on an entry or search read to receive stored text with {{NAME}} references unresolved. An editor needs this so a form cannot save a resolved value over the reference a user typed.
PUT and DELETE on an entry require the revision you last read, supplied as If-Match: "<rev>" or ?rev=<n>. A mismatch returns 409 conflict. See Optimistic concurrency below.
The engine field is fts5 when SQLite’s full-text index answered, or scan when the portable engine walked the entries. truncated is only true for a scan result, and it means total counts only what was examined. Omit sort to rank by relevance; adding a sort overrides relevance ranking.
Stream a media asset — no authentication required, no /api/ prefix
The file is read from the store as it is sent and never held whole. A single Range: bytes=… header answers 206 Partial Content with Content-Range; a range past the end of the file answers 416. A whole (non-range) response carries no Content-Length. Every response carries X-Content-Type-Options: nosniff and Content-Security-Policy: sandbox. Images, video, audio, and PDF are served inline; SVG and all other types are sent as attachment so a browser downloads them rather than rendering them as a page. An <img> tag still displays an attachment.
PUT and DELETE on an entry require the revision you expect. Supply it as If-Match: "<rev>" or ?rev=<n>. Every entry response carries its current rev, so send back the one you read. A mismatch returns 409 conflict, which prevents two editor tabs from overwriting each other silently.
# Read the entry and capture its revcurl http://localhost:8090/api/projects/default/envs/prod/collections/posts/01J8ABCD \ -H "Authorization: Bearer $SILO_KEY"# Replace it, sending the rev you readcurl -X PUT http://localhost:8090/api/projects/default/envs/prod/collections/posts/01J8ABCD \ -H "Authorization: Bearer $SILO_KEY" \ -H "If-Match: \"3\"" \ -H "Content-Type: application/json" \ -d '{"title": "Updated title"}'