Search Composal documentation

Search public documentation, commands, and release notes.

Agent API

Use the organization-scoped HTTP API to create, configure and message persistent agents, retrieve replies and manage lifecycle and triggers.

Contents

Use the versioned HTTP API directly from your application. The OpenAPI document is the canonical source for request schemas, response fields and operation IDs. Generate a client from it if you need types; the planned composalai/agent-sdk is not required and is not available yet.

Manage sharing

Creation accepts optional visibility: "private"; the default is "organization". Read agent.visibility and agent.sharing_version from the agent response. An owner or manager can change sharing with:

HTTP
PATCH /api/v1/organizations/acme/agents/AGENT/sharing
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{"expected_version": 7, "visibility": "organization"}

Echo the current integer sharing_version as expected_version; it is distinct from Git configuration/lifecycle versions. A stale version returns 409. After a timeout, GET the agent to confirm its audience before submitting another change. This endpoint does not use an idempotency key. An organization member cannot change sharing or configuration simply because they can message the agent. Membership removal or switching to private revokes ungranted access and prevents further task execution for that requester.

Shared messages use the owner's configured tools and selected accounts. Only the owner can edit account grants. Provider-event consent includes the audience; visibility changes can require the owner to renew that consent. Organization sharing includes future members; membership is checked live for access.

Authenticate and select an agent

Send a Composal personal access token in Authorization: Bearer …. The token acts as its user, within that user's organization membership and agent access. New agents default to organization sharing: members can view and message them, while only the owner and explicit managers can change settings. Private agents require an explicit grant even for organization administrators. Existing agents also become organization-shared at rollout; owners/managers can switch them to private afterward.

The base collection is:

Text
https://composal.ai/api/v1/organizations/{organization_id}/agents

organization_id accepts an accessible organization slug or public ID. An agent selector accepts its readable url_slug, legacy slug or agent_… public ID. Browser links use /{organization}/agent/{url_slug}; aliases are reserved within that organization and stay stable when the display name changes. Save returned public IDs in your integration. Private Git heads and task IDs are opaque values, not SQL IDs.

The examples below use Bash, curl and jq. Load COMPOSAL_API_TOKEN through your secret manager. This helper passes the token through stdin rather than a command-line argument; do not enable shell tracing around credentials.

Bash
export ORG=your-organization
export BASE="https://composal.ai/api/v1/organizations/$ORG/agents"
api() {
  curl --fail-with-body --silent --show-error --config - "$@" <<EOF
header = "Authorization: Bearer ${COMPOSAL_API_TOKEN:?Set COMPOSAL_API_TOKEN}"
header = "Content-Type: application/json"
EOF
}

Create and activate

Create an agent using a name and configuration. A slug is generated automatically. Choose a stable idempotency key for this creation and retain the exact request file until its outcome is known.

Bash
cat > create-agent.json <<'JSON'
{"name":"Research assistant","configuration":{"instructions":"Research the topics I send and report your sources."}}
JSON
api "$BASE" --request POST \
  --header 'Idempotency-Key: research-agent-create-001' \
  --data-binary @create-agent.json > created-agent.json
export AGENT="$(jq -r '.agent.id' created-agent.json)"
api "$BASE/$AGENT" > agent.json

Creation returns 201, or 202 with agent.setup_pending: true while private state initialization recovers. Follow Retry-After and repeat the GET until setup is ready. Do not create a second agent to bypass pending setup.

Read agent.lifecycle_actions and the returned versions. When activate is available, build the activation request from that snapshot:

Bash
jq '{expected_version: .agent.lifecycle_version,
     configuration_version: .agent.configuration_version}' agent.json > activate-agent.json
api "$BASE/$AGENT/activate" --request POST \
  --header 'Idempotency-Key: research-agent-activate-001' \
  --data-binary @activate-agent.json

Keep this file and key unchanged when retrying an uncertain activation. Reloading state and choosing a different version is a new operation with a new key.

Send work and read replies

main is the default conversation. Supply another identifier to keep separate work in a different conversation. Sending creates an input; the response is not the agent's answer.

Bash
cat > message.json <<'JSON'
{"conversation_id":"main","text":"Describe your instructions and the tools available to you."}
JSON
api "$BASE/$AGENT/inputs" --request POST \
  --header 'Idempotency-Key: research-message-001' \
  --data-binary @message.json
api "$BASE/$AGENT/conversations/main" > conversation.json
jq '.conversation.inputs[] | {input_id: .input.id, task: .task}' conversation.json

A ready conversation response has setup_pending: false and a conversation object. Each input's task can be null before admission, then includes its id, status, reason and result. A completed task has a saved result. Check failures, cancellations and blocked states explicitly. Responses also include pending questions/approvals and the task stop control where available.

Poll this GET with a bounded backoff until the relevant task has an outcome. next_before selects older messages: pass it as ?before=…; it is not a cursor for new activity. To follow new work, reread the latest page. Use separate conversation IDs when your application needs separate work contexts; steering inputs can join an existing task.

