# Taskfy Projects API: contexto para agentes

- Ambiente: Produção
- Base URL: https://taskfy.tec.br (use somente este domínio)
- Documentação: https://taskfy.tec.br/docs/api/projects
- OpenAPI: https://taskfy.tec.br/api/v1/projects/openapi.json
- Contexto para agentes: https://taskfy.tec.br/docs/api/projects/agent-context.md
- Prompt: https://taskfy.tec.br/docs/api/projects/prompt.txt
- API base: https://taskfy.tec.br/api/v1/projects/

Autenticação: `Authorization: Bearer $TASKFY_PROJECTS_API_KEY` (prefixo `erp_proj_`). Nunca imprima, registre em log ou faça commit da chave.

Se houver divergência entre este texto e o OpenAPI, prefira o OpenAPI.

---
# Taskfy Projects API

## Quick start

1. Read env vars (never hardcode secrets):
   - `TASKFY_BASE_URL`: origin of the ERP (e.g. `https://app.example.com`)
   - `TASKFY_PROJECTS_API_KEY`: Bearer key (`erp_proj_…`) from Projetos → API
2. Prefer the bundled CLI when available:
   `python scripts/taskfy_projects_api.py projects`
3. Without CLI, call HTTP with header `Authorization: Bearer $TASKFY_PROJECTS_API_KEY`.
4. Never print, log, or commit the API key.

## Workflow

1. `GET /api/v1/projects/workspaces/`: list clients with ids for `workspace_id` when creating projects.
2. `GET /api/v1/projects/`: list visible projects (ACL of key owner).
3. Ask which project if more than one; do not guess.
4. `GET /api/v1/projects/{id}/board-state/`: active sprint + backlog + demands + cards/comments.
5. `GET /api/v1/projects/{id}/columns/` when you need empty columns too.
6. `GET /api/v1/projects/{id}/members/` before assigning cards (`assignee_ids` = `user.id`).
7. `GET /api/v1/projects/{id}/labels/` before tagging cards (`label_ids`).
8. Prefer `POST .../cards/bulk/` with `dry_run` first for multi-card changes (one snapshot).
9. Convert demands via `POST .../demands/{id}/convert/` (idempotent).
10. Checklists: `GET/POST .../cards/{id}/checklists/`; items via `.../checklists/{cid}/items/`.
11. For writes: an explicit user request authorizes the matching POST/PATCH; confirm only DELETE, restore, destructive or ambiguous changes.
12. After writes, list `/snapshots/` if rollback may be needed (TTL 5 days).
13. New project: `GET /workspaces/` for client id, `GET /templates/?workspace_id=…` for `template_id`, then `POST /`.
14. Template edits (`/templates/{tid}/…`) only affect projects created afterwards; check `can_edit` first.
15. Columns of an existing board: `POST/PATCH/DELETE .../columns/` and `POST .../columns/reorder/` (full `column_ids`). DELETE moves cards to the first remaining column (snapshot first).
16. Form templates: `/form-templates/` (CRUD + fields + publish); attach with `POST /{id}/forms/`; read `.../forms/{iid}/responses/`.

## Endpoints

