Skip to main content
Base URL:
Endpoints require Authorization: Bearer sk_atoll_... unless noted. The public MCP plugin profile also accepts OAuth 2.1 access tokens bound to the Atoll MCP resource; those connections select one explicitly authorized agent profile on each actor-dependent call. The Google Chat link-token endpoint requires an authenticated human web session, and the Google Chat event callback requires a Google-signed OIDC ID token.

Authentication

For agent keys, auth.projectAccess[] reports effective view, edit, or admin access. Project-scoped agents intentionally remain org guests, membership changes are read live, and standard agent keys normally have scopes: []. OAuth-bound agent responses also include non-secret auth.oauth provenance fields documented in the fields reference. profile_ref is an opaque selector, not a credential.

Organizations

Projects

A successful project create also creates Backlog, Todo, In Progress, and Done columns; a Default board view containing those columns; and All Tasks, My Tasks, and Recently Updated custom views. If any default cannot be created, the transaction rolls back and no partial project remains.

Project access

Adding a project member accepts { "memberId": "...", "accessLevel": "view" | "edit" | "admin" }. If accessLevel is omitted, Atoll defaults it to edit; invalid values return 400.

Issues

Issue-centric initiative links follow both resource boundaries. The collection read requires access to the issue, omits linked initiatives the caller cannot read, and returns 200. For project-bound issues, linking and unlinking require edit/admin access to the issue project, which must already be linked to the initiative. Eligible non-guests may link or unlink writable projectless issues. Every mutation also requires edit/admin access to every project linked to the initiative. Directly requested unreadable resources on mutations are concealed as 404. Project reads and writes have different authorization thresholds. Visibility or view access permits reads; issue creation requires an explicit effective edit or admin grant on the target project. Task Activity returns { data, items, limit, offset, hasMore }. limit defaults to 50 and is clamped to 1..100; offset defaults to 0. The timeline contains meaningful task history only, including issue lifecycle, comments, assignees, labels, dependencies, initiative/target links, GitHub PR links and updates, attachments, and subtasks. Notification, webhook, realtime, and delivery records are separate concerns and are excluded. Pre-contract rows may provide partial history because older events are not fabricated or backfilled. The existing issue-detail route accepts a UUID, bare number, #number, ATOLL-number, TSK-number, or an unambiguous project-derived prefix. It filters candidates through the caller’s current project access before deciding whether the reference is unique and never matches titles or partial text. Invalid, missing, and ambiguous references return structured recovery data without disclosing inaccessible candidates. The two initiative issue-link POST routes below also resolve issue references and persist canonical UUIDs; the initiative milestone-link POST route resolves a milestone UUID or exact name; other mutation routes remain UUID-addressed. For project-bound issues, PR-link reads require project access and creation requires edit or admin access. Eligible non-guests may read and create links for projectless issues. Authorization is bound to the issue’s current parent before child reads or writes and occurs before URL parsing or GitHub metadata lookup. Atoll accepts canonical GitHub pull request URLs, extracts the owner/repo/number, and rejects malformed or non-PR URLs with 400. When GitHub metadata is available, Atoll stores the current PR title, status, canonical URL, and head SHA during attach; webhooks keep linked PR state fresh after subsequent GitHub events. Issue list filters include status, priority, projectId, assigneeId, teamId, milestoneId, q (title and description for full issue lists), open, includeArchived, orderBy, orderDir, limit, offset, and the optional response shape controls below. open=true excludes terminal statuses done and cancelled, plus archived issues, while preserving custom and other non-terminal statuses. It takes precedence over includeArchived. Compact issue views (view=board or view=list) also support board-toolbar filters: assignee (unassigned or member ID, including multi-assignee links), initiativeId, scope (mine or blocked), and q over title plus issue number. Full issue-list items include the canonical project-prefixed identifier and collision-free projectSlug for project issues, or null for projectless issues. Compact view=board and view=list items do not include these fields. Main list endpoints keep their legacy resource-key JSON by default. Add shape=envelope or response_shape=cli to opt into the CLI-compatible envelope: { resource, items, total, limit, offset, nextOffset, truncated, hint }. Create, update, and bulk-create accept recurrenceDays as a unique array of weekday values from mon through sun when recurrenceType is weekly. Issue reads normalize the selected days into calendar order and expose both recurrence_days and recurrence_schedule: { type, interval, days }. Unrelated issue updates preserve the existing recurrence schedule.

Comments, subtasks, dependencies, labels, and attachments

