Skip to content

Api contract

This document defines the external API contract for the background agents platform. It specifies the endpoints, request/response schemas, error format, authentication, pagination, and rate limiting. Current channels (CLI and webhook integrations) interact with the platform through this API, mediated by the input gateway.

This is a design-level specification, not an OpenAPI file. Implementation may generate an OpenAPI spec from the CDK API Gateway definition; this document is the source of truth for the contract.

  • Use this doc for: endpoint paths, payload shapes, auth requirements, and error codes.
  • Current channels: CLI and webhook integrations.
  • Not in scope here: internal orchestration internals (see ORCHESTRATOR.md).

Relationship to other docs:

  • INPUT_GATEWAY.md — describes the gateway’s role (normalize, validate, dispatch) and the conceptual internal message/notification schemas.
  • ORCHESTRATOR.md — defines the task state machine, data model, and lifecycle that this API exposes.
  • SECURITY.md — authentication and authorization model.

EnvironmentBase URL
Productionhttps://{api-id}.execute-api.{region}.amazonaws.com/v1
Custom domainhttps://api.{customer-domain}/v1

API versioning uses a path prefix (/v1). Breaking changes increment the version (/v2). Non-breaking additions (new optional fields, new endpoints) do not require a version bump.


All endpoints require authentication. The API supports multiple authentication methods depending on the channel:

ChannelAuth methodHeaderEndpoint scope
CLI / REST APICognito JWT (ID token)Authorization: Bearer <token>All /tasks and /webhooks management endpoints
WebhookHMAC-SHA256 signatureX-Webhook-Id + X-Webhook-Signature: sha256=<hex>POST /v1/webhooks/tasks only

The gateway extracts the platform user ID (user_id) from the authenticated identity (Cognito sub for JWT, or webhook record lookup for HMAC) and attaches it to all internal messages. Downstream services never see raw tokens or secrets.


  • Content type: application/json
  • Character encoding: UTF-8
  • Maximum request body size: 1 MB (configurable)

All successful responses return:

{
"data": { ... }
}

List endpoints return:

{
"data": [ ... ],
"pagination": {
"next_token": "...",
"has_more": true
}
}

All errors return a consistent structure:

{
"error": {
"code": "TASK_NOT_FOUND",
"message": "Task abc-123 not found.",
"request_id": "req-uuid-here"
}
}
FieldTypeDescription
codeStringMachine-readable error code (see Error codes section).
messageStringHuman-readable description.
request_idStringUnique request ID for tracing and support. Also returned in the X-Request-Id response header.
HeaderDescription
X-Request-IdUnique request ID (ULID). Present on all responses.
X-RateLimit-LimitRequests allowed per window (see Rate limiting).
X-RateLimit-RemainingRequests remaining in current window.
X-RateLimit-ResetUnix timestamp when the window resets.

Clients may include an Idempotency-Key header on POST requests. If a request with the same key was already processed (within a 24-hour TTL), the API returns the original response without creating a duplicate resource. See ORCHESTRATOR.md — Admission control for the implementation.


Creates a new task. The orchestrator runs admission control, context hydration, and starts the agent session.

POST /v1/tasks

Request body:

FieldTypeRequiredDescription
repoStringYesGitHub repository in owner/repo format.
issue_numberNumberNoGitHub issue number. If provided, the issue title, body, and comments are fetched during context hydration.
task_descriptionStringNoFree-text task description. At least one of issue_number or task_description must be provided.
max_turnsNumberNoMaximum agent turns (1–500). Controls how many reasoning/tool-call iterations the agent can perform. Defaults to 100 if omitted.
max_budget_usdNumberNoMaximum cost budget in USD (0.01–100). When reached, the agent stops regardless of remaining turns. If omitted, no budget limit is applied (turn limit and session timeout still apply).
attachmentsArrayNoMulti-modal attachments (images, files). See Attachments schema below.

Attachments schema:

