REST API

API Documentation

Manage projects, tools, Grove cards, and to-dos programmatically. Build integrations, automate workflows, or sync data with other tools.

Authentication

All API requests require a bearer token. Create one in your organization's Settings → API Tokens page (admin or owner role required).

curl -H "Authorization: Bearer thk_your_token_here" \
  -H "User-Agent: MyApp ([email protected])" \
  https://www.thickethq.com/api/v1/projects

Required headers

  • Authorization: Bearer thk_... — your API token
  • User-Agent: AppName (contact) — your app name and contact email or URL
  • Content-Type: application/json — for POST/PUT requests

Rate Limiting

API requests are limited to 50 requests per 10 seconds per token. When you exceed the limit, you'll receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait.

Every response includes rate limit headers:

  • X-RateLimit-Limit — requests allowed per window
  • X-RateLimit-Remaining — requests remaining in current window
  • X-RateLimit-Reset — Unix timestamp when the window resets

Pagination

List endpoints return paginated results. Use page and per_page query parameters to navigate. Default: 25 results per page, maximum: 100.

The Link header follows RFC 5988 and provides URLs for next, prev, first, and last pages. The X-Total-Count header contains the total number of resources for the primary paginated list in the response. For Grove cards, those headers apply to the top-level cards array, not the per-status grouped arrays.

# Response headers
Link: <https://...?page=2&per_page=25>; rel="next", <https://...?page=1&per_page=25>; rel="first"
X-Total-Count: 47

Caching

GET responses include an ETag header. On subsequent requests, pass it as If-None-Match to receive a 304 Not Modified response when content hasn't changed, saving bandwidth.

# First request — save the ETag
curl -i -H "Authorization: Bearer thk_..." \
  https://www.thickethq.com/api/v1/projects
# ETag: W/"a1b2c3d4e5f6g7h8"

# Subsequent request — send it back
curl -H "Authorization: Bearer thk_..." \
  -H 'If-None-Match: W/"a1b2c3d4e5f6g7h8"' \
  https://www.thickethq.com/api/v1/projects
# 304 Not Modified (no body)

Errors

All errors return a consistent JSON shape:

{
  "error": "Human-readable message",
  "code": "MACHINE_READABLE_CODE",
  "detail": "Optional additional context"
}
StatusCodeDescription
400BAD_REQUESTInvalid request parameters
400MISSING_USER_AGENTMissing User-Agent header
401UNAUTHORIZEDMissing or invalid credentials
401TOKEN_REVOKEDToken has been revoked
401TOKEN_EXPIREDToken has expired
403INSUFFICIENT_PERMISSIONToken lacks required permission
404RESOURCE_NOT_FOUNDResource doesn't exist or is inaccessible
422VALIDATION_ERRORRequest body failed validation
429RATE_LIMITEDToo many requests — check Retry-After header
500INTERNAL_ERRORServer error — retry with exponential backoff

Versioning

All endpoints are under /api/v1/. Every response includes an X-Api-Version header. We commit to no breaking changes within a version. New fields may be added to responses — your client should ignore unknown fields. If we deprecate an endpoint, we'll announce it with a Sunset header well in advance.

Endpoints

Projects

GET/api/v1/projects

List all projects the authenticated user can access.

Query parameters

page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Project access rules

  • INVITE_ONLY projects are only returned to explicitly invited project members.
  • ALL_ACCESS projects are returned to internal non-client organization members automatically.
  • Client users never get implicit access. Clients must always be explicitly added as project members.

Permission required: projects:read

POST/api/v1/projects

Create a new project.

Request body

name*
string
Project name
description
string
Project description

Permission required: projects:write

curl -X POST https://www.thickethq.com/api/v1/projects \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "name": "Marketing Website",
    "description": "Q2 website redesign"
  }'
GET/api/v1/projects/:id

Get a single project by ID if the authenticated user can access it.

Invite-only projects require explicit membership. All-access projects are readable by internal non-client organization members, but client users still need an explicit project membership.

Permission required: projects:read

PUT/api/v1/projects/:id

Update general project fields like name, description, color, or status.

Request body

