Atlas
Guides

Scheduled Tasks

Run recurring queries on a cron schedule and deliver results via email, Slack, or webhook.

Scheduled tasks run natural language queries on a cron schedule and deliver results to configured recipients. Use them for daily reports, weekly digests, or real-time alerting via webhooks.

SaaS

Scheduled tasks are pre-enabled on app.useatlas.dev — no configuration needed. Create tasks from the admin console or API immediately after signup.

Self-hosted prerequisites

  • ATLAS_SCHEDULER_ENABLED=true
  • An internal database (DATABASE_URL) for task persistence
  • Authentication enabled (any mode except none)

Task Definition

A scheduled task consists of a question, a cron schedule, and delivery configuration.

# Create a task that runs every day at 9 AM and emails the results
curl -X POST http://localhost:3001/api/v1/scheduled-tasks \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Daily revenue check",
    "question": "What was yesterday total revenue?",
    "cronExpression": "0 9 * * *",
    "deliveryChannel": "email",
    "recipients": [
      { "type": "email", "address": "team@example.com" }
    ]
  }'
FieldTypeRequiredDescription
namestringYesTask name (1--200 chars)
questionstringYesNatural language question (1--2000 chars)
cronExpressionstringYesStandard 5-field cron expression
deliveryChannelstringNoemail, slack, or webhook (default: webhook)
recipientsarrayNoDelivery destinations
connectionIdstringNoTarget datasource (default if omitted)
approvalModestringNoauto or manual (default: auto)

Cron Expression Format

Standard 5-field cron syntax:

minute  hour  day-of-month  month  day-of-week
0-59    0-23  1-31          1-12   0-6 (0=Sunday)
ExpressionSchedule
0 9 * * *Daily at 9:00 AM
0 9 * * 1-5Weekdays at 9:00 AM
0 0 * * 1Every Monday at midnight
0 9 1 * *First of every month at 9:00 AM
*/15 * * * *Every 15 minutes

Delivery Channels

Email

On app.useatlas.dev, email delivery is pre-configured — just add recipients and you're set.

Self-hosted deployments require Resend for email delivery:

RESEND_API_KEY=re_...
ATLAS_EMAIL_FROM="Atlas <reports@example.com>"  # Optional, default: Atlas <noreply@useatlas.dev>

Recipient format:

{ "type": "email", "address": "user@example.com" }

Slack

Requires the Slack integration to be configured.

{ "type": "slack", "channel": "#analytics" }

For multi-workspace OAuth, include teamId:

{ "type": "slack", "channel": "#analytics", "teamId": "T0123456" }

Webhook

Sends a JSON payload to an HTTPS endpoint.

{
  "type": "webhook",
  "url": "https://hooks.example.com/atlas",
  "headers": { "X-Custom-Header": "value" }
}

Webhook URLs must use HTTPS in production. Private/internal IPs (localhost, 127., 10., 172.16-31., 192.168., 169.254.*) are blocked. Sensitive headers (authorization, cookie) are filtered.


Execution Backends (Self-Hosted)

SaaS

On app.useatlas.dev, the scheduler backend is managed for you. This section is for self-hosted operators only.

The scheduler needs a mechanism to trigger periodic checks for due tasks.

bun (Default)

In-process tick loop using setInterval. No external dependencies.

ATLAS_SCHEDULER_BACKEND=bun

Best for single-instance deployments. The tick loop starts automatically when the API server boots.

webhook

External cron service calls the tick endpoint. Multi-instance safe.

ATLAS_SCHEDULER_BACKEND=webhook
ATLAS_SCHEDULER_SECRET=your-random-secret

Configure your external cron to call:

# External cron calls this endpoint to trigger a scheduler tick
curl -X POST https://api.example.com/api/v1/scheduled-tasks/tick \
  -H "Authorization: Bearer $ATLAS_SCHEDULER_SECRET"

vercel

Uses Vercel's built-in cron service. Configure in vercel.json:

{
  "crons": [{
    "path": "/api/v1/scheduled-tasks/tick",  // Vercel calls this endpoint on schedule
    "schedule": "*/5 * * * *"                // Every 5 minutes
  }]
}
ATLAS_SCHEDULER_BACKEND=vercel
# CRON_SECRET is auto-injected by Vercel