| Method | Path | Scope |
|--------|------|-------|
| GET | `/api/v1/projects/` | read |
| POST | `/api/v1/projects/` | write + `projects.manage` |
| GET | `/api/v1/projects/workspaces/` | read |
| GET/POST | `/api/v1/projects/form-templates/` | read / write |
| GET/PATCH/DELETE | `/api/v1/projects/form-templates/{tid}/` | read / write |
| GET/POST | `/api/v1/projects/form-templates/{tid}/fields/` | read / write |
| PATCH/DELETE | `/api/v1/projects/form-templates/{tid}/fields/{fid}/` | write |
| POST | `/api/v1/projects/form-templates/{tid}/fields/reorder/` | write |
| POST | `/api/v1/projects/form-templates/{tid}/publish/` | write |
| GET | `/api/v1/projects/form-templates/{tid}/responses/` | read |
| GET/POST | `/api/v1/projects/{id}/forms/` | read / write |
| GET/PATCH/DELETE | `/api/v1/projects/{id}/forms/{iid}/` | read / write |
| GET | `/api/v1/projects/{id}/forms/{iid}/responses/` | read |
| GET | `/api/v1/projects/{id}/forms/{iid}/responses/{rid}/` | read |
| GET | `/api/v1/projects/templates/` | read |
| GET/PATCH | `/api/v1/projects/templates/{tid}/` | read / write+admin |
| POST | `/api/v1/projects/templates/{tid}/columns/` (+ `reorder/`) | write+admin |
| PATCH/DELETE | `/api/v1/projects/templates/{tid}/columns/{cid}/` | write+admin |
| PUT | `/api/v1/projects/templates/{tid}/columns/{cid}/automations/` | write+admin |
| POST | `/api/v1/projects/templates/{tid}/sprints/` (+ `reorder/`) | write+admin |
| PATCH/DELETE | `/api/v1/projects/templates/{tid}/sprints/{sid}/` | write+admin |
| POST | `/api/v1/projects/templates/{tid}/cards/` | write+admin |
| PATCH/DELETE | `/api/v1/projects/templates/{tid}/cards/{card_id}/` | write+admin |
| GET | `/api/v1/projects/{id}/board-state/` | read |
| GET | `/api/v1/projects/{id}/columns/` | read |
| POST | `/api/v1/projects/{id}/columns/` | write + project admin + `kanban.manage` |
| PATCH/DELETE | `/api/v1/projects/{id}/columns/{cid}/` | write + project admin + `kanban.manage` |
| POST | `/api/v1/projects/{id}/columns/reorder/` | write + project admin + `kanban.manage` |
| GET | `/api/v1/projects/{id}/members/` | read |
| GET | `/api/v1/projects/{id}/labels/` | read |
| GET/PATCH | `/api/v1/projects/{id}/demands/{demand_id}/` | read / write |
| POST | `/api/v1/projects/{id}/demands/{demand_id}/convert/` | write |
| GET/POST | `/api/v1/projects/{id}/sprints/` | read / write |
| POST | `/api/v1/projects/{id}/sprints/{sid}/activate/` | write |
| POST | `/api/v1/projects/{id}/sprints/{sid}/close/` | write |
| GET | `/api/v1/projects/{id}/planning/` | read |
| GET/POST | `/api/v1/projects/{id}/automations/` | read / write+admin |
| PATCH/DELETE | `/api/v1/projects/{id}/automations/{rule_id}/` | write+admin |
| GET/POST | `/api/v1/projects/{id}/documents/` | read / write+docs |
| GET/PATCH/DELETE | `/api/v1/projects/{id}/documents/{doc_id}/` | read / write+docs |
| GET | `/api/v1/projects/{id}/cards/{card_id}/` | read |
| POST | `/api/v1/projects/{id}/cards/` | write |
| POST | `/api/v1/projects/{id}/cards/bulk/` | write |
| PATCH | `/api/v1/projects/{id}/cards/{card_id}/` | write |
| POST | `/api/v1/projects/{id}/cards/{card_id}/move-sprint/` | write |
| DELETE | `/api/v1/projects/{id}/cards/{card_id}/` | write (soft delete) |
| GET/POST | `/api/v1/projects/{id}/cards/{card_id}/comments/` | read / write |
| PATCH/DELETE | `/api/v1/projects/{id}/cards/{card_id}/comments/{cid}/` | write |
| GET/POST | `/api/v1/projects/{id}/cards/{card_id}/checklists/` | read / write |
| PATCH/DELETE | `/api/v1/projects/{id}/cards/{card_id}/checklists/{clid}/` | write |
| GET/POST | `/api/v1/projects/{id}/cards/{card_id}/checklists/{clid}/items/` | read / write |
| PATCH/DELETE | `/api/v1/projects/{id}/cards/{card_id}/checklists/{clid}/items/{iid}/` | write |
| GET | `/api/v1/projects/{id}/snapshots/` | read |
| GET | `/api/v1/projects/{id}/snapshots/{sid}/` | read |
| GET | `/api/v1/projects/{id}/snapshots/{sid}/diff/` | read |
| POST | `/api/v1/projects/{id}/snapshots/{sid}/restore/` | write |

OpenAPI: `https://taskfy.tec.br/api/v1/projects/openapi.json`  
Human docs: `https://taskfy.tec.br/docs/api/projects`

## Safety

- Key inherits owner ACL/RBAC; 403 means no permission: do not retry with other projects.
- Soft delete only; restores use snapshots.
- Do not use session UI routes under `/projetos/…/kanban/…`.

