HTTP API
Project API keys let a script start a coding-agent run, poll compact status and results, drive plan approval, and mark a finished session done without the UI. Existing /api/workflows/* routes are unchanged.
Authentication
Create a key in project settings (owner or admin). Send it as Authorization: Bearer flw_…. The project is implied by the key. Missing or revoked keys return 401. A key from another project cannot read or mutate that project's runs (403 / 404). Conflicting coding starts return 409.
Project settings → API keys → Create key Store the token once. It is never shown again.
Start a coding run
POST /api/v1/runs starts the base coding-agent workflow or a child coding workflow. Send only the fields you want to override. Hidden child defaults (provider, location, model, runtime, planMode, designMode, and promptTemplate) stay intact. A hidden child agentProvider is not overwritten by project settings. Local starts need a runtime environment: pass runtimeEnvironmentId or the project's first enabled environment is used. Without one the request returns 400. A supplied repositoryId is resolved against the key's project before the run opens: malformed, disabled, cross-project, or unmapped repositories return 400. designMode: true requires planMode and is rejected before start if the combination is incompatible.
curl -X POST https://YOUR_HOST/api/v1/runs \
-H "Authorization: Bearer flw_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflowSlug": "debug-issue",
"input": {
"prompt": "Fix the null pointer in checkout."
}
}'Attachments
Upload first with POST /api/v1/attachments (multipart file), then pass input.attachments, or send files on the start request as multipart files. You can also pass a public https input.fileUrl (or input.fileUrls). Flowman fetches that URL immediately, stores it as a normal attachment, and injects it into the agent as an attached file. Limits match the UI: 5 files, 20 MB each, images, PDFs, and text. Unused uploads can be removed with DELETE /api/v1/attachments/:attachmentId before they are attached to a run.
curl -X POST https://YOUR_HOST/api/v1/attachments \
-H "Authorization: Bearer flw_YOUR_KEY" \
-F "file=@screenshot.png"
curl -X POST https://YOUR_HOST/api/v1/runs \
-H "Authorization: Bearer flw_YOUR_KEY" \
-F 'workflowSlug=coding-agent' \
-F 'input={"prompt":"Use the attached screenshot.","attachments":[{"id":"workflowAttachments_..."}]}' \
-F "files=@notes.txt"
curl -X POST https://YOUR_HOST/api/v1/runs \
-H "Authorization: Bearer flw_YOUR_KEY" \
-F 'workflowSlug=api-cloud-ocr' \
-F 'input={"prompt":"Extract receipt fields.","fileUrl":"https://....convex.cloud/api/storage/...","fileName":"receipt.jpg","contentType":"image/jpeg"}'Poll status and result
GET /api/v1/runs/:runSlug returns run status, current step, terminal output, compact attachment metadata, and the coding session. session.output is the latest completed implementation or follow-up reply. Housekeeping turns such as worktree refresh or base sync are skipped. If no conversational turn has finished yet, the stored session transcript is used (for example planning text). It is null until either exists. waitingFor is none, answers, or plan_decision. Design sessions include session.designMode.
curl https://YOUR_HOST/api/v1/runs/run_abc123 \
-H "Authorization: Bearer flw_YOUR_KEY"{
"runSlug": "run_abc123",
"status": "waiting",
"currentStep": "Implementing Cursor prompt",
"waitingFor": "none",
"session": {
"sessionId": "codingAgentSessions_...",
"status": "implemented",
"designMode": false,
"plan": null,
"questions": [],
"output": "The only top-level file is README.md.",
"error": null
}
}Plan loop
When waitingFor is plan_decision, call POST /api/v1/runs/:runSlug/approve or /reject. Design-mode sessions must use POST /api/v1/runs/:runSlug/finalize instead of approve — /finalize is only for design sessions. For questions, POST /api/v1/runs/:runSlug/answers with { "answers": [{ "questionId", "optionId" }] }.
curl -X POST https://YOUR_HOST/api/v1/runs/run_abc123/approve \
-H "Authorization: Bearer flw_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'Follow-up
After implementation, POST /api/v1/runs/:runSlug/follow-up with { "prompt": "…" } queues another turn on the same session. Poll again for the reply in session.output.
curl -X POST https://YOUR_HOST/api/v1/runs/run_abc123/follow-up \
-H "Authorization: Bearer flw_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"In one sentence, what is the name of the only top-level file?"}'Mark done
When the session is implemented or chat_failed, POST /api/v1/runs/:runSlug/done marks it done — the same as Mark done in the UI. The body may be empty. A successful call returns { "ok": true, "completionQueued": false }. completionQueued is true when a Jira completion turn is queued instead of closing immediately. Open pull requests or a session that is not ready return 400.
curl -X POST https://YOUR_HOST/api/v1/runs/run_abc123/done \
-H "Authorization: Bearer flw_YOUR_KEY"