daybook/docs/api.md
ritte 37cd96fa7a Add multi-user auth, settings area, and unified label feed
Turns Daybook into a multi-user tool and reworks the feed UI:

- Multi-user: Django session login (username/password + remember-me),
  per-user personal API tokens (DRF authtoken), whole app gated behind
  login. Each artifact has an owner; feeds are private per user.
- Rename persona -> source (free-text label for whatever posted an
  artifact: agent-profile, script, or person). source + tags form one
  unified label system; single label filter matches tags, source has
  its own filter/column/sidebar.
- Settings area (gear/user menu, top-right): Account (view/regenerate
  token) and Demo Data (in-app button seeding one artifact per kind).
  User account management via Django admin (superusers only).
- Feed rework: rolling 7-day window with sticky inline date separators
  and infinite scroll; left sidebar is now a Sources list with per-source
  unread counts; artifact type shown as text after title; source in its
  own column.
- Unread tracking: read_at on Artifact, marked read when previewed,
  bold titles for unread, sidebar unread badges, old banner removed.
- Tag colours: global shared registry, hash-based default with optional
  per-tag override via tag_colors on POST/PATCH.
- Ingest: token resolves owner; same source name under two users' tokens
  stays isolated. daybook_post.py renamed persona->source, requires a
  real token, --check accepts a bearer token. Docs updated to match.

Migrations 0004-0008 rename persona, add owner (nullable -> backfill to
first superuser -> required), and add read_at. Fresh installs no-op the
backfill.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 09:06:46 +02:00

10 KiB

Daybook API

Daybook is built around agents publishing artifacts. Agents send content; Daybook assigns IDs, stores files, normalizes browser views, and indexes metadata.

Base URL while running locally:

http://127.0.0.1:8001

Authentication for write endpoints (POST/PATCH) — a personal per-user API token, from Settings → Account in the app:

Authorization: Bearer <your-personal-token>

Every artifact belongs to the user whose token authenticated the POST — feeds are private per user.

Read endpoints for artifacts (GET /api/artifacts, GET /api/artifacts/{id}) require a logged-in browser session, not a bearer token — visit the app in a browser and log in first.

GET /api/capabilities accepts either a session or a bearer token, so agents can check connectivity before posting.

Artifact Kinds

Currently supported kinds:

Kind Upload type Rendered behavior Raw support
html_fragment JSON text Wrapped with a Daybook theme No
html_full JSON text Stored as complete HTML Yes
markdown JSON text Rendered to HTML and original .md is preserved Yes
deck JSON text Wrapped as a simple HTML slide/deck page No
image Multipart file Browser image preview No
pdf Multipart file Browser PDF preview No
binary Multipart file Stored and downloadable No

Source Fields

Every artifact can identify who or what produced it. source is a free-text label — an agent-profile name, a script name, a person's name, whatever posted it.

In the feed UI, source gets its own column and drives the left sidebar; tags render as coloured chips next to the title. The API exposes both separately, plus a combined labels list (source + tags, each with a resolved colour) for convenience.

Field Required Default Notes
source Yes None Label identifying what posted this, slugged for storage.
source_type No agent One of agent, person, system.
source_name No source Human-readable source name.
source_id No Empty string Stable external agent/person/system ID.
metadata No {} Free-form object for model/run/tool metadata.

Example source metadata:

{
  "source": "hermes-research",
  "source_type": "agent",
  "source_name": "Hermes Agent",
  "source_id": "agent-hermes-prod",
  "metadata": {
    "model": "gpt-5.5",
    "run_id": "run-2026-06-10-001",
    "tool": "nightly-brief"
  }
}

Create Text Artifact

Endpoint:

POST /api/artifacts
Content-Type: application/json
Authorization: Bearer <your-personal-token>

Required fields:

Field Notes
source Required.
kind One of html_fragment, html_full, markdown, deck.
title Required.
content Required text content.

Optional fields:

Field Notes
theme Defaults by kind. Common values: report, tasks, deck, base.
tags Array or comma-separated string.
tag_colors Object mapping tag name → #RRGGBB. Sets a colour override for those tags.
source_prompt Prompt or instruction that led to the artifact.
source_type Defaults to agent.
source_name Defaults to source.
source_id Optional stable source ID.
metadata Optional object.

Tag colours

Tags are coloured automatically: the server hashes the tag name into a fixed palette, so the same tag always gets the same colour without agents doing anything.

To force a specific colour, pass tag_colors:

{"tags": ["urgent", "billing"], "tag_colors": {"urgent": "#d1453b"}}

Colour overrides are global and shared across all users — setting a colour for urgent changes it everywhere, for everyone. Values must be #RRGGBB; anything else returns 400. Overrides can also be set via PATCH on an existing artifact.

PowerShell example:

$body = @{
  source = "hermes-research"
  source_type = "agent"
  source_name = "Hermes Agent"
  source_id = "agent-hermes-local"
  kind = "markdown"
  theme = "report"
  title = "Morning Brief"
  content = "# Morning Brief`n`n- Review overnight output`n- Plan next actions"
  tags = @("brief", "daily")
  source_prompt = "Prepare a concise morning brief."
  metadata = @{
    model = "gpt-5.5"
    run_id = "brief-001"
  }
} | ConvertTo-Json -Depth 5

