Developers
API: basics
The VentionDesk API is the same one the interface uses. For automations — Claude, your own scripts, CI — you sign in with an API key. All URLs start with https://api.ventiondesk.com; all bodies and responses are JSON in UTF-8.
Signing in with an API key
The owner creates a key under Settings › API keys › “New key”. It appears exactly once in plain text.
Every request carries it as a bearer token:
curl https://api.ventiondesk.com/v1/tasks?status=todo \
-H "Authorization: Bearer vd_live_…"
There is no separate endpoint for exchanging it: VentionDesk recognises the key by its prefix, checks it on every request (revoked? expired?) and then works with a session for the key’s account. The same database rules therefore apply to the key as to a person — it only sees what its scope allows.
| Prefix | Kind | What for |
|---|---|---|
vd_live_ | agent | pull tasks and report back, read projects, write notes, MCP |
vd_intake_ | intake | only submit tickets (POST /v1/intake/tickets), see Ticket intake |
vd_pub_ | public project key | not a sign-in key: it is in forms and web apps and can only send reports to one project |
Instead of a key, the sign-in of an AI app connected via OAuth can also come as a bearer token. It counts like an agent key with the permissions and projects the person chose at consent — never with the person’s own permissions — and ends immediately when disconnected.
An agent key is an account of its own with the role Agent. It is meant for the endpoints that show a scope in the API reference, plus GET /v1/me and the agent runs. Endpoints that require “owner” or “owner and staff members” are served by the web interface with the session of a signed-in person; to an API key they answer with 403.
Scopes
What an agent key may do is determined by its scopes:
| Scope | Meaning | Available for |
|---|---|---|
projects:read | Read projects | agent keys |
tasks:read | Read tasks | agent keys |
tasks:write | Create tasks and report back | agent keys |
tickets:read | Read tickets | agent keys, only if the workspace explicitly enables it |
tickets:write | Create tickets, write internal notes and drafts | agent keys, only if the workspace explicitly enables it |
tickets:reply | Reply to customers on tickets by email | agent keys, only if the workspace explicitly enables it |
notes:write | Write notes | agent keys |
deployments:read | Read commits, pull requests and deployments | agent keys |
agent:run | Start and finish runs | agent keys |
intake:write | Submit tickets through the intake | intake keys only |
invoices:write | Create invoice drafts and change line items | agent keys, only if the workspace explicitly enables it |
errors:read | Read the projects' errors | agent keys, only if the workspace explicitly enables it |
errors:write | Mark errors as resolved | agent keys, only if the workspace explicitly enables it |
time:read | Read the projects' time entries (without rates and amounts) | agent keys, only if the workspace explicitly enables it |
stats:read | Read the projects' key figures for reports (without money) | agent keys, only if the workspace explicitly enables it |
quotes:write | Create and change quote drafts | agent keys, only if the workspace explicitly enables it |
roadmap:write | Create, change and order roadmap phases | agent keys, only if the workspace explicitly enables it |
ideas:write | Create ideas | agent keys, only if the workspace explicitly enables it |
A scope appears on exactly the endpoints that honour it — the API reference names it per endpoint. deployments:read appears on no endpoint: it takes effect through the database rules of the mirrored commits, pull requests and deployments.
Project binding
A key applies to all projects or to a selection. A bound key only sees data of its projects — and deliberately nothing without a project, such as tickets not yet assigned to any project. The API answers a row outside its projects with 404 or 403. An intake key is always bound to exactly one project.
Changing the scope needs no new key. The owner changes scopes and projects under “Permissions” in the key list; the next request already works with the new scope. A revoked or expired key is rejected on its next request.
Errors
Every error response has the same shape:
{ "ok": false, "error": "Diese Aufgabe gibt es nicht.", "code": "not_found" }
error is a sentence for people (in the language from Accept-Language, German without that header), code a stable English value for programs. Common responses:
| Status | code | Meaning |
|---|---|---|
| 400 | validation_error | query or path parameters invalid |
| 401 | unauthorized | no key or an invalid key |
| 403 | forbidden | role, scope or project is not sufficient |
| 404 | not_found | does not exist — or not for this key |
| 409 | conflict | the state does not fit, for example a task already claimed |
| 413 | payload_too_large | body over 1 MB |
| 422 | validation_error | body invalid; error names the first field objected to as field: message |
| 423 | betrieb_gesperrt | the workspace is suspended, for example because no paid subscription is running |
| 429 | rate_limited | too many requests, wait briefly |
| 500 | internal_error | error at VentionDesk |
Every response carries an ID in the x-request-id header. If you send one yourself (up to 128 characters), it is used. Give it to support when something goes wrong.
Rate limits
Counted per minute, per key or per IP address:
| Area | Requests per minute |
|---|---|
all /v1 endpoints together | 300 |
POST /mcp | 60 |
| ticket intake (form, browser, server) | 10 per IP |
payment links /pay/… | 20 per IP |
Above the limit, the API answers with 429 and rate_limited; the RateLimit and RateLimit-Policy headers state the current count and the limit.
Lists and filters
List endpoints filter via query parameters and limit with limit; there are no pages with cursor or offset. Examples:
GET /v1/tasks?projectSlug=shop&status=todo&claimable=true&limit=20— tasks that can be pulled right now.GET /v1/projects?status=active— active projects.
Which parameters an endpoint knows and which fields it returns is listed in the API reference — read from the same contract VentionDesk uses to check the request.
The typical round of an agent
GET /v1/tasks?claimable=true— what needs doing?POST /v1/tasks/:id/claim— claim the task atomically. Two runs never get the same one.- Work; in between,
POST /v1/tasks/:id/resultwith"state": "arbeitet"as a sign of life. POST /v1/tasks/:id/resultwithkontrolle,frage,master,blockiertorerledigt— withkontrolleanderledigt, with evidence inevidence.
If you don’t want to build this yourself, use Claude and MCP.