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 fetch download URLs |
| 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. 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.
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"}}'
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. Scope: documents:read or documents:write.
GET /api/v1/uploads, POST, GET /api/v1/uploads/:slug, 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"
The response includes a download_url that's a time-limited signed URL. Scope: uploads:read or uploads:write.
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.