Chapter: API: basics

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.

PrefixKindWhat for
vd_live_agentpull tasks and report back, read projects, write notes, MCP
vd_intake_intakeonly submit tickets (POST /v1/intake/tickets), see Ticket intake
vd_pub_public project keynot 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:

ScopeMeaningAvailable for
projects:readRead projectsagent keys
tasks:readRead tasksagent keys
tasks:writeCreate tasks and report backagent keys
tickets:readRead ticketsagent keys, only if the workspace explicitly enables it
tickets:writeCreate tickets, write internal notes and draftsagent keys, only if the workspace explicitly enables it
tickets:replyReply to customers on tickets by emailagent keys, only if the workspace explicitly enables it
notes:writeWrite notesagent keys
deployments:readRead commits, pull requests and deploymentsagent keys
agent:runStart and finish runsagent keys
intake:writeSubmit tickets through the intakeintake keys only
invoices:writeCreate invoice drafts and change line itemsagent keys, only if the workspace explicitly enables it
errors:readRead the projects' errorsagent keys, only if the workspace explicitly enables it
errors:writeMark errors as resolvedagent keys, only if the workspace explicitly enables it
time:readRead the projects' time entries (without rates and amounts)agent keys, only if the workspace explicitly enables it
stats:readRead the projects' key figures for reports (without money)agent keys, only if the workspace explicitly enables it
quotes:writeCreate and change quote draftsagent keys, only if the workspace explicitly enables it
roadmap:writeCreate, change and order roadmap phasesagent keys, only if the workspace explicitly enables it
ideas:writeCreate ideasagent 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:

StatuscodeMeaning
400validation_errorquery or path parameters invalid
401unauthorizedno key or an invalid key
403forbiddenrole, scope or project is not sufficient
404not_founddoes not exist — or not for this key
409conflictthe state does not fit, for example a task already claimed
413payload_too_largebody over 1 MB
422validation_errorbody invalid; error names the first field objected to as field: message
423betrieb_gesperrtthe workspace is suspended, for example because no paid subscription is running
429rate_limitedtoo many requests, wait briefly
500internal_errorerror 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:

AreaRequests per minute
all /v1 endpoints together300
POST /mcp60
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

  1. GET /v1/tasks?claimable=true — what needs doing?
  2. POST /v1/tasks/:id/claim — claim the task atomically. Two runs never get the same one.
  3. Work; in between, POST /v1/tasks/:id/result with "state": "arbeitet" as a sign of life.
  4. POST /v1/tasks/:id/result with kontrolle, frage, master, blockiert or erledigt — with kontrolle and erledigt, with evidence in evidence.

If you don’t want to build this yourself, use Claude and MCP.

API: basics | VentionDesk Docs