Skip to content

Automations

Automations

An automation is background work in src/automation/+<name>.automation.ts: scheduled, event-driven, webhook-triggered, or started by hand. Besides an integration’s durable bodies (a feed’s read, an outbound rule’s run), it is the only place for external I/O — HTTP, AI, mail and chat sends, file rendering and geocoding. The literal declares the triggers and the authority; the body attaches with a.run(…), exactly once.

API reference: automation

// src/automation/+permit_expiry_watch.automation.ts (templates/construction)
import { automation } from '@norbital-ai/bolt';
import { DAILY_0600, digestOutput, jsonRows } from '../lib/digest.ts';

const a = automation({
  description:
    'Sweeps the active and expiring permits to work every morning at 6am and publishes the first 25 as a JSON extract …',
  on: DAILY_0600,
  runAs: ['construction_read'],
  output: digestOutput
});
export default a;

a.run(function* (_, ctx) {
  const { rows } = yield* ctx.read('permits_to_work', {
    where: { status: { in: ['active', 'expiring_soon'] } },
    orderBy: { requested_date: 'asc' },
    limit: 25
  });
  return { generated_at: ctx.now, count: rows.length, rows: jsonRows(rows) };
});

// src/lib/digest.ts (excerpt)
export const digestOutput = {
  kind: 'object',
  fields: {
    generated_at: { kind: 'instant' },
    count: { kind: 'int' },
    rows: { kind: 'list', of: { kind: 'json' } }
  }
} as const;

export const DAILY_0600 = { cron: '0 6 * * *' } as const;

Triggers

  • { cron, tz? } — five fields or a macro, in the workspace zone unless tz says otherwise.
  • { created | updated | deleted: '<collection>', where?, delay? } — updated may also name fields. where is evaluated once on the firing write; the run receives the ids of the rows that fired (a deleted run also gets their pre-images) and re-checks their state itself.
  • { webhook: '/<path>', verify: { scheme, secret } } — with the schemes bearer, hmac-sha256, svix, stripe, slack and meta.
  • No on — started only by hand: bolt.start from a page, a toolbar start item, ctx.schedule from an action or another run, or the agent.
// src/automation/+review_job_assignment_suspicion.automation.ts (templates/field-operations, excerpt)
const review = automation({
  description: 'When an assignment is filed or changes: inspects photos awaiting facts, then reviews …',
  on: [
    { created: 'job_assignments' },
    {
      updated: 'job_assignments',
      fields: ['suspicion_checked_at'],
      where: { suspicion_checked_at: { isNull: true } }
    }
  ],
  input: {
    /** Event runs: the assignments that fired. */
    ids: { kind: 'list', of: { kind: 'id', of: 'job_assignments' }, optional: true },
    /** Manual runs may target one unchecked assignment. */
    assignment_id: { kind: 'id', of: 'job_assignments', optional: true }
  },
  output: { kind: 'object', fields: { assignment_count: { kind: 'int' } /* … */ } },
  runAs: ['suspicion_review_automation'],
  concurrency: { max: 1 }
});
export default review;

Authority

runAs names the policies the run acts under, and its reads and writes are judged against their grants like anyone else’s. runAs: 'trigger' runs as whoever started it and is allowed only on an automation with no on. A member may start an automation when one of their policies lists it under automations.

The run

A run is one invocation. Its ctx reads, acts, schedules and notifies as its runAs, and adds send, request, web, files, ai and geo, plus progress, attempt and cause. The declared output is the run’s readable result, shown by RunStatus and on the runs page. Work that does not fit one invocation fans out with ctx.schedule per chunk, or schedules its own continuation.

Scheduling

ctx.schedule(name, input) queues a run as soon as possible; ctx.schedule(name, input, { at, key? }) queues it later, and a queued run with the same key is replaced. retry: { attempts, backoff? } retries only transient failures, and concurrency: { max } bounds parallel runs. Nothing polls: every future run is a queued row with a due time.

Automation, action or transform?

  • Transform — derivations and refusals inside the write itself.
  • Action — a named, typed write a caller waits for, with no I/O.
  • Automation — anything with I/O or AI, anything on a clock or after a commit, and bulk work that reports progress.