When a blocker changes projects, PATCH the issue with one explicit dependencyReleaseMappings: [{ dependencyId, releaseColumnId }] entry for each blocking dependency. Destination columns must belong to the new project; the transaction rejects incomplete mappings and projectless moves with blockers. The top-level aliases dependency_release_mappings and legacy releaseColumnMappings, plus item aliases dependency_id and release_column_id, remain supported. The dependency GET response contains blocking and blockedBy arrays. Each entry has id, createdAt, and an authorized issue projection with id, number, identifier, projectSlug, title, and status. issue is null for an inaccessible target; a projectless authorized target remains visible with both issue.identifier: null and issue.projectSlug: null. Each entry also includes releaseColumnId (with release_column_id compatibility alias), a releaseColumn projection, and satisfied when the blocking issue is authorized. A blocking target projection may be issue: null independently when the dependent target is inaccessible. POST defaults the release point to the blocking project’s done column; PATCH accepts { "releaseColumnId": "uuid" } or { "release_column_id": "uuid" }. The blocking issue must belong to a project; a projectless issue may be the blocked target. The dependency-release migration backfills existing dependencies to the blocking project’s done column before enforcing the persistent reference. During a rolling deployment, compatibility reads may omit release fields from older rows; treat missing release metadata as the legacy open-blocker behavior until the migration is applied. Issue comments inherit issue project permissions: guests cannot access comments on unprojected issues, and write operations require write access to the issue’s project. Comment bodies accept Markdown/plain text or existing rich-text HTML. Atoll stores and returns comment bodies as sanitized HTML. If sanitization leaves no visible text or safe media, the request returns 400 with body is required for direct comments or comment_body is required for issue updates with comment_body. Structured mentions are recommended for agents and integrations. Direct comments accept mentions: [{ "member_id": "member-id" }]; issue update comments accept comment_mentions: [{ "member_id": "member-id" }]. Use the stable Atoll org member ID, not an auth user ID or display name. Markdown and HTML atoll:member links remain backward-compatible. List-comment responses include comments[].mentioned_members, an array of { id, display_name, type } recipient summaries for persisted mentions. The array is empty when none are recorded. The single-comment route does not currently include this list-only enrichment. Responses that create comments include outcome.persistence: { status: "persisted", comment_id } and outcome.mentions, with the legacy top-level mentions alias. created counts new notification rows, deduped counts idempotently reused rows, and notification_rows.status: "failed" reports notification setup failure without changing persisted comment state. transport.dispatch: "scheduled" means asynchronous Google Chat work was scheduled, not delivered; this also covers repairing a missing durable delivery row. already_scheduled means an existing delivery row was found and no new scheduling was needed. transport.final is null while any final delivery is unknown, and is mixed when all recipient deliveries are terminal but differ. Inspect recipients[].transport.final for mixed results. transport.error exposes a safe error code and retryable flag when status lookup or scheduling fails. Each skipped target includes member_id and reason. Set reply_to_comment_id to reply to a non-deleted comment on the same issue. List, create, and single-comment responses expose that ID plus a shallow reply_to_comment parent containing its id, body, author identity, and source_metadata. Replies remain flat in presentation; a reply cannot change its parent after creation. Structured mentions continue to fan out normally, and the parent author receives a direct-reply notification unless they authored the reply. Only agent-authored comments can set source_metadata. It is a strict object with harness, at least one of thread_id or session_id, and optional host_id; unknown fields are rejected. These identifiers are routing context, not a place for credentials, prompts, or arbitrary runtime state. Omit source_metadata unless the host exposes a real thread or session ID, and never invent one. Attachments also inherit issue project permissions: project-scoped reads require project access, while upload and delete require edit or admin. Guests cannot access attachments on unprojected issues; non-guests follow the org-level issue rule and may read, upload, and delete them. List and upload responses include id, filename, file_size, mime_type, uploaded_by, created_at, and a relative url; storage bucket/path fields are not exposed. Resolve url against the Atoll base URL and resend the same authentication. This is an authenticated API path, not a public or transferable storage URL. Clients that previously consumed absolute public URLs must migrate. Empty files return 400; files over 10 MiB return 413. Declared images must be signature-valid PNG, JPEG, GIF, or WebP. SVG and other declared image types return 400. Non-image files are accepted, but only validated raster images are served inline with nosniff; other files download as application/octet-stream. Content responses are private and no-store. Upload durably prepares the exact reconciliation input before sending bytes to Storage, activates it after upload, and only then attempts the attachment row. If request-time outcomes remain unverified, the service worker resumes the state machine. Cleanup is tombstoned under the same object lock as creation before bytes are removed. User deletion atomically retires surviving create work before removing the row, and tombstone expiry performs a final idempotent Storage removal. Direct attachment deletion and permanent issue, project, or organization deletion commit the attachment-row or parent cascade first and atomically queue both transitional and private bucket paths for cleanup. A service-authenticated worker processes bounded due jobs every 15 minutes and retries provider failures. Direct deletion also attempts cleanup immediately; when it is deferred, the endpoint returns 202 with { "ok": true, "cleanup_pending": true }. Parent deletion returns after durable queueing, so physical object cleanup may still be pending.