Execution Model

Each tick cycle:

  1. Fetch all enabled tasks where next_run_at <= now()
  2. Lock each task atomically (prevents double-execution across instances)
  3. Run up to ATLAS_SCHEDULER_MAX_CONCURRENT tasks in parallel
  4. For each task: execute the agent query, deliver results, log the run
  5. Recompute next_run_at from the cron expression

Concurrency and Timeouts

SettingEnv VarDefaultDescription
Max concurrentATLAS_SCHEDULER_MAX_CONCURRENT5Max tasks per tick
Task timeoutATLAS_SCHEDULER_TIMEOUT60000Per-task deadline (ms)
Tick intervalATLAS_SCHEDULER_TICK_INTERVAL60Seconds between ticks (bun backend only)

Failure Handling

  • Task timeout -- Run marked as failed with timeout error
  • Agent error -- Run marked as failed, error logged
  • Partial delivery failure -- If 1 of 3 recipients fails, the run is still success. Delivery failures are tracked separately via deliveryStatus
  • Lock contention -- Second instance skips the task (atomic locking)

Delivery Status Tracking

Each run tracks delivery outcome independently from agent execution status:

deliveryStatusMeaning
pendingDelivery in progress or not yet attempted
sentAll recipients received the delivery successfully
failedOne or more deliveries failed (see deliveryError for details)

The delivery status appears in run history and the admin UI. Runs that completed before this feature was added will show a null delivery status.

Delivery Preview

Preview what a delivery will look like before the task runs. The preview endpoint generates sample output using mock data:

# Preview what a delivery will look like — uses mock data, does not run the agent
curl -X POST http://localhost:3001/api/v1/scheduled-tasks/:id/preview \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json"

Returns the formatted output for the task's configured channel (email HTML, Slack blocks, or webhook JSON payload). The admin UI includes a "Preview" button on each task row that opens the formatted preview in a dialog.


API Endpoints

All routes require authentication and are available at /api/v1/scheduled-tasks.

MethodPathDescription
GET/List tasks (paginated, filterable by enabled)
POST/Create a new task
GET/:idGet task with recent runs
PUT/:idUpdate task fields
DELETE/:idDisable task (soft delete for audit trail)
POST/:id/runTrigger immediate execution
POST/:id/previewPreview delivery output (dry-run with mock data)
GET/:id/runsList past runs
POST/tickServerless tick (webhook/vercel backends)

See the SDK for typed client methods.


Environment Variables (Self-Hosted)

SaaS

On app.useatlas.dev, these variables are managed automatically. This section is for self-hosted operators only.

VariableDefaultDescription
ATLAS_SCHEDULER_ENABLEDSet to true to enable
ATLAS_SCHEDULER_BACKENDbunbun, webhook, or vercel
ATLAS_SCHEDULER_MAX_CONCURRENT5Max concurrent tasks
ATLAS_SCHEDULER_TIMEOUT60000Per-task timeout (ms)
ATLAS_SCHEDULER_TICK_INTERVAL60Tick interval in seconds
ATLAS_SCHEDULER_SECRETShared secret for /tick endpoint
RESEND_API_KEYRequired for email delivery
ATLAS_EMAIL_FROMAtlas <noreply@useatlas.dev>Email from address

The scheduled tasks default email address (noreply@useatlas.dev) differs from the actions framework default (atlas@notifications.useatlas.dev). Set ATLAS_EMAIL_FROM to use a consistent address across both.

See Environment Variables for the full reference.


Troubleshooting

Tasks never trigger -- Check ATLAS_SCHEDULER_ENABLED=true and that the backend matches your deployment (bun, webhook, or vercel). The tick interval defaults to 60 seconds.

Email delivery fails -- Verify RESEND_API_KEY is set. Check debug logs for the Resend API response.

Duplicate runs -- The scheduler uses an in-process lock. For multi-instance deployments, use the webhook backend with an external scheduler.

See Troubleshooting for more diagnostic steps.


See Also

On this page