Docs

REST API v1

Drive your TallyWeek workspace from scripts, Zapier or Make, or any other platform. Same permission model as the app.

Two ways to drive a workspace from outside the app, both backed by the same server-side core and the same permission model the app uses:

URLAuthFor
MCP serverhttps://tallyweek.com/api/mcpAuthorization: Bearer jk_… or /api/mcp/<key>Claude, ChatGPT, Claude Code, Cursor, any MCP client
REST API v1https://tallyweek.com/api/v1/...Authorization: Bearer jk_…Your own scripts, Zapier/Make, other platforms

API keys

Keys are created in Workspace home, API & MCP by anyone whose workspace role has the Change workspace settings permission (owners by default). API keys are part of the Pro plan.

A key acts as one workspace member and carries scopes:

  • Every call is first authorised as that member. Their workspace role, the permissions matrix and their board role (owner / editor / contributor / viewer) apply exactly as in the app. Private boards they cannot open do not exist to the key.
  • The key's scopes then clamp what is left. A read-only key held by an owner cannot write; an items:delete key held by a viewer still cannot delete.
ScopeAllows
readList/read workspace, folders, boards, groups, columns, items, updates, activity, members
items:writeCreate/edit items and subitems, values, assignees, move between groups
items:deleteTrash, restore, permanently delete items
updates:writePost updates and replies (with @mentions)
structure:writeCreate/rename/reorder/configure groups and columns; hide/restore built-in columns
structure:deleteDelete groups (and their items) and columns
boards:writeCreate boards and folders, rename, move into folders, settings, visibility
boards:deleteArchive, trash, restore, permanently delete boards; delete folders
members:manageShare boards, change board roles/general access, send invites

The full key is shown once at creation; only its SHA-256 hash is stored. Revoke a key from the same tab; it stops working immediately. See Permissions and sharing for the roles and matrix a key inherits.

Basics

Base URL https://tallyweek.com/api/v1. JSON in, JSON out. Every response is { "ok": true, "data": … } or { "ok": false, "error": "…" } with a matching HTTP status (400 bad input, 401 bad key, 403 not allowed, 404 not found).

Board ids may be the UUID or the numeric short id from the board URL. Wherever a user is expected, pass a user id, email, or full name.

curl -H "Authorization: Bearer jk_…" https://tallyweek.com/api/v1/me

Workspace

MethodPathBody / queryScope
GET/menoneany
GET/workspacemembers, folders, boardsread
GET/membersnoneread
GET/search?q=limitread

Folders

MethodPathBodyScope
GET/foldersnoneread
POST/folders{ name, parentFolderId?, color? }boards:write
PATCH/folders/:id{ name?, color?, parentFolderId? (null = root), position? }boards:write
DELETE/folders/:idboards inside move to the rootboards:delete

Boards

MethodPathBody / queryScope
GET/boards?folderId=&includeArchived=&includeTrashed=read
POST/boards{ name, folderId?, template? (blank|crm|project|content|bugs), groups?: [names], columns?: [{name,type,settings}], description?, boardType? }boards:write
GET/boards/:id?items=false for structure onlyread
PATCH/boards/:id{ name?, description?, folderId? (null = root), visibility? (workspace|private), generalAccessRole? (editor|contributor|viewer|none), settings? }boards:write
POST/boards/:id/archive{ archived: true|false }boards:delete
DELETE/boards/:id?mode=trash (default) |permanentboards:delete
POST/boards/:id/restorenoneboards:delete

GET /boards/:id returns statuses (keys + labels, including custom ones), columns (built-in owner/status/priority/due plus custom columns with ids and settings), column_order, hidden_native_columns, groups[].items[] (each item with values[], assignees[], subitems[], url) and automations.

Groups

MethodPathBodyScope
POST/boards/:id/groups{ name, color?, position?: "top"|"bottom" }structure:write
PUT/boards/:id/groups/order{ groupIds: [...] }structure:write
PATCH/groups/:id{ name?, color?, archived?, position? }structure:write
DELETE/groups/:iddeletes its items toostructure:delete