Milestones

Project-bound milestone reads require effective project access. Create and update require edit or admin project access. Unreadable milestones are concealed as 404; insufficient write access on a readable project returns 403. Milestone deletion remains restricted to organization owners and admins. Create requests require name and may include description, due_date/dueDate, and status (active or closed); the status is persisted atomically with the milestone.

Goals, KPIs, and initiatives

KPI collections are organization-wide Strategy resources. Owners and admins can read and write them. Other non-guest organization members can read KPIs, snapshots, and redacted per-KPI sync metadata, but cannot create, update, delete, or record snapshots. Guest and project-scoped agents receive 403 for the collection; direct KPI routes, snapshot routes, and per-KPI sync read/draft routes conceal unreadable objects as 404. Per-KPI sync draft creation and proposed-config validation require a readable KPI and keep the existing draft-only, no-network agent restrictions. Detail responses include read-only relationship projections:
  • GET /api/orgs/{id}/initiatives/{initiativeId} embeds initiative.kpi_impacts only for linked KPIs readable by the caller. Unreadable KPI relationships are omitted.
  • GET /api/orgs/{id}/kpis/{kpiId} embeds kpi.initiative_impacts for visible initiatives across all initiative statuses, using project-aware initiative filtering.
These intended-impact relationships are separate from KPI snapshot attribution. Use the initiative KPI-impact POST/DELETE endpoints for relationship mutations; use KPI snapshot endpoints and attributed_to_initiative_id for measurement attribution. | GET | /api/orgs/{id}/initiatives/{initiativeId}/targets | List initiative targets | | POST | /api/orgs/{id}/initiatives/{initiativeId}/targets | Create target | | GET | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId} | Get target | | PATCH | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId} | Update target | | DELETE | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId} | Delete target | | GET | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId}/issues | List readable target issue links, including readable projectless issues for non-guests | | POST | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId}/issues | Link issue by UUID, number, #number, ATOLL-number, TSK-number, or unambiguous project-derived prefix; the issue project must already be linked to the initiative, while eligible non-guests may link writable projectless issues | Initiative issue-link POST bodies accept an issue UUID, bare issue number, #number, ATOLL-number, TSK-number, or an unambiguous project-derived prefix. Initiative target issue-link POST bodies use the same formats. Initiative milestone-link POST bodies accept either the milestone UUID or its exact name. Resolution is limited to the initiative’s authoritative project scope; cross-organization, cross-project, unreadable, and projectless-milestone references return 404. Successful writes store the canonical resource UUID. Missing identifiers return 400 with the route-specific *_id is required message; malformed identifiers return 400 with Invalid resource identifier; ambiguous references return 409 with Resource identifier is ambiguous; concealed or missing references return 404 with Not found; and resolver failures return 500 with Failed to resolve resource access. | DELETE | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId}/issues/{issueId} | Unlink issue from target; a project-bound issue’s project must already be linked to the initiative, while eligible non-guests may unlink writable projectless issues | | GET | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId}/milestones | List readable project-bound target milestone links; projectless milestones are unsupported | | POST | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId}/milestones | Link milestone to target; its project must already be linked to the initiative, and projectless milestones are unsupported | | DELETE | /api/orgs/{id}/initiatives/{initiativeId}/targets/{targetId}/milestones/{milestoneId} | Unlink milestone from target; its project must already be linked to the initiative, and projectless milestones are unsupported | | GET | /api/orgs/{id}/heartbeat | Heartbeat summary for the authenticated agent | | GET | /api/orgs/{id}/strategy/audit | Audit strategy chain for structural gaps + health issues (guests forbidden) | Reading a project-linked initiative or its child resources requires access to at least one linked project. Explicit initiative-project links and projects inferred from direct issue/milestone links form the authoritative scope. Updating it or mutating issue, milestone, or target links requires edit/admin access to every linked project. A requested issue or milestone project must already be linked to the initiative. KPI-impact reads omit unreadable KPIs; KPI-impact writes require write access to the target initiative and read access to the same-org KPI. Projectless initiative writes remain owner/admin-only, and this relationship permission does not grant KPI definition, sync, or snapshot write access. Projectless initiatives are readable by non-guest organization members and writable only by organization owners/admins. Unreadable initiatives return 404; readable initiatives without sufficient write access return 403. Initiative targets are not KPI snapshots. Use progress targets for initiative outputs and gate targets for launch prerequisites where pace language would be misleading. Target list and detail responses include only issue and milestone link IDs the caller can read. For a shared initiative, access through one project does not expose links from another project. Strategy audit applies the same boundary. Owners and admins receive organization-wide execution evidence. Other non-guest members receive project-bound issues, milestones, target links, and target findings only for readable projects. A restricted member with no readable projects receives no issue or target execution evidence. Guests cannot use Strategy audit.

