What’s the Process For — API reference
Base URL: https://www.whatstheprocessfor.com/api/v1
This document is designed to be pasted directly into a Claude (or any LLM) conversation alongside your API key. When prompted for a task — “create a process called X”, “find all my processes about Y”, “publish the one about Z” — Claude can read this reference and construct the correct HTTP calls.
How to use this reference with Claude
- Copy the full contents of this page (there’s a “Copy as Markdown” button
at
/developers/api, orcurl https://www.whatstheprocessfor.com/developers/api.md). - Paste it into a Claude conversation as context.
- Tell Claude what you want to do, and give it your API key. It will translate natural-language intent into HTTP calls.
For a deeper integration — tool calls instead of paste-in context — install
the MCP server: see /developers/mcp.
Authentication
All requests require an API key. You can pass it either way:
Authorization: Bearer wtpf_<your-key>
or
X-API-Key: wtpf_<your-key>
Key scopes
Every key has one of two scopes:
user— personal key, acts on the owner’s personal processes (no org)organization— org key, acts in the org with the role of whoever minted it. A key minted by a “member” cannot do things only owners/admins can do, even if the key itself has theprocesses:writepermission.
Minting keys:
- Any signed-in user can mint a personal key at
/account/api-keys. - Owners/admins mint org keys at
/org/<slug>/api-keys. - Members submit a request there; an owner/admin approves, and the plaintext is shown once to the approver to share via a secure channel.
Permission scopes
Each key also carries one or more permission scopes:
| Scope | Lets the key… |
|---|---|
processes:read | List processes, fetch a process + steps |
processes:write | Create, update, publish, upload images via /uploads |
processes:delete | Permanently delete a process |
workflows:read | List workflows, fetch a workflow + ordered processes, list per-user permissions |
workflows:write | Create / edit / reorder workflows, add or remove processes, grant or revoke per-user permissions, mark or unmark training progress |
workflows:delete | Permanently delete a workflow (cascades to its links + progress) |
members:read | List org members and pending invites |
members:write | Update member roles, send invites |
members:delete | Remove members, revoke invites |
Default on creation: processes:read + processes:write. delete,
workflows:*, and members:* are opt-in. Pass them in the scopes array
when creating the key (or when filing a member request for admin approval).
Workflows + members require an organization-scoped key. Personal keys (
scope: user) cannot manage workflows or org membership and will get403 This endpoint requires an organization-scoped API key.
A 403 Missing required scope: <scope> response means the key you’re using
doesn’t carry that scope — ask the key’s owner to mint a new one (or file a
request for admin approval) with the scope you need.
Rate limits
API access is open to every plan. Throttled per plan:
| Plan | Per minute | Per day |
|---|---|---|
| Free | 10 | 200 |
| Trial | 20 | 500 |
| Starter | 30 | 2,000 |
| Team | 60 | 10,000 |
| Business | 300 | 50,000 |
| Enterprise | 1,000 | unlimited |
Both windows apply. On overflow you get 429 Too Many Requests with:
{
"error": "Rate limit exceeded",
"window": "minute",
"limit": 10,
"reset_at": "2026-04-23T18:41:00.000Z",
"retry_after_seconds": 32
}
…and headers:
Retry-After: 32
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1745433660
Claude, when you see a 429: wait retry_after_seconds (or the Retry-After
header) before retrying. Don’t burst-retry — you’ll just keep getting 429s.
Endpoints
GET /me
Required scope: any (authenticated).
Confirms the key is valid and returns who it’s acting as. Great first call from a new client to verify connectivity.
Response 200:
{
"key_id": "9f2e…",
"scope": "organization",
"user_id": "51a7…",
"organization_id": "c0d1…",
"organization_name": "Acme Inc",
"organization_slug": "acme",
"scopes": ["processes:read", "processes:write"]
}
For personal keys, organization_* fields are null.
GET /processes
Required scope: processes:read.
Lists processes visible to the actor, paginated.
Query parameters:
| Param | Type | Default | Notes |
|---|---|---|---|
limit | integer | 25 | 1–100 |
cursor | ISO 8601 | null | Pass next_cursor from the previous page |
visibility | string | null | Filter: public / organization / private / unlisted |
q | string | null | Title search (ILIKE) |
Response 200:
{
"items": [
{
"id": "proc-abc…",
"slug": "how-to-onboard-a-new-client",
"short_id": "a1b2c3",
"title": "How to onboard a new client",
"visibility": "organization",
"organization_id": "c0d1…",
"created_by": "51a7…",
"step_count": 8,
"featured_image": "https://…",
"category": "onboarding",
"created_at": "2026-04-10T14:22:18Z",
"updated_at": "2026-04-22T09:05:40Z",
"url": "https://www.whatstheprocessfor.com/process/a1b2c3"
}
],
"next_cursor": "2026-04-10T14:22:18Z"
}
next_cursor is null when there are no more pages.
GET /processes/:id
Required scope: processes:read.
Full process document with its steps. Returns 404 if the process doesn’t
exist or 403 if the key’s actor doesn’t have view permission.
Response 200:
{
"id": "proc-abc…",
"slug": "how-to-onboard-a-new-client",
"short_id": "a1b2c3",
"title": "How to onboard a new client",
"content": "Detailed intro text…",
"visibility": "organization",
"organization_id": "c0d1…",
"created_by": "51a7…",
"step_count": 8,
"featured_image": "https://…",
"category": "onboarding",
"created_at": "2026-04-10T14:22:18Z",
"updated_at": "2026-04-22T09:05:40Z",
"url": "https://www.whatstheprocessfor.com/process/a1b2c3",
"steps": [
{
"step_number": 1,
"step_title": "Send welcome email",
"step_description": "Use the template at…",
"step_image": "https://…",
"step_video": "https://youtube.com/watch?v=…"
}
]
}
POST /uploads
Required scope: processes:write.
Upload an image and receive a public URL you can pass as featured_image
or step_image when creating or updating a process. This is the
recommended way to attach images you don’t already host somewhere — the
process create/update endpoints only accept reachable URLs, so without
this endpoint you’d need your own image host first.
Request: multipart/form-data with a file field.
| Field | Type | Notes |
|---|---|---|
file | file | Required. JPEG, PNG, GIF, or WebP. Max 10 MB. |
Response 201:
{
"url": "https://…supabase.co/storage/v1/object/public/process-images/…",
"path": "user-id/api/1745433660-screenshot.png",
"content_type": "image/png",
"size_bytes": 184320
}
Errors:
400— missingfile, wrong content-type, unsupported MIME type, or > 10 MB401— missing/invalid key403— key lacksprocesses:write429— rate limit hit (uploads count against the same per-minute / per-day quota)
The returned url is permanent and public; safe to embed anywhere. The
upload counts against the actor’s plan storage the same way an in-app
upload does.
POST /processes
Required scope: processes:write.
Create a process. For org keys, the process belongs to the org and
defaults to organization visibility. For personal keys, it defaults to
public visibility and has no org. Override with explicit visibility.
Request body:
{
"title": "How to onboard a new client",
"description": "Short summary shown in listings",
"content": "Optional long-form intro text",
"category": "onboarding",
"visibility": "organization",
"featured_image": "https://…",
"steps": [
{
"step_title": "Send welcome email",
"step_description": "Use the template at…",
"step_image": "https://…",
"step_video": "https://youtube.com/watch?v=…"
}
]
}
Required: title, steps (1–50 items). Each step needs a step_title.
Any image or video URL you pass must be reachable (2xx on a HEAD request)
or the request is rejected with 400.
Response 201:
{
"id": "proc-new…",
"slug": "how-to-onboard-a-new-client",
"short_id": "x9y8z7",
"title": "How to onboard a new client",
"visibility": "organization",
"organization_id": "c0d1…",
"step_count": 1,
"featured_image": "https://…",
"created_at": "2026-04-23T19:00:00Z",
"url": "https://www.whatstheprocessfor.com/process/x9y8z7"
}
PUT /processes/:id
Required scope: processes:write.
Update any of: title, content, description, category, visibility,
step_count, featured_image. Omitted fields are left unchanged. Requires
edit permission on the process (owner, org admin, or the process’s
created_by — same rules as the web UI).
Each update creates a version snapshot, so previous states are recoverable.
Response 200:
{ "id": "proc-new…", "updated_at": "2026-04-23T19:05:12Z" }
DELETE /processes/:id
Required scope: processes:delete.
Permanent delete. Requires full permission on the process. There is no
undo via the API — recovery requires admin intervention. Prefer setting
visibility: "private" via PUT if you just want to hide something.
Response 200:
{ "id": "proc-new…", "deleted": true }
POST /processes/:id/publish
Required scope: processes:write.
Sets the process’s visibility. Defaults to public when no body is sent.
Use this when you want a single explicit “publish” call instead of a PUT
with a visibility field. Processes are reachable at their URL the moment
they’re created, so this endpoint is really just a visibility flip.
{ "visibility": "public" }
Response 200:
{ "id": "proc-new…", "visibility": "public", "published": true }
Workflows
A workflow is an ordered group of processes used for training and
onboarding. Each workflow lives inside one organization, has a name +
description, holds an ordered list of processes (workflow_processes with
a position integer), and tracks per-user completion (workflow_progress).
All workflow endpoints require an organization-scoped key. Role gates are the same as the dashboard:
- Owner / admin — create, edit, delete workflows; grant or revoke per-user permissions; invite + remove members.
- Owner / admin / manager — add, remove, reorder processes inside a workflow; mark or unmark progress for any user.
- Any active member — list workflows, fetch a workflow’s contents, mark or unmark their own progress.
GET /workflows
Required scope: workflows:read.
Lists workflows in the calling key’s organization, paginated.
Query parameters:
| Param | Type | Default | Notes |
|---|---|---|---|
limit | integer | 25 | 1–100 |
cursor | ISO 8601 | null | Pass next_cursor from previous page |
q | string | null | Name search (ILIKE) |
include_inactive | boolean | false | Include archived (is_active = false) |
Response 200:
{
"items": [
{
"id": "wf-abc…",
"organization_id": "c0d1…",
"name": "New hire onboarding",
"description": "Day-1 through week-2 checklist",
"is_active": true,
"created_by": "51a7…",
"created_at": "2026-04-10T14:22:18Z",
"updated_at": "2026-04-22T09:05:40Z"
}
],
"next_cursor": "2026-04-10T14:22:18Z"
}
POST /workflows
Required scope: workflows:write. Owner / admin only.
{
"name": "New hire onboarding",
"description": "Day-1 through week-2 checklist",
"is_active": true
}
URLs in name or description are rejected. is_active defaults to true.
GET /workflows/:id
Required scope: workflows:read. Returns the workflow + its ordered
processes (with a snapshot of each linked process):
{
"workflow": { "id": "wf-abc…", "name": "New hire onboarding", "...": "..." },
"processes": [
{
"id": "wp-1…",
"workflow_id": "wf-abc…",
"process_id": "proc-1…",
"position": 0,
"is_required": true,
"estimated_duration_minutes": 15,
"added_at": "2026-04-10T14:25:00Z",
"processes": {
"id": "proc-1…",
"slug": "send-welcome-email",
"short_id": "a1b2c3",
"title": "Send welcome email",
"visibility": "organization",
"status": "published",
"step_count": 4,
"featured_image": "https://…"
}
}
]
}
PUT /workflows/:id
Required scope: workflows:write. Owner / admin only. Body accepts any of
name, description, is_active. Omitted fields are left unchanged.
DELETE /workflows/:id
Required scope: workflows:delete. Owner / admin only. Cascades to
workflow_processes and workflow_progress. There is no undo.
POST /workflows/:id/processes
Required scope: workflows:write. Owner / admin / manager. Adds a process
to the workflow. Inserts at end-of-list unless position is provided.
{
"process_id": "proc-1…",
"position": 0,
"is_required": true,
"estimated_duration_minutes": 15
}
A process can only appear once in a given workflow (409 on duplicate). Private processes that don’t belong to the org cannot be added.
PUT /workflows/:id/processes/reorder
Required scope: workflows:write. Owner / admin / manager. Pass the full
ordered list of process_id values; the API sets position to each item’s
array index.
{ "process_ids": ["proc-1…", "proc-3…", "proc-2…"] }
DELETE /workflows/:id/processes/:processId
Required scope: workflows:write. Owner / admin / manager. Removes the
link only — the underlying process is untouched.
GET /workflows/:id/permissions
Required scope: workflows:read. Owner / admin only.
Lists per-user permission grants on this workflow (separate from the
catch-all org-role gate, which always applies). Useful when you want to
give a member access to a specific workflow without elevating their org
role.
PUT /workflows/:id/permissions
Required scope: workflows:write. Owner / admin only. Idempotent — if the
user already has a grant on this workflow it is updated.
{ "user_id": "51a7…", "permission_level": "edit" }
permission_level is one of view, execute, edit, full.
DELETE /workflows/:id/permissions/:userId
Required scope: workflows:write. Owner / admin only.
POST /workflows/:id/progress
Required scope: workflows:write.
Marks a process_id complete for user_id inside this workflow. Members
can mark their own progress; owner / admin / manager can mark for any user
in the org. The process must already be linked to the workflow.
{
"user_id": "51a7…",
"process_id": "proc-1…",
"notes": "Completed during week-1 walkthrough"
}
Duplicate marks return 409.
DELETE /workflows/:id/progress
Required scope: workflows:write. Same role rules as marking. Pass the
target in the body so callers can unmark idempotently:
{ "user_id": "51a7…", "process_id": "proc-1…" }
Members
All member endpoints require an organization-scoped key.
GET /members
Required scope: members:read. Any active member can list.
{
"items": [
{
"id": "uo-1…",
"user_id": "51a7…",
"role": "admin",
"status": "active",
"joined_at": "2026-01-12T09:00:00Z",
"full_name": "Alex Doe",
"avatar_url": "https://…"
}
]
}
PUT /members/:userId
Required scope: members:write. Owner / admin only.
{ "role": "manager" }
Allowed roles: admin, manager, member. Ownership transfer is not
supported via the API. Admins cannot mutate other admins or the owner. You
cannot change your own role.
DELETE /members/:userId
Required scope: members:delete. Owner / admin only.
Removes the member from the org. Cannot remove the owner or yourself. Admins cannot remove other admins.
GET /invites
Required scope: members:read. Owner / admin only. Lists pending invites
(unaccepted, not yet expired or otherwise).
POST /invites
Required scope: members:write. Owner / admin only. Creates an invite,
sends a branded invite email, and returns the invite row plus
invite_link so you can share it manually if delivery fails.
{ "email": "new.hire@example.com", "role": "member" }
Returns 403 if the org’s plan user-cap is already reached, 409 if a non-expired invite already exists for that email.
DELETE /invites/:id
Required scope: members:delete. Owner / admin only. Revokes a pending
invite by ID.
Errors
All error responses share a consistent envelope:
{ "error": "Human-readable message", "details": "…optional context…" }
| Status | Meaning |
|---|---|
| 400 | Bad request (missing field, invalid URL, >50 steps, etc.) |
| 401 | Missing/invalid/revoked API key |
| 403 | Missing scope, or actor lacks permission on the resource |
| 404 | Resource not found |
| 409 | Conflict (duplicate pending request, already-approved, etc.) |
| 429 | Rate limit exceeded — see the Retry-After header |
| 500 | Server error (please report if persistent) |
Versioning
The current version is v1, served at /api/v1/*. Breaking changes would
land under /api/v2 and leave v1 working for at least 12 months after
announcement. Follow the API changelog at /developers#changelog.
Quick examples
whoami
curl -H "Authorization: Bearer wtpf_..." \
https://www.whatstheprocessfor.com/api/v1/me
Create a process
curl -X POST \
-H "Authorization: Bearer wtpf_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Morning standup checklist",
"steps": [
{ "step_title": "Open Jira board" },
{ "step_title": "Each team member gives a 30-sec update" },
{ "step_title": "Flag blockers in #standup" }
]
}' \
https://www.whatstheprocessfor.com/api/v1/processes
Upload an image, then attach it to a new process
# 1. Upload the image — the response gives you a public URL
UPLOAD=$(curl -s -X POST \
-H "Authorization: Bearer wtpf_..." \
-F "file=@/path/to/screenshot.png" \
https://www.whatstheprocessfor.com/api/v1/uploads)
IMAGE_URL=$(echo "$UPLOAD" | jq -r .url)
# 2. Use that URL as featured_image when creating a process
curl -X POST \
-H "Authorization: Bearer wtpf_..." \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Onboarding flow\",
\"featured_image\": \"$IMAGE_URL\",
\"steps\": [{ \"step_title\": \"Send welcome email\", \"step_image\": \"$IMAGE_URL\" }]
}" \
https://www.whatstheprocessfor.com/api/v1/processes
Claude, when a user gives you a local file path or a screenshot to attach:
upload it via POST /uploads first, then pass the returned url into
featured_image or step_image. Don’t try to embed base64 — the process
endpoints only accept HTTP(S) URLs.
Find and publish a draft
# List drafts
curl -H "Authorization: Bearer wtpf_..." \
"https://www.whatstheprocessfor.com/api/v1/processes?visibility=private&q=standup"
# Publish by ID
curl -X POST \
-H "Authorization: Bearer wtpf_..." \
-H "Content-Type: application/json" \
-d '{ "visibility": "public" }' \
https://www.whatstheprocessfor.com/api/v1/processes/proc-abc/publish