name
string
New project name
description
string
New description
color
string
Hex color value
status
string
ACTIVE or ARCHIVED
Project access type is changed through a dedicated endpoint: PUT /api/v1/projects/:id/access.

Permission required: projects:write

PUT/api/v1/projects/:id/access

Change a project between invite-only and all-access without introducing project-level roles.

Request body

accessType*
string
INVITE_ONLY or ALL_ACCESS

Who can change it: the project creator, an organization admin, or the organization owner.

What it means: ALL_ACCESS grants implicit access to internal non-client organization members only. Clients still require explicit project membership. INVITE_ONLY always requires explicit membership.

curl -X PUT https://www.thickethq.com/api/v1/projects/PROJECT_ID/access   -H "Authorization: Bearer thk_..."   -H "Content-Type: application/json"   -H "User-Agent: MyApp ([email protected])"   -d '{
    "accessType": "ALL_ACCESS"
  }'

# Response
{
  "project": {
    "id": "PROJECT_ID",
    "slug": "marketing-website-ab123",
    "access_type": "ALL_ACCESS"
  },
  "authorization_basis": "ADMIN"
}

Permission required: projects:write

DELETE/api/v1/projects/:id

Trash a project.

Permission required: projects:write

Tools

Tools are the building blocks within a project — Grove boards, to-do lists, and more.

GET/api/v1/projects/:id/tools

List all tools in a project.

Permission required: tools:read

POST/api/v1/projects/:id/tools

Create a new tool in a project.

Request body

name*
string
Tool name
type*
string
Tool type (e.g., GROVE, TODOLIST)

Permission required: tools:write

GET/api/v1/projects/:id/tools/:toolId

Get a single tool by ID.

Permission required: tools:read

PUT/api/v1/projects/:id/tools/:toolId

Update a tool.

Request body

name
string
New tool name

Permission required: tools:write

DELETE/api/v1/projects/:id/tools/:toolId

Delete a tool from a project.

Permission required: tools:write

PUT/api/v1/projects/:id/tools/:toolId/enable

Enable a tool.

Permission required: tools:write

PUT/api/v1/projects/:id/tools/:toolId/disable

Disable a tool.

Permission required: tools:write

PUT/api/v1/projects/:id/tools/:toolId/position

Update a tool's position in the project.

Request body

position*
integer
New position (0-indexed)

Permission required: tools:write

Grove Cards

Grove cards are items on a project's Grove board. All Grove endpoints are scoped to a project.

GET/api/v1/projects/:id/grove

List Grove tools in a project.

Permission required: kanban:read

GET/api/v1/projects/:id/grove/cards

List cards in a project's Grove board. Returns both a stable flat list and trustworthy grouped column slices.

Query parameters

status
string
Optional status filter: CONSIDERING, IN_PROGRESS, or DONE. When provided, both cards and grouped are scoped to that status.
archived
boolean
Include archived cards too when set to true (default: false)
page
integer
Page number (default: 1). Applies to the flat list and, when no status filter is supplied, independently to each grouped status column.
per_page
integer
Results per page, 1-100 (default: 25)

cards is the primary paginated list. It is ordered deterministically across all statuses by status order (CONSIDERINGIN_PROGRESSDONE), then by position asc,created_at asc, and id asc.

grouped contains per-status page slices that are paginated within each column, not just the current flat page re-grouped. Column-level pagination details live under pagination.grouped.statuses.

X-Total-Count and Link headers describe the top-level cards list only.

Permission required: kanban:read

