Script types

Learn about hooks, jobs, API routes, entities, fields, pages and the file/folder rules the platform uses to find them.

Backfill extensions don’t register handlers in code. The platform finds them by filesystem convention, similar to how Next.js finds pages. Every script’s path determines its role.

TypePathWhat registers it
Hooksrc/hooks/<entity-kebab>/<event>.tsDefault export. Folder name → entity (kebab → PascalCase: sales-receiptSalesReceipt). Filename → event.
Jobsrc/jobs/<name>.tsNamed exports schedule (cron string) and run (async function).
API routesrc/api/<path>.tsNamed exports for HTTP verbs (GET, POST, PUT, PATCH, DELETE). URL = /<path>, including nested folders.
Custom entitysrc/entities/<Name>.tsDefault export from defineEntity({...}). Name must be PascalCase.
Fieldssrc/fields/<EntityName>.tsNamed exports from defineEntityField({...}) or defineLineField(...). Filename supplies the host entity.
UI page / contributionsrc/ui or any path you chooseNot auto-discovered — referenced explicitly from backfill.config.ts via pages[].render or ui.{tabs,panels,actions}[].render.

Hooks, jobs, API routes, custom entities, and fields are auto-registered by their location. UI pages and contributions are declared in the manifest, because the platform needs metadata (id, title, route, kind, nav order) it can’t read off the filesystem.

What goes in the manifest vs. what’s discovered

The manifest (backfill.config.ts) declares metadata: extension key, name, version, permissions, settings defaults, page list, UI contributions. It does not register hooks, jobs, routes, entities, or fields — those come from the file tree. Delete src/hooks/invoice/before-save.ts and the next deploy stops invoking that hook. There’s no second place to clean up.

Folder ↔ entity name mapping

Hook folder names convert kebab → PascalCase with no separator:

FolderEntity
invoice/Invoice
sales-receipt/SalesReceipt
journal-entry/JournalEntry

If the entity isn’t a standard one and you haven’t declared it via defineEntity in src/entities/, deploy fails:

src/hooks/foo/before-save.ts: unknown entity type 'Foo'

Validation runs locally

backfill build and backfill deploy both run the same discovery checks before anything ships. You’ll see errors like:

unknown hook event 'on-save'
route must export at least one HTTP method handler (e.g. export const POST = ...)
entity name 'invoice' must be PascalCase (e.g. RevenueSchedule)

Rename a file, re-run backfill build, see the verdict. There’s no separate registration step — the file tree is the registration.