Members, teams, agents, and integrations

Existing legacy avatar URLs in the old attachments bucket are served through an authenticated compatibility proxy after issue attachments become private. Member PATCH and DELETE can return 409 when the actor’s or target member’s authorization changes before the atomic mutation commits. Refetch the member and current permissions before retrying, and retry only if the action remains authorized. Avatar upload/removal is restricted to the target member or an organization owner/admin, and both caller and target are bound to the organization in the request path. Cross-organization IDs return 404. Upload responses contain only member.id and member.avatar_url. Avatar pointer updates use compare-and-set semantics. A concurrent change returns 409; a successful mutation whose retired Storage object still needs durable cleanup returns 202 with cleanup_pending: true. A 409 response is { "error": "Avatar changed concurrently" } and includes cleanup_pending: true only when cleanup of a staged or retired avatar object remains queued. Due cleanup jobs are drained by an authenticated 15-minute service worker as well as opportunistically during avatar requests. Uploads over 2MB return 413. Agent creation scopes: send { name, projectIds: [...] } for a project-scoped guest agent, or legacy { name, projectId, projectIds? } to include a default project. Send { name, personal: true } for a personal guest agent that inherits the human owner’s project access; personal creation rejects explicit projectId or projectIds. Omitting project scope creates an org-wide agent and remains owner/admin-only.

Boards, views, templates, updates, and analytics

Project-bound reads in this section require effective project access; mutations require edit or admin access. Board reassignment must stay within the project, and reorder rejects duplicate, missing, partial, or mixed-project IDs before changing any positions. It also requires unique non-negative integer positions. Creation appends; direct position fields on create or patch are rejected. Status-update creation follows the same write boundary. Issue-template listing is intersected with effective project access. Project-template reads need project access and mutations need edit or admin. Organization-wide templates are readable by non-guests and manageable only by organization owners/admins; guests and project-scoped agents do not receive them. Unreadable or cross-organization template/project IDs return 404, while a readable project with insufficient write access returns 403. Analytics, project health, and organization activity are intersected with the caller’s accessible projects. Empty guest scope returns empty data without enumerating organization projects. Eligible non-guests may also receive projectless organization data. Project-bound issue activity requires access to its issue project; eligible non-guests may read projectless issue activity. An explicitly requested inaccessible analytics project is concealed as 404. Analytics requires from and to values that are each either a calendar-valid YYYY-MM-DD date or a timezone-qualified RFC 3339 timestamp (Z or an explicit UTC offset). The ordered range may span no more than 366 days. Partial dates, timezone-less timestamps, normalized invalid dates, reversed ranges, and longer ranges return 400.

Automation, webhooks, billing, and GitHub

Private inbox endpoints require an authenticated organization member whose ID is explicitly configured in INBOX_OPERATOR_MEMBER_IDS; a missing allowlist fails closed. Incoming HTML is sanitized, attachments remain private, unknown aliases are quarantined, and reply drafts are never sent. Alias matching checks To, then CC, then BCC; the first configured alias wins. The webhook body is limited to 256 KiB. Attachments over 10 MiB each or 25 MiB per message are recorded as skipped_oversize. Drafts accept To and optional CC, may use only configured inbox aliases as their From address, and save idempotently.

Notifications and public endpoints

Current-member notifications can include mention.created, issue.assigned, comment.added, and issue.status_changed. Comment writes can request structured mentions with mentions[].member_id or comment_mentions[].member_id; comment-create responses include mention fanout proof. Notification preferences support in_app and google_chat channels. The single Google Chat preference is stored under mention.created and controls mentions, assignments, and direct-reply comments; ordinary comments and status changes are excluded. Disabling google_chat stops future Chat delivery without acknowledging in-app notifications. If in_app mentions are muted but google_chat mentions are enabled, Atoll can still create an acknowledged notification row for Chat delivery without surfacing it in the bell or heartbeat. Disabling in-app mention.created delivery also attempts to acknowledge that member’s currently unread mention notifications; when cleanup succeeds, muted mentions leave both the bell and heartbeat attention_items. New direct-message installations receive an unprompted welcome before configuration. help, /help, @Atoll help, and configured Help command ID 1 return setup instructions distinct from the welcome. When verified-email auto-linking is missing, unresolved, or ambiguous, the user sends connect; classic Chat interaction apps then receive REQUEST_CONFIG and Workspace add-ons receive basic_authorization_prompt. The same command lets an already-linked user reconnect or add another workspace. Add-on callbacks require the endpoint URL audience and exact per-project add-on service account email. Connect-session and member endpoints require an authenticated human web session; one-time setup messages remain a manual fallback. Google Chat mention cards include the task title, a safely formatted plain-text comment preview limited to 500 characters, and an Open in Atoll button. Rich-text markup is removed and Google Chat card formatting characters are escaped.