## Additional resources

- Contract details: [reference.md](reference.md)
- CLI (portable package only): `scripts/taskfy_projects_api.py`

---

# Taskfy Projects API: Reference

## Auth

```
Authorization: Bearer erp_proj_…
```

Scopes: `read` or `write` (write includes read). Key is bound to a user; project access follows ACL plus `kanban.view` / write ACL. Criar projeto exige `projects.manage`; editar modelos exige owner/admin de workspace admin.

## Endpoints

| Method | Path | Notes |
|--------|------|--------|
| GET | `/api/v1/projects/` | Projetos visíveis |
| POST | `/api/v1/projects/` | Criar projeto (`projects.manage`) |
| GET | `/api/v1/projects/workspaces/` | Clientes/workspaces com `id` para `workspace_id` |
| GET/POST | `/api/v1/projects/form-templates/` | Modelos de formulário (não confundir com `/templates/`) |
| GET/PATCH/DELETE | `/api/v1/projects/form-templates/{tid}/` | Detalhe / editar / soft delete |
| GET/POST | `/api/v1/projects/form-templates/{tid}/fields/` | Campos do modelo |
| PATCH/DELETE | `/api/v1/projects/form-templates/{tid}/fields/{fid}/` | Editar / remover campo |
| POST | `/api/v1/projects/form-templates/{tid}/fields/reorder/` | `field_ids` completos |
| POST | `/api/v1/projects/form-templates/{tid}/publish/` | Link da empresa (`share_uuid`) |
| GET | `/api/v1/projects/form-templates/{tid}/responses/` | Respostas da instância da empresa |
| GET/POST | `/api/v1/projects/{id}/forms/` | Listar / vincular modelo ao projeto |
| GET/PATCH/DELETE | `/api/v1/projects/{id}/forms/{iid}/` | Settings / soft delete |
| GET | `/api/v1/projects/{id}/forms/{iid}/responses/` | Respostas (`per_page` 20\|50\|100) |
| GET | `/api/v1/projects/{id}/forms/{iid}/responses/{rid}/` | Detalhe com `values[]` |
| GET | `/api/v1/projects/templates/` | Modelos para criação (`?workspace_id=`) ou `?editable=1` |
| GET/PATCH | `/api/v1/projects/templates/{tid}/` | Detalhe / dados do modelo |
| POST | `/api/v1/projects/templates/{tid}/columns/` | Nova coluna |
| PATCH/DELETE | `/api/v1/projects/templates/{tid}/columns/{cid}/` | Renomear/cor / remover |
| POST | `/api/v1/projects/templates/{tid}/columns/reorder/` | `column_ids` completos |
| PUT | `/api/v1/projects/templates/{tid}/columns/{cid}/automations/` | Substitui automações |
| POST | `/api/v1/projects/templates/{tid}/sprints/` | Nova sprint padrão |
| PATCH/DELETE | `/api/v1/projects/templates/{tid}/sprints/{sid}/` | Editar / remover |
| POST | `/api/v1/projects/templates/{tid}/sprints/reorder/` | `sprint_ids` completos |
| POST | `/api/v1/projects/templates/{tid}/cards/` | Novo card padrão |
| PATCH/DELETE | `/api/v1/projects/templates/{tid}/cards/{card_id}/` | Editar / remover |
| GET | `/api/v1/projects/{id}/board-state/` | Sprint ativa + backlog + demandas |
| GET | `/api/v1/projects/{id}/columns/` | **Todas** as colunas, inclusive vazias |
| POST | `/api/v1/projects/{id}/columns/` | Nova coluna no fim (admin + `kanban.manage`) |
| PATCH/DELETE | `/api/v1/projects/{id}/columns/{cid}/` | Nome/cor / remover (cards → primeira coluna restante) |
| POST | `/api/v1/projects/{id}/columns/reorder/` | `column_ids` completos |
| GET | `/api/v1/projects/{id}/members/` | Membros (`user.id` → `assignee_ids`) |
| GET | `/api/v1/projects/{id}/labels/` | Etiquetas (`id` → `label_ids`) |
| GET/PATCH | `/api/v1/projects/{id}/demands/{demand_id}/` | PATCH só em `open` |
| POST | `/api/v1/projects/{id}/demands/{demand_id}/convert/` | Idempotente |
| GET/POST | `/api/v1/projects/{id}/sprints/` | Listar / criar |
| POST | `/api/v1/projects/{id}/sprints/{sid}/activate/` | Ativar planejada |
| POST | `/api/v1/projects/{id}/sprints/{sid}/close/` | Encerrar (+ avanço automático) |
| GET | `/api/v1/projects/{id}/planning/` | Gantt / estimativa de sprints |
| GET/POST | `/api/v1/projects/{id}/automations/` | Regras + presets / criar |
| PATCH/DELETE | `/api/v1/projects/{id}/automations/{rule_id}/` | Toggle / remover |
| GET/POST | `/api/v1/projects/{id}/documents/` | Listar / criar (sync GED) |
| GET/PATCH/DELETE | `/api/v1/projects/{id}/documents/{doc_id}/` | Ler / atualizar / lixeira |
| POST | `/api/v1/projects/{id}/cards/` | Criar card |
| POST | `/api/v1/projects/{id}/cards/bulk/` | Lote + `dry_run` |
| GET/PATCH/DELETE | `/api/v1/projects/{id}/cards/{card_id}/` | Soft delete |
| POST | `/api/v1/projects/{id}/cards/{card_id}/move-sprint/` | `target: active\|backlog` |
| GET/POST | `/api/v1/projects/{id}/cards/{card_id}/comments/` | Comentários |
| PATCH/DELETE | `/api/v1/projects/{id}/cards/{card_id}/comments/{cid}/` | Autor ou `kanban.manage` |
| GET/POST | `/api/v1/projects/{id}/cards/{card_id}/checklists/` | Checklists (+ `items` no POST) |
| PATCH/DELETE | `/api/v1/projects/{id}/cards/{card_id}/checklists/{clid}/` | Renomear / remover |
| GET/POST | `/api/v1/projects/{id}/cards/{card_id}/checklists/{clid}/items/` | Itens |
| PATCH/DELETE | `.../checklists/{clid}/items/{iid}/` | `title` / `is_done` |
| GET | `/api/v1/projects/{id}/snapshots/` | Lista |
| GET | `/api/v1/projects/{id}/snapshots/{sid}/` | Detalhe + payload |
| GET | `/api/v1/projects/{id}/snapshots/{sid}/diff/` | Diff vs atual |
| POST | `/api/v1/projects/{id}/snapshots/{sid}/restore/` | Restore |

