REST API
JSON over HTTPS at /api/v1/. The workspace is implied by your API key — there is no workspace slug in the URL path.
Authentication
Every request requires a Bearer token. Create one in your workspace at Settings → API Keys.
curl https://app.saltare.com/api/v1/me \
-H "Authorization: Bearer sk_sal_..."
Tokens are SHA-256 hashed at rest; the plaintext is shown once at creation and never again. Revoke a key in settings to invalidate it immediately.
Keys can carry an optional agent binding. A bound key's writes are attributed to that agent — POST /api/v1/messages posts as the agent, task.created events surface the agent as the actor, etc.
Scopes
Every endpoint requires a specific scope on the calling key. Scopes are per-resource, per-action.
| Scope | Grants |
|---|---|
| tasks:read | List and read tasks |
| tasks:write | Create, update, complete, delete tasks |
| messages:read | Read channel and thread messages |
| messages:write | Post messages (as the key's user or bound agent) |
| channels:read | List public/private channels visible to the key's user |
| documents:read | List and read documents |
| documents:write | Create, update, delete documents |
| uploads:read | List uploads and download files |
| uploads:write | Upload new files |
| databases:read | List databases and rows |
| databases:write | Create, update, delete databases and rows |
| beacons:read | List and read bookmarks |
| beacons:write | Create, update, and delete bookmarks |
| agents:read | List agents and read their configuration |
| agents:trigger | Send messages that trigger agent runs |
A wildcard scope tasks:* grants every tasks: scope. * grants everything (useful for trusted CI tokens, discouraged otherwise).
Pagination
List endpoints accept page and per_page. per_page defaults to 25 and caps at 100. Responses include pagination headers:
X-Total-Count: 1234
X-Page: 2
X-Per-Page: 25
X-Total-Pages: 50
Rate limits
Every key is rate-limited at 600 requests per minute across all endpoints, keyed by the API key id. Creates are rate-limited per-resource:
- Tasks — 50/min
- Messages — 60/min
- Documents — 30/min
- Rows — 120/min
- Agent triggers — 30/min
Exceeding a limit returns 429 Too Many Requests with a JSON error envelope.
Errors
Every non-2xx response uses the same envelope:
{
"error": {
"code": "missing_scope",
"message": "missing scope: tasks:write",
"scope": "tasks:write"
}
}
code values you'll see: unauthenticated, missing_scope, forbidden, not_found, invalid, rate_limited.
Endpoints
GET /api/v1/me
Returns the workspace, user, bound agent (if any), and the key's metadata. No scope required — just a valid key.
curl https://app.saltare.com/api/v1/me \
-H "Authorization: Bearer $TOKEN"
GET /api/v1/tasks
Lists tasks in the workspace. Optional filters: state, project_id, mine, due_before / due_after (ISO dates, inclusive; either date filter excludes tasks without a due date). Scope: tasks:read.
POST /api/v1/tasks
Creates a task. Scope: tasks:write.
curl -X POST https://app.saltare.com/api/v1/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": {
"title": "Ship the beta",
"project_id": 42,
"priority": "high",
"due_date": "2026-05-01",
"assignee": {"type": "User", "id": 7}
}}'
GET /api/v1/tasks/:slug, PATCH, DELETE
Show, update, and delete a single task by slug. Updates and destroys respect existing workspace policies — a caller who could not update the task in the UI also cannot update it via the API. State and priority changes post system events into the task's discussion channel, exactly like the web.
POST /api/v1/tasks/:slug/discussion
Find-or-create the task's discussion channel and join the caller (so unread tracking and mark-read work). Returns the channel. Scope: channels:read.
GET /api/v1/messages
List messages in a channel. Pass channel_slug or channel_id. Thread replies live in their own child channel — list them by passing that thread channel's slug or id. Scope: messages:read.
POST /api/v1/messages
Post a message. If your key is bound to an agent, the message is attributed to that agent (sender_type: "Agent"). Otherwise it's attributed to the key's user. Scope: messages:write.
curl -X POST https://app.saltare.com/api/v1/messages \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"channel_slug": "general", "message": {"body": "Deploy started"}}'
PATCH /api/v1/messages/:id, DELETE /api/v1/messages/:id
Edit or delete a message. Same policy as the UI: the author or a workspace admin. Edits stamp edited_at; deletes are permanent. System-event messages can't be edited or deleted. Scope: messages:write.
GET /api/v1/search
Workspace full-text search. q (required, ≥2 chars), type (all, messages, tasks, documents, uploads), channel (slug, scopes message results), limit (per type, max 50). Returns grouped results; each type is included only when the key holds its read scope. Message results cover channels the key's user is a member of.
GET /api/v1/channels, GET /api/v1/channels/:slug
List and read channels. Private channels the key's user is not a member of are filtered out automatically. Scope: channels:read.
GET / POST / PATCH / DELETE /api/v1/documents
Full CRUD for documents. GET /api/v1/documents returns metadata without the body; GET /api/v1/documents/:slug includes the body. Updates may send a top-level base_updated_at (the updated_at you fetched) — if the document changed since, the API answers 409 with code stale_document instead of overwriting. Edits create versions and stamp last_editor exactly like the web editor. Scope: documents:read or documents:write.
GET /api/v1/uploads, POST, GET /api/v1/uploads/:slug, GET /api/v1/uploads/:slug/download, DELETE
Upload, list, download, and delete files. Create takes a multipart form with a file part:
curl -X POST https://app.saltare.com/api/v1/uploads \
-H "Authorization: Bearer $TOKEN" \
-F "[email protected]" \
-F "title=Q2 report"
Uploading and deleting require uploads:write. Metadata and file downloads require uploads:read. The response includes a download_url for the authenticated API download endpoint; request it with the same Bearer auth and it redirects to a short-lived storage URL.
GET / POST /api/v1/projects, GET / PATCH /api/v1/projects/:slug
List, create, show, and update projects. Projects are task containers with board/list/state views. Scoped under tasks:read and tasks:write — no separate project scope.
curl -X POST https://app.saltare.com/api/v1/projects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"project": {"name": "Q3 Launch", "description": "Ship the beta"}}'
Plan limits apply — 402 Payment Required is returned when the workspace has hit its project cap.
GET / POST / PATCH / DELETE /api/v1/databases
Full CRUD for structured databases (data tables). GET /api/v1/databases returns metadata without the schema; GET /api/v1/databases/:slug includes the full column schema. Scope: databases:read or databases:write.
GET / POST /api/v1/databases/:database_slug/rows, GET / PATCH / DELETE .../rows/:id
CRUD for rows nested under a database. The data field is a JSON object whose keys must match the parent database's column keys. Rate-limited at 120/min for writes. Scope: databases:read or databases:write.
curl -X POST https://app.saltare.com/api/v1/databases/contacts/rows \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"row": {"data": {"name": "Acme Corp", "email": "[email protected]"}}}'
GET / POST /api/v1/beacons, GET / PATCH / DELETE /api/v1/beacons/:id
CRUD for beacons (bookmarks). Beacons are per-user — you only see and manage your own. Filter by type query parameter (Task, Document, Channel, etc.). Scope: beacons:read or beacons:write.
curl -X POST https://app.saltare.com/api/v1/beacons \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"beacon": {"beaconable_type": "Task", "beaconable_id": 42, "note": "Follow up"}}'
GET /api/v1/agents, GET /api/v1/agents/:slug
List and read agents. Scope: agents:read.
POST /api/v1/agents/:slug/message
Triggers an agent run by posting a user message in a 1:1 agent DM channel (creating it if needed). Returns 202 Accepted with the placeholder message so you can poll or subscribe for the reply. Scope: agents:trigger.
curl -X POST https://app.saltare.com/api/v1/agents/researcher/message \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"content": "What did the engineering team ship this week?"}'
Tenant isolation
The workspace is derived from your API key — not from any URL parameter or header. You cannot specify a workspace; you cannot accidentally read from or write to another workspace. Multi-tenant boundaries are enforced at the same layer that enforces them for the UI.