Install
openclaw skills install @demmersong/voyagier-cliopenclaw skills install @demmersong/voyagier-cliSearch flights, hotels, and activities; compose trip plans; take them to a paid checkout — from the terminal. Everything syncs to the web app at voyagier.com/plans/{id}.
npm install -g @voyagier/cli
voyagier login # interactive — keeps the token out of shell history
# or, for scripts/agents: pipe the token via stdin (never pass it as an argument)
printf '%s' "$PAT" | voyagier auth set-token -
voyagier doctor --json # verify auth + schema + state + version
Get a PAT: voyagier.com → Settings → Personal Access Tokens → Create.
Or use env vars for CI/scripts:
export VOYAGIER_TOKEN=***
export VOYAGIER_API_URL=https://travel.voyagier.com/api # optional (default); only honored alongside VOYAGIER_TOKEN; CLI appends /graphql
No install permissions? Zero-install works for every command: npx @voyagier/cli doctor --json.
This skill is a quick orientation. The full, always-current integration contract ships with the CLI itself:
voyagier agent-docs # prints AGENT.md: JSON shapes, error-code table, bookability, quirks
Read it once per session before non-trivial work. Everything below is a summary of that document.
MCP-native host? The CLI doubles as a Model Context Protocol stdio server — voyagier mcp — exposing this same surface (plan → search → selection-options → select → plan-status → quote → book) as tools, with identical error codes and the same price-gated book. Prefer it over shelling out in shell-less environments. (send is intentionally not exposed.)
A trip plan is a goal graph. plan-trip scaffolds the plan + default goals (flights, hotel, dates, destination, travellers); you compose the trip by searching against goals and selecting options on the resulting selections. plan-status tells you what's left; book closes with a price-gated checkout.
Always pass --json (per-command flag; chat, telemetry, and most auth subcommands don't take it).
# 0. Health check
voyagier doctor --json
# 1. Resolve a client (idempotent by email) — plans require one
voyagier clients upsert --email "smith@example.com" --name "Smith Family" --type Individual --json
# 2. Scaffold the plan + goal graph (--client takes id, email, or name)
voyagier plan-trip --client "Smith Family" --title "Smith — Tokyo" --json
# Read nextSteps in the output — they are the exact compose commands for this plan.
# 3. Add travellers (required before search; gender/DOB required for flight checkout)
voyagier travellers add --plan <PLAN_ID> --first John --last Smith --type Adult --json
# Optional loyalty (applied at checkout best-effort — never blocks a booking):
# --frequent-flyer DL:1234567 (FF number verbatim) · --hotel-loyalty HI:12345678 (digits only, NO chain prefix)
# 4. Search → select. search --json returns a COMPACT envelope:
# { selectionId, optionCount, topOptions[≤10] } (+ returnSelectionId for round trips).
# Options are often inline; if optionCount is 0 the fetch is still running — poll.
voyagier search flights --plan <PLAN_ID> --from JFK --to NRT --date 2026-09-15 --return 2026-09-22 --json
voyagier selection-options <SELECTION_ID> --wait --json # poll until terminal status
voyagier select --selection-id <SELECTION_ID> --option-id <OPTION_ID> --wait --json
# Round trip: pick BOTH legs (same optionId appears in both lists — intended).
# Then the fare/cabin pick: the "Flight Booking Details" goal exposes a FlightClass
# selection (defaults to Economy — pick only to change cabin). Find it via plan-status.
# 5. Readiness — ONE call: what's blocked, what's next
voyagier plan-status <PLAN_ID> --json
# Switch on data.readiness: BLOCKED → act on blockers[] via nextSteps[];
# IN_PROGRESS → poll; READY_TO_BOOK → dry-run; BOOKED → done.
# 6. Close: pre-flight, then a price-GATED checkout (the gate is REQUIRED)
voyagier book <PLAN_ID> --dry-run --json # blockers + data.chargeableSubtotal + nextStep
voyagier book <PLAN_ID> --expect-total <subtotal> --json # checkout only at exactly that price
# Without --expect-total/--max-total, book refuses (VALIDATION). Price drift → PRICE_CHANGED, no checkout.
# Alternative closes:
voyagier quote <PLAN_ID> --json # offer snapshot + ready-to-run acceptance command
voyagier send <PLAN_ID> --yes --json # email client an invite to pay self-serve (NOT idempotent; needs --yes)
{ error: true, code, message, details? } — branch on code. Exit 1 = handled, 2 = unexpected. The full code table lives in agent-docs.{ ok, data, planContext }; older ones are flat. jq keys when in doubt; agent-docs documents every shape per command. (The MCP server normalises both styles into one canonical envelope: { ok: true, data, planContext? } on success, { ok: false, error: { code, message, details? } } on failure.)--plan <id> (same value both ways is fine; different values error).book requires the price gate — --expect-total <amt> (exact, cents-compared) or --max-total <amt> (cap). Get the number from book --dry-run (data.chargeableSubtotal).book — unpaid (Pending) sessions are invisible to the CLI; a retry mints a second payable link.plan-status vs book --dry-run tie-breaker: if plan-status shows only unverified blockers but dry-run says blockers: [], trust the dry-run and proceed.isBookable in the cart. Luxury/boutique properties may need direct booking.book --dry-run/quote are the chargeable truth).from $X total · N nights (~$Y/nt); room options carry a per-night breakdown. Date ranges are inclusive of the end date.select does not hold the price.EXPIRED_OFFER/STALE_PLAN_STATE → re-run the search.--plan <id> on select when running parallel workflows (guards the global state files against cross-plan mixups).book and send (real charges / real client email).~/.voyagier/credentials.json (mode 0600).--dry-run on book previews without creating a checkout.