{
  "cards": [
    {
      "id": "...",
      "title": "Tighten API docs",
      "status": "CONSIDERING",
      "position": 0,
      "created_at": "2026-03-09T22:00:00.000Z"
    }
  ],
  "grouped": {
    "CONSIDERING": [
      {
        "id": "...",
        "title": "Tighten API docs",
        "status": "CONSIDERING",
        "position": 0
      }
    ],
    "IN_PROGRESS": [],
    "DONE": []
  },
  "pagination": {
    "flat": {
      "page": 1,
      "per_page": 25,
      "total_count": 47,
      "total_pages": 2,
      "returned_count": 25,
      "has_next_page": true,
      "has_prev_page": false,
      "next_page": 2,
      "prev_page": null,
      "order": {
        "status": ["CONSIDERING", "IN_PROGRESS", "DONE"],
        "position": "asc",
        "created_at": "asc",
        "id": "asc"
      }
    },
    "grouped": {
      "mode": "per-status",
      "page": 1,
      "per_page": 25,
      "status_filter": null,
      "order": {
        "position": "asc",
        "created_at": "asc",
        "id": "asc"
      },
      "statuses": {
        "CONSIDERING": {
          "page": 1,
          "per_page": 25,
          "total_count": 12,
          "total_pages": 1,
          "returned_count": 12,
          "has_next_page": false,
          "has_prev_page": false,
          "next_page": null,
          "prev_page": null
        },
        "IN_PROGRESS": {
          "page": 1,
          "per_page": 25,
          "total_count": 20,
          "total_pages": 1,
          "returned_count": 20,
          "has_next_page": false,
          "has_prev_page": false,
          "next_page": null,
          "prev_page": null
        },
        "DONE": {
          "page": 1,
          "per_page": 25,
          "total_count": 15,
          "total_pages": 1,
          "returned_count": 15,
          "has_next_page": false,
          "has_prev_page": false,
          "next_page": null,
          "prev_page": null
        }
      }
    }
  }
}
POST/api/v1/projects/:id/grove/cards

Create a new card in a project's Grove board.

Request body

title*
string
Card title
description
string
Card description (plain text)
status
string
CONSIDERING (default), IN_PROGRESS, or DONE
assigneeId
string (UUID)
User ID to assign the card to
tags
string[]
Card tags (e.g. ["LensCherry", "priority:urgent"])
dueDate
string (ISO 8601)
Due date

Permission required: kanban:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/grove/cards \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "title": "Implement feature X",
    "status": "IN_PROGRESS"
  }'
GET/api/v1/projects/:id/grove/cards/:cardId

Get a single Grove card by ID.

Permission required: kanban:read

PUT/api/v1/projects/:id/grove/cards/:cardId

Update a Grove card. Send only the fields you want to change.

Request body (all optional)

title
string
New title
description
string
New description
status
string
CONSIDERING, IN_PROGRESS, or DONE
assigneeId
string (UUID)
Reassign to a user
archived
boolean
Archive or unarchive
tags
string[]
Update tags
dueDate
string (ISO 8601)
Update due date

Permission required: kanban:write

DELETE/api/v1/projects/:id/grove/cards/:cardId

Permanently delete a Grove card.

Permission required: kanban:write

PUT/api/v1/projects/:id/grove/cards/:cardId/move

Move a card to a new column and position.

Request body

status*
string
Target column: CONSIDERING, IN_PROGRESS, or DONE
position*
integer
Position in the column (0-indexed)

Permission required: kanban:write

To-do Lists

To-do lists organize todos within a project. Each list contains ordered todo items.

GET/api/v1/projects/:id/todolists

List all to-do lists in a project.

Permission required: todos:read

POST/api/v1/projects/:id/todolists

Create a new to-do list in a project.

Request body

name*
string
List name
description
string
List description

Permission required: todos:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/todolists \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "name": "Launch Checklist"
  }'
GET/api/v1/projects/:id/todolists/:listId

Get a single to-do list by ID.

Permission required: todos:read

PUT/api/v1/projects/:id/todolists/:listId

Update a to-do list.

Request body

name
string
New list name
description
string
New description

Permission required: todos:write

DELETE/api/v1/projects/:id/todolists/:listId

Delete a to-do list and all its todos.

Permission required: todos:write

To-dos

GET/api/v1/projects/:id/todolists/:listId/todos

List all todos in a to-do list.

Query parameters

page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Permission required: todos:read

POST/api/v1/projects/:id/todolists/:listId/todos

Create a new todo in a list.

Request body

content*
string
Todo content
assigneeId
string (UUID)
User ID to assign the todo to
dueDate
string (ISO 8601)
Due date

Permission required: todos:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/todolists/LIST_ID/todos \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "content": "Review pull request"
  }'
GET/api/v1/projects/:id/todos/:todoId

