Skip to main content

Slog API

Slog is a headless project management system accessible via REST API and the Model Context Protocol (MCP).

Base URL

http://slog.ai

Authentication

All resource endpoints require authentication via an Oauth Bearer token or an API key.

API key authentication

API keys are automatically created by the CLI. They can also be used using the /api-keys endpoints to create additional keys with custom names and expiration dates. To authenticate with an API key include it in the Authorization header, e.g.
Authorization: Basic sk_abc123
This can most easily be done using curl. The colon after the key prevents curl from prompting for a password as well.
curl https://slog.ai/tasks -u sk_abc123:
The API key can also be included in the Slog-Api-Key header:
Slog-Api-Key: <key>

Oauth authentication

Oauth tokens are generally created and handled by other services. If you have a token you can use it directly for API calls using the Authorization header:
Authorization: Bearer <token>

Response Format

Single resources are wrapped in a key matching the resource type. Lists use the plural name with a pagination object.
{ "task": { "id": "...", "title": "..." } }
{
  "tasks": [...],
  "pagination": { "limit": 50, "nextCursor": "task_01jq..." }
}
Errors always use this shape:
{ "error": { "message": "Field \"foo\" is not filterable" } }

Resources

ResourceDescription
TasksPrimary unit of work
ProjectsGroups tasks together
TeamsOrganizational unit; defines task reference prefixes (e.g. PLAT-42)
MilestonesTime-boxed goals

Query Parameters

All list endpoints support the following query parameters.

Field Selection

Request only the fields you need. Nested relations use brace notation.
GET /tasks?fields=id,identifier,title,assignee{id,name}
If omitted, each resource returns a minimal default set (e.g. id, identifier, title, status for tasks).

Return a single record by ID or reference

GET /tasks/123
GET /tasks/PLAT-123

Filtering

Filters use the form filter[op][field]=value.
OperatorDescription
eqExact match
neqNot equal
gt / gteGreater than / greater than or equal
lt / lteLess than / less than or equal
containsCase-insensitive substring
startsWithCase-insensitive prefix
inMatch any of a comma-separated list
Multiple filters are combined with AND:
GET /tasks?filter[eq][status]=TODO&filter[gte][dueDate]=2026-01-01
GET /tasks?filter[in][status]=TODO,IN_PROGRESS
GET /tasks?filter[contains][title]=auth
Only fields marked as filterable for a given resource can be filtered. See each resource’s field table.

Sorting

Sorts use the form sort[field]=direction. Multiple sorts are applied in order.
GET /tasks?sort[dueDate]=asc&sort[priority]=desc
Only fields marked as sortable can be sorted.

Pagination

All list endpoints use cursor-based pagination.
ParameterDefaultMaxDescription
limit50250Number of results per page
cursorID of the last item from the previous page
GET /tasks?limit=20
GET /tasks?limit=20&cursor=task_01jq...
The response pagination.nextCursor is null when there are no more pages.

Batch Requests

Fetch multiple resources in a single round-trip.
POST /batch
Content-Type: application/json

{
  "requests": [
    "/tasks?fields=id,title,status&limit=10",
    "/tasks/PLAT-42?fields=id,title,description,assignee{name}",
    "/projects/proj_01jq.../milestones"
  ]
}
Response:
{
  "results": [
    { "tasks": [...], "pagination": { "limit": 10, "nextCursor": null } },
    { "task": { "id": "...", "title": "...", "description": "...", "assignee": { "name": "..." } } },
    { "milestones": [...], "pagination": { "limit": 50, "nextCursor": null } }
  ]
}
Each result corresponds to the request at the same index. If an individual request fails, that slot contains { "error": { "message": "...", "code": "..." } } and the rest still succeed. Batch supports GET-style requests only (list, get, list sub-resource).

IDs and Identifiers

Every record has an id (a sortable UPID string, e.g. task_01jq...). Tasks additionally have a human-readable identifier generated from the team prefix and an incrementing sequence number (e.g. PLAT-42). The GET /tasks/:id endpoint accepts either format:
GET /tasks/task_01jqabcdef...
GET /tasks/PLAT-42

Authentication Endpoints

These endpoints are outside the main resource router and do not require a pre-existing token.

OAuth

GET /auth/google          Redirect to Google OAuth
GET /auth/google/callback
GET /auth/github          Redirect to GitHub OAuth
GET /auth/github/callback
On success the callback redirects with a JWT:
{ "data": { "token": "eyJ...", "user": { "id": "...", "name": "...", "email": "..." } } }

Current User

GET /auth/me
Authorization: Bearer <token>
{ "data": { "user": { "id": "...", "name": "...", "email": "..." } } }

Logout

POST /auth/logout
Authorization: Bearer <token>
Returns 204 No Content.

API Key Endpoints

API keys allow programmatic access without OAuth. They are created per-user and scoped to your account.

List Keys

GET /api-keys
Authorization: Bearer <token>
{
  "data": [
    {
      "id": "key_01jq...",
      "name": "CI pipeline",
      "prefix": "sl_abc",
      "lastUsedAt": "2026-04-25T10:00:00Z",
      "expiresAt": null,
      "createdAt": "2026-01-01T00:00:00Z"
    }
  ]
}
Full key values are never returned after creation.

Create Key

POST /api-keys
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "CI pipeline",
  "expiresAt": "2027-01-01T00:00:00Z"
}
expiresAt is optional. The full key is returned only in this response:
{
  "data": {
    "key": "sl_abc.secretpart...",
    "prefix": "sl_abc",
    "name": "CI pipeline"
  }
}

Delete Key

DELETE /api-keys/:id
Authorization: Bearer <token>
Returns 204 No Content.