## Workspaces (clientes)

```
GET /api/v1/projects/workspaces/
→ { "workspaces": [{ "id", "name", "document", "workspace_type", "is_admin_workspace" }], "count": N }
```

Use `id` com `is_admin_workspace=false` como `workspace_id` ao criar projeto de cliente.

## Criar projeto

```json
POST /api/v1/projects/
{
  "name": "Site institucional",
  "workspace_id": "uuid-cliente|null",
  "template_id": "uuid|null",
  "gestor_id": "uuid?",
  "project_manager_id": "uuid?",
  "description": "string",
  "status": "planning|in_progress|paused|done",
  "start_date": "2026-10-06",
  "end_date": null
}
```

- Descubra clientes em `GET /workspaces/` antes de enviar `workspace_id`.
- Sem `workspace_id` → projeto interno (empresa admin).
- `gestor_id` / `project_manager_id` omitidos → dono da chave. Só colaboradores internos.
- `template_id` deve estar em `GET /templates/?workspace_id=…`; copia colunas, automações, sprints e cards padrão.
- Respeita módulo Projetos e limite de projetos do plano (403/400).
- 400 traz `errors` por campo (`name`, `workspace_id`, `template_id`, …). Resposta 201: `{ "project": {...} }`.

## Formulários de projeto

`/form-templates/` = modelos reutilizáveis (Configurações → Projetos → Formulários).  
`/templates/` = modelos de **tipo de projeto** (Kanban). Não misturar.

```json
POST /form-templates/  { "name": "Briefing", "description": "…" }
POST /form-templates/{tid}/fields/
{ "label": "E-mail", "name": "email", "field_type": "email", "is_required": true }
POST /form-templates/{tid}/fields/
{ "label": "Tipo", "field_type": "select", "options": ["A", "B"] }
POST /form-templates/{tid}/publish/  → form_instance.public_url
POST /{project_id}/forms/  { "template_id": "…" }  → clona para o projeto
GET  /{project_id}/forms/{iid}/responses/?per_page=20
GET  /{project_id}/forms/{iid}/responses/{rid}/
```

