REST API for programmatic access to boop lists and items. Designed for agents, scripts, and integrations to interact without using a browser.
https://<convex-deployment>.convex.site
Two auth modes are supported:
JWT session (for interactive/browser use) via the Authorization header:
Authorization: Bearer <your-jwt-token>
To obtain a JWT token, use the standard auth flow:
POST /auth/initiatewith{ "email": "your@email.com" }POST /auth/verifywith{ "sessionId": "...", "code": "..." }(OTP from email)- Use the returned
tokenin subsequent requests
API key (for unattended agents) via the X-API-Key header:
X-API-Key: pa_live_...
API keys are long-lived credentials scoped to specific actions. Mint one with a
JWT session (see Agent API v1). When an X-API-Key header is
present it takes precedence over the Authorization header. Keys carry scopes
(lists:read, items:read, items:write); a request missing the required
scope returns 401. JWT sessions have full access.
GET /api/agent/lists
Returns all lists the authenticated user has access to.
Response:
{
"lists": [
{
"_id": "abc123...",
"name": "Shopping List",
"ownerDid": "did:webvh:...",
"createdAt": 1704067200000,
"role": "owner"
}
]
}GET /api/agent/lists/:listId
GET /api/agent/lists/:listId/items
Returns a list and all its items.
Response:
{
"list": {
"_id": "abc123...",
"name": "Shopping List",
"ownerDid": "did:webvh:...",
"createdAt": 1704067200000,
"assetDid": "did:peer:..."
},
"items": [
{
"_id": "item123...",
"name": "Milk",
"checked": false,
"createdByDid": "did:webvh:...",
"createdAt": 1704067200000,
"order": 0,
"description": "2% organic",
"priority": "high"
}
],
"role": "owner"
}POST /api/agent/lists/:listId/items
Content-Type: application/json
{
"name": "Buy groceries",
"description": "From Whole Foods",
"priority": "high",
"dueDate": 1704153600000,
"url": "https://example.com"
}
Response (201 Created):
{
"itemId": "item456...",
"item": {
"_id": "item456...",
"name": "Buy groceries",
"checked": false,
"createdByDid": "did:webvh:...",
"description": "From Whole Foods",
"priority": "high",
"dueDate": 1704153600000,
"url": "https://example.com"
}
}PATCH /api/agent/items/:itemId
Content-Type: application/json
{
"checked": true,
"name": "Updated name",
"description": "Updated description",
"priority": "medium",
"dueDate": 1704240000000,
"url": "https://new-url.com"
}
All fields are optional. To clear a field, set it to null:
{
"priority": null,
"dueDate": null
}Response:
{
"success": true,
"item": {
"_id": "item123...",
"name": "Updated name",
"checked": true,
...
}
}DELETE /api/agent/items/:itemId
Response:
{
"success": true
}| Field | Type | Description |
|---|---|---|
name |
string | Item title (required for creation) |
description |
string | Optional notes/details |
checked |
boolean | Whether the item is complete |
priority |
"high" | "medium" | "low" | Priority level |
dueDate |
number | Unix timestamp in milliseconds |
url |
string | Link to PR, URL, or reference |
order |
number | Position in list (lower = higher) |
createdByDid |
string | DID of user who created the item |
checkedByDid |
string | DID of user who checked the item |
createdAt |
number | Creation timestamp |
checkedAt |
number | When item was checked |
| Role | Permissions |
|---|---|
owner |
Full access (read, write, delete, share) |
editor |
Read and write access |
viewer |
Read-only access |
Endpoints for unattended agents: mint API keys (JWT-only), then read and write
boop lists using either a JWT session or an X-API-Key.
Note: the Convex router has no
:parampath segments, so identifiers are passed as query parameters (?keyId=,?listId=) rather than path segments.
Key management always requires a JWT session — you cannot mint or revoke a key with a key.
POST /api/v1/keys— create a key.- body:
{ "label": "CI Agent", "scopes": ["lists:read","items:read","items:write"] } scopesis optional and defaults to["lists:read","items:read","items:write"].- response:
{ "id": "...", "key": "pa_live_...", "prefix": "pa_live_..." } - The raw
keyis returned once and never stored — save it immediately.
- body:
GET /api/v1/keys— list your active keys.- response:
{ "keys": [{ "id", "prefix", "label", "scopes", "createdAt" }] }(never the hash or raw key)
- response:
DELETE /api/v1/keys?keyId=<id>— revoke a key you own.- response:
{ "success": true }
- response:
GET /api/v1/lists— list the caller's lists (lists:read).- response:
{ "lists": [...] }
- response:
GET /api/v1/lists/items?listId=<id>— get a list and its items (items:read).- response:
{ "list": {...} | null, "items": [...] }
- response:
The existing mutation endpoints now also accept an X-API-Key. All require the
items:write scope (there is no lists:write scope yet):
POST /api/lists/create,POST /api/lists/deletePOST /api/items/add,POST /api/items/check,POST /api/items/uncheck,POST /api/items/remove,POST /api/items/reorder
Item writes made via an API key are attributed to the key's owner DID (or its
agentDid, if set). The item-authorship credential remains an unsigned
placeholder — cryptographic signing is out of scope for now.
lists:read— read listsitems:read— read itemsitems:write— create/modify/delete lists and items
All errors return JSON with an error field:
{
"error": "Error message here"
}| Status | Description |
|---|---|
| 400 | Bad request (missing/invalid parameters) |
| 401 | Unauthorized (missing/invalid token) |
| 403 | Forbidden (no access to resource) |
| 404 | Not found |
| 405 | Method not allowed |
| 500 | Server error |
# Get all lists
curl -H "Authorization: Bearer $TOKEN" \
https://your-deployment.convex.site/api/agent/lists
# Get a specific list with items
curl -H "Authorization: Bearer $TOKEN" \
https://your-deployment.convex.site/api/agent/lists/abc123xyz
# Add an item
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "New task", "priority": "high"}' \
https://your-deployment.convex.site/api/agent/lists/abc123xyz/items
# Check off an item
curl -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"checked": true}' \
https://your-deployment.convex.site/api/agent/items/item123xyz
# Delete an item
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
https://your-deployment.convex.site/api/agent/items/item123xyzconst BASE_URL = "https://your-deployment.convex.site";
const TOKEN = "your-jwt-token";
// Get all lists
const lists = await fetch(`${BASE_URL}/api/agent/lists`, {
headers: { Authorization: `Bearer ${TOKEN}` }
}).then(r => r.json());
// Add an item
const newItem = await fetch(`${BASE_URL}/api/agent/lists/${listId}/items`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ name: "New task", priority: "high" })
}).then(r => r.json());
// Check off an item
await fetch(`${BASE_URL}/api/agent/items/${itemId}`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ checked: true })
});