Skip to content

Odeva CLI

The Odeva CLI is the fastest way to build apps on Odeva. It scaffolds a working project, registers the app on the platform, exposes your local server through a public tunnel for webhook testing, and submits the app for marketplace review.

It does the same things you can do in the Odeva admin, with less clicking.

Terminal window
npm i -g @odeva/cli

Requires Node 20 or Bun 1.1+. Verify:

Terminal window
odeva --version
Terminal window
odeva auth login

Opens your browser, you confirm the device code matches, and a long-lived token is saved to ~/.config/odeva/credentials.json (mode 0600). If your account belongs to multiple organisations, you’ll be prompted to pick one. Switch later with odeva auth select-org.

Terminal window
odeva app init

Prompts for a directory name and writes a Hono-on-Bun starter with:

  • odeva.app.toml — name, slug, scopes, webhook subscriptions.
  • src/index.ts — Hono server with health, install, and webhook routes.
  • src/install.ts — install handshake that exchanges the install_code for a per-installation API key.
  • src/installations.tsInstallationStore interface backed by SQLite (swap for Postgres at scale).
Terminal window
cd my-odeva-app
odeva app config link

Creates the app server-side and writes its client_id back to odeva.app.toml. On the first link, the CLI also writes the client_secret to .odeva.env (gitignored, mode 0600) — this is the only time you see the secret. If you lose it, run odeva app config rotate-secret.

Terminal window
odeva app dev

Starts your dev server, opens a Cloudflare tunnel, registers the webhook subscriptions from odeva.app.toml against the tunnel URL, writes webhook signing secrets to .env.odeva.local, and tears subscriptions down on Ctrl-C. .odeva.env is loaded into the child process, so your install handler has the credentials it needs.

The shorthand webhook config uses /webhooks/<event> as the handler path:

[webhooks]
api_version = "2026-01"
subscriptions = ["reservation.created"]

Use the object form when the route should differ from the event name:

[[webhooks.subscriptions]]
topic = "reservation.created"
uri = "/webhooks/reservations"

While odeva app dev is running, changes to odeva.app.toml and .odeva.env reload webhook registrations and restart the local app process.

Test the install flow by visiting:

https://<tunnel-url>/install?install_code=<code>

(The real install_code is issued by the platform when an organisation clicks Install in the marketplace.)

With odeva app dev running, send a signed sample payload to the matching local handler:

Terminal window
odeva webhook trigger reservation.created

Example output:

POST http://localhost:3000/webhooks/reservation.created
event: reservation.created
delivery: evt_test_f6904722334a6558
signed: yes
✓ 200 OK (27ms)
{"received":true}

The generated Hono template verifies x-odeva-signature with ODEVA_WEBHOOK_SECRET or the event-specific ODEVA_WEBHOOK_SECRET__<EVENT> value, then logs the full nested payload for inspection.

The app template includes a small SDK example at GET /sdk/accommodations. Set the organization slug in .odeva.env:

Terminal window
ODEVA_ORGANIZATION_SLUG=my-org

Let odeva app dev reload, then request:

Terminal window
curl http://localhost:3000/sdk/accommodations

The route demonstrates a server-side SDK call:

import { OdevaClient } from "@odeva/booking-sdk";
const odeva = new OdevaClient({ organizationSlug });
const accommodations = await odeva.searchAccommodations({
startDate: "2026-07-01",
endDate: "2026-07-08",
guests: 2,
limit: 5,
});

For browser-only sites, see the JavaScript SDK guide for the CDN build and public booking examples.

Terminal window
odeva app submit

Submits the app for marketplace review. Reviewers check that the install flow works, the UI is reasonable, and the requested scopes match the app’s purpose. They don’t review your code.

Check status any time:

Terminal window
odeva app status

If the app is rejected, the reviewer’s notes appear in odeva app status. Address the feedback and re-run odeva app submit.

CommandWhat it does
odeva auth loginOAuth device-code sign-in. Saves a CLI token.
odeva auth whoamiShow the active user, organisation, and API URL.
odeva auth select-orgSwitch which organisation the CLI acts on.
odeva auth logoutForget the saved credentials.
odeva app initScaffold a new app from a template.
odeva app config linkRegister the app on Odeva. Writes client_id + client_secret.
odeva app config rotate-secretMint a new client_secret. The old one stops working.
odeva app devRun locally with a public tunnel and auto-registered webhooks.
odeva app submitSubmit the app for marketplace review.
odeva app statusShow review status and reviewer notes.
odeva webhook listList webhook subscriptions on the active organisation.
odeva webhook trigger <topic>Fire a sample payload at your local handler.

Run odeva <command> --help for flags and details on any command.

Environment variables override defaults:

  • ODEVA_API_URL — point at a non-production API (default: https://booking.odeva.app).
  • ODEVA_TOKEN — bypass the browser flow with a pre-existing CLI token. Useful in CI.
  • XDG_CONFIG_HOME — relocate credentials.json (defaults to ~/.config).