bookrail
v0.4.1
Published
Bookrail CLI: booking infrastructure as code, designed to be driven by coding agents.
Maintainers
Readme
bookrail
The Bookrail command line interface: describe a booking business as code, push it to the
Bookrail API, and run every booking operation from a terminal, with --json on every command so
that a person and a coding agent use the same tool.
When to use it, and when not
Use it when an app lets people book something that can run out (a court, a room, a stylist, a table, a class, a bike for three days) and you do not want to write the engine: availability, capacity under concurrency with no double bookings, holds, cancellation and payment policies, time zones, signed webhooks. It is also the fastest way for a coding agent to set Bookrail up, because it needs a shell and nothing else.
Do not use it for one person's own calendar or for a link where others pick a meeting time: a
scheduling link product fits that better, and Bookrail has no hosted booking page, no calendar
sync and no UI components. Inside the application, call the API from your server with
@bookrail/node rather than by running the CLI.
Ten lines that work
npx bookrail signup # a person, once: confirms a link by email
npx bookrail doctor # every check, with a fix when one fails
npx bookrail init --template padel # writes bookrail.config.ts
npx bookrail push --dry-run # read the plan
npx bookrail push # apply it
npx bookrail services list # the svc_ ids the API assigned
npx bookrail availability next --service svc_... # the first bookable instant
npx bookrail bookings create --service svc_... --start <that instant> --customer-email [email protected]
npx bookrail bookings get bk_... # read back what was written
npx bookrail webhooks listen # watch the events arriveAdd --json to any of them to get one envelope instead of a table.
Read this next
- bookrail.dev/llms.txt: every documentation page as markdown, one line each, and llms-full.txt with all of them in one file.
- The edge cases of booking: what goes wrong in a booking engine written by hand, and the test that proves Bookrail handles each case.
- openapi.json: the API contract, generated from the schemas that validate each request.
- For AI agents: the order of operations, the exit codes and the errors you will actually hit.
Rules for agents
- A person signs up, never the agent.
bookrail signupneeds someone to open a link in their mailbox and to accept the Terms for their business. Ask the person to run it in their own terminal. Never add--accept-termsor--approve-clausesunless the person has said they read the Terms and accept them. - Test unless the person says live. Test is the default environment. A
sk_live_key used without--liveis refused before any request leaves the process, and--liveis typed only when the person asked for it. - The secret key stays on the server. Never in browser or mobile code, never in a variable the bundler exposes to the client, never committed, never printed. There are no publishable keys yet.
- Irreversible means asked. Deletions, cancellations and the deletions in a
pushplan need--yes. Pass it only when the person approved that change. - When something fails, read
fix. Then runbookrail doctor.
What it can do
| Group | Commands |
| --- | --- |
| Credentials | signup, login, logout, whoami, env, version, doctor |
| Configuration as code | init (a template per vertical), push, pull, diff |
| Objects | locations, resources, resource_groups, schedules, services, policies, customers |
| Operating | availability (with next, check, --explain), holds, bookings (create, get, list, confirm, cancel, reschedule, check-in, no-show, complete) |
| Events | webhooks (including listen), events list --follow |
| Payments | stripe connect, stripe status, stripe disconnect --yes, payments get, payments list |
| Offline | schema, examples, docs |
| Agents | mcp install --client claude-code \| cursor \| vscode \| windsurf \| generic |
bookrail --help and bookrail <command> --help are the reference, and the site publishes
exactly that output at bookrail.dev/docs/cli.
It installs nothing but itself and its few dependencies, none of which has dependencies of its
own. Exit codes: 0 success, 1 user or configuration, 2 authentication, 3 network or
service, 4 conflict.
Connecting Stripe
bookrail stripe connect prints a Stripe authorisation link, opens it in your browser, and
waits until you have authorised. Your Stripe account stays yours: charges are made directly on
it and Bookrail never sees or stores a Stripe key of yours, so there is nothing to paste.
--no-open on a machine with no desktop, --no-wait in a script, stripe status to read the
account back, stripe disconnect --yes to revoke.
Taking a deposit
bookrail bookings create --service svc_... --start 2026-10-05T09:00:00+02:00 --payment deposit--payment deposit uses the deposit rule of the policy, --payment full charges the whole
price. The booking is created pending and holds its slot, and the output carries the
client_secret, the account and the platform publishable key to pass to Stripe.js. The secret
is shown once and is stored nowhere: bookrail payments get pay_... reads it back from
Stripe for as long as the payment is pending.
bookrail payments list --booking bk_... shows what has been charged and what has gone back.
A refund is what bookrail bookings cancel does, according to your policy; there is no
payments refund, because there is no endpoint behind it.
Not here yet: a deferred balance, saved cards, charging a no-show, and rescheduling a booking that has money on it.
Your plan
Every account is on a plan (free, pro, scale or enterprise). bookrail whoami prints it,
with how many confirmed live bookings and how much paid volume the account has used this month
(a calendar month in UTC), and bookrail doctor has a plan_usage check: ok below 80 % of the
bookings the plan includes, warn from 80 %, and fail at 100 % of the free plan, which from
that moment answers every new live booking with 402 plan_limit_reached. The numbers are the
account's live ones even with a test key, and the test environment never counts. When a booking
is refused, bookrail bookings create exits 1 and the fix says how to move to a paying plan.
Configuration as code
bookrail.config.ts describes locations, schedules, resources, groups, policies and services.
push matches objects by metadata.config_id, never by name, so a second push updates instead
of duplicating, and an object with no config_id is reported as unmanaged and left alone.
Deletions need --yes. --adopt kind:config_id=remote_id takes over one existing object,
explicitly.
Where the key comes from
BOOKRAIL_SECRET_KEYin the environment, if set.~/.config/bookrail/credentials.json(or$XDG_CONFIG_HOME/bookrail/credentials.json), written bybookrail signupandbookrail loginwith mode 600.
The key is never printed. whoami and env show it masked.
Output
{ "ok": true, "environment": "test", "data": {}, "next_steps": ["..."] }{ "ok": false, "environment": "test",
"error": { "code": "...", "message": "...", "param": "...", "doc_url": "...", "fix": "..." } }fix is an instruction, not a diagnosis. Colour and decoration are off whenever stdout is not
a terminal, and always off with --json.
Documentation
- Quickstart, timed against the production API.
- CLI basics and the full CLI reference.
- Coding agents, including the official skill for
Claude Code, the rule for Cursor and the fragment for
AGENTS.md.
Status
Early access. The API is live at https://api.bookrail.dev, keys are self service
(npx bookrail signup, or bookrail.dev/signup, hands you a test
key and a live key on the free plan, and bookrail.dev/dashboard
makes and revokes more), and this package is on npm as
bookrail, Apache 2.0, with its source in
github.com/bookrail-dev/bookrail under
packages/cli. logs, requests, keys, projects, dev, migrate and upgrade answer
not_yet_available, because there is no endpoint behind them.
Licence
Apache-2.0.
