Quickback Docs

Actions

Custom API endpoints for business logic beyond CRUD. One file per action, in a feature's actions/ directory.

Actions are custom API endpoints for business logic beyond CRUD operations. They enable workflows, integrations, and complex operations.

Overview

Quickback supports two types of actions:

AspectRecord-BasedStandalone
Route{METHOD} /:id/{actionName}Custom path (required)
Record fetchingAutomaticNone (record is undefined)
Firewall appliedYesNo
PreconditionsSupported via access.recordNot applicable
Response typesJSON onlyJSON, stream, file
Use caseAdvance application, reject candidateAI chat, bulk import from job board, webhooks

Standalone actions are signalled by the presence of a path: field in the action config. Record-based actions omit path and bind to the feature's primary table.

Where do actions live?

One file per action. Each action is its own module under the feature's actions/ directory:

features/applications/
├── applications.ts             ← defineTable (primary table)
├── candidates.ts               ← defineTable (sibling table)
├── actions/
│   ├── advance.ts              ← defineAction, binds to applications
│   ├── reject.ts               ← defineAction, binds to applications
│   ├── stats.ts                ← defineAction, standalone (has `path:`)
│   └── candidates/
│       └── disqualify.ts       ← defineAction, binds to candidates
└── lib/
    └── inputs.ts               ← shared schemas (optional, copied verbatim)
LayoutBinds to
actions/<name>.tsThe feature's primary table (table file matching the feature name), or standalone if path: is set.
actions/<table>/<name>.tsThe sibling table file <table>.ts at the feature root. Standalone actions inside a subdir ignore the binding.

The action's filename (sans .ts) is the action name and the URL segment, and it must be a valid camelCase identifiersendMessage.ts, not send-message.ts (hyphenated names are a compile error, since the name becomes a generated identifier). Uniqueness is enforced per (bound table, name) pair — sibling-bound actions on different tables may share a verb (actions/episodes/publish.ts and actions/shows/publish.ts mount at /episodes/:id/publish and /shows/:id/publish and compile cleanly). The compiler errors only when two action files would mount at the same URL — for example, a flat actions/foo.ts (binds to primary) plus actions/<primary>/foo.ts, or two standalones with the same (method, path). Standalone actions are unique per (method, path).

Tableless features have no top-level *.ts files; every action under actions/ must be standalone (have path:). Use this for utility endpoints like webhooks, reports, or integrations that don't operate on a specific record.

Retired layouts

These are rejected at load time with migration guidance:

  • actions.ts (bundled multi-action file)
  • _feature.ts (shared schemas + non-table-bound actions)
  • handlers/ (separate handler-file directory)
  • defineActions(table, {...}) (the multi-action factory)
  • defineActions(null, {...}) (tableless variant)
  • *-actions.ts / *.actions.ts filename patterns

For shared Zod schemas or helpers, put them under <feature>/lib/ and import from each action file. Files under lib/ are copied verbatim into the generated output.

The rest of this section

Related: Transitions for state-machine policy, Triggers for after-commit hooks, and the Actions API for calling an action over HTTP.

On this page