Operating model
Aurora's contract model
Aurora's external API is not just a set of CRUD routes. It is a workspace-scoped execution surface for people and automation, which means identity resolution, queue semantics, and comment trails matter just as much as request syntax.
Discovery before mutation
Read workspace structure first so later writes can use valid teams, users, statuses, and queue identities.
Use stable natural keys
Prefer teamKey, projectKey, issueKey, and statusKey whenever your client can avoid opaque ids.
Make create flows idempotent
Use Idempotency-Key headers so retries do not fork records when workers, webhooks, or agents repeat work.
Recover from machine codes
Branch on code and details instead of parsing human text when references are stale or invalid.
Client architecture
Discovery and caching
Treat discovery as a lightweight schema sync for the current token workspace. It should happen before mutation and then refresh only when the surrounding workspace configuration actually changes.
| Endpoint | What to cache | Refresh guidance |
|---|---|---|
| GET /api/external | Token queue identity, workspace metadata, endpoint map, capability flags | Per token workspace session |
| GET /api/external/teams | teamId and teamKey pairs | Refresh on team-management changes |
| GET /api/external/users | ownerId, reporterId, assigneeId, automationAgents | Refresh on membership or token-name changes |
| GET /api/external/statuses | Valid statuses for a team | Refresh when moving across teams or after status configuration changes |
Practical caching rule
Identity and lookup
Prefer keys first, ids second
The docs, the API, and the plugin all work best when clients speak in team keys, project keys, and issue keys wherever they can.
Prefer these inputs
These are easier for operators to reason about and easier for agent prompts to generate correctly.
- teamKey over teamId when humans already know the team key
- projectKey plus teamKey over projectId when building prompts or task plans
- issueKey over issueId when the issue was referenced in chat or copied from the UI
- statusKey over statusId when a team's workflow keys are already known
Use ids when
Opaque ids are still correct when they come from a trusted previous read or when disambiguation is required.
- The client already persisted the id from a previous Aurora response
- A project key is ambiguous without a team id or team key
- You are patching the exact record you just fetched by id
- Your workflow stores canonical Aurora ids internally for subsequent calls
Create flows
Retry safety with Idempotency-Key
The fastest way to create duplicate projects or issues is to let a worker or agent retry a create call without a stable key. Aurora's create routes are designed so you do not have to.
curl -X POST https://www.auroraworkos.com/api/external/projects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: source-system:project:weekly-scorecard" \
-d '{
"name": "Weekly Scorecard"
}'Key design rule
source-system:entity-type:external-id. That makes retries deterministic across restarts, not just within one process lifetime.Large collections
Cursor pagination
When you are reading lists for sync, triage, or queue pickup, do not assume one page is enough. Aurora's list routes expose a cursor model that is intentionally simple to integrate.
| Route | Supports | Typical use |
|---|---|---|
| GET /api/external/projects | limit, cursor | Workspace and team-scoped project inventory |
| GET /api/external/issues | limit, cursor | Issue triage, backlog review, automation pickup |
| GET /api/external/meetings | limit, cursor | Meeting history and workflow state review |
GET /api/external/issues?limit=50
{
"issues": [...],
"pageInfo": {
"hasMore": true,
"nextCursor": "opaque-cursor",
"limit": 50
}
}Error recovery
Structured errors are part of the contract
The Aurora API returns machine-usable errors so clients and agents can choose a recovery path without scraping human prose.
{
"error": "Human-readable message",
"code": "MACHINE_CODE",
"details": {
"field": "value"
},
"suggestion": "Recovery guidance for a client or agent."
}| Code | Meaning | Recommended recovery |
|---|---|---|
| UNAUTHORIZED | The request did not include a valid, active API token or OAuth access token. | Refresh credentials and retry after confirming you are calling /api/external or /api/mcp with the right auth method. |
| TEAM_NOT_FOUND | The supplied teamId or teamKey could not be resolved inside the token workspace. | Refresh team discovery and retry with a valid teamId or teamKey. |
| NO_TEAM_AVAILABLE | A create request needed a default team but the workspace has no usable team. | Create or restore a workspace team, then retry the request with teamId or teamKey. |
| PROJECT_KEY_AMBIGUOUS | A projectKey was supplied without enough team context to resolve it uniquely. | Add teamKey or switch to projectId. |
| PROJECT_NOT_FOUND | The requested project could not be found or is outside the caller's workspace/project access boundary. | Refresh project discovery, confirm project access, and retry with a valid projectId or teamKey/projectKey pair. |
| PROJECT_TEAM_MISMATCH | The resolved project does not belong to the supplied team context. | Use the project's actual teamKey/teamId or switch to projectId-only lookup. |
| PROJECT_KEY_CONFLICT | The requested project key is already used in the target team. | Choose a unique project key or update the existing project instead. |
| NAME_REQUIRED | A project, external owner preset, or agenda status write was sent without a required name. | Validate payloads before write calls and retry with a non-empty name. |
| TEAM_CHANGE_BLOCKED | A project team move was requested while the project still has issues. | Move or resolve project issues first, or leave the project on its current team. |
| ISSUE_NOT_FOUND | The requested issue could not be found or is outside the caller's workspace/project access boundary. | Refresh issue lookup with GET /api/external/issues or GET /api/external/issues/by-key/{issueKey}. |
| PROJECT_REQUIRED | An issue or agenda action requires a target project but none was supplied or resolved. | Provide projectId or projectKey with team context when needed. |
| TITLE_REQUIRED | An issue create request was sent without a non-empty title. | Validate issue payloads before write calls and retry with title. |
| STATUS_INVALID_FOR_PROJECT | The chosen status does not belong to the issue's project team. | Reload statuses for the target team and retry with a valid statusId or statusKey. |
| STATUS_INVALID_FOR_PROJECT_WORKFLOW | The chosen status is hidden or disallowed by the project's workflow settings. | Reload project workflow or board data and choose a visible, allowed status. |
| STATUS_NOT_FOUND | The supplied statusId or statusKey could not be resolved. | Refresh statuses for the target team and retry with a current status reference. |
| WORKFLOW_TRANSITION_BLOCKED | The requested status move is blocked by project workflow transition rules. | Move through an allowed transition path or adjust the project workflow first. |
| MEETING_NOT_FOUND | The requested meeting could not be found in the caller's accessible workspace/projects. | Refresh meeting discovery and confirm the token has project access when the meeting is project-linked. |
| MEETING_TITLE_REQUIRED | A meeting create or update request was sent without a non-empty title. | Provide title before retrying the meeting write. |
| INVALID_MEETING_TIME | Meeting or agenda dates/times were malformed or inconsistent. | Use ISO datetimes for startsAt/endsAt and YYYY-MM-DD for agenda dates, with end after start. |
| RECURRENCE_START_REQUIRED | A recurring meeting was requested without a usable start date. | Provide startsAt when recurrenceType is not none. |
| AGENDA_ITEM_NOT_FOUND | The requested agenda item could not be found inside the supplied meeting. | Reload the meeting agenda and retry with a current agendaItemId. |
| AGENDA_ITEM_TITLE_REQUIRED | An agenda item create request was sent without a non-empty title. | Provide title before retrying the agenda item write. |
| AGENDA_STATUS_NOT_FOUND | The supplied agenda status id could not be resolved in the workspace. | Reload meeting settings and retry with a current agenda status id. |
| AGENDA_STATUS_DELETE_BLOCKED | The agenda status cannot be deleted because agenda items still reference it. | Move agenda items to another status, then retry deletion. |
| OWNER_NOT_IN_WORKSPACE | An owner, reporter, actor, or token-backed user identity could not be resolved in the workspace. | Refresh users, confirm the user is a workspace member, or recreate the API token from a current workspace member account. |
| OWNER_NOT_FOUND | An agenda owner or external owner preset could not be found. | Reload agenda owners or meeting settings before retrying. |
| OWNER_REQUIRED | An agenda owner write did not include an internal user or external owner value. | Provide userId, presetId, externalName, or externalEmail. |
| ASSIGNEE_NOT_IN_WORKSPACE | The requested assignee is not a workspace member or active automation-agent id. | Refresh users and retry with a valid human user id or api-token:<name> identity. |
| ASSIGNEE_NOT_IN_PROJECT | The requested assignee does not have access to the target project. | Grant project access to the user or automation agent, or choose a different assignee. |
| ASSIGNEE_NOT_IN_PROJECT_TEAM | The requested human assignee is not a member of the target project team. | Add the user to the project team or use an allowed automation-agent assignee. |
| ASSIGNEE_FILTER_CONFLICT | The request combined assignedToMe with an explicit assigneeId. | Use either assignedToMe=true or assigneeId, not both. |
| REPORTER_NOT_IN_WORKSPACE | The requested reporter is not a workspace member. | Refresh users and retry with a workspace member user id. |
| COMMENT_BODY_REQUIRED | A comment create request was sent without a non-empty body. | Validate comment payloads before write calls and retry with a non-empty string body. |
| COMMENT_PARENT_NOT_FOUND | A threaded reply referenced a parent comment that does not belong to the issue. | Reload issue comments and retry with a current parentCommentId. |
| INVALID_TARGET_TYPE | A meeting work-link request used an unsupported targetType. | Use issue, project, discussion_topic, or agenda_item. |
| TARGET_REQUIRED | A meeting work-link request omitted both targetId and targetKey. | Provide targetId, or targetKey for supported issue/project targets. |
| UNSUPPORTED_ACTION | The request attempted a guarded operation such as converting without an issue action or sending an empty update. | Read the response suggestion/details, adjust the payload, and retry through the supported route. |
Queues and orchestration
Automation queues and visible execution
Aurora treats API token names as first-class queue identities. That makes assignment, polling, and durable comment trails predictable for both background workers and visible chat-driven flows.
Recommended automation pattern
Use tokens as queue identities, not as a hidden replacement for human ownership.
- Use GET /api/external first to discover token.assigneeId and capability flags before queue polling.
- Treat the API token name as the queue identity for automation assignment and assignedToMe filtering.
- Prefer visible chat-orchestrated execution for ambiguous work and reserve background workers for deterministic tasks.
- Leave humans as reporterId, move issues through todo -> in-progress -> in-review, and always post a summary comment on completion.
curl "https://www.auroraworkos.com/api/external/issues?assignedToMe=true&statusKey=todo" \
-H "Authorization: Bearer $TOKEN"Agent setup
Bind Codex to an Aurora workspace
Use workspace settings to generate a secure connect command for the repo or worktree Codex should operate from. The command opens Aurora in the browser, creates a dedicated token, stores it in macOS Keychain, and writes the local project binding.
sh plugins/aurora-api/scripts/bind-project.sh connect \
--base-url https://www.auroraworkos.com \
--workspace-id <workspace-id> \
--token-name "Codex automation" \
--execution-mode chat-orchestratedexport AURORA_PAT_WORKSPACE=aurora_pat_...
sh plugins/aurora-api/scripts/bind-project.sh bind-token \
--base-url https://www.auroraworkos.com \
--token-env-var AURORA_PAT_WORKSPACE \
--project <binding-id> \
--execution-mode chat-orchestratedMultiple local bindings
~/.codex/aurora-projects.json. Cwd-based resolution picks the most specific matching projectPath, and --project can select a binding explicitly for polling or staged handoff commands.Recipes
Workflow recipes
These patterns are the shortest path to reliable real-world Aurora integrations.
1. GET /api/external
2. GET /api/external/teams
3. GET /api/external/users
4. GET /api/external/statuses
5. POST /api/external/projects
6. POST /api/external/issues1. GET /api/external/issues/by-key/PE-001
2. POST /api/external/issues/{issueId}/comments
3. PATCH /api/external/issues/{issueId}
4. Re-read the issue if the next step depends on the new status or assignee1. GET /api/external/meetings/settings
2. GET /api/external/meetings/{meetingId}/agenda-items
3. POST /api/external/meetings/{meetingId}/agenda-items/{agendaItemId}/convert
4. POST /api/external/meetings/{meetingId}/agenda-items/{agendaItemId}/notes/publish
5. POST /api/external/issues/{issueId}/comments (optional summary or handoff note)Human-friendly companion pages
Know the boundaries
Current constraints
These are the constraints clients should plan around today instead of discovering them after they ship.
| Constraint | What it means |
|---|---|
| One workspace per token | A token cannot traverse workspaces. Resolve everything within its workspace boundary. |
| No token scopes yet | A valid token can use all supported external routes for its workspace. |
| External routes only | Tokens and connector access work on /api/external/* and /api/mcp, not internal app pages. |
| OAuth connector callers differ from PAT callers | Connector calls run as the approving human user; PAT calls run as the token queue identity. |
| Files and attachments are internal today | File repository APIs plus issue and meeting attachment routes require normal app-session auth; PAT external file routes are not exposed yet. |
| Child background workers may need broader sandboxing | If a hidden Codex child must call Aurora or other external services, workspace-write may not be enough. |