Invoke-RestMethod `
  -Uri "http://127.0.0.1:8001/api/artifacts" `
  -Method Post `
  -Headers @{ Authorization = "Bearer <your-personal-token>" } `
  -ContentType "application/json" `
  -Body $body

Response shape:

{
  "id": "a1b2c3d4",
  "url": "http://127.0.0.1:8001/a/a1b2c3d4",
  "api_url": "http://127.0.0.1:8001/api/artifacts/a1b2c3d4",
  "raw_url": "http://127.0.0.1:8001/a/a1b2c3d4?raw=1",
  "raw_available": true,
  "source": "hermes-research",
  "title": "Morning Brief",
  "kind": "markdown",
  "theme": "report",
  "day": "2026-06-10",
  "created_at": "2026-06-10T15:00:00+00:00",
  "tags": ["brief", "daily"],
  "labels": [
    {"name": "hermes-research", "color": "#4f6df5"},
    {"name": "brief", "color": "#e0578c"},
    {"name": "daily", "color": "#2fa88f"}
  ],
  "source_prompt": "Prepare a concise morning brief.",
  "source_type": "agent",
  "source_name": "Hermes Agent",
  "source_id": "agent-hermes-local",
  "metadata": {
    "model": "gpt-5.5",
    "run_id": "brief-001"
  },
  "read_at": null
}

read_at is null until the artifact is opened in the feed viewer, then holds the timestamp it was first read. Unread artifacts render in bold and count toward the per-source unread badges in the sidebar.

Create File Artifact

Endpoint:

POST /api/artifacts
Content-Type: multipart/form-data
Authorization: Bearer <your-personal-token>

Required fields:

Field Notes
source Required.
title Required.
file Required file part.

File kind is inferred from content type:

Content type Artifact kind
image/* image
application/pdf pdf
Anything else binary

PowerShell/curl example:

curl.exe -X POST "http://127.0.0.1:8001/api/artifacts" `
  -H "Authorization: Bearer <your-personal-token>" `
  -F "source=orion" `
  -F "source_type=agent" `
  -F "source_name=Orion Agent" `
  -F "title=Architecture Diagram" `
  -F "tags=diagram,architecture" `
  -F "file=@C:\path\to\diagram.svg;type=image/svg+xml"

List Artifacts

Endpoint:

GET /api/artifacts

Requires a logged-in browser session. Returns only the current user's artifacts.

Supported filters (combine freely; all are ANDed):

Query param Meaning
source Exact match on the artifact's source.
label Matches any of the artifact's tags.
day Exact day, YYYY-MM-DD.
kind Exact artifact kind.
q Case-insensitive title fragment.

source and label are separate filters: source matches only the source field, label matches only the tags list.

Examples:

Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts"
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts?source=hermes-research"
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts?kind=markdown&q=brief"
Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts?day=2026-06-10&label=daily"

Get Artifact Metadata

Endpoint:

GET /api/artifacts/{id}

Example:

Invoke-RestMethod "http://127.0.0.1:8001/api/artifacts/a1b2c3d4"

Update Artifact Metadata

Endpoint:

PATCH /api/artifacts/{id}
Content-Type: application/json
Authorization: Bearer <your-personal-token>

Editable fields:

Field Notes
title Must not be blank.
tags Array or comma-separated string.
tag_colors Object mapping tag name → #RRGGBB. Updates the global colour registry.
source_prompt String.
source_type agent, person, or system.
source_name String.
source_id String.
metadata Object. Replaces existing metadata.

You can only PATCH artifacts your own token owns; others return 404.

Not editable yet:

Field Reason
content Content replacement/revisions are intentionally deferred.
source Affects storage layout and feed identity.
kind Affects renderer and storage behavior.
day Affects time-ordered feed and storage layout.

PowerShell example:

$body = @{
  title = "Morning Brief - Reviewed"
  tags = @("brief", "daily", "reviewed")
  metadata = @{
    model = "gpt-5.5"
    run_id = "brief-001"
    reviewed = $true
  }
} | ConvertTo-Json -Depth 5

Invoke-RestMethod `
  -Uri "http://127.0.0.1:8001/api/artifacts/a1b2c3d4" `
  -Method Patch `
  -Headers @{ Authorization = "Bearer <your-personal-token>" } `
  -ContentType "application/json" `
  -Body $body

Rendered And Raw Content

Rendered browser view:

GET /a/{id}

Raw/source view:

GET /a/{id}?raw=1

Raw currently exists for:

Kind Raw content
markdown Preserved .md source.
html_full Stored HTML file served as text.

If raw content is unavailable, Daybook returns a small HTML fallback page.

Capabilities

Endpoint:

GET /api/capabilities

Example response:

{
  "artifact_kinds": ["html_fragment", "html_full", "markdown", "deck", "image", "pdf", "binary"],
  "filters": ["source", "label", "day", "kind", "q"],
  "views": ["rendered", "raw"],
  "raw_supported_for": ["markdown", "html_full"],
  "source_types": ["agent", "person", "system"],
  "auth": "bearer_token"
}

Agents should call this endpoint if they need to adapt to the current Daybook instance, or simply to check the instance is reachable and their token is valid. Unlike the other read endpoints, it accepts a bearer token as well as a browser session:

curl -H "Authorization: Bearer <your-personal-token>" http://127.0.0.1:8001/api/capabilities

Current MVP Constraints

  • Each user has a personal ingest API token; there is no shared token.
  • Reader/browser access requires login; feeds are private per user.
  • Content modification/revisions are deferred.
  • Office document conversion is not implemented.
  • Manual browser upload/drag-and-drop is deferred.
  • Pagination/cursors are not implemented yet.