{
"attachments": [
{
"type": "image",
"content_type": "image/png",
"data": "<base64-encoded>",
"filename": "screenshot.png"
},
{
"type": "url",
"url": "https://example.com/spec.pdf"
}
]
}
FieldTypeRequiredDescription
typeStringYesimage, file, or url.
content_typeStringNoMIME type (for inline data).
dataStringNoBase64-encoded content (for inline uploads). Max 10 MB per attachment after decoding.
urlStringNoURL to fetch (for URL-based attachments).
filenameStringNoOriginal filename (for display and logging).

Request headers:

HeaderRequiredDescription
AuthorizationYesBearer token.
Idempotency-KeyNoClient-supplied idempotency key (string, max 128 chars).

Response: 201 Created

{
"data": {
"task_id": "01HYX...",
"status": "SUBMITTED",
"repo": "org/myapp",
"issue_number": 42,
"branch_name": "bgagent/01HYX.../fix-auth-bug",
"created_at": "2025-03-15T10:30:00Z"
}
}

Error responses:

StatusCodeCondition
400VALIDATION_ERRORMissing required fields, invalid repo format, no task description or issue, invalid max_turns (not an integer or outside 1–500 range), invalid max_budget_usd (not a number or outside 0.01–100 range).
401UNAUTHORIZEDMissing or invalid auth token.
409DUPLICATE_TASKIdempotency key matches an existing task (returns the existing task in data).
422REPO_NOT_ONBOARDEDRepository is not registered with the platform. Repos are onboarded via CDK deployment (Blueprint construct), not via a runtime API. See REPO_ONBOARDING.md.
429RATE_LIMIT_EXCEEDEDUser exceeded the per-user rate limit.

Returns the full details of a single task. Users can only access their own tasks.

GET /v1/tasks/{task_id}

Path parameters:

ParameterTypeDescription
task_idStringTask identifier (ULID).

Response: 200 OK

{
"data": {
"task_id": "01HYX...",
"status": "RUNNING",
"repo": "org/myapp",
"issue_number": 42,
"task_description": "Fix the authentication bug in the login flow",
"branch_name": "bgagent/01HYX.../fix-auth-bug",
"session_id": "sess-uuid",
"pr_url": null,
"error_message": null,
"created_at": "2025-03-15T10:30:00Z",
"updated_at": "2025-03-15T10:31:15Z",
"started_at": "2025-03-15T10:31:10Z",
"completed_at": null,
"duration_s": null,
"cost_usd": null,
"build_passed": null,
"max_turns": 100,
"max_budget_usd": null
}
}
FieldTypeDescription
max_turnsNumber or nullMaximum agent turns for this task. Always present in the response — reflects the effective value (user-specified or platform default of 100).
max_budget_usdNumber or nullMaximum cost budget in USD for this task. Null if no budget limit was specified.

Error responses:

StatusCodeCondition
401UNAUTHORIZEDMissing or invalid auth token.
403FORBIDDENTask belongs to a different user.
404TASK_NOT_FOUNDTask does not exist.

Returns tasks for the authenticated user, with optional filters. Paginated.

GET /v1/tasks

Query parameters:

ParameterTypeRequiredDefaultDescription
statusStringNo(all)Filter by status: SUBMITTED, HYDRATING, RUNNING, FINALIZING, COMPLETED, FAILED, CANCELLED, TIMED_OUT. Comma-separated for multiple (e.g. RUNNING,HYDRATING).
repoStringNo(all)Filter by repository (owner/repo).
limitNumberNo20Page size (1–100).
next_tokenStringNo(none)Pagination token from a previous response.

Response: 200 OK

{
"data": [
{
"task_id": "01HYX...",
"status": "RUNNING",
"repo": "org/myapp",
"issue_number": 42,
"task_description": "Fix the authentication bug...",
"branch_name": "bgagent/01HYX.../fix-auth-bug",
"pr_url": null,
"created_at": "2025-03-15T10:30:00Z",
"updated_at": "2025-03-15T10:31:15Z"
}
],
"pagination": {
"next_token": "eyJsYXN0...",
"has_more": true
}
}

The list response returns a summary (subset of fields). Use GET /v1/tasks/{task_id} for full details.

Error responses:

