Actions Framework
Enable approval-gated write operations like sending emails and creating JIRA tickets from the agent.
The action framework lets the Atlas agent perform write operations -- sending emails, creating JIRA tickets, and more -- with configurable approval gates. Actions go through a request-approve-execute lifecycle that gives humans control over what the agent does.
Prerequisites
- Authentication enabled (any mode except
none) - Internal database (
DATABASE_URL) recommended for persistent action log (in-memory fallback available but lost on restart) - For email actions:
RESEND_API_KEY - For JIRA actions:
JIRA_BASE_URL,JIRA_EMAIL,JIRA_API_TOKEN
Enable
ATLAS_ACTIONS_ENABLED=trueActions require authentication (any mode except none) and an internal database (DATABASE_URL) for the action log.
Approval Modes
Each action has an approval mode that controls whether human approval is required before execution.
| Mode | Behavior | Who can approve |
|---|---|---|
auto | Execute immediately, no approval needed | N/A |
manual | Queue for approval | analyst or admin |
admin-only | Queue for approval, admin required | admin only |
In admin-only mode, the user who requested the action cannot approve their own request (separation of duties).
Action Lifecycle
Agent requests action → pending
↓
┌────────────────┼────────────────┐
↓ ↓ ↓
auto_approved approved denied
↓ ↓
executed executed
↓ ↓
success/failed/ success/failed/
timed_out timed_out
↓ ↓
rolled_back* rolled_back*
* reversible actions only- auto mode:
pending→auto_approved→executed(orfailed/timed_out) - manual / admin-only mode:
pending→approved/denied→executed(orfailed/timed_out) - rollback (reversible actions):
executed/auto_approved→rolled_back
When execution exceeds the configured timeout, the action transitions to timed_out instead of remaining in-flight. The timeout duration is logged in the audit trail.
Rollback
Reversible actions can be undone after execution. When an action plugin declares reversible: true and its execute handler returns rollbackInfo, the action can be rolled back via the API or the admin console.
How Rollback Works
- The action plugin declares
reversible: truein itsPluginActiondefinition - The execute handler returns a
rollbackInfoobject containing the method and parameters needed to undo the action - Atlas stores
rollbackInfoin the action log alongside the execution result - An admin triggers rollback via the API or admin console
- The action transitions to
rolled_backstatus via compare-and-swap (CAS) - The rollback handler is dispatched using the stored
rollbackInfo
Status Transition
Rollback is only available from the executed or auto_approved statuses:
executed / auto_approved → rolled_backActions in any other status (pending, denied, failed, timed_out, or already rolled_back) cannot be rolled back.
API
# Rollback an executed action
curl -X POST http://localhost:3001/api/v1/actions/<id>/rollback \
-H "Authorization: Bearer <key>"Request: No body required.
Response (success):
{
"id": "action-uuid",
"status": "rolled_back",
"action_type": "jira:create",
"target": "PROJ-123",
"summary": "Created JIRA issue PROJ-123",
"rollback_info": { "method": "transition", "params": { "issueKey": "PROJ-123" } },
"error": null
}Response (rollback handler error):
{
"id": "action-uuid",
"status": "rolled_back",
"action_type": "jira:create",
"target": "PROJ-123",
"summary": "Created JIRA issue PROJ-123",
"rollback_info": { "method": "transition", "params": { "issueKey": "PROJ-123" } },
"error": "JIRA API returned 403",
"warning": "Rollback status updated but the rollback handler reported an error. The side-effect may not have been reversed."
}When the rollback handler fails, the status still transitions to rolled_back but the response includes a warning and error field so operators know the side-effect may not have been reversed.
Admin Console
Admins can rollback actions from the admin console at /admin/actions. Executed reversible actions show a Rollback button.
Error Cases
| Scenario | HTTP Status | Error Code |
|---|---|---|
| No internal database configured | 404 | not_available |
| Invalid action ID format | 400 | invalid_request |
| Action not found | 404 | not_found |
Action not reversible (no rollback_info) | 409 | conflict |
| Action already rolled back or not in rollbackable state | 409 | conflict |
| Insufficient role | 403 | forbidden |
| Unexpected server error | 500 | internal_error |
| Rollback handler failed | 200 | Response includes warning and error fields |
Declaring Actions as Reversible
In your action plugin, set reversible: true and return rollbackInfo from the execute handler:
import { z } from "zod";
import { tool } from "@useatlas/plugin-sdk/ai";
import type { PluginAction } from "@useatlas/plugin-sdk";
const action: PluginAction = {
name: "createJiraIssue",
description: "Create a JIRA issue",
tool: tool({
description: "Create a JIRA issue from analysis findings",
inputSchema: z.object({ summary: z.string(), project: z.string() }),
execute: async ({ summary, project }) => {
const issue = await createIssue({ summary, project });
return {
issueKey: issue.key,
url: issue.url,
// Return rollbackInfo so the action can be undone
rollbackInfo: {
method: "transition",
params: { issueKey: issue.key },
},
};
},
}),
actionType: "jira:create",
reversible: true, // Enables the rollback button/API
defaultApproval: "manual",
requiredCredentials: ["JIRA_API_TOKEN"],
};The rollbackInfo.method and rollbackInfo.params are stored in the action log and passed to the rollback handler when triggered.
SDK
const result = await atlas.rollbackAction("action-uuid");
console.log(result.status); // "rolled_back"
if (result.warning) {
console.warn(result.warning); // Handler error — side-effect may persist
}See SDK Reference for the full method signature.
Built-in Actions
Email (email:send)
Send email reports via Resend.
Default approval: admin-only
Required credentials:
RESEND_API_KEY-- Resend API token
Optional:
ATLAS_EMAIL_FROM-- From address (default:Atlas <atlas@notifications.useatlas.dev>)ATLAS_EMAIL_ALLOWED_DOMAINS-- Comma-separated domain whitelist for recipients
Input:
to-- Recipient email address(es)subject-- Email subject linebody-- Email body (HTML)
JIRA (jira:create)
Create JIRA issues from data insights.
Default approval: manual
Required credentials:
JIRA_BASE_URL-- JIRA instance URL (e.g.,https://myco.atlassian.net)JIRA_EMAIL-- Authentication emailJIRA_API_TOKEN-- API token
Optional:
JIRA_DEFAULT_PROJECT-- Default project key when not specified
Input:
summary-- Issue title (max 255 chars)description-- Issue descriptionproject-- Project key (optional, falls back toJIRA_DEFAULT_PROJECT)labels-- Optional labels
This action is reversible -- on rollback, Atlas transitions the created issue to "Closed" (best-effort, depends on JIRA workflow).
Configuration
Via Environment Variables
ATLAS_ACTIONS_ENABLED=true
ATLAS_ACTION_TIMEOUT=300000 # 5 minute execution timeout (ms)Via atlas.config.ts
Override approval modes, role requirements, and timeouts per action:
import { defineConfig } from "@atlas/api/lib/config";
export default defineConfig({
actions: {
// Default approval mode and timeout for all actions unless overridden
defaults: {
approval: "manual",
timeout: 300000, // 5 minute default (ms)
},
// Per-action overrides — key is the action type identifier
"email:send": {
approval: "admin-only", // Only admins can approve email sends
requiredRole: "admin",
timeout: 30000, // 30 second timeout for email sends
credentials: {
RESEND_API_KEY: { env: "RESEND_API_KEY" }, // Validated at startup
},
},
"jira:create": {
approval: "manual", // Any analyst or admin can approve
requiredRole: "analyst",
},
},
});Timeout resolution: With a config file, per-action timeout overrides defaults.timeout. The ATLAS_ACTION_TIMEOUT env var is only used when no config file is present (it populates defaults.timeout automatically). When no timeout is configured, actions run without a time limit.
See Configuration for the full config schema.
Approving and Denying Actions
Web UI
Pending actions appear in the chat UI with Approve and Deny buttons. Admins can also manage actions from the Admin Console at /admin/actions.
API
# Approve a pending action
curl -X POST http://localhost:3001/api/v1/actions/<id>/approve \
-H "Authorization: Bearer <key>"
# Deny with an optional reason (recorded in the action log)
curl -X POST http://localhost:3001/api/v1/actions/<id>/deny \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d '{"reason": "Not relevant"}'Slack
When using the Slack integration, pending actions show as ephemeral messages with Approve and Deny buttons visible only to the requesting user.
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/actions | List actions (filter by status, default: pending) |
GET | /api/v1/actions/:id | Get action details |
POST | /api/v1/actions/:id/approve | Approve a pending action |
POST | /api/v1/actions/:id/deny | Deny a pending action |
POST | /api/v1/actions/:id/rollback | Rollback an executed reversible action |
Role Requirements
| Role | Can request actions | Can approve manual | Can approve admin-only |
|---|---|---|---|
viewer | Yes | No | No |
analyst | Yes | Yes | No |
admin | Yes | Yes | Yes |
See Authentication for role configuration.
Building Custom Actions
Action plugins follow the same pattern as built-in actions. See Plugin Authoring Guide for the action plugin type, which registers custom tools with approval gates and credential validation.
Troubleshooting
Actions not appearing -- Verify ATLAS_ACTIONS_ENABLED=true and authentication is configured (any mode except none). Without DATABASE_URL, actions still work but use in-memory storage.
Approval stuck -- Check that the approving user has the required role. In admin-only mode, the requester cannot self-approve.
See Troubleshooting for more diagnostic steps.
See Also
- Plugin Authoring Guide — Building custom action plugins
- Configuration — Declarative action configuration in
atlas.config.ts - Authentication — Role-based approval (actions require auth)
- Admin Console — Monitor and manage actions from the web UI
- Environment Variables —
ATLAS_ACTIONS_ENABLEDand per-action settings