Get a single todo by ID.

Permission required: todos:read

PUT/api/v1/projects/:id/todos/:todoId

Update a todo.

Request body (all optional)

content
string
New content
assigneeId
string (UUID)
Reassign to a user
dueDate
string (ISO 8601)
Update due date

Permission required: todos:write

DELETE/api/v1/projects/:id/todos/:todoId

Permanently delete a todo.

Permission required: todos:write

PUT/api/v1/projects/:id/todos/:todoId/complete

Mark a todo as complete.

Permission required: todos:write

PUT/api/v1/projects/:id/todos/:todoId/uncomplete

Mark a todo as incomplete.

Permission required: todos:write

PUT/api/v1/projects/:id/todos/:todoId/position

Reorder a todo within its list.

Request body

position*
integer
New position (0-indexed)

Permission required: todos:write

Messages

Messages are posted to a project's message board. Think announcements, updates, and discussions.

GET/api/v1/projects/:id/messages

List messages in a project. Returns paginated results.

Query parameters

page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Permission required: messages:read

POST/api/v1/projects/:id/messages

Create a new message.

Request body

title*
string
Message title
content*
string
Message body
category_id
string (UUID)
Message category ID
visible_to_clients
boolean
Whether the message is visible to client users

Permission required: messages:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/messages \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "title": "Weekly Update",
    "content": "Here is what we shipped this week..."
  }'
GET/api/v1/projects/:id/messages/:msgId

Get a single message by ID.

Permission required: messages:read

PUT/api/v1/projects/:id/messages/:msgId

Update a message.

Request body (all optional)

title
string
New title
content
string
New body
category_id
string (UUID)
Update category
visible_to_clients
boolean
Update client visibility
pinned
boolean
Pin or unpin the message

Permission required: messages:write

DELETE/api/v1/projects/:id/messages/:msgId

Trash a message.

Permission required: messages:write

Schedule

Basecamp-style model: each project has a Schedule tool, and the dated items on it are schedule entries.

GET/api/v1/projects/:id/schedule

Get the project's Schedule tool. Includes links to the schedule entries collection.

Permission required: schedule:read

GET/api/v1/projects/:id/schedule/entries

List schedule entries in a project. Returns paginated results. Recurring entries are occurrence-aware — when date filters are provided, only entries with occurrences in the window are returned.

Query parameters

status
string
Filter by status: active (default), archived, or trashed
starts_after
string (ISO 8601)
Include entries/occurrences starting on or after this date. Alias: start_date
starts_before
string (ISO 8601)
Include entries/occurrences starting on or before this date. Alias: end_date
page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Recurring entries: When date filters are provided, recurring entries are included only if they have at least one occurrence in the requested window. Recurring entries include next_occurrence and occurrence_end fields — computed relative to the filter window, or relative to now if no date filter is set.

Permission required: schedule:read

POST/api/v1/projects/:id/schedule/entries

Create a new schedule entry.

Request body

title*
string
Schedule entry title
description
string
Schedule entry description
location
string
Schedule entry location
start_time*
string (ISO 8601)
Start time
end_time*
string (ISO 8601)
End time
all_day
boolean
Whether this is an all-day event
video_link
string
Video call link
recurrence_rule
string
iCal RRULE for recurring entries (e.g. FREQ=WEEKLY;BYDAY=MO,WE,FR)
participant_ids
string[]
Array of user UUIDs to add as participants

Permission required: schedule:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/schedule/entries \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "title": "Sprint Planning",
    "start_time": "2026-03-10T10:00:00Z",
    "end_time": "2026-03-10T11:00:00Z",
    "location": "Conference Room B",
    "recurrence_rule": "FREQ=WEEKLY;BYDAY=TU",
    "participant_ids": ["USER_UUID_1", "USER_UUID_2"]
  }'
GET/api/v1/projects/:id/schedule/entries/:entryId

Get a single schedule entry by ID. Recurring entries include next_occurrence and occurrence_end fields computed relative to now.

Permission required: schedule:read

PUT/api/v1/projects/:id/schedule/entries/:entryId

Update a schedule entry.

Request body (all optional)