StatusCodeCondition
400VALIDATION_ERRORInvalid status value, invalid limit, invalid next_token.
401UNAUTHORIZEDMissing or invalid auth token.

Cancels a running task. See ORCHESTRATOR.md — Cancellation behavior by state for what happens in each state.

DELETE /v1/tasks/{task_id}

Path parameters:

ParameterTypeDescription
task_idStringTask identifier (ULID).

Response: 200 OK

{
"data": {
"task_id": "01HYX...",
"status": "CANCELLED",
"cancelled_at": "2025-03-15T11:00:00Z"
}
}

Error responses:

StatusCodeCondition
401UNAUTHORIZEDMissing or invalid auth token.
403FORBIDDENTask belongs to a different user.
404TASK_NOT_FOUNDTask does not exist.
409TASK_ALREADY_TERMINALTask is already in a terminal state (COMPLETED, FAILED, CANCELLED, TIMED_OUT).

Returns the audit trail for a task (state transitions, key events). Useful for debugging.

GET /v1/tasks/{task_id}/events

Path parameters:

ParameterTypeDescription
task_idStringTask identifier (ULID).

Query parameters:

ParameterTypeRequiredDefaultDescription
limitNumberNo50Page size (1–100).
next_tokenStringNo(none)Pagination token.

Response: 200 OK

{
"data": [
{
"event_id": "01HYX...",
"event_type": "task_created",
"timestamp": "2025-03-15T10:30:00Z",
"metadata": {}
},
{
"event_id": "01HYX...",
"event_type": "admission_passed",
"timestamp": "2025-03-15T10:30:01Z",
"metadata": { "queue_position": 0 }
},
{
"event_id": "01HYX...",
"event_type": "session_started",
"timestamp": "2025-03-15T10:31:10Z",
"metadata": { "session_id": "sess-uuid" }
}
],
"pagination": {
"next_token": null,
"has_more": false
}
}

Event types (see OBSERVABILITY.md for the full list):

Fixed event types: task_created, admission_passed, admission_rejected, hydration_started, hydration_complete, session_started, session_ended, pr_created, task_completed, task_failed, task_cancelled, task_timed_out

Step-level event types (from the blueprint framework): The orchestrator emits events for each pipeline step following the pattern {step_name}_{started|completed|failed}. For built-in steps these overlap with the fixed types above (e.g. hydration_started). For custom Lambda steps (see REPO_ONBOARDING.md), the step name is user-defined (e.g. sast-scan_started, sast-scan_completed, prepare-environment_failed). Step event metadata includes StepOutput.metadata from the step execution.

Error responses:

StatusCodeCondition
401UNAUTHORIZEDMissing or invalid auth token.
403FORBIDDENTask belongs to a different user.
404TASK_NOT_FOUNDTask does not exist.

External systems (CI pipelines, GitHub Actions, custom automation) can create tasks via HMAC-authenticated webhook requests. Webhook integrations are managed through Cognito-authenticated endpoints; task submission uses a separate endpoint with HMAC-SHA256 authentication.

These endpoints are protected by Cognito JWT (same as the task endpoints).

Creates a new webhook integration and returns the shared secret (shown only once).

POST /v1/webhooks

Request body:

FieldTypeRequiredDescription
nameStringYesHuman-readable name for the integration (1-64 chars, alphanumeric, spaces, hyphens, underscores). Must start and end with an alphanumeric character.

Response: 201 Created

{
"data": {
"webhook_id": "01HYX...",
"name": "My CI Pipeline",
"secret": "<webhook-secret-64-hex-characters>",
"created_at": "2025-03-15T10:30:00Z"
}
}

The secret is a 32-byte random value (64 hex characters). Store it securely — it cannot be retrieved after this response. The secret is stored in AWS Secrets Manager under the name bgagent/webhook/{webhook_id}.

Error responses:

StatusCodeCondition
400VALIDATION_ERRORMissing or invalid webhook name.
401UNAUTHORIZEDMissing or invalid auth token.

Returns the authenticated user’s webhook integrations. Paginated.

GET /v1/webhooks

Query parameters:

