Open a TaskStream board when a project is created through the API
Some checks failed
Tests / lint (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / test-postgres (push) Has been cancelled
Tests / mcp (push) Has been cancelled

`taskstream: true` on POST /v1/projects also creates the project in
TaskStream, public, as the caller (their Pireon token is passed on), and
the answer carries `taskstream_key` and `taskstream_url`. The key is
`taskstream_key` or one made from the default-language slug.

The board is opened before the project is saved: if TaskStream refuses
it, nothing is created here and the reason comes back as a validation
error (502 when TaskStream does not answer). Machine tokens need the
server's own TASKSTREAM_TOKEN. Updates ignore the flag, and duplicates do
not inherit the link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 21:12:36 +02:00
parent 9390db18c2
commit d30dd971d7
15 changed files with 621 additions and 10 deletions

View File

@@ -1566,6 +1566,16 @@ paths:
`technology_ids`, `service_ids`, and `members` with each one's role.
`published_at` and `unpublish_at` require `projects.publish`. For a
cover, upload it with `POST /v1/media` and `for: projects`.
With `taskstream: true` the project also gets a **public** board in
TaskStream, the collective's issue tracker, and the answer carries its
link in `taskstream_url`. Its key is `taskstream_key`, or one made from
the slug in the default language (one word: its first ten characters;
several: their initials). The board is opened first: if TaskStream
refuses it (a key in use, say), nothing is created here either and the
reason comes back as a validation error. It is opened as the caller,
whose Pireon token is passed on; a machine token cannot ask for one
unless the server has a `TASKSTREAM_TOKEN` of its own.
requestBody:
required: true
content:
@@ -1582,6 +1592,8 @@ paths:
data: { $ref: "#/components/schemas/Project" }
"403": { $ref: "#/components/responses/Forbidden" }
"422": { $ref: "#/components/responses/ValidationError" }
"502":
description: TaskStream did not answer; nothing was created.
/v1/projects/{project}:
parameters:
@@ -5503,6 +5515,8 @@ components:
is_featured: { type: boolean }
needs_help: { type: boolean }
demo_url: { type: string, nullable: true, format: uri }
taskstream_key: { type: string, nullable: true, description: "The project's key in TaskStream, when it has a board there." }
taskstream_url: { type: string, nullable: true, format: uri, readOnly: true, description: Where its TaskStream board opens. }
cover_image: { type: string, nullable: true, description: The stored path — what a client PATCHes back. }
cover_image_media:
allOf: [{ $ref: "#/components/schemas/Image" }]
@@ -5549,6 +5563,14 @@ components:
is_featured: { type: boolean }
needs_help: { type: boolean }
demo_url: { type: string, format: uri, nullable: true }
taskstream:
type: boolean
description: On creation only. Also open a public board for it in TaskStream; see `POST /v1/projects`.
taskstream_key:
type: string
nullable: true
pattern: "^[A-Za-z][A-Za-z0-9]{1,9}$"
description: On creation only, with `taskstream`. The key in TaskStream; made from the slug when left out.
cover_image: { type: string, nullable: true, description: A path from `POST /v1/media` with `for=projects`. }
started_at: { type: string, format: date, nullable: true }
ended_at: { type: string, format: date, nullable: true }