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.
| Type | Path | What registers it |
|---|---|---|
| Hook | src/hooks/<entity-kebab>/<event>.ts | Default export. Folder name → entity (kebab → PascalCase: sales-receipt → SalesReceipt). Filename → event. |
| Job | src/jobs/<name>.ts | Named exports schedule (cron string) and run (async function). |
| API route | src/api/<path>.ts | Named exports for HTTP verbs (GET, POST, PUT, PATCH, DELETE). URL = /<path>, including nested folders. |
| Custom entity | src/entities/<Name>.ts | Default export from defineEntity({...}). Name must be PascalCase. |
| Fields | src/fields/<EntityName>.ts | Named exports from defineEntityField({...}) or defineLineField(...). Filename supplies the host entity. |
| UI page / contribution | src/ui or any path you choose | Not 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:
| Folder | Entity |
|---|---|
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.