GET /{id}/updates provides an agent-wide feed of completed, failed, cancelled and blocked outcomes. Paginate older entries with before=next_before. POST /{id}/updates/read acknowledges a displayed sequence for the authenticated viewer; it does not change task execution.

Configuration and versions

PATCH /{id}/configuration accepts {expected_version, configuration}. Use agent.configuration_version as the expected version. Configuration is a replacement document: start with the returned configuration, change the fields you intend, and preserve selected work repositories, connections, triggers and limits. Activate the returned new revision before using it for new work.

Activation uses lifecycle_version plus configuration_version; pause, resume and archive use lifecycle_version. These versions are opaque Git commit identities. Use returned lifecycle_actions and capability fields rather than inferring allowed actions from a status string.

Configuration includes instructions, skills, tools, work repositories, connection_grants, trigger_bindings, destinations, timezone, model policy, budgets and memory policy. Defaults are filled when creating a configuration; nested policy objects must follow the complete schema. Secrets do not belong in configuration or the agent repository.

Endpoint reference

Paths below are relative to the agent collection. Read the generated OpenAPI schemas for exact bodies and pagination.

MethodPathPurpose
GET / POST/List authorized agents / create a draft
GET/{id}Configuration, versions and current capabilities
PATCH/{id}/sharingChange organization/private audience using integer sharing_version
PATCH/{id}/configurationSave a new configuration revision
GET/{id}/revisionsRead configuration revision history
POST/{id}/activate, /{id}/pause, /{id}/resume, /{id}/archiveChange lifecycle with an expected version
GET / POST/{id}/inputsRead accepted inputs / send a message or bound reply
GET/{id}/conversations/{conversation}Read messages, task status and saved replies
GET/{id}/updatesRead durable task outcomes
POST/{id}/updates/readMark displayed outcomes read
POST/{id}/tasks/stopRequest stopping a task using task_id and the conversation's version
POST/{id}/approvalsAnswer a pending approval using its exact binding
GET / PATCH/{id}/memory, /{id}/filesRead or update managed memory/files with their versioned contracts
POST/{id}/schedule_preview, /{id}/webhooks/previewPreview proposed triggers without delivering work

Apps and incoming events

Connect accounts through the organization-level /api/v1/organizations/{organization_id}/agent_connections API. Custom MCP registration is POST …/agent_connections/custom_mcp; authorization is a separate step. Reference the returned account and current authority generation in configuration.connection_grants as agent_connection_…:GENERATION. Select only accounts the controlling user owns. Revocation or replacement is checked again at tool use.

Agent webhook management uses GET/POST /{id}/webhooks and POST /{id}/webhooks/{endpoint_id}/rotate or /revoke. Public delivery goes to the endpoint URL returned by creation, under /api/webhooks/agents/{endpoint_id}. Follow its signing and event schema; store the returned signing secret securely. Endpoint creation alone does not activate a trigger: save its binding and activate the revision.

Webhook deliveries use a different authentication scheme from management API calls:

  • X-Composal-Event-Id: a stable event ID (1–128 letters, digits, underscores or hyphens).
  • X-Composal-Timestamp: Unix time in seconds, within five minutes of server time.
  • X-Composal-Signature: v1= followed by the lowercase hex HMAC-SHA256 of timestamp + "." + event_id + "." + raw_body, using the endpoint's signing key.

Send UTF-8 JSON with this envelope, limited to 64 KiB. Sign the exact bytes you send, including whitespace:

JSON
{"schema":1,"type":"resource.changed","subject":"item-42","data":{"id":"item-42"}}

Keep the same event ID and body when retrying delivery; refresh the timestamp and signature if needed. Reusing an event ID with different content conflicts. A 202 delivery receipt is acceptance, not a completed agent task.

Supported platform events use POST /{id}/events with a configured binding_id and published event_id. Provider-event consent uses GET /{id}/provider-sources and GET/POST /{id}/provider-grants, with POST /{id}/provider-grants/{grant_id}/revoke for revocation. Follow returned eligibility and consent requirements; app tool access does not imply event delivery support.

Errors and retries

  • 401 / 403 / 404: authenticate again or check the user's current access. Private agent selection can return 404 rather than disclose its existence.
  • 409: state changed or requires reconciliation. For a definite version conflict, reload and make a new decision with a new key. An unknown external effect is not permission to submit it again.
  • 422: fix the request using returned validation errors. A changed request needs a new key.
  • 429 / 503: follow Retry-After and use bounded retries. Preserve the same key, body and expected versions for the same intended mutation.
  • Network timeout: acceptance is uncertain. Keep the original request/key and reconcile or retry that exact operation. Never generate a new key automatically.

Task failure is reported in conversation/outcome data even when its GET succeeds. A 201 message receipt, successful HTTP response or running worker does not prove the task completed. Stopping a task does not undo effects already started; uncertain usage and effects can remain under reconciliation.