Columns

MethodPathBodyScope
GET/boards/:id/columnsnoneread
POST/boards/:id/columns{ name?, type, options?, settings?, afterColumnId? }structure:write
PUT/boards/:id/columns/order{ keys: [column ids and/or owner|status|priority|due] }structure:write
PUT/boards/:id/columns/native/:key{ hidden: true|false }structure:write
PATCH/columns/:id{ name?, type?, settings?, options?, addOptions? }structure:write
DELETE/columns/:idnonestructure:delete

Column types: text number date checkbox dropdown phone long_text timeline link files email rating progress tags time_tracking country connect dependency mirror, plus priority (creates a preset dropdown).

Items

MethodPathBody / queryScope
GET/boards/:id/items?groupId=&status=&priority=&assigneeId=&search=&includeSubitems=&includeArchived=&limit=&offset=read
POST/boards/:id/items{ name, groupId? | groupName?, status?, priority?, dueDate?, startDate?, assignees?: [user], values?: {}, position?: "top"|"bottom" }items:write
GET/items/:idnoneread
PATCH/items/:id{ name?, status?, priority?, dueDate?, startDate?, groupId?, values?: {}, archived? }items:write (+ moveItems for groupId)
POST/items/:id/move{ groupId?, index? } (0-based; omit = end)items:write
POST/items/:id/subitemssame as create itemitems:write
POST/items/:id/assignees{ add?: [user], remove?: [user] }items:write
DELETE/items/:id?mode=trash (default) |permanentitems:delete
POST/items/:id/restorenoneitems:delete
GET/items/:id/updates?limit=read
POST/items/:id/updates{ body, mentions?: [user], parentMessageId? }updates:write
GET/items/:id/activity?limit=read

values is keyed by column id or name. Shapes by type: text-like: string; number: number; rating: 0 to 5; progress: 0 to 100; date: YYYY-MM-DD; checkbox: boolean; dropdown: an option label; timeline: {start,end}; link: url string or {url,text}; tags: [strings]; country: 2-letter code; files: [{url,name,size?,type?}]; time_tracking: seconds; connect/dependency: [item ids]. null clears a value.

Status accepts a key (backlog in_motion in_review blocked shipped or a board's custom s_… key) or its label. Priority: low medium high urgent.

Sharing

MethodPathBodyScope
GET/boards/:id/membersnoneread
PUT/boards/:id/members{ user, role: owner|editor|contributor|assigned_contributor|viewer }members:manage
DELETE/boards/:id/members/:usernonemembers:manage
POST/invites{ email, role?, boardId? } returns { emailed, link, role }members:manage

What happens on a write

Writes go through the same paths the app uses: status changes record activity and notify assignees, moves reorder items, updates deliver @mentions and notifications. Every change is logged in the item's activity as the acting member, streams to open boards in real time, and runs the board's automations (item created, status, priority, column changed). Email notifications are not sent for API-made changes; in-app notifications are.

Example: build a board from scratch

K="Authorization: Bearer jk_…"; B=https://tallyweek.com/api/v1

curl -s -H "$K" -H 'Content-Type: application/json' $B/boards \
  -d '{"name":"Launch plan","template":"project"}'
# returns data.id, data.groups[], data.columns[]

curl -s -H "$K" -H 'Content-Type: application/json' $B/boards/<id>/columns \
  -d '{"name":"Channel","type":"dropdown","options":["Email","Social","Ads"]}'

curl -s -H "$K" -H 'Content-Type: application/json' $B/boards/<id>/items \
  -d '{"name":"Draft announcement","groupName":"To do","status":"in_motion","priority":"high",
       "dueDate":"2026-10-01","assignees":["dana@example.com"],"values":{"Channel":"Email","Progress":25}}'

Related

Start a free workspace for your team.

14 days of Pro with no card, then Free for up to 3 members with every view and every automation.