Skip to content

Developers

REST API

Use API tokens to read and change projects, issues, comments, members and channels from scripts and integrations.

Deckbell has a REST API for scripts, internal tools and integrations. It works with JSON over HTTPS, and every request uses an API token that belongs to you and one workspace.

To be told about changes as they happen instead of polling, use webhooks. To use Deckbell from an AI assistant, see the MCP server.

Create an API token

  1. Open Settings → API tokens (in the Workspace group).
  2. Choose New token, give it a name you'll recognize (for example "Zapier" or "CI"), and tick what it may do. Each area (projects, issues, comments, chat, members) has Read and Write; write includes read.
  3. Pick when it expires, or Never.
  4. Copy the token (it starts with dkb_). It's shown only once; Deckbell keeps only a fingerprint of it.

A token acts as you: it can never do more than your role in the workspace allows, even with every scope. It works in that one workspace only, and stops working if you leave the workspace or are deactivated.

The token list shows when each token was last used. Revoke a token you no longer need, or if it leaks; anything using it stops working right away. Workspace owners and admins also see everyone else's tokens and can revoke them. Creating and revoking tokens is recorded in the audit log.

Make a request

Send the token in the Authorization header:

curl https://api.deckbell.com/api/v1/issues?assignee=me \
  -H "Authorization: Bearer dkb_your_token"

Create an issue:

curl https://api.deckbell.com/api/v1/issues \
  -H "Authorization: Bearer dkb_your_token" \
  -H "Content-Type: application/json" \
  -d '{"project": "WEB", "title": "Checkout button is broken", "priority": "high", "labels": ["Bug"], "state": "Todo"}'

Statuses, labels and priorities can be given by name (case doesn't matter), and people by their member id or "me".

Endpoints

EndpointWhat it doesScope
GET /meThe person, workspace and scopes of the tokenany
GET /projects, GET /projects/{key}List projects, get oneprojects:read
POST /projects, PATCH /projects/{key}Create or edit a projectprojects:write
GET /projects/{key}/statesA project's statuses (board columns)projects:read or issues:read
GET /projects/{key}/labels, POST …/labelsList or add labelsread / projects:write
GET /issuesList and search issues (filters below)issues:read
POST /issuesCreate an issueissues:write
GET /issues/{id}One issue, by key (WEB-42) or idissues:read
PATCH /issues/{id}Change title, description, status, priority, assignee, labels, estimate, or archive itissues:write
DELETE /issues/{id}Delete an issue for goodissues:write
GET /issues/{id}/comments, POST …/commentsRead or add commentscomments:read / comments:write
GET /membersPeople in the workspace and their rolesmembers:read
GET /channels, GET /channels/{channel}Channels you're inchat:read
GET /channels/{channel}/messages, POST …/messagesRead or post messageschat:read / chat:write

Projects can be referred to by key (WEB), slug or id, and channels by name (general) or id. Direct messages aren't available through the API.

Filtering issues

GET /issues takes any of these query parameters (several values are comma-separated):

  • project: project key, slug or id
  • state: status name or id, e.g. In Progress
  • state_category: backlog, unstarted, started, completed, canceled
  • assignee: a member id, me, none, or agent:<id>
  • label, priority (none, urgent, high, medium, low)
  • q: text in the title or description
  • updated_since, created_since: an ISO 8601 date-time
  • include_archived=true to include archived issues
  • sort: -updated_at (default), updated_at, created_at, -created_at, priority

Pagination

Lists return a page of results and a cursor for the next one:

{
  "data": [ … ],
  "pagination": { "next_cursor": "eyJpZCI6…", "has_more": true, "limit": 25 }
}

Pass cursor=<next_cursor> to get the next page, and limit (1 to 100) to change the page size.

Errors

Errors always have the same shape, with a stable code:

{
  "error": {
    "code": "validation_failed",
    "message": "The title field is required.",
    "details": { "errors": { "title": ["The title field is required."] } }
  }
}
StatusCodeMeaning
401unauthenticatedNo token, or it's invalid, expired or revoked
403insufficient_scopeThe token lacks a scope (details.required_scope says which)
403forbiddenYour role doesn't allow this
404not_foundIt doesn't exist in this workspace
422validation_failedSomething in the request is wrong (details.errors per field)
402plan_limitYour plan's limit is reached (for example the Free plan's issue limit)
429rate_limitedToo many requests; wait Retry-After seconds

Rate limits

Each token may make a number of requests per minute, by your workspace's plan: 60 on Free, 300 on Pro, 600 on Business and 1,200 on Enterprise. Every response has X-RateLimit-Limit and X-RateLimit-Remaining headers. Calls from the MCP server count towards the same limit.

Plans and permissions

The API is available on every plan. Your plan's limits apply just as in the app (for example, the Free plan's number of projects and issues), and so does your role: a token made by someone who can't create projects can't create them either. Assigning an issue to an AI agent through the API starts the agent, as it does in the app.

Still stuck? Email us and we’ll help.