title
string
New title
description
string
New description
location
string
New location
start_time
string (ISO 8601)
New start time
end_time
string (ISO 8601)
New end time
all_day
boolean
Toggle all-day
video_link
string
Update video link
recurrence_rule
string
iCal RRULE (e.g. FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR). Set to null to remove recurrence.
participant_ids
string[]
Replace participants with this list of user UUIDs
status
string
Set to active, archived, or trashed

Permission required: schedule:write

DELETE/api/v1/projects/:id/schedule/entries/:entryId

Trash a schedule entry (soft-delete — sets status to trashed).

Permission required: schedule:write

Occurrences

Expand recurring schedule entries into individual occurrences. Inspired by the Basecamp API pattern of date-addressable occurrences.

GET/api/v1/projects/:id/schedule/entries/:entryId/occurrences

List occurrences of a schedule entry within a date range. For recurring entries, expands the RRULE into individual occurrences. For non-recurring entries, returns the entry itself if it falls within the range.

Query parameters (both required)

starts_after*
string (ISO 8601)
Start of the date range (inclusive). Alias: start_date
starts_before*
string (ISO 8601)
End of the date range (inclusive). Alias: end_date

Response: { occurrences: [{ start_time, end_time, schedule_entry }] }

Permission required: schedule:read

curl "https://www.thickethq.com/api/v1/projects/PROJECT_ID/schedule/entries/ENTRY_ID/occurrences?starts_after=2026-04-01&starts_before=2026-04-30" \
  -H "Authorization: Bearer thk_..." \
  -H "User-Agent: MyApp ([email protected])"
GET/api/v1/projects/:id/schedule/entries/:entryId/occurrences/:date

Get a specific occurrence of a recurring entry by date. The :date parameter must be in YYYYMMDD format. Returns 404 if no occurrence exists on that date.

Permission required: schedule:read

curl "https://www.thickethq.com/api/v1/projects/PROJECT_ID/schedule/entries/ENTRY_ID/occurrences/20260407" \
  -H "Authorization: Bearer thk_..." \
  -H "User-Agent: MyApp ([email protected])"

Documents

Documents are rich text docs in a project's Docs tool. Supports versioning — every title/content change creates a new version.

GET/api/v1/projects/:id/documents

List documents in a project. Returns paginated results.

Query parameters

status
string
Filter by status: draft, published, or scheduled
folder_id
string (UUID)
Filter by folder
page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Permission required: docs:read

POST/api/v1/projects/:id/documents

Create a new document.

Request body

title*
string
Document title
content*
string
Document body
status
string
Status: draft (default), published, or scheduled
folder_id
string (UUID)
Folder to place the document in
color
string
Document color
visible_to_clients
boolean
Whether the document is visible to client users
notify_option
string
Notification option for publishing
scheduled_for
string (ISO 8601)
Scheduled publish date (when status is "scheduled")

Permission required: docs:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/documents \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "title": "Engineering Onboarding Guide",
    "content": "Welcome to the team! Here is everything you need...",
    "status": "published"
  }'
GET/api/v1/projects/:id/documents/:docId

Get a single document by ID. Includes the latest 5 versions.

Permission required: docs:read

PUT/api/v1/projects/:id/documents/:docId

Update a document. Creates a new version if title or content changed.

Request body (all optional)

title
string
New title
content
string
New body
status
string
Update status
folder_id
string (UUID)
Move to a folder
color
string
Update color
visible_to_clients
boolean
Update client visibility
notify_option
string
Update notification option
scheduled_for
string (ISO 8601)
Update scheduled publish date
position
integer
Reorder within folder

Permission required: docs:write

DELETE/api/v1/projects/:id/documents/:docId

Trash a document.

Permission required: docs:write

Comments

Comments can be added to messages, todos, documents, and schedule entries. Polymorphic — one API for all content types.

GET/api/v1/projects/:id/comments

List all comments in a project. Optionally filter by content type.

Query parameters

commentable_type
string
Filter by type: message, task, document, or schedule_entry
commentable_id
string (UUID)
Filter by specific resource ID
page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Permission required: comments:read

POST/api/v1/projects/:id/messages/:msgId/comments

