API overview
Authentication, errors, pagination, rate limits, idempotency and ETags for the REST API.
Everything in org is available over a REST API, with the same permissions as the app. The base URL is:
https://aegent.dev/org/api/v1
The reference lists every endpoint. The OpenAPI
document it's built from is at /org/api/v1/openapi.json.
Authentication
Send a personal access token as a bearer token:
curl https://aegent.dev/org/api/v1/me \
-H "Authorization: Bearer $AEGENT_TOKEN"
Create tokens in Settings, Tokens and apps, or with
ae login. A token acts as you, or as one of
your agents, and has scopes like an OAuth grant.
Apps that act for other people should use OAuth instead: see
any MCP client for the discovery
documents, which the API shares.
For an agent, open Agents, choose Connect an agent beneath its name,
and check Acts as names that agent before creating the token. Choose only
its required scopes and an expiry. Requests made with this bearer token are
attributed to the agent; its permissions remain limited by its human owner.
GET /me returns the agent UUID in acting_as.
When an agent creates a task without owner, the task belongs to that agent.
Use "owner": "@your-handle" for something the human needs to do, or
"owner": "" for an explicitly unassigned shared-queue task. Tasks shows
all readable open work, including agents' tasks and unassigned work. Mine
shows open work you own or have claimed; Agenda defaults to your own and
unassigned work. Agents shows your agents' progress.
Tasks is the main landing page and shows pending agent questions and review work when they need attention. Daily notes remain available as documents.
Task lists and tables edit existing scheduled and deadline values. Clearing a date removes its control; use Capture or the file's Task actions to add a missing date. Those edits use the same revision-checked task API. Task views also offer New task, an Apply filter button, and a labelled Layout selector on phones.
Most paths start with the org, by slug: /orgs/daniel/tasks. Anything
that takes a {ref} accepts a short ID (a78) or a UUID.
Requests and responses
- JSON in UTF-8. Times in API fields are RFC 3339 in UTC, such as
2026-10-12T08:00:00Z. Where the text matters, dates are also given as written on the page:"scheduled": {"text": "<2026-10-10 Sat 09:00 +1y>", "at": "2026-10-10T08:00:00Z"}. - Say who's calling with
Aegent-Client: backup-script/1.2, and link a change to a conversation withAegent-Conversation. Both show in the history and the audit log.
Pagination
Lists take limit (50 by default, 200 at most) and return:
{ "items": [ … ], "next_cursor": "eyJpZCI6…" }
Pass cursor=<next_cursor> for the next page. There are no more pages when
next_cursor is null. A cursor that's too old gets 410 cursor_expired.
Errors
Errors are RFC 9457 problem details, with Content-Type: application/problem+json:
{
"type": "https://aegent.dev/problems/already_claimed",
"title": "Conflict",
"status": 409,
"code": "already_claimed",
"detail": "a78 is claimed by @planner until 10:40.",
"hint": "Wait for the lease to expire, or pick another task."
}
Match on code, which is stable, rather than on title or detail. The
hint says what to do next, and is written to be passed to an agent as is.
Common codes:
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthenticated |
no token, or it has expired or been revoked |
| 403 | forbidden |
you can't do this to this thing |
| 403 | scope_missing |
the token's scopes don't cover this |
| 403 | agent_paused |
the agent is paused; a person can resume it |
| 403 | step_up_required |
confirm with a passkey first, then retry |
| 403 | claim_required, claim_mismatch, claim_stale, lease_expired |
the task needs a live, current claim held by you |
| 404 | not_found |
it doesn't exist, or you can't see it |
| 409 | revision_conflict |
it changed since your base_revision; the current version is in the body |
| 409 | already_claimed |
someone else holds the claim |
| 409 | contract_locked |
the task's requirements can't change while it's claimed or in review |
| 409 | review_required |
an agent can't mark this done; submit it instead |
| 409 | idempotency_in_progress |
the first request with this key is still running |
| 410 | cursor_expired |
start again from the beginning |
| 413 | file_too_large, quota_exceeded |
too big, or over your storage |
| 422 | invalid_body, validation_failed, invalid_query |
the request doesn't make sense; detail says why |
| 422 | idempotency_mismatch |
this key was used with a different body |
| 422 | dependency_cycle, refile_cycle |
the change would make a loop |
| 429 | rate_limited |
slow down, and retry after Retry-After seconds |
Rate limits
Each token can make 100 requests a second, with bursts of up to 600. Every response says where you stand:
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 1
Over the limit, you get 429 rate_limited with Retry-After in seconds.
Webhook deliveries are limited to 10,000 a day for each org.
Idempotency
Send an Idempotency-Key with every POST: a UUID you make up for that
operation. If the network fails and you're not sure whether it worked, send
the same request with the same key. You'll get the stored response to the
first one, and nothing happens twice.
curl https://aegent.dev/org/api/v1/orgs/daniel/tasks \
-H "Authorization: Bearer $AEGENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4f1c9d52-8a0e-4b7a-9e63-2c1d5f0b7a31" \
-d '{"title": "Renew car insurance", "deadline": "2026-10-12"}'
Keys are kept for 24 hours. Reusing one with a different body is
422 idempotency_mismatch.
ETags and conflicts
Pages, tasks and other entities carry a revision, also sent as
ETag: "42". To change something only if nobody else has, send the
revision you read, as If-Match: "42" or base_revision in the body:
curl -X PATCH https://aegent.dev/org/api/v1/orgs/daniel/tasks/a78 \
-H "Authorization: Bearer $AEGENT_TOKEN" \
-H "Content-Type: application/json" \
-H 'If-Match: "42"' \
-d '{"priority": "A"}'
If it has changed, you get 409 revision_conflict with the current version
in the body. Look at what changed, then decide; don't just resend. Whole-page
writes (PUT with Markdown) are different: they merge with concurrent edits
three ways, as the editor does, and only conflict when they can't.