ParameterTypeRequiredDefaultDescription
include_revokedStringNofalseSet to true to include revoked webhooks.
limitNumberNo20Page size (1-100).
next_tokenStringNo(none)Pagination token from a previous response.

Response: 200 OK

{
"data": [
{
"webhook_id": "01HYX...",
"name": "My CI Pipeline",
"status": "active",
"created_at": "2025-03-15T10:30:00Z",
"updated_at": "2025-03-15T10:30:00Z",
"revoked_at": null
}
],
"pagination": {
"next_token": null,
"has_more": false
}
}

Error responses:

StatusCodeCondition
401UNAUTHORIZEDMissing or invalid auth token.

Soft-revokes a webhook integration. The webhook can no longer authenticate requests. The secret is scheduled for deletion with a 7-day recovery window. The revoked webhook record is automatically deleted from DynamoDB after 30 days (configurable via webhookRetentionDays). After deletion, GET /v1/webhooks will no longer return the record.

DELETE /v1/webhooks/{webhook_id}

Path parameters:

ParameterTypeDescription
webhook_idStringWebhook identifier (ULID).

Response: 200 OK

{
"data": {
"webhook_id": "01HYX...",
"name": "My CI Pipeline",
"status": "revoked",
"created_at": "2025-03-15T10:30:00Z",
"updated_at": "2025-03-15T12:00:00Z",
"revoked_at": "2025-03-15T12:00:00Z"
}
}

Error responses:

StatusCodeCondition
401UNAUTHORIZEDMissing or invalid auth token.
404WEBHOOK_NOT_FOUNDWebhook does not exist, or belongs to a different user.
409WEBHOOK_ALREADY_REVOKEDWebhook is already revoked.

Creates a task via webhook. Uses HMAC-SHA256 authentication instead of Cognito JWT. The task is owned by the Cognito user who created the webhook integration.

POST /v1/webhooks/tasks

Request body: Same as POST /v1/tasks (see Create task).

Required headers:

HeaderRequiredDescription
X-Webhook-IdYesWebhook integration ID.
X-Webhook-SignatureYessha256=<hex-hmac> — HMAC-SHA256 of the raw request body using the webhook secret.
Idempotency-KeyNoClient-supplied idempotency key (same semantics as POST /v1/tasks).

Authentication flow (two-phase):

  1. A Lambda REQUEST authorizer extracts the X-Webhook-Id header and verifies that both X-Webhook-Id and X-Webhook-Signature are present.
  2. Looks up the webhook record in DynamoDB; verifies status: active.
  3. On success, returns an Allow policy with context: { userId, webhookId }. On failure, returns Deny.
  4. The webhook handler fetches the shared secret from Secrets Manager (cached in-memory with 5-minute TTL).
  5. Computes HMAC-SHA256(secret, raw_request_body) and compares with the provided signature using constant-time comparison (crypto.timingSafeEqual).
  6. On success, creates the task. On failure, returns 401 Unauthorized.

HMAC verification is performed by the handler (not the authorizer) because API Gateway REST API v1 does not pass the request body to Lambda REQUEST authorizers. Authorizer result caching is disabled (resultsCacheTtl: 0) because each request has a unique signature.

Response: 201 Created — Same as POST /v1/tasks.

Error responses:

StatusCodeCondition
400VALIDATION_ERRORMissing required fields, invalid repo format, no task description or issue, invalid max_turns, invalid max_budget_usd.
401UNAUTHORIZEDMissing webhook headers, webhook not found, revoked, or invalid signature.
409DUPLICATE_TASKIdempotency key matches an existing task.

Channel metadata: Tasks created via webhook record channel_source: 'webhook' and channel_metadata including webhook_id, source_ip, user_agent, and api_request_id for audit purposes.


Rate limits are enforced per authenticated user.

LimitValueScopeResponse
Request rate60 requests/minutePer user, across all endpoints429 Too Many Requests
Task creation rate10 tasks/hourPer user, POST /v1/tasks only429 with code RATE_LIMIT_EXCEEDED
Concurrent tasksConfigurable (default: 3–5)Per user, running tasksNew tasks above the limit are rejected with 409 CONCURRENCY_LIMIT_EXCEEDED. See ORCHESTRATOR.md — Admission control.

