Back to API reference
For AI agents
Agent API Brief
Copy this entire brief into your agent's system prompt or integration instructions. It covers authentication, editor content rules, database rows, worked examples, and common mistakes.
Copy-paste integration brief
You integrate with SoLoRecall (SoLoDocs) via the public REST API.
## Links
| Resource | URL |
| --- | --- |
| Human API reference (endpoint explorer) | https://solorecall.com/api |
| Agent brief (copy-paste page) | https://solorecall.com/api/agents |
| Base URL | https://api.solorecall.com |
| OpenAPI JSON (machine-readable contract) | https://api.solorecall.com/documentation/openapi.json |
| Swagger UI (when enabled) | https://api.solorecall.com/documentation |
## Authentication
1. In the SoLoRecall web app: **Settings → API Keys → Create API key**
2. Send the key on every request:
```http
Authorization: Bearer sr_YOUR_KEY_HERE
Content-Type: application/json
```
The raw `sr_...` value is shown only once at creation. Store it securely.
## Critical rules (read before writing content)
1. **Page and row bodies** go in the block's top-level `content` array (JSON array of editor blocks). Do **not** put editor bodies only in `properties.content`.
2. **Row field values** (Status, Agent, Priority, dates, etc.) go in `properties` on the block — not in `metadata`, not embedded in paragraph text.
3. **Use SoLoRecall block type names**, not TipTap/ProseMirror names:
- `paragraph` — not `text`
- `heading-1`, `heading-2`, `heading-3` — not `heading`
- `bulleted-list` — not `bulletListItem` or `bulleted_list_item`
- `numbered-list` — not `orderedListItem`
- `to_do` — not `todo` or `checkbox`
4. **Image blocks** must include a top-level `url`. Do not put the image URL only inside paragraph text.
5. **Database rows**: use `type: "database_row"` and set `database_id` to the parent database block ID.
6. **Database create**: `POST /api/pages` or `POST /api/blocks` with `type: "database"` automatically seeds `database_schemas` with a default `title` column plus `created_time`, `created_by`, `last_edited_time`, and `last_edited_by`. No separate schema bootstrap call is required.
7. **Prefer pages API** for page-like blocks and database rows; use blocks API for child editor blocks under a page.
The backend normalizes aliases on **write** (create/update). The web app also normalizes on **read** so older agent-created pages still render. Always emit canonical types above so stored content stays clean.
## Editor content block schema
Each item in `content` is an object with at least `type` and usually `children`:
```json
{
"type": "paragraph",
"children": [{ "text": "Plain text here." }]
}
```
### Supported types (common)
`paragraph`, `heading-1`, `heading-2`, `heading-3`, `bulleted-list`, `numbered-list`, `image`, `video`, `audio`, `embed`, `to_do`, `quote`, `callout`, `code`, `divider`, `toggle`, `table`
### Headings
```json
{ "type": "heading-2", "children": [{ "text": "Section title" }] }
```
### Images
```json
{
"type": "image",
"url": "https://cdn.example.com/asset.png",
"children": [{ "text": "" }]
}
```
### Lists
```json
{ "type": "bulleted-list", "children": [{ "text": "First item" }] }
```
### Full page body example
```json
[
{
"type": "heading-2",
"children": [{ "text": "Review asset before posting" }]
},
{
"type": "paragraph",
"children": [{ "text": "Fresh asset passed review. Inspect the image below." }]
},
{
"type": "image",
"url": "https://cdn.example.com/asset.png",
"children": [{ "text": "" }]
},
{
"type": "bulleted-list",
"children": [{ "text": "Set Status to Approved if good to publish" }]
}
]
```
## Primary endpoints
| Task | Method | Path |
| --- | --- | --- |
| Create page, database, or row | `POST` | `/api/v1/pages` |
| Update page/row properties or body | `PUT` | `/api/v1/pages/{id}` |
| Fetch one page/row | `GET` | `/api/v1/pages/{id}` |
| List pages (lightweight) | `GET` | `/api/v1/pages` |
| Export page as md/html/json | `GET` | `/api/v1/pages/{id}/export` |
| Create child block under a page | `POST` | `/api/v1/blocks` |
| Update any block | `PATCH` | `/api/v1/blocks/{id}` |
| Read nested block tree | `GET` | `/api/v1/blocks/{id}/hierarchy` |
| Upload media | `POST` | `/api/v1/media/upload` |
| Get signed media URL | `GET` | `/api/v1/media/{id}/url` |
| List reminders | `GET` | `/api/v1/reminders` |
| Create reminder | `POST` | `/api/v1/reminders` |
| Update reminder | `PUT` | `/api/v1/reminders/{reminder_id}` |
| List due reminders | `GET` | `/api/v1/reminders/due` |
| Preview Quick Task from natural language | `POST` | `/api/v1/tasks/from-natural-language/preview` |
| Create Quick Task from natural language | `POST` | `/api/v1/tasks/from-natural-language` |
| List Quick Task database options | `GET` | `/api/v1/tasks/database-options` |
Quick Task parsing uses a dedicated structured-output model rather than the user's main chat model. The default Quick Task model is OpenAI Codex `gpt-5.5` (`TASK_CAPTURE_PROVIDER=openai-codex`, `TASK_CAPTURE_MODEL=gpt-5.5`), and Settings -> Quick Task stores per-user `quickTaskProvider` / `quickTaskModel` overrides. Operators can still override the server fallback with `TASK_CAPTURE_PROVIDER` and `TASK_CAPTURE_MODEL`. Preview/create accept `{ "text": "...", "timezone": "America/Detroit", "reference_at": "ISO-8601", "database_id": "..." }` and return parsed title, due date, reminder offset, ambiguities, and `target_database`.
Users can set `quickTaskDatabaseId` in workspace settings (Settings -> Quick Task) to choose which database receives captured tasks. The database-options endpoint lists visible database blocks plus page wrappers that link to, embed, or parent a database; it loads direct database blocks separately from page wrappers so Appwrite-compatible query translation cannot hide standalone databases. The endpoint uses the page/sidebar title for display when a page represents the database, while saving the real referenced database id. Options include separate `page_title`, `database_title`, and `title_mismatch` fields so the settings UI can show labels such as `SoLo Tasks (database: Tasks)` when the page name and real database name differ. The endpoint uses the same block visibility rules as the page list (`created_by`, and Appwrite `owner_id`/`user_id` where available) and requests an explicit large option page instead of relying on Appwrite's default page size. The web dialog sends the selected database id explicitly on both preview and create, and the backend still falls back to the saved setting when REST/native callers omit `database_id`. When unset, SoLoRecall uses the user's owned built-in Tasks template database using the same owner-field rules; if none exists yet, it installs the template automatically on the first create. Users can also set `quickTaskSkipPreview` to skip the preview step and create tasks immediately (including widget shortcuts that arrive with `preview=1`). When the selected database uses copied or renamed task columns, task creation reads `database_schemas` and writes the title, status, due date, priority, notes, and reminder sidecar to the selected database's actual schema property ids instead of assuming canonical ids such as `title` or `due`.
| Snooze reminder | `POST` | `/api/v1/reminders/{reminder_id}/snooze` |
| Dismiss reminder | `POST` | `/api/v1/reminders/{reminder_id}/dismiss` |
| Cancel reminder | `POST` | `/api/v1/reminders/{reminder_id}/cancel` |
Full endpoint list: https://solorecall.com/api
## Date property values (due dates, reminders, recurrence)
Database **date** columns store row values in `properties.<property_id>`. Use an ISO string for date-only values, or an object when reminders or repeat rules apply:
```json
{
"start": "2026-06-08T17:00:00.000Z",
"reminder": "at_time",
"reminder_schedules": [
{
"id": "schedule-uuid-1",
"offset": "at_time",
"days_of_week": [2, 5],
"time": "17:00"
},
{
"id": "schedule-uuid-2",
"offset": "1_day_before",
"days_of_week": [2, 5]
}
],
"recurrence": "weekly_on_days",
"recurrence_days_of_week": [2, 5],
"recurrence_end_type": "never"
}
```
| Field | Purpose |
| --- | --- |
| `start` / `end` | Due window (`end` optional). |
| `reminder` | Legacy single offset (`none`, `at_time`, `5_minutes_before`, … `1_week_before`, or `inherit`). |
| `reminder_schedules` | Multiple reminder rules per date column. Each rule has stable `id`, `offset`, optional `days_of_week` (`0=Sun` … `6=Sat`), and optional `time` (`HH:mm`). |
| `recurrence` | Repeat preset: `none`, `daily`, `weekdays`, `weekends`, `weekly`, `weekly_on_days`, `biweekly`, `monthly`, `twice_monthly`, `quarterly`, `yearly`, `custom`, or `inherit`. |
| `recurrence_end_type` | `never`, `on_date`, or `after_count`. |
| `recurrence_end_date` / `recurrence_end_count` | End rule when not `never`. |
| `recurrence_days_of_week` | Weekdays for `weekly_on_days` or custom weekday repeats. |
| `recurrence_days_of_month` | Days for `twice_monthly` (e.g. `[1, 15]`). |
| `recurrence_interval` / `recurrence_unit` | Custom every-N repeats (`unit`: `day`, `week`, `month`, `year`). |
**Important for API-only agents:** saving a row with `PUT /api/v1/pages/{id}` stores the date value on the block but does **not** automatically sync reminder sidecars. The web app syncs sidecars through its own client layer. To deliver notifications, also create or update matching rows via `/api/v1/reminders` (one sidecar per `reminder_schedules[].id`, keyed by `metadata.schedule_id`).
Reminder sidecars are personal (scoped to the API key owner). Responses return `recurrence_rule` as a parsed object when stored as JSON.
## Worked example: create a database row with properties + body
Use when creating approval tasks, status rows, or any database entry with a rich note body.
```bash
curl -X POST "https://api.solorecall.com/api/v1/pages" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"title": "SoLo approval needed - asset review",
"type": "database_row",
"database_id": "DATABASE_BLOCK_ID",
"properties": {
"status": { "name": "Stalled", "color": "blue" },
"agent": { "name": "Vector", "color": "purple" },
"priority": { "name": "High", "color": "red" }
},
"content": [
{
"type": "heading-2",
"children": [{ "text": "Review asset before posting" }]
},
{
"type": "paragraph",
"children": [{ "text": "Fresh asset passed review. Inspect the image below." }]
},
{
"type": "image",
"url": "https://cdn.example.com/asset.png",
"children": [{ "text": "" }]
},
{
"type": "bulleted-list",
"children": [{ "text": "Approve in SoLoRecall if good to publish" }]
}
]
}'
```
Replace `DATABASE_BLOCK_ID` with the actual database block UUID. Property keys must match the database schema (e.g. `status`, `agent`, `priority`).
## Worked example: recurring due date with multiple reminders
Use when a task row should repeat on selected weekdays and fire more than one reminder schedule.
```bash
curl -X PUT "https://api.solorecall.com/api/v1/pages/ROW_BLOCK_ID" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"due": {
"start": "2026-06-10T21:00:00.000Z",
"reminder_schedules": [
{
"id": "a1111111-1111-4111-8111-111111111111",
"offset": "at_time",
"days_of_week": [2, 5],
"time": "17:00"
}
],
"recurrence": "weekly_on_days",
"recurrence_days_of_week": [2, 5],
"recurrence_end_type": "never"
}
}
}'
```
Then sync the notification sidecar (replace property id and schedule id to match your row):
```bash
curl -X POST "https://api.solorecall.com/api/v1/reminders" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"target_block_id": "ROW_BLOCK_ID",
"source_property_id": "due",
"due_at": "2026-06-10T21:00:00.000Z",
"remind_at": "2026-06-10T21:00:00.000Z",
"timezone": "America/Detroit",
"channel": "push",
"recurrence_rule": {
"version": 1,
"preset": "weekly_on_days",
"days_of_week": [2, 5],
"end_type": "never",
"anchor_at": "2026-06-10T21:00:00.000Z"
},
"metadata": {
"version": 1,
"source": "database_date_property",
"schedule_id": "a1111111-1111-4111-8111-111111111111",
"reminder_offset": "at_time",
"reminder_days_of_week": [2, 5],
"reminder_time": "17:00",
"recurrence_preset": "weekly_on_days"
}
}'
```
Dismiss recurring series: `POST /api/v1/reminders/{id}/dismiss` with `{ "scope": "occurrence" }` to skip one fire, or `{ "scope": "series" }` to cancel the full series.
## Worked example: update row status and body
```bash
curl -X PUT "https://api.solorecall.com/api/v1/pages/ROW_BLOCK_ID" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"status": { "name": "Approved", "color": "green" }
},
"content": [
{
"type": "paragraph",
"children": [{ "text": "Approved for publish." }]
}
]
}'
```
`PUT` deep-merges `properties` and replaces `content` when provided.
## Worked example: create a normal page
```bash
curl -X POST "https://api.solorecall.com/api/v1/pages" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"title": "Meeting notes",
"type": "page",
"content": [
{
"type": "heading-1",
"children": [{ "text": "Meeting notes" }]
},
{
"type": "paragraph",
"children": [{ "text": "Discussion summary goes here." }]
}
]
}'
```
## Worked example: upload then embed an image
```bash
# 1. Upload file (multipart)
curl -X POST "https://api.solorecall.com/api/v1/media/upload" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-F "file=@/path/to/image.png"
# 2. Use returned media id or URL in an image block
curl -X PUT "https://api.solorecall.com/api/v1/pages/PAGE_ID" \
-H "Authorization: Bearer sr_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"content": [
{
"type": "image",
"url": "SIGNED_OR_PUBLIC_URL_FROM_UPLOAD",
"children": [{ "text": "" }]
}
]
}'
```
## Common mistakes (avoid these)
| Mistake | Result | Fix |
| --- | --- | --- |
| `type: "heading"` in `content` | Unknown block type in UI | Use `heading-1`, `heading-2`, or `heading-3` |
| `type: "bulletListItem"` | Unknown block type | Use `bulleted-list` |
| Image URL only in paragraph text | Empty image widget | Add `type: "image"` block with top-level `url` |
| Row fields in `metadata` | Dropdowns do not update | Put values in `properties` |
| Body only in `properties.content` | Editor may not render body | Use top-level `content` array |
| Missing `database_id` on row create | Row not linked to database | Set `type: "database_row"` and `database_id` |
| Creating row via `POST /api/v1/blocks` without `database_id` | Orphan row | Prefer `POST /api/v1/pages` for rows |
## Response shape (typical)
```json
{
"id": "uuid",
"type": "database_row",
"database_id": "uuid",
"parent_id": null,
"properties": { "status": { "name": "Stalled", "color": "blue" } },
"content": [{ "type": "paragraph", "children": [{ "text": "..." }] }],
"created_by": "user_uuid",
"last_edited_by": "user_uuid"
}
```
Exact fields vary by endpoint. Use OpenAPI JSON for full request/response schemas.
## When in doubt
1. Check https://solorecall.com/api for the endpoint and method.
2. Use canonical block types and top-level `content`.
3. Put database column values in `properties`.
4. Fetch the created block with `GET /api/v1/pages/{id}` and verify `content` and `properties` before reporting success.