Add a comment to a message.

Request body

content*
string
Comment body

Permission required: comments:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/messages/MSG_ID/comments \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "content": "Great update! Looks like we are on track."
  }'
POST/api/v1/projects/:id/todos/:todoId/comments

Add a comment to a todo.

Request body

content*
string
Comment body

Permission required: comments:write

POST/api/v1/projects/:id/documents/:docId/comments

Add a comment to a document.

Request body

content*
string
Comment body

Permission required: comments:write

POST/api/v1/projects/:id/schedule/entries/:entryId/comments

Add a comment to a schedule entry.

Request body

content*
string
Comment body

Permission required: comments:write

PUT/api/v1/projects/:id/comments/:commentId

Update a comment.

Request body

content*
string
New comment body

Permission required: comments:write

DELETE/api/v1/projects/:id/comments/:commentId

Trash a comment.

Permission required: comments:write

Chat

Project chat rooms (Campfire-style). Post lines, read history. No real-time — poll for new messages.

GET/api/v1/projects/:id/chats

List chat rooms for a project.

Permission required: chat:read

GET/api/v1/projects/:id/chats/:chatId/lines

List lines in a chat room. Returns paginated results, newest first.

Query parameters

page
integer
Page number (default: 1)
per_page
integer
Results per page, 1-100 (default: 25)

Permission required: chat:read

POST/api/v1/projects/:id/chats/:chatId/lines

Post a new line to a chat room.

Request body

content*
string
Line content
reply_to_id
string (UUID)
ID of the line to reply to

Permission required: chat:write

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/chats/CHAT_ID/lines \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "content": "Deploy looks good, shipping it!"
  }'
GET/api/v1/projects/:id/chats/:chatId/lines/:lineId

Get a single chat line by ID.

Permission required: chat:read

DELETE/api/v1/projects/:id/chats/:chatId/lines/:lineId

Trash a chat line.

Permission required: chat:write

People

People in your organization and project members.

GET/api/v1/people

List all people in your organization.

Permission required: people:read

GET/api/v1/people/:personId

Get a single person by ID.

Permission required: people:read

GET/api/v1/projects/:id/members

List explicit project members. Alias: /api/v1/projects/:id/people

Permission required: people:read

POST/api/v1/projects/:id/members

Add an explicit project member. Alias: /api/v1/projects/:id/people

Request body

person_id
string
Person ID to add as an explicit project member
email
string
Organization member email to resolve server-side when you do not have the person ID

Who can add people: internal non-client team users with project access, plus organization admin and owner roles even on invite-only projects they are not already a member of.

Who cannot: collaborators and clients cannot manage project membership even if they can read the project.

Identity input: send either person_id or email, not both.

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/people \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "person_id": "PERSON_ID"
  }'
curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/people \
  -H "Authorization: Bearer thk_..." \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyApp ([email protected])" \
  -d '{
    "email": "[email protected]"
  }'

This resolves the email against organization membership first, then adds that person as an explicit project member.

Permission required: projects:write

POST/api/v1/projects/:id/members/me

Add yourself as an explicit project member. Alias: /api/v1/projects/:id/people/me

Who can use it: the same internal non-client team users who are allowed to manage project membership, including organization admins/owners for invite-only projects.

Why it exists: use this when the caller cannot conveniently resolve their own person UUID client-side.

curl -X POST https://www.thickethq.com/api/v1/projects/PROJECT_ID/people/me \
  -H "Authorization: Bearer thk_..." \
  -H "User-Agent: MyApp ([email protected])"

Permission required: projects:write

DELETE/api/v1/projects/:id/members/:personId

Remove an explicit project member. Alias: /api/v1/projects/:id/people/:personId

Who can remove people: internal non-client team users with project access, plus organization admin and owner roles even on invite-only projects they are not already a member of.

Creator protection: the project creator cannot be removed.

Permission required: projects:write

GET/api/v1/my/profile

Get your own profile. No special permission required — any valid token works.

curl https://www.thickethq.com/api/v1/my/profile \
  -H "Authorization: Bearer thk_..." \
  -H "User-Agent: MyApp ([email protected])"

Webhooks

