SecondBrain/reference/planka-v2-api-gotchas.md

4.3 KiB

summary tags scope source date last_updated
Planka v2.1.1 API and Docker behaviors that differ from or are absent in its docs — auth/PAT limits, first-login terms flow, card-move and project-delete quirks, container uid — plus service-account credentials location for planka.hyperthrive.io.
type/reference
tool/planka
project/backlog-pilot
domain/self-hosting
global ovh-prod Planka deploy, 2026-07-08 2026-07-08 2026-07-10

Planka v2 API Gotchas (verified on v2.1.1)

Discovered during the ovh-prod deploy (2026-07-08), verified against a live v2.1.1 instance. Directly relevant to the backlog CLI in ~/dev/ruby-gems and any Planka automation.

  1. No scoped API keys / PATs. Only POST /api/access-tokens with {emailOrUsername, password} → bearer JWT. Upstream issue #945 still open as of v2.1.1. Automation must hold real user credentials; a scoped-token proxy is the workaround.
  2. First-ever login requires terms acceptance. The first POST /api/access-tokens returns E_FORBIDDEN with a pendingToken and step: "accept-terms". Flow: GET /api/terms → take item.signaturePOST /api/access-tokens/accept-terms with {pendingToken, signature} → real token. Subsequent logins are normal.
  3. Card move needs both fields. PATCH /api/cards/:id requires both listId and position; listId alone returns E_MISSING_OR_INVALID_PARAMS.
  4. Project delete is not cascading. DELETE /api/projects/:id returns 422 "Must not have boards" — delete each board first. The web UI hits the same constraint but fails silently: the project vanishes locally, then reappears on reload with no error shown (confirmed 2026-07-10 on the personal-account project). A non-manager attempting the delete gets a 404 (existence hidden, same as gotcha #6). Escape hatch when no manager creds are at hand: insert a row into project_manager directly in Postgres (docker exec planka-postgres psql -U planka), then delete boards + project via the API as that user.
  5. Sails literal-alias routes. Some delete routes embed a literal parameter-name segment: card-label detach is DELETE /api/cards/:cardId/card-labels/labelId::labelId (the path literally contains labelId: before the id). Normal REST-shaped guesses 404. When a route 404s unexpectedly, check server/config/routes.js in the Planka source. Related: GET /api/tasks/:id doesn't exist (404s to SPA HTML — check Content-Type); cross-board card move needs boardId in the PATCH on top of listId+position.
  6. Webhooks are instance-level and admin-only. GET/POST /api/webhooks etc.; a non-admin gets 404 (Sails hides existence, not 403). Not board/project-scoped; events are comma-separated strings in, arrays out.
  7. Attachment download auth differs. GET /attachments/:id/download/:filename (outside /api) authenticates via Cookie: accessToken=<jwt> (or x-api-key) — Authorization: Bearer 401s on this route. Also means VCR/webmock setups must filter Cookie headers, not just Authorization, or the JWT leaks into cassettes.
  8. Container uid. The ghcr.io/plankanban/planka image runs as uid 1000 (node). A bind-mounted /app/data must be chown 1000 or attachment/avatar uploads fail (container starts healthy either way — the failure is silent until first upload).

Service accounts (planka.hyperthrive.io)

Created 2026-07-08 because of gotcha #1 (no PATs — automation holds real user creds). Credentials live on the OVH server (ssh alias ovh-vps) at ~/services/planka/bot-credentials.env (mode 600); admin creds remain in ~/services/planka/.env. Copy to Bitwarden.

  • planka_bot — role projectOwner; for the planka-api gem / backlog CLI / cron tick. Env vars: PLANKA_BASE_URL, PLANKA_USERNAME, PLANKA_PASSWORD.
  • hermes — role boardUser (deliberately restricted; Hermes agent is less trusted). Env vars: HERMES_PLANKA_USERNAME, HERMES_PLANKA_PASSWORD.
  • Usernames must match /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/ — no hyphens (hence planka_bot, not planka-bot).

Deploy as-built details live in the repo: ovh-prod/docs/planka-deploy-prd.md. Related: vault-backlog-pilot-plan, backlog-system-options-research.