Envio público continua em `/projetos/f/<share_uuid>/` (sem ingestão autenticada nesta API).

## Modelos de projeto

Escrita exige escopo `write` + owner/admin de workspace admin (igual a Configurações → Modelos). Super workspace edita modelos `global`/`internal`; os demais editam modelos `workspace` dos seus clientes. Alterações só afetam projetos criados depois.

```json
PATCH /templates/{tid}/
{ "name": "…", "description": "…", "project_mode": "stages|sprints|hybrid",
  "sprint_policy": "one_at_a_time|planned_sequence|automatic_sequence",
  "show_planning_tab": true, "show_demands_tab": true, "is_active": true, "order": 0 }

POST /templates/{tid}/columns/          { "name": "Review", "color": "#94a3b8" }
POST /templates/{tid}/columns/reorder/  { "column_ids": ["uuid", "…todas…"] }
PUT  /templates/{tid}/columns/{cid}/automations/
{ "automations": [{ "preset_slug": "set_due_date_on_enter", "params": { "business_days": 3 } }] }

POST /templates/{tid}/sprints/
{ "name": "Sprint 1", "duration_amount": 2, "duration_unit": "weeks", "goal": "…", "auto_start": true }

POST /templates/{tid}/cards/
{ "title": "Kickoff", "scope": "every_sprint|specific_sprint", "template_sprint_id": "uuid?",
  "target_column_id": "uuid?", "card_type": "task|epic", "template_epic_id": "uuid?",
  "priority": 3, "checklist_items": ["Agenda", "Ata"], "is_active": true }
```

- `scope` e `workspace` do modelo não são editáveis pela API.
- `PUT …/automations/` com lista vazia remove todas; presets em `GET /{project_id}/automations/`.
- `checklist_items`: até 50 itens de até 200 caracteres.

## Colunas do quadro (projeto existente)

Exige escopo `write`, admin do projeto e role `kanban.manage` (mesma regra da tela do Kanban). Não altera o modelo do projeto.

```json
POST   .../columns/                 { "name": "Homologação", "color": "#94a3b8" }
PATCH  .../columns/{cid}/           { "name": "QA" }            // name e/ou color
POST   .../columns/reorder/         { "column_ids": ["uuid", "…todas…"] }
DELETE .../columns/{cid}/
```

- POST cria no fim do quadro; resposta `201` com `column`.
- DELETE move os cards para a primeira coluna restante (gera snapshot antes) e falha (400) se for a última coluna.
- reorder exige **todas** as colunas do quadro, sem ids de outro quadro.

## Column flags (automáticos)

- Nome contém `concl` ou `done` → preenche `completed_at` (criação ou movimento).
- Nome contém `bloque` ou `block` → marca `is_blocked` ao entrar.
- Sair de coluna de bloqueio → limpa `is_blocked` (salvo `is_blocked` explícito no PATCH).

## PATCH card body

Envie só os campos que mudam:

```json
{
  "title": "string",
  "description": "string",
  "priority": 1,
  "due_date": "2026-09-20",
  "start_date": null,
  "is_archived": false,
  "is_blocked": false,
  "card_type": "task",
  "epic_id": "uuid|null",
  "sprint_id": "uuid|null",
  "sprint": "active|backlog",
  "column_id": "uuid",
  "order": 0,
  "assignee_ids": ["uuid"],
  "label_ids": ["uuid"]
}
```

`completed_at` **não** é gravável diretamente: deriva da coluna.

`epic_id` vincula um card `task` a um card `epic` do mesmo board. `null` ou `""` remove o vínculo. Cards com `card_type=epic` não aceitam `epic_id` preenchido. Também aceite em `POST .../cards/` e no bulk (`update_card` / `create_card`).

`assignee_ids` só aceita `user.id` de membros do projeto (`GET .../members/`).
`label_ids` só aceita labels do projeto (`GET .../labels/`). Lista vazia remove todos.

## Membros e etiquetas

