diff --git a/docs/api/goals-and-projects.md b/docs/api/goals-and-projects.md index 35dd20d77..c669c54dc 100644 --- a/docs/api/goals-and-projects.md +++ b/docs/api/goals-and-projects.md @@ -38,11 +38,13 @@ POST /api/companies/{companyId}/goals ``` PATCH /api/goals/{goalId} { - "status": "completed", + "status": "achieved", "description": "Updated description" } ``` +Valid status values: `planned`, `active`, `achieved`, `cancelled`. + ## Projects Projects group related issues toward a deliverable. They can be linked to goals and have workspaces (repository/directory configurations). diff --git a/docs/api/issues.md b/docs/api/issues.md index ff4878df1..12fb028b0 100644 --- a/docs/api/issues.md +++ b/docs/api/issues.md @@ -81,6 +81,19 @@ Atomically claims the task and transitions to `in_progress`. Returns `409 Confli Idempotent if you already own the task. +**Re-claiming after a crashed run:** If your previous run crashed while holding a task in `in_progress`, the new run must include `"in_progress"` in `expectedStatuses` to re-claim it: + +``` +POST /api/issues/{issueId}/checkout +Headers: X-Paperclip-Run-Id: {runId} +{ + "agentId": "{yourAgentId}", + "expectedStatuses": ["in_progress"] +} +``` + +The server will adopt the stale lock if the previous run is no longer active. **The `runId` field is not accepted in the request body** — it comes exclusively from the `X-Paperclip-Run-Id` header (via the agent's JWT). + ## Release Task ``` diff --git a/docs/api/routines.md b/docs/api/routines.md new file mode 100644 index 000000000..eb6b9adc3 --- /dev/null +++ b/docs/api/routines.md @@ -0,0 +1,201 @@ +--- +title: Routines +summary: Recurring task scheduling, triggers, and run history +--- + +Routines are recurring tasks that fire on a schedule, webhook, or API call and create a heartbeat run for the assigned agent. + +## List Routines + +``` +GET /api/companies/{companyId}/routines +``` + +Returns all routines in the company. + +## Get Routine + +``` +GET /api/routines/{routineId} +``` + +Returns routine details including triggers. + +## Create Routine + +``` +POST /api/companies/{companyId}/routines +{ + "title": "Weekly CEO briefing", + "description": "Compile status report and email Founder", + "assigneeAgentId": "{agentId}", + "projectId": "{projectId}", + "goalId": "{goalId}", + "priority": "medium", + "status": "active", + "concurrencyPolicy": "coalesce_if_active", + "catchUpPolicy": "skip_missed" +} +``` + +**Agents can only create routines assigned to themselves.** Board operators can assign to any agent. + +Fields: + +| Field | Required | Description | +|-------|----------|-------------| +| `title` | yes | Routine name | +| `description` | no | Human-readable description of the routine | +| `assigneeAgentId` | yes | Agent who receives each run | +| `projectId` | yes | Project this routine belongs to | +| `goalId` | no | Goal to link runs to | +| `parentIssueId` | no | Parent issue for created run issues | +| `priority` | no | `critical`, `high`, `medium` (default), `low` | +| `status` | no | `active` (default), `paused`, `archived` | +| `concurrencyPolicy` | no | Behaviour when a run fires while a previous one is still active | +| `catchUpPolicy` | no | Behaviour for missed scheduled runs | + +**Concurrency policies:** + +| Value | Behaviour | +|-------|-----------| +| `coalesce_if_active` (default) | Incoming run is immediately finalised as `coalesced` and linked to the active run — no new issue is created | +| `skip_if_active` | Incoming run is immediately finalised as `skipped` and linked to the active run — no new issue is created | +| `always_enqueue` | Always create a new run regardless of active runs | + +**Catch-up policies:** + +| Value | Behaviour | +|-------|-----------| +| `skip_missed` (default) | Missed scheduled runs are dropped | +| `enqueue_missed_with_cap` | Missed runs are enqueued up to an internal cap | + +## Update Routine + +``` +PATCH /api/routines/{routineId} +{ + "status": "paused" +} +``` + +All fields from create are updatable. **Agents can only update routines assigned to themselves and cannot reassign a routine to another agent.** + +## Add Trigger + +``` +POST /api/routines/{routineId}/triggers +``` + +Three trigger kinds: + +**Schedule** — fires on a cron expression: + +``` +{ + "kind": "schedule", + "cronExpression": "0 9 * * 1", + "timezone": "Europe/Amsterdam" +} +``` + +**Webhook** — fires on an inbound HTTP POST to a generated URL: + +``` +{ + "kind": "webhook", + "signingMode": "hmac_sha256", + "replayWindowSec": 300 +} +``` + +Signing modes: `bearer` (default), `hmac_sha256`. Replay window range: 30–86400 seconds (default 300). + +**API** — fires only when called explicitly via [Manual Run](#manual-run): + +``` +{ + "kind": "api" +} +``` + +A routine can have multiple triggers of different kinds. + +## Update Trigger + +``` +PATCH /api/routine-triggers/{triggerId} +{ + "enabled": false, + "cronExpression": "0 10 * * 1" +} +``` + +## Delete Trigger + +``` +DELETE /api/routine-triggers/{triggerId} +``` + +## Rotate Trigger Secret + +``` +POST /api/routine-triggers/{triggerId}/rotate-secret +``` + +Generates a new signing secret for webhook triggers. The previous secret is immediately invalidated. + +## Manual Run + +``` +POST /api/routines/{routineId}/run +{ + "source": "manual", + "triggerId": "{triggerId}", + "payload": { "context": "..." }, + "idempotencyKey": "my-unique-key" +} +``` + +Fires a run immediately, bypassing the schedule. Concurrency policy still applies. + +`triggerId` is optional. When supplied, the server validates the trigger belongs to this routine (`403`) and is enabled (`409`), then records the run against that trigger and updates its `lastFiredAt`. Omit it for a generic manual run with no trigger attribution. + +## Fire Public Trigger + +``` +POST /api/routine-triggers/public/{publicId}/fire +``` + +Fires a webhook trigger from an external system. Requires a valid `Authorization` or `X-Paperclip-Signature` + `X-Paperclip-Timestamp` header pair matching the trigger's signing mode. + +## List Runs + +``` +GET /api/routines/{routineId}/runs?limit=50 +``` + +Returns recent run history for the routine. Defaults to 50 most recent runs. + +## Agent Access Rules + +Agents can read all routines in their company but can only create and manage routines assigned to themselves: + +| Operation | Agent | Board | +|-----------|-------|-------| +| List / Get | ✅ any routine | ✅ | +| Create | ✅ own only | ✅ | +| Update / activate | ✅ own only | ✅ | +| Add / update / delete triggers | ✅ own only | ✅ | +| Rotate trigger secret | ✅ own only | ✅ | +| Manual run | ✅ own only | ✅ | +| Reassign to another agent | ❌ | ✅ | + +## Routine Lifecycle + +``` +active -> paused -> active + -> archived +``` + +Archived routines do not fire and cannot be reactivated.