Webhooks notify your application in real-time when scheduleEntries happen in Thicket. Configure an HTTPS endpoint and we'll POST scheduleEntry payloads to it with an HMAC-SHA256 signature for verification.

Verifying webhook signatures

Each delivery includes an X-Webhook-Signature header with format sha256=HMAC_HEX. Compute HMAC-SHA256 of the raw request body using your webhook secret and compare with a timing-safe equality check.

Available scheduleEntries

Task scheduleEntries

  • task.created — a new task was created
  • task.updated — a task's fields were changed
  • task.completed — a task was marked as complete
  • task.deleted — a task was permanently deleted

Card scheduleEntries

  • card.created — a new card was added to a Grove board
  • card.updated — a card's fields were changed (title, description, assignee, etc.)
  • card.moved — a card was moved to a different column
  • card.deleted — a card was permanently deleted

Delivery behavior

  • Deliveries timeout after 10 seconds
  • Failed deliveries retry up to 3 times with exponential backoff (0s, 10s, 60s)
  • 4xx responses are not retried (considered permanent failures)
  • Maximum 10 active webhooks per organization
GET/api/v1/webhooks?organizationId=...

List active webhooks for an organization.

Requires: admin or owner role (session auth)

POST/api/v1/webhooks

Create a new webhook. Returns the signing secret once — store it securely.

Request body

organizationId*
string (UUID)
Organization ID
url*
string
HTTPS endpoint URL to receive webhook events
events
string[]
Event types to subscribe to. Default: all events

Requires: admin or owner role (session auth). Max 10 active webhooks per org.

# Example webhook payload POSTed to your URL
{
  "event": "card.created",
  "created_at": "2026-02-27T18:30:00.000Z",
  "data": {
    "card": {
      "id": "...",
      "title": "Implement feature X",
      "status": "IN_PROGRESS"
    },
    "project_id": "project-uuid",
    "tool_id": "grove-board-uuid"
  }
}
GET/api/v1/webhooks/:webhookId

Get webhook details and recent delivery history (last 25 deliveries).

Requires: admin or owner role (session auth)

PUT/api/v1/webhooks/:webhookId

Update a webhook's URL, scheduleEntries, or active status.

Request body (all optional)

url
string
New HTTPS endpoint URL
events
string[]
New scheduleEntry subscriptions
active
boolean
Enable or disable the webhook

Requires: admin or owner role (session auth)

DELETE/api/v1/webhooks/:webhookId

Permanently delete a webhook and its delivery history.

Requires: admin or owner role (session auth)

Token Management

These endpoints require session authentication (browser) and admin or owner role in the organization.

GET/api/v1/tokens?organizationId=...

List active API tokens for an organization.

Requires: admin or owner role (session auth)

POST/api/v1/tokens

Create a new API token. Returns the token value once — store it securely.

Request body

organizationId*
string (UUID)
Organization ID
name*
string
Token name (1-100 characters)
permissions
string[]
Array of permissions. Default: all permissions
expiresAt
string (ISO 8601)
Optional expiration date

Requires: admin or owner role (session auth). Max 25 active tokens per org.

DELETE/api/v1/tokens/:tokenId

Revoke a token. Takes effect immediately.

Requires: admin or owner role (session auth)

Permissions

API tokens are scoped with permissions selected at creation time:

PermissionAccess
projects:readRead projects
projects:writeCreate, update, delete, and manage project access and members
tools:readRead tools within projects
tools:writeCreate, update, enable/disable, reorder, and delete tools
kanban:readRead Grove cards and board data
kanban:writeCreate, update, move, and delete Grove cards
todos:readRead to-do lists and todos
todos:writeCreate, update, complete, reorder, and delete todos and lists
messages:readRead messages and categories
messages:writeCreate, update, pin, and delete messages
schedule:readRead scheduled entries
schedule:writeCreate, update, and delete entries
docs:readRead documents and folders
docs:writeCreate, update, and delete documents
comments:readRead comments on any content type
comments:writeCreate, update, and delete comments
chat:readRead chat rooms and lines
chat:writePost and delete chat lines
people:readRead organization members and project people

Questions about the API? Get in touch.