The AQ API
Updated September 16, 2026 · first published August 1, 2026 · by the AQ team
The AQ API is the programmatic interface to AQ workspaces. With it, your applications and agents can list, create, and close workspaces, read each workspace's event log, tracked pull requests, and preview URLs, start, steer, stop, and archive conversations with AQ Composer (the built-in agent), and react to workspace activity through signed webhooks instead of polling. It comes as three production surfaces that operate on the same workspaces and the same permission model: a versioned REST API for deterministic application workflows, a hosted MCP server so MCP-compatible agents can discover and call the same operations as tools, and signed outbound webhooks for event-driven systems. Every surface authenticates with scoped API keys that act as the person who created them, every REST write is retry-safe through idempotency keys, and the complete contract is machine-readable OpenAPI 3.1. This guide takes you from a new API key to a working integration, including copy-paste tests that do not change data.
Start here
| Surface | URL | Use it when |
|---|---|---|
| REST API | https://aq.dev/v1 | Your application needs predictable requests, responses, pagination, and retry-safe writes. |
| MCP server | https://aq.dev/mcp | An MCP-compatible agent should discover and use AQ tools directly. |
| Webhooks | Configured through /v1/webhooks | Your service should react to workspace, pull request, agent, Linear, comment, or runner events. |
| OpenAPI | /v1/openapi.json | You need the complete machine-readable OpenAPI 3.1 contract or want to generate a client. |
The fastest safe test is: create a read-only key, call GET /v1/me, list workspaces, then ask the MCP server for its tool list. Those calls are all read-only.
In this guide
- Create a key and make the first read-only request
- Choose scopes and understand the permission model
- Find the complete REST endpoint map
- Implement workspace, event, and agent-event pagination
- Retry REST writes safely with idempotency keys
- Create, inspect, and close a test workspace
- Start, poll, steer, stop, and archive an AQ Composer run
- Connect and test an MCP client
- Register, verify, deduplicate, and troubleshoot webhooks
- Handle errors and rate limits
- Run the safe production smoke-test checklist
Quickstart: authenticate and make your first request
1. Create an API key
Open AQ Settings, choose Connections, find API keys, and select Create key. Give the key a name that identifies its consumer, such as release-bot or local-mcp-test, then select only the scopes it needs.
Keys start with aq_ and are shown exactly once. AQ stores only a SHA-256 hash of the key. Copy it into your password manager or secret store before leaving the creation screen.
2. Put the key in your shell without saving it in history
read -rsp "AQ API key: " AQ_API_KEY
export AQ_API_KEY
echo
Do not commit the key, put it in a query string, paste it into an issue, or expose it to browser-side JavaScript. Revoke a key immediately from Settings if it may have leaked.
3. Verify the key and its scopes
curl -fsS https://aq.dev/v1/me -H "Authorization: Bearer $AQ_API_KEY" | jq
A successful response identifies the user and team the key acts as, along with the scopes attached to it:
{
"user": {
"id": "user-id",
"email": "[email protected]",
"name": "Your Name"
},
"tenant_id": "tenant-id",
"account_id": "account-id",
"scopes": ["workspaces:read", "events:read"],
"key_id": "ak_..."
}
4. Make a read-only request
curl -fsS "https://aq.dev/v1/workspaces?limit=5" -H "Authorization: Bearer $AQ_API_KEY" | jq
Workspace responses use stable, public field names such as id, title, status, repo_id, branch_name, pr_url, and timestamps. Runner identifiers, filesystem paths, credentials, and raw infrastructure errors are never included.
Authentication, permissions, and scopes
Send the key as an HTTP Bearer token on every REST or MCP request:
Authorization: Bearer aq_your_key
An API key is personal, not a team-wide service account. It acts as the user who created it, sees only workspaces that user can access, and is re-checked against live team and account membership. Revoking the key, removing the user, or suspending the account stops access. API keys never inherit AQ host-operator privileges.
| Scope | Allows | Typical use |
|---|---|---|
workspaces:read | List and fetch workspaces and connected repos | Dashboards, inventory, MCP discovery |
workspaces:write | Create and close workspaces | Issue intake, automation, workspace lifecycle |
events:read | Read workspace event logs | Activity feeds and polling integrations |
prs:read | Read pull requests tracked for a workspace | Release and review workflows |
previews:read | Read preview status and URLs | QA links and review dashboards |
webhooks:manage | List, create, and delete webhook endpoints | Event-driven integrations |
agent:run | Start, inspect, steer, stop, and archive AQ Composer conversations | Agent orchestration from applications or other agents |
Use separate keys for separate consumers. A read-only reporting job should not share a key with a system that creates workspaces or runs agents.
REST API reference by task
Every endpoint except the public OpenAPI document requires a Bearer key. Methods and paths below are the current v1 surface.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /v1/me | Any valid key | Inspect the acting user, team, key id, and scopes. |
| GET | /v1/repos | workspaces:read | List repositories connected to the team. |
| GET | /v1/workspaces | workspaces:read | List visible workspaces, newest first. |
| POST | /v1/workspaces | workspaces:write | Create a workspace. |
| GET | /v1/workspaces/{id} | workspaces:read | Fetch one visible workspace. |
| POST | /v1/workspaces/{id}/close | workspaces:write | Close a workspace and stop its terminals and previews. |
| GET | /v1/workspaces/{id}/events | events:read | Read the append-only workspace event log. |
| GET | /v1/workspaces/{id}/prs | prs:read | List pull requests associated with a workspace. |
| GET | /v1/workspaces/{id}/previews | previews:read | Read preview status, visibility, and URLs. |
| POST | /v1/workspaces/{id}/agent | agent:run | Start an AQ Composer conversation with an initial task. |
| GET | /v1/agent-sessions/{id} | agent:run | Read agent status, budget state, spend, and timestamps. |
| GET | /v1/agent-sessions/{id}/events | agent:run | Poll ordered progress events using after_seq. |
| GET | /v1/agent-sessions/{id}/messages | agent:run | Read the conversation transcript. |
| POST | /v1/agent-sessions/{id}/messages | agent:run | Queue a follow-up or start the next turn now. deliver=now stops the current turn first. |
| DELETE | /v1/agent-sessions/{id}/messages/{messageId} | agent:run | Retract a queued message before a turn claims it. |
| PATCH | /v1/agent-sessions/{id}/messages/order | agent:run | Reorder the queued messages. |
| POST | /v1/agent-sessions/{id}/messages/{messageId}/deliver | agent:run | Run a queued message next, stopping the current turn if one is in flight. |
| POST | /v1/agent-sessions/{id}/interrupt | agent:run | Stop the current turn while keeping the conversation open. |
| POST | /v1/agent-sessions/{id}/archive | agent:run | Close the conversation tab while retaining its transcript. |
| GET | /v1/webhooks | webhooks:manage | List endpoints and discover all supported event types. |
| POST | /v1/webhooks | webhooks:manage | Create an endpoint and receive its signing secret once. |
| DELETE | /v1/webhooks/{id} | webhooks:manage | Delete an endpoint. |
Request and response schemas, field types, enums, limits, and error responses are defined in the OpenAPI 3.1 document.
Pagination
Workspace lists use an opaque cursor. Pass the returned next_cursor unchanged to the next request:
curl -fsS "https://aq.dev/v1/workspaces?limit=50&cursor=opaque-cursor" -H "Authorization: Bearer $AQ_API_KEY"
Workspace events are returned newest first and use a timestamp cursor. Pass next_before back as the before query parameter. Agent progress events are different: they are ordered and carry a monotonic string seq. Pass the newest sequence you have seen as after_seq to fetch only later events.
Retry-safe REST writes with idempotency keys
Every REST POST and DELETE requires an Idempotency-Key header of at most 255 characters. Generate one key for each logical action and keep it stable across retries. A UUID is a good default.
curl -fsS -X POST https://aq.dev/v1/workspaces -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: 9683499e-966d-4d4b-bc6c-05c9216da279" -H "Content-Type: application/json" -d '{"title":"Fix the auth bug","repo_id":"your-repo-id"}'
- A retry with the same method, path, body, key, and API key replays the stored response and adds
Idempotency-Replayed: true. - Reusing the key with a different body returns
422 idempotency_key_reused. - Retrying while the first request is still running returns
409 retry_in_progress. Wait briefly and retry with the same key. - Do not generate a fresh idempotency key merely because the client timed out. That would describe a new logical action and could create a duplicate.
End-to-end REST example: create, inspect, and close a workspace
First choose a repository id:
curl -fsS https://aq.dev/v1/repos -H "Authorization: Bearer $AQ_API_KEY" | jq '.repos[] | {id, name, default_branch}'
Create the workspace and save its id:
CREATE_RESPONSE=$(curl -fsS -X POST https://aq.dev/v1/workspaces -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: docs-smoke-create-1" -H "Content-Type: application/json" -d '{"title":"API smoke test","repo_id":"your-repo-id"}')
WORKSPACE_ID=$(printf '%s' "$CREATE_RESPONSE" | jq -r '.workspace.id')
printf '%s' "$CREATE_RESPONSE" | jq
Inspect its activity, pull requests, and previews:
curl -fsS "https://aq.dev/v1/workspaces/$WORKSPACE_ID/events" -H "Authorization: Bearer $AQ_API_KEY" | jq
curl -fsS "https://aq.dev/v1/workspaces/$WORKSPACE_ID/prs" -H "Authorization: Bearer $AQ_API_KEY" | jq
curl -fsS "https://aq.dev/v1/workspaces/$WORKSPACE_ID/previews" -H "Authorization: Bearer $AQ_API_KEY" | jq
Close the test workspace when you are done:
curl -fsS -X POST "https://aq.dev/v1/workspaces/$WORKSPACE_ID/close" -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: docs-smoke-close-1" | jq
Run and steer AQ Composer over REST
AQ Composer (the built-in AQ agent) is a durable conversation attached to an active workspace. Starting a run queues work, and a worker normally begins it within seconds. The agent edits the workspace's worktree and reports progress through ordered events and transcript messages. It does not automatically push or open a pull request.
The key needs agent:run, the workspace must be active, and AQ Composer must be enabled for the team. It is included (beta) for every organization by default; a team can have it turned off. A disabled team receives 403 agent_disabled.
The workspace also needs a prepared worktree, which happens the first time someone opens it in AQ. Starting a conversation in a never-opened workspace still succeeds, but the session immediately rests in waiting_for_user with error_code set to workspace_not_ready and a system message in the transcript explaining the fix. Open the workspace once, then send a message to resume. A temporarily unreachable runner behaves the same way with error_code set to runner_unavailable: nothing is lost, and the conversation resumes on the next message.
Start a conversation
RUN_RESPONSE=$(curl -fsS -X POST "https://aq.dev/v1/workspaces/$WORKSPACE_ID/agent" -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: agent-run-1" -H "Content-Type: application/json" -d '{"message":"Inspect the failing tests, explain the cause, and fix it."}')
AGENT_SESSION_ID=$(printf '%s' "$RUN_RESPONSE" | jq -r '.agent_session.id')
printf '%s' "$RUN_RESPONSE" | jq
Start, get, and interrupt respond with the session object. The fields your poller needs are status, error_code, and credits_spent_micros; title is the tab label the conversation gets in the workspace:
{
"agent_session": {
"id": "as_...",
"workspace_id": "workspace-id",
"title": "AQ",
"status": "queued",
"budget_state": "active",
"credits_spent_micros": "0",
"error_code": null,
"created_at": "2026-08-02T18:20:00.000Z"
}
}
Poll progress incrementally
curl -fsS "https://aq.dev/v1/agent-sessions/$AGENT_SESSION_ID/events?after_seq=0&limit=200" -H "Authorization: Bearer $AQ_API_KEY" | jq
Persist the largest returned seq and use it as the next after_seq. The stream mirrors what the workspace tab shows: message entries, one tool event per tool call the agent makes while working, checkpoint markers for the worktree snapshot taken before each turn, and status_changed transitions. Read the conversation itself from the messages endpoint:
curl -fsS "https://aq.dev/v1/agent-sessions/$AGENT_SESSION_ID/messages" -H "Authorization: Bearer $AQ_API_KEY" | jq
| Status | Meaning | What the client should do |
|---|---|---|
queued | Waiting for a worker | Continue polling. |
running | The current turn is active | Read events and messages; send a message only when you intend to steer. |
waiting_for_user | The agent is resting or needs input | Read the transcript and send a reply to resume. If error_code is set, fix that first: workspace_not_ready means open the workspace once. |
paused | The run is paused by a control or budget gate | Inspect budget_state and error_code. |
completed, failed, cancelled | Terminal states | Stop polling. Start a new conversation for more work. |
Reply, stop a turn, or archive the conversation
# Reply or steer. A reply to waiting_for_user resumes the run.
curl -fsS -X POST "https://aq.dev/v1/agent-sessions/$AGENT_SESSION_ID/messages" -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: agent-reply-1" -H "Content-Type: application/json" -d '{"content":"Use the smaller fix and rerun the focused tests."}'
# Stop only the current turn. The conversation remains open.
curl -fsS -X POST "https://aq.dev/v1/agent-sessions/$AGENT_SESSION_ID/interrupt" -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: agent-stop-1"
# Close the conversation tab. Its transcript remains readable by id.
curl -fsS -X POST "https://aq.dev/v1/agent-sessions/$AGENT_SESSION_ID/archive" -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: agent-archive-1"
Connect an MCP client
AQ's MCP endpoint uses stateless Streamable HTTP with OAuth. In a client that supports remote MCP servers (Cursor, Claude, ChatGPT connectors, and others), add the URL and nothing else:
{
"mcpServers": {
"aq": {
"url": "https://aq.dev/mcp"
}
}
}
The first time the client connects, your browser opens an AQ sign-in and consent screen. Approve, and the client receives its own access token, no key copying. Each connected app shows up in Settings → Connections → API keys (named MCP: <client>), and revoking it there disconnects the app immediately.
Headless or key-based clients
If your client cannot run a browser OAuth flow, mint a key in Settings → Connections → API keys and send it as a Bearer header: the same key used by REST:
{
"mcpServers": {
"aq": {
"url": "https://aq.dev/mcp",
"headers": {
"Authorization": "Bearer aq_your_key"
}
}
}
}
Prefer a client feature that reads the token from an environment variable or secret store when one is available.
| MCP tool | Required scope | Effect |
|---|---|---|
list_workspaces | workspaces:read | List visible workspaces with bounded cursor pagination. |
get_workspace | workspaces:read | Fetch one workspace. |
list_repos | workspaces:read | List connected repositories. |
create_workspace | workspaces:write | Create a workspace. |
close_workspace | workspaces:write | Close a workspace; requires confirm=true. |
get_workspace_events | events:read | Read workspace activity. |
list_workspace_prs | prs:read | List tracked pull requests. |
get_preview_url | previews:read | Read preview status and URLs. |
start_agent | agent:run | Start an AQ Composer conversation. |
get_agent_run | agent:run | Read status, the transcript tail, and recent events. |
send_agent_message | agent:run | Steer or resume the agent. |
stop_agent | agent:run | Stop the current turn while keeping the conversation open. |
The MCP server only advertises tools allowed by the presented key's scopes. Webhook management is REST-only. REST idempotency headers do not apply to MCP tool calls, so a client that automatically retries a mutating tool should first check current state before repeating create_workspace or start_agent.
Test MCP without installing a client
List the tools visible to your key:
curl -fsS https://aq.dev/mcp -H "Authorization: Bearer $AQ_API_KEY" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" --data '{
"jsonrpc":"2.0",
"id":1,
"method":"tools/list",
"params":{}
}' | jq
Call a read-only tool:
curl -fsS https://aq.dev/mcp -H "Authorization: Bearer $AQ_API_KEY" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" --data '{
"jsonrpc":"2.0",
"id":2,
"method":"tools/call",
"params":{
"name":"list_workspaces",
"arguments":{"limit":5}
}
}' | jq
The server is stateless and POST-only. A browser visit or other GET /mcp request intentionally returns 405. Raw MCP POSTs must accept both application/json and text/event-stream, even though AQ currently returns JSON responses.
Receive signed webhooks
Webhook endpoints let a service react without polling. Production endpoints must be public HTTPS URLs. AQ rejects credentials in webhook URLs, private and link-local address ranges, internal hostnames, redirects, and hostnames that resolve to private addresses at delivery time.
Create an endpoint
curl -fsS -X POST https://aq.dev/v1/webhooks -H "Authorization: Bearer $AQ_API_KEY" -H "Idempotency-Key: webhook-create-1" -H "Content-Type: application/json" -d '{
"url":"https://example.com/webhooks/aq",
"description":"Production activity feed",
"event_types":["workspace.pr_opened","workspace.agent_error"]
}' | jq
The response returns the endpoint and an aqwh_ signing secret. The secret is shown once and is never retrievable again. Store it before discarding the response:
{
"webhook": {
"id": "whe_...",
"url": "https://example.com/webhooks/aq",
"event_types": ["workspace.pr_opened", "workspace.agent_error"],
"created_at": "2026-08-02T18:20:00.000Z"
},
"secret": "aqwh_..."
}
Pass an empty event_types array, or omit it, to subscribe to every supported event. GET /v1/webhooks returns your endpoints as webhooks plus the complete supported list as event_types, so your integration can discover new event types without a docs check.
Delivery format
POST /webhooks/aq
content-type: application/json
user-agent: AQ-Webhooks/1.0
aq-webhook-id: whd_...
aq-event-type: workspace.pr_opened
aq-signature: t=1785690000,v1=hex-hmac-sha256
{
"id": "whd_...",
"type": "workspace.pr_opened",
"created_at": "2026-08-02T18:20:00.000Z",
"data": {
"id": "event-id",
"workspace_id": "workspace-id",
"type": "pr_opened",
"payload": {
"prNumber": 42,
"prUrl": "https://github.com/org/repo/pull/42"
},
"created_at": "2026-08-02T18:20:00.000Z",
"read_at": null
}
}
Webhook data uses the same scrubbed public DTOs as REST. Treat event delivery as at least once: deduplicate using the stable id or aq-webhook-id.
Verify the signature before parsing or processing
import crypto from "node:crypto";
export function verifyAqWebhook(rawBody, signature, secret) {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(signature ?? "");
if (!match) return false;
const timestamp = Number(match[1]);
const received = Buffer.from(match[2], "hex");
const expected = Buffer.from(
crypto.createHmac("sha256", secret)
.update(String(timestamp) + "." + rawBody)
.digest("hex"),
"hex",
);
const fresh = Math.abs(Date.now() / 1000 - timestamp) <= 300;
return fresh && received.length === expected.length
&& crypto.timingSafeEqual(received, expected);
}
Use the exact raw request bytes in the HMAC input, not parsed and re-serialized JSON. Reject stale timestamps to prevent replay, compare digests in constant time, and return a 2xx only after your system has durably accepted the event.
Event types
- Lifecycle:
workspace.created,workspace.closed,workspace.reopened. - Agent:
workspace.agent_error,workspace.agent_stopped,workspace.agent_stale,workspace.agent_idle. - Pull requests:
workspace.pr_opened,workspace.pr_merged,workspace.pr_closed. - Linear and comments:
workspace.linear_comment,workspace.linear_state_changed,workspace.comment_added. - Runtime health:
workspace.terminal_exited_unexpectedly,workspace.setup_hook_failed,workspace.prepare_failed,workspace.runner_lost,workspace.stranded.
Retries and endpoint health
AQ considers any 2xx response successful and times out a delivery after 10 seconds. Failed deliveries receive up to six total attempts, with delays of 1 minute, 5 minutes, 30 minutes, 2 hours, and 12 hours. A delivery is dead-lettered after its final failed attempt. An endpoint is automatically disabled after 20 consecutive failed deliveries; create a new endpoint after fixing the receiver.
Errors, limits, and troubleshooting
REST errors use a stable JSON envelope with a machine-readable snake_case code:
{"error":"insufficient_scope","scope":"events:read"}
| Status or error | Meaning | What to do |
|---|---|---|
401 unauthorized | The Bearer header is missing, malformed, revoked, or no longer backed by active membership. | Call /v1/me, check the header, then create a new key if needed. |
403 insufficient_scope | The key is valid but cannot perform this operation. | Create a least-privilege replacement key with the returned scope. |
403 agent_disabled | The team has AQ Composer turned off. | Ask AQ to enable the built-in agent for the team. |
400 idempotency_key_required | A POST or DELETE arrived without an Idempotency-Key header. | Send the header on every REST write. See the idempotency section above. |
404 not_found | The resource does not exist or is not visible to the acting user. | Check the id and the user's workspace access. AQ intentionally does not distinguish the two cases. |
409 retry_in_progress | The first request using this idempotency key has not finished. | Wait briefly and retry the identical request with the same key. |
409 session_archived | The conversation tab was closed with the archive endpoint. | The transcript stays readable by id. Start a new conversation for more work. |
409 run_finished | The conversation is already in a terminal state. | Read its final transcript and start a new conversation for more work. |
422 idempotency_key_reused | The same key was used with a different request body. | Use the original body, or use a new key for a genuinely new action. |
429 rate_limited | The key exceeded the default 240 requests per minute, or an agent concurrency gate was reached. | Honor Retry-After and add backoff with jitter. |
MCP 405 | The client sent GET or DELETE to the stateless endpoint. | Use a Streamable HTTP MCP client or POST JSON-RPC messages. |
MCP 406 | The raw client did not accept both required media types. | Send Accept: application/json, text/event-stream. |
The default API and MCP limit is 240 requests per minute per key. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. A 429 also includes Retry-After.
A safe production smoke-test checklist
- Create a temporary key with only
workspaces:read. - Call
GET /v1/meand verify the user, team, and scopes. - Call
GET /v1/workspaces?limit=1. - POST an MCP
tools/listrequest and confirm only read tools are advertised. - Call the MCP
list_workspacestool. - Revoke the temporary key and confirm
GET /v1/mereturns401.
Only after those read-only checks pass should you create a write-scoped key and run the create-and-close workspace example. Agent tests change files and may consume credits, so run them in a disposable workspace and only after confirming the team has AQ Composer enabled.
Complete reference
Import https://aq.dev/v1/openapi.json into an OpenAPI client generator, API client, or contract test suite for the complete field-level reference. For MCP, tools/list is the live source of truth because it returns exactly the tools allowed by the presented key.
Frequently asked questions
What is the AQ API?
The AQ API is the programmatic interface to AQ workspaces: a versioned REST API at https://aq.dev/v1, a hosted MCP server at https://aq.dev/mcp, and signed outbound webhooks. All three operate on the same workspaces with the same permission model as the AQ dashboard, authenticate with scoped aq_ API keys, and are fully described by the OpenAPI 3.1 document at /v1/openapi.json.
What can you do with the AQ API?
List, create, and close workspaces; read workspace event logs, tracked pull requests, and preview status and URLs; start, poll, steer, stop, and archive AQ Composer conversations; and register webhook endpoints so your service reacts to workspace, agent, pull request, Linear, comment, and runner events without polling. Typical integrations are dashboards, issue intake automation, activity feeds, agent orchestration, and event-driven workflows.
Does AQ have an API?
Yes. AQ exposes a versioned REST API at https://aq.dev/v1, signed webhooks for workspace events, and an MCP server at https://aq.dev/mcp. The machine-readable OpenAPI 3.1 reference lives at /v1/openapi.json.
How do I get an AQ API key?
Create one in Settings under API keys. Keys start with aq_, are shown once at creation, and act as you inside your team: a key sees exactly the workspaces you can see, with the scopes you chose at creation.
Does AQ have an MCP server?
Yes, at https://aq.dev/mcp (Streamable HTTP). Any compatible MCP client can list workspaces, create new ones, read event logs, fetch preview URLs, and use the AQ Composer tools allowed by its scopes. Clients that support OAuth connect with just the URL (your browser opens an AQ consent screen once), and headless clients can authenticate with the same aq_ API key as REST.
What are the AQ API rate limits?
240 requests per minute per key by default. When a key exceeds the limit, the API returns 429 with a Retry-After header. Responses also report the limit, remaining requests, and reset time in rate-limit headers.
Does the AQ REST API use the same permissions as the dashboard?
Yes. An API key acts as the user who created it and can only see workspaces that user can access. AQ also checks the key's explicit scopes. Removing the user from the team, suspending the account, or revoking the key stops access.
Can an MCP client start and steer an AQ coding agent?
Yes. A key with the agent:run scope can use start_agent, get_agent_run, send_agent_message, and stop_agent. AQ Composer must also be enabled for the team, and the target workspace must be active.
Are AQ webhooks delivered exactly once?
No. Treat delivery as at least once. AQ retries failures, so receivers should verify the signature and deduplicate using the stable delivery id in the payload or aq-webhook-id header before applying side effects.
How should I retry an AQ API mutation after a timeout?
Send the identical REST request with the same Idempotency-Key. AQ replays the original response instead of executing the mutation twice. Do not generate a new key for a timeout retry. MCP tool calls do not carry REST idempotency headers, so verify state before automatically retrying a mutating MCP tool.