Rate limit status is communicated via response headers (see Standard response headers).


CodeHTTP StatusDescription
VALIDATION_ERROR400Request body or query parameters are invalid.
UNAUTHORIZED401Missing, expired, or invalid authentication.
FORBIDDEN403Authenticated but not authorized (e.g. accessing another user’s task).
TASK_NOT_FOUND404Task ID does not exist.
DUPLICATE_TASK409Idempotency key matches an existing task.
TASK_ALREADY_TERMINAL409Cannot cancel a task that is already in a terminal state.
WEBHOOK_NOT_FOUND404Webhook does not exist or belongs to a different user.
WEBHOOK_ALREADY_REVOKED409Webhook is already revoked.
REPO_NOT_ONBOARDED422Repository is not registered with the platform. Repos are onboarded via CDK deployment, not via a runtime API. There are no /v1/repos endpoints.
INVALID_STEP_SEQUENCE500The blueprint’s step sequence is invalid (missing required steps or incorrect ordering). This indicates a CDK configuration error that slipped past synth-time validation. Visible via GET /v1/tasks/{id} as error_code. See REPO_ONBOARDING.md.
RATE_LIMIT_EXCEEDED429User exceeded rate limit.
INTERNAL_ERROR500Unexpected server error. Includes request_id for support.
SERVICE_UNAVAILABLE503Downstream dependency unavailable (e.g. DynamoDB, AgentCore). Retry with backoff.

List endpoints use token-based pagination (not offset-based). This is consistent with DynamoDB’s ExclusiveStartKey pattern.

  • The response includes pagination.next_token (opaque string) and pagination.has_more (boolean).
  • To fetch the next page, pass next_token as a query parameter.
  • Tokens are short-lived (valid for the duration of a session, not persisted). Do not store or cache them.
  • Results are ordered by created_at descending (newest first) unless otherwise specified.

The API is implemented as an Amazon API Gateway REST API (or HTTP API) with Lambda integrations:

EndpointLambda handlerAuthDescription
POST /v1/taskscreateTaskHandlerCognitoValidates, creates task record, triggers orchestrator.
GET /v1/taskslistTasksHandlerCognitoQueries DynamoDB UserStatusIndex GSI.
GET /v1/tasks/{task_id}getTaskHandlerCognitoReads task from DynamoDB, enforces ownership.
DELETE /v1/tasks/{task_id}cancelTaskHandlerCognitoUpdates task status, signals orchestrator to cancel.
GET /v1/tasks/{task_id}/eventsgetTaskEventsHandlerCognitoQueries DynamoDB TaskEvents table.
POST /v1/webhookscreateWebhookHandlerCognitoCreates webhook integration, generates SM secret.
GET /v1/webhookslistWebhooksHandlerCognitoQueries user’s webhooks from DynamoDB UserIndex GSI.
DELETE /v1/webhooks/{webhook_id}deleteWebhookHandlerCognitoSoft-revokes webhook, schedules SM secret deletion.
POST /v1/webhooks/taskswebhookCreateTaskHandlerHMACCreates task via webhook (shared core with createTaskHandler).
webhookAuthorizerFnREQUEST authorizer: verifies webhook exists and is active.
  • All endpoints enforce user ownership: a user can only access tasks where task.user_id matches the authenticated user’s platform ID. Webhooks enforce ownership at the management layer — only the webhook creator can list, view, or revoke it.
  • For Cognito-authenticated endpoints, the user_id is extracted from the JWT claims (sub) and passed to handlers via the request context.
  • For webhook-authenticated endpoints, the user_id is extracted from the webhook record by the Lambda REQUEST authorizer and injected into the authorizer context (event.requestContext.authorizer.userId).
  • Handlers never trust client-supplied user IDs.

The API request/response schemas defined here are the external contract. The input gateway normalizes API requests into the internal message schema (see INPUT_GATEWAY.md) before dispatching to the task pipeline. The internal schema may include additional fields (e.g. channel_metadata, normalized_at) that are not exposed in the API.