Public API · v1

Build on Workel

Workspace-scoped API keys, a clean REST surface, and signed webhooks — connect your sites, forms, and systems to your Workel workspace.

base URL https://api.workel.com/api/public/v1 auth Bearer wk_… format JSON
01

Quickstart

  1. Create an API key

    In Workel, open Settings → Developers → Create key (workspace owners and admins only). Pick the scopes the integration needs, then copy the wk_… secret — it is shown exactly once.

  2. Confirm the key works
    curl https://api.workel.com/api/public/v1/me \
      -H "Authorization: Bearer wk_YOUR_KEY"

    GET /me returns your workspace, the key's scopes, and its rate-limit state — the "is my key working" endpoint.

  3. Create your first task
    curl -X POST https://api.workel.com/api/public/v1/tasks \
      -H "Authorization: Bearer wk_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: order-4821" \
      -d '{"project_id": "PROJECT_ID", "title_text": "New order #4821"}'

    Find PROJECT_ID via GET /projects. The task appears on the team's board in realtime, attributed to the key's creator with a via API label.

02

Authentication & scopes

Every request carries Authorization: Bearer wk_<secret>. Keys are minted by a workspace owner or admin and act as their creator: writes are recorded and notified as that person (labeled via API). If the creator loses owner/admin membership, the key stops working within ~60 seconds. Keys can be rotated with an overlap window and revoked instantly from the Developers tab — a key can never mint, widen, or manage keys or webhooks itself.

Each key holds only the scopes you grant it:

ScopeGrants
read:projectsList/read projects and their board columns
read:tasksList/read tasks and their comments
read:eventsList calendar events
read:membersList active workspace members (incl. email, for identity mapping)
write:tasksCreate and update tasks
write:eventsCreate calendar events
write:commentsComment on tasks
Keep secrets server-side. Never ship a wk_ key in browser or mobile code. If a key leaks: Developers tab → Revoke (instant), or Rotate with zero grace. Each key's last-used time and IP are shown to help triage.
03

Endpoints

Full request/response schemas, parameters, and examples for every operation live in the API reference (rendered from the OpenAPI 3.1 spec, which is verified against the live route table by an automated parity test).

PathScopeDescription
GET/meKey + workspace + rate-limit state
GET/projectsread:projectsList visible projects with task counts
GET/projects/{id}read:projectsOne project
GET/projects/{id}/cardsread:projectsBoard columns — target one on task create
GET/membersread:membersActive members: id, name, role, email
GET/tasksread:tasksTasks across visible projects; filters: project_id, card_id, completed, due_before/after, updated_since
POST/taskswrite:tasksCreate a task (column optional — defaults to the first open one)
GET/tasks/{id}read:tasksOne task, incl. description and assignees
PATCH/tasks/{id}write:tasksUpdate title, description, priority, due date/time, progress
GET/tasks/{id}/commentsread:tasksA task's comments
POST/tasks/{id}/commentswrite:commentsComment on a task
GET/eventsread:eventsWorkspace + project calendar events in a date window
POST/eventswrite:eventsCreate a calendar event, with optional invitees

Conventions

Errors

Always {"error": {type, code, message, param?, request_id}}. Every response carries an X-Request-Id header — quote it in support requests.

Pagination

Cursor-based: {"data": […], "meta": {"next_cursor"}}. No offset pages — polling under concurrent writes stays consistent.

Idempotency

Every POST honors Idempotency-Key. Same key + same body replays the stored response (Idempotent-Replay: true); same key + different body → 409. Failed attempts are never stored, so retries re-run the work.

Rate limits

Per key and per workspace, never by IP — plus a tighter write budget on POST/PATCH, so a runaway loop can't eat your read capacity. A write spends one of each. 429s name the exceeded limiter and include retry_after; GET /me reports all three.

Tenancy

A key sees exactly one workspace. Resources outside it — or in private/personal spaces — return 404, never 403.

Attribution

Writes appear in Workel as the key's creator, labeled via API, so teammates always know a machine acted.

04

Webhooks

Register HTTPS endpoints from Settings → Developers → Webhooks and Workel calls you when things change — including changes your team makes in the Workel UI, so you never poll. Seven event types:

task.created task.updated task.completed task.deleted comment.created event.created project.created

Deliveries retry with backoff for hours on failure; an endpoint that keeps failing is auto-disabled (re-enable it from the tab, where a full delivery log and a "Send test event" button live). Payloads are deliberately thin — identifiers and safe fields, never free text; fetch detail with your key.

Verify every delivery

Each request is signed over its exact raw body:

Workel-Signature:  t=1755950000,v1=5257a86…   ← HMAC-SHA256(secret, "{t}.{rawBody}")
Workel-Event-Id:   evt_8f3ka92x               ← stable, dedupe on it
Workel-Event-Type: task.completed
// Node — express example (use the RAW body, not parsed JSON)
const crypto = require("crypto");
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  if (Math.abs(Date.now()/1000 - Number(parts.t)) > 300) return false; // 5-min replay window
  const expected = crypto.createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
// PHP
function verify(string $rawBody, string $header, string $secret): bool {
    parse_str(str_replace(',', '&', $header), $parts);
    if (abs(time() - (int) $parts['t']) > 300) return false;
    $expected = hash_hmac('sha256', $parts['t'].'.'.$rawBody, $secret);
    return hash_equals($expected, $parts['v1']);
}

During a signing-secret rotation, deliveries carry v1= entries for both the current and previous secret, so verification never breaks mid-rotation.

05

Go deeper

Full API reference →

Every operation with parameters, request bodies, response schemas, and error shapes.

OpenAPI 3.1 spec ↓

Machine-readable. Generate clients or diff releases.

Postman collection ↓

All 13 requests, grouped and pre-authed with {{apiKey}}; POSTs carry auto-generated idempotency keys.

Postman environment ↓

Base URL + key + id variables. Import both, paste your wk_… key, run Confirm a key is working.