Code Agents (Cursor-backed)
Create cloud code agents that work on GitHub repos, open PRs, and accept follow-up prompts — powered by Cursor's cloud SDK.
Code agents are cloud-based coding assistants backed by the Cursor SDK. You define a template (code agent) with a default repo, branch, model, and preamble, then launch sessions that clone the repo, execute your prompt, and optionally open a pull request.
Concepts
| Term | Description |
|---|---|
| Code Agent | A reusable template: default repo URL, branch, model, preamble, and PR behavior flags. |
| Code Session | A single execution of a template against a repo. Contains one or more messages, tracks status, and can produce a PR. |
| Message | A prompt sent to a session. The first message starts the session; subsequent messages are follow-ups on the same workspace. |
| Event | A timestamped record of session activity (stdout, tool calls, file edits, etc.) streamed from the Cursor runtime. |
Lifecycle
Create code agent template
→ Start session (POST /api/code-sessions)
→ Session status: pending → creating → running → finished/error/cancelled/expired
→ Send follow-up messages (POST /api/code-sessions/{id}/messages)
→ Cancel (POST /api/code-sessions/{id}/cancel)
→ Archive (POST /api/code-sessions/{id}/archive)
API Reference
All endpoints require Authorization: Bearer fj_live_....
Code Agent Templates
List Templates
GET /api/code-agents
Query parameters:
| Parameter | Type | Description |
|---|---|---|
orgId | string | Target org (multi-org only) |
Response 200:
[
{
"id": "ca-123",
"org_id": "org-456",
"name": "Bug Fixer",
"description": "Fixes bugs from GitHub issues",
"model_id": "auto",
"model_params": null,
"default_repo_url": "https://github.com/acme/app",
"default_starting_ref": "main",
"auto_create_pr": true,
"work_on_current_branch": false,
"skip_reviewer_request": false,
"preamble": "You are a senior engineer. Fix the bug described in the prompt.",
"created_by": "user-789",
"created_at": "2026-05-06T12:00:00Z",
"updated_at": "2026-05-06T12:00:00Z"
}
]
Get Template
GET /api/code-agents/{codeAgentId}
Response 200: Full code agent object (same shape as list item, all fields populated).
Create Template
POST /api/code-agents
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Display name (default: "New code agent") |
description | string | No | What this template does |
orgId | string | No | Target org (multi-org only) |
modelId | string | No | Model ID (default: "auto") |
modelParams | object | No | Model-specific parameters |
defaultRepoUrl | string | No | Default GitHub repo URL |
defaultStartingRef | string | No | Default branch (default: "main") |
autoCreatePR | boolean | No | Auto-create PR on completion (default: true) |
workOnCurrentBranch | boolean | No | Work directly on the starting ref instead of creating a new branch (default: false) |
skipReviewerRequest | boolean | No | Skip requesting reviewers on PRs (default: false) |
preamble | string | No | System prompt prepended to every session prompt |
Response 201: The created code agent object.
curl -X POST https://api.flapjack.dev/api/code-agents \
-H "Authorization: Bearer fj_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Bug Fixer",
"defaultRepoUrl": "https://github.com/acme/app",
"preamble": "Fix the bug described in the prompt. Write tests.",
"autoCreatePR": true
}'
Update Template
PATCH /api/code-agents/{codeAgentId}
Request body: Any of the create fields. Only include fields you want to change.
Response 200: The updated code agent object.
Delete Template
DELETE /api/code-agents/{codeAgentId}
Response 200: { "ok": true }
Code Sessions
List Sessions
GET /api/code-sessions
Query parameters:
| Parameter | Type | Description |
|---|---|---|
codeAgentId | string | Filter by template |
status | string | Filter by status (pending, creating, running, finished, error, cancelled, expired) |
archived | "true" | "false" | Filter by archived flag |
orgId | string | Target org (multi-org only) |
Response 200:
[
{
"id": "cs-456",
"org_id": "org-789",
"code_agent_id": "ca-123",
"cursor_agent_id": "cursor-abc",
"repo_url": "https://github.com/acme/app",
"starting_ref": "main",
"status": "finished",
"latest_run_id": "run-xyz",
"pr_url": "https://github.com/acme/app/pull/42",
"pr_branch": "fix/login-bug",
"archived": false,
"model_id": "auto",
"created_at": "2026-05-20T10:00:00Z",
"started_at": "2026-05-20T10:00:05Z",
"completed_at": "2026-05-20T10:03:22Z"
}
]
Start Session
POST /api/code-sessions
Creates a new session from a code agent template and immediately begins execution.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
codeAgentId | string | Yes | Template to run |
prompt | string | Yes | The task for the code agent |
cursorApiKey | string | Yes | Cursor API key (encrypted at rest, cleared on archive) |
orgId | string | No | Target org (multi-org only) |
repoUrl | string | No | Override template's default repo |
startingRef | string | No | Override template's default branch |
autoCreatePR | boolean | No | Override template's PR behavior |
workOnCurrentBranch | boolean | No | Override template's branch behavior |
skipReviewerRequest | boolean | No | Override template's reviewer behavior |
modelId | string | No | Override template's model |
modelParams | object | No | Override template's model params |
webhookUrl | string | No | HTTPS URL for status webhooks |
Response 201:
{
"session_id": "cs-456",
"message_id": "msg-001"
}
curl -X POST https://api.flapjack.dev/api/code-sessions \
-H "Authorization: Bearer fj_live_..." \
-H "Content-Type: application/json" \
-d '{
"codeAgentId": "ca-123",
"prompt": "Fix the login bug in src/auth.ts — the JWT expiry check is off by one hour",
"cursorApiKey": "cursor_..."
}'
Get Session
GET /api/code-sessions/{codeSessionId}
Returns the session with its full message history.
Response 200:
{
"id": "cs-456",
"org_id": "org-789",
"code_agent_id": "ca-123",
"status": "finished",
"pr_url": "https://github.com/acme/app/pull/42",
"pr_branch": "fix/login-bug",
"error": null,
"archived": false,
"webhook_url": null,
"last_event_seq": 142,
"last_event_at": "2026-05-20T10:03:22Z",
"messages": [
{
"id": "msg-001",
"cursor_run_id": "run-abc",
"role": "user",
"prompt": "Fix the login bug...",
"status": "finished",
"result_text": "Fixed the JWT expiry check...",
"duration_ms": 197000,
"pr_url": "https://github.com/acme/app/pull/42",
"error": null,
"started_at": "2026-05-20T10:00:05Z",
"completed_at": "2026-05-20T10:03:22Z",
"created_at": "2026-05-20T10:00:00Z"
}
]
}
Update Session
PATCH /api/code-sessions/{codeSessionId}
Request body:
| Field | Type | Description |
|---|---|---|
webhookUrl | string | null | Update or clear the webhook URL (must be public HTTPS) |
Response 200: The updated session object.
Send Follow-Up Message
POST /api/code-sessions/{codeSessionId}/messages
Send a follow-up prompt to an existing session. The session must not be archived.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Follow-up task |
Response 201:
{ "message_id": "msg-002" }
Get Session Events
GET /api/code-sessions/{codeSessionId}/events
Read the event timeline for a session. Use since for polling.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
since | number | Return events with seq greater than this value (default: 0) |
messageId | string | Filter events to a specific message |
limit | number | Max events to return (default: 200, max: 500) |
Response 200:
{
"events": [
{
"id": "evt-001",
"session_id": "cs-456",
"message_id": "msg-001",
"cursor_run_id": "run-abc",
"event_type": "tool_call",
"payload": { "tool": "edit_file", "path": "src/auth.ts" },
"seq": 1,
"created_at": "2026-05-20T10:00:10Z"
}
],
"last_seq": 142
}
Poll pattern: call with since=0, then use the returned last_seq as since on subsequent calls.
Cancel Session
POST /api/code-sessions/{codeSessionId}/cancel
Cancel a running or creating session. Only sessions with status creating or running can be cancelled.
Response 200: { "ok": true, "status": "cancelled" }
Errors:
502 cursor_cancel_failed— upstream Cursor cancel failed; retry409 CANCEL_UNSUPPORTED— the Cursor run doesn't support cancellation; wait for natural completion
Archive Session
POST /api/code-sessions/{codeSessionId}/archive
Archive a terminal session. This clears the encrypted Cursor API key from storage. The session must be in a terminal status (finished, error, cancelled, expired) — cancel active sessions before archiving.
Response 200: { "ok": true, "archived": true }
Errors:
409 SESSION_ACTIVE— cancel the session first502 archive_failed— upstream archive failed; retry
MCP Tools
All code agent/session operations are available as MCP tools:
| Tool | Description |
|---|---|
flapjack_list_code_agents | List code-agent templates |
flapjack_get_code_agent | Get a template by ID |
flapjack_create_code_agent | Create a new template |
flapjack_update_code_agent | Update a template |
flapjack_delete_code_agent | Delete a template |
flapjack_list_code_sessions | List sessions (filter by template, status, archived) |
flapjack_get_code_session | Get session with messages |
flapjack_start_code_session | Start a new session from a template |
flapjack_update_code_session | Update session (webhookUrl) |
flapjack_send_code_session_message | Send a follow-up prompt |
flapjack_list_code_session_events | Read session event timeline |
flapjack_cancel_code_session | Cancel a running session |
flapjack_archive_code_session | Archive and clear stored key |
Security Notes
- The
cursorApiKeyis encrypted at rest using AES-256-GCM and never returned in API responses. - Archiving a session permanently clears the encrypted key.
webhookUrlmust be a public HTTPS URL — private IPs and localhost are rejected.- Sessions use service-role writes with org isolation enforced by membership checks.