---
name: context-log
description: Reference for the Context Log API (v1) -- topics, entries, uploads, context retrieval, and API key management. Use when the user references their saved notes, project context, or topics they keep in Context Log, or wants to call the API directly.
---

# Context Log API

Context Log stores the user's Markdown notes, organized into topics. This covers every `/api/v1` endpoint: auth, request/response shapes, and query params.

## Auth

The API key is in the `CONTEXT_LOG_API_KEY` environment variable. If it is not set, ask the user to create a key at `https://context.shahram.dev/app/keys` and add it to their environment themselves, rather than pasting it into the chat.

Two auth methods:

- `X-API-Key: sk_...` header -- works on Topics, Entries, Uploads, and read-only `GET /api/v1/context`. Scoped to the topics assigned to the key: `GET /api/v1/topics` returns only those topics, and any topic or entry outside the key's scope returns `404`. `POST /api/v1/topics` creates a topic and adds it to the key's scope automatically.
- Dashboard session cookie -- works on everything above (unrestricted across the account), plus `/api/v1/keys*`. Key management is session-only; an API key gets `401` there.

| Endpoint | X-API-Key | Session |
| --- | --- | --- |
| `GET/POST /api/v1/topics` | yes | yes |
| `PUT/DELETE /api/v1/topics/:id` | yes | yes |
| `GET/POST /api/v1/topics/:id/entries` | yes | yes |
| `PUT/DELETE /api/v1/entries/:id` | yes | yes |
| `POST /api/v1/uploads/presign` | yes | yes |
| `GET /api/v1/context` | yes | yes |
| `GET/POST /api/v1/keys`, `PUT/DELETE /api/v1/keys/:id` | no (401) | yes |

## Envelope

Every JSON success response nests its payload under `data`:

```
{ "data": { "<key>": ... }, "pagination"?: { "page": number, "perPage": number } }
```

Errors are always `{ "error": string }`. `DELETE` requests return `204` with no body.

## Response types

```ts
type Pagination = { page: number; perPage: number };
type Topic = { id: string; title: string; slug: string; createdAt: string; updatedAt: string };
type EntryAttachment = { filename: string; contentType: string; sizeBytes: number; url: string | null };
type Entry = { id: string; topicSlug?: string; content: string; createdAt: string; updatedAt: string; attachments?: EntryAttachment[] };
```

## Endpoints

### List topics

`GET /api/v1/topics?page=&perPage=` -> `{ data: { topics: Topic[] }, pagination }`

```bash
curl -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/topics?page=1&perPage=20"
```

### Create topic

`POST /api/v1/topics` body `{ title }` -> `{ data: { topic: Topic } }` (201)

```bash
curl -X POST -H "X-API-Key: $CONTEXT_LOG_API_KEY" -H "Content-Type: application/json" \
  -d '{"title":"My Project"}' "https://context.shahram.dev/api/v1/topics"
```

### Rename topic

`PUT /api/v1/topics/:id` body `{ title }` -> `{ data: { topic: Topic } }`

```bash
curl -X PUT -H "X-API-Key: $CONTEXT_LOG_API_KEY" -H "Content-Type: application/json" \
  -d '{"title":"Renamed"}' "https://context.shahram.dev/api/v1/topics/<id>"
```

### Delete topic

`DELETE /api/v1/topics/:id` -> `204` (also deletes its entries' attachment files)

```bash
curl -X DELETE -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/topics/<id>"
```

### List entries in a topic

`GET /api/v1/topics/:id/entries` -> `{ data: { entries: Entry[] } }` (all entries for the topic, no pagination)

```bash
curl -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/topics/<id>/entries"
```

### Create entry

`POST /api/v1/topics/:id/entries` body `{ content, attachments? }` -> `{ data: { entry: Entry } }` (201)

```bash
curl -X POST -H "X-API-Key: $CONTEXT_LOG_API_KEY" -H "Content-Type: application/json" \
  -d '{"content":"## Deploy notes\n\nShipped v2."}' "https://context.shahram.dev/api/v1/topics/<id>/entries"
```

`attachments` is an array of `{ attachmentId, key, filename, contentType, sizeBytes }` from a prior `/api/v1/uploads/presign` call.

### Update entry

`PUT /api/v1/entries/:id` body `{ content }` -> `{ data: { entry: Entry } }`

```bash
curl -X PUT -H "X-API-Key: $CONTEXT_LOG_API_KEY" -H "Content-Type: application/json" \
  -d '{"content":"Updated body."}' "https://context.shahram.dev/api/v1/entries/<id>"
```

### Delete entry

`DELETE /api/v1/entries/:id` -> `204` (also deletes any attachment files)

```bash
curl -X DELETE -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/entries/<id>"
```

### Presign an upload

`POST /api/v1/uploads/presign` body `{ files: [{ filename, contentType, sizeBytes }] }` -> `{ data: { uploads: [...] } }`

```bash
curl -X POST -H "X-API-Key: $CONTEXT_LOG_API_KEY" -H "Content-Type: application/json" \
  -d '{"files":[{"filename":"notes.pdf","contentType":"application/pdf","sizeBytes":12345}]}' \
  "https://context.shahram.dev/api/v1/uploads/presign"
```

Each item in `uploads` has `attachmentId`, `key`, `uploadUrl`, and `headers` for a direct `PUT` of the file bytes. Pass `attachmentId`/`key`/`filename`/`contentType`/`sizeBytes` back in the entry's `attachments` array once the upload completes.

### Fetch context (entries)

`GET /api/v1/context` -> json default `{ data: { entries: Entry[] }, pagination }`; `format=markdown` returns `text/markdown`.

Returns entries only. Discover topic slugs with `GET /api/v1/topics` first, then pass them to `/api/v1/context`.

```bash
curl -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/context?topics=alice-my-project&format=json&order=asc&page=2"
```

More variations:

```bash
# All topics on the key, json, newest first (defaults)
curl -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/context"

# Search recent notes since a date
curl -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/context?q=deploy&since=2026-07-01"

# Omit attachment links
curl -H "X-API-Key: $CONTEXT_LOG_API_KEY" "https://context.shahram.dev/api/v1/context?attachments=false"
```

#### Context query params

| Param | Use |
| --- | --- |
| `topics` | Comma-separated topic slugs. Omit for every topic on the key. |
| `q` | Case-insensitive substring match over entry content (`ILIKE`, not fuzzy or ranked). |
| `since` / `until` | ISO dates; filter entries by `createdAt`. |
| `order` | `asc` or `desc` by `updatedAt` (default `desc`, most recently updated first). |
| `page` | Page number, min 1 (default 1). |
| `perPage` | Entries per page, min 1, max 100 (default 50). |
| `format` | `json` (default) or `markdown`. |
| `attachments` | `false` to omit attachment links (default `true`). |

## Key management (session only)

`GET /api/v1/keys` -> `{ data: { apiKeys: Array<{ id, name, keyPrefix, topicIds, lastUsedAt, createdAt }> } }`

```bash
curl -H "Cookie: <session cookie>" "https://context.shahram.dev/api/v1/keys"
```

`POST /api/v1/keys` body `{ name, topicIds: string[] }` -> `{ data: { apiKey: { id, name, key, keyPrefix, topicIds, lastUsedAt, createdAt } } }` (201; raw `key` is shown once and never again)

`PUT /api/v1/keys/:id` body `{ name?, topicIds? }` -> `{ data: { apiKey: { id, name, keyPrefix, topicIds, lastUsedAt, createdAt } } }`

`DELETE /api/v1/keys/:id` -> `204`

## Errors

`401` missing or invalid key/session, `404` unknown topic, entry, or key (topic lookups on `/api/v1/context` include `availableTopics`), `400` invalid body or query params, `409` duplicate topic title.