```json
GET .../members/
→ { "members": [{ "user": { "id", "name", "email" }, "can_write": true, ... }] }

GET .../labels/
→ { "labels": [{ "id", "name", "color", "order" }] }

PATCH .../cards/{card_id}/
{ "assignee_ids": ["user-uuid"], "label_ids": ["label-uuid"] }
```

## Checklists

```json
POST .../cards/{card_id}/checklists/
{
  "title": "Definition of Done",
  "items": ["Testes", "Code review", { "title": "Deploy" }]
}

POST .../cards/{card_id}/checklists/{clid}/items/
{ "title": "Novo item" }

PATCH .../cards/{card_id}/checklists/{clid}/items/{iid}/
{ "is_done": true }
```

GET card (`.../cards/{id}/`) inclui `checklists` e `checklist_progress`. Marcar todos os itens como feitos pode disparar automação `move_when_checklist_done`.

## Mover entre backlog e sprint ativa

```json
POST .../cards/{card_id}/move-sprint/
{ "target": "active" }
```

`target`: `active` (sprint ativa) ou `backlog`. Também no PATCH: `"sprint": "active"|"backlog"`. Bulk: `{"action":"move_sprint","card_id":"…","target":"active"}`.

## Sprints

```json
POST .../sprints/
{
  "name": "Sprint 3",
  "start_date": "2026-10-01",
  "duration_amount": 2,
  "duration_unit": "weeks",
  "auto_start": false,
  "goal": "..."
}
```

- `POST .../sprints/{id}/activate/`: ativa planejada (falha se já houver outra ativa).
- `POST .../sprints/{id}/close/`: encerra; com política `planned_sequence` / `automatic_sequence` devolve `next_sprint`.
- `GET .../planning/`: estimativa Gantt (não atribui cards automaticamente).

## Automações

```json
POST .../automations/
{
  "column_id": "uuid",
  "preset_slug": "set_due_date_on_enter",
  "params": { "business_days": 3 }
}
```

PATCH: `{ "is_enabled": false }`. Escrita exige admin do projeto + `kanban.manage`.

## Documentos

```json
POST .../documents/
{ "title": "RFC", "content_markdown": "# ..." }
```

GET detalhe inclui `content_markdown`. Escrita exige `projects.documentation`. Sync GED automático.

## Comentários

```json
POST .../cards/{card_id}/comments/
{ "body": "Markdown/texto", "mentioned_ids": ["uuid?"] }
```

- Autor do comentário = dono da chave API.
- PATCH/DELETE: só o autor ou role `kanban.manage`.
- Sem anexos nesta API (texto + menções).
- Comentários **não** geram snapshot isolado (evita spam); entram no payload se houver snapshot de escrita estrutural.

Bulk: `add_comment`, `update_comment`, `delete_comment`.

```json
POST .../demands/{id}/convert/
{ "column_id": "uuid?", "sprint_id": "uuid?" }
```

Resposta `201` na primeira conversão; `200` + `"idempotent": true` se já convertida (mesmo card).

## Bulk

```json
{
  "dry_run": true,
  "operations": [
    {"action": "move_card", "card_id": "uuid", "column_id": "uuid", "order": 0},
    {"action": "update_card", "card_id": "uuid", "title": "…", "epic_id": "uuid?"},
    {"action": "create_card", "title": "…", "column_id": "uuid", "epic_id": "uuid?"},
    {"action": "delete_card", "card_id": "uuid"},
    {"action": "convert_demand", "demand_id": "uuid"},
    {"action": "add_comment", "card_id": "uuid", "body": "texto"}
  ]
}
```

- Valida **todas** as operações antes de aplicar.
- Com `dry_run: false`, gera **um** snapshot para o lote inteiro.
- Máximo 100 operações.

## Errors

| HTTP | Meaning |
|------|---------|
| 401 | Missing/invalid key |
| 403 | Payment gate, scope write missing, or ACL |
| 404 | Not found |
| 409 | Demanda convertida/encerrada |
| 429 | Rate limit |

## Snapshots

Writes em cards (e convert/bulk aplicados) criam snapshot (sprint ativa + backlog), TTL **5 dias**. Preferir `cards/bulk` para evitar metralhadora de snapshots.

## Env

| Variable | Required |
|----------|----------|
| `TASKFY_BASE_URL` | yes |
| `TASKFY_PROJECTS_API_KEY` | yes: never embed in skill files |
