A Rust command-line client for Resy focused on automation-friendly, JSON-only output. It is designed for agents and scripts to drive booking workflows and other reservation automations.
Note
See my resy-booking skill
for an example of how to drive resyctl from an agent.
Via Homebrew:
brew install evanpurkhiser/personal/resyctlVia Arch Linux AUR:
yay -S resyctlFrom crates.io:
cargo install resyctlFrom source:
cargo install --path .resyctl auth login # prompts for email and password
resyctl auth status# 1) Search for a restaurant.
resyctl search "ishq" --limit 2 \
| jq -r '.venues[:2][] | "\(.id): \(.name) [\(.locality // "?")]"'
# 84214: Ishq [New York]
# 66703: Ishi Omakase & Premium Sake [New York]
# Use the first result for the rest of the flow.
VENUE_ID=84214
# 2) Look up venue details (address, phone, website, Resy/Maps URLs, about).
resyctl venue "$VENUE_ID" \
| jq '.venue | {name, neighborhood, address, phone, website, resy_url, google_maps_url}'
# {
# "name": "Ishq",
# "neighborhood": "Chelsea",
# "address": "240 5th Ave, New York, NY 10001",
# "phone": "+12125551234",
# "website": "https://ishq.com/",
# "resy_url": "https://resy.com/cities/new-york-ny/venues/ishq",
# "google_maps_url": "https://www.google.com/maps/search/?api=1&query=Ishq+240+5th+Ave%2C+New+York%2C+NY+10001"
# }
# 3) Check availability for party size 2 on a specific date.
resyctl availability "$VENUE_ID" --date 2026-05-23 --party-size 2 \
| jq -r '.slots[:4][] | "\(.slot_id[0:12])[…] | \(.start) | \(.type // "?")"'
# eyJjb25maWdfa[…] | 2026-05-23 12:15:00 | Bar Seat
# eyJjb25maWdfa[…] | 2026-05-23 12:15:00 | Dining Room
# eyJjb25maWdfa[…] | 2026-05-23 12:30:00 | Bar Seat
# eyJjb25maWdfa[…] | 2026-05-23 12:30:00 | Dining Room
# Save a slot id to quote/book.
SLOT_ID=$(resyctl availability "$VENUE_ID" --date 2026-05-23 --party-size 2 \
| jq -r '.slots[] | select(.start=="2026-05-23 13:30:00" and .type=="Dining Room") | .slot_id' \
| head -n1)
echo "${SLOT_ID:0:12}[…]"
# eyJjb25maWdfa[…]
# 4) Quote details for the slot (cancellation fee/cutoff/payment summary).
resyctl quote "$SLOT_ID" \
| jq '{
cancellation_fee_amount: .quote.cancellation_fee_amount,
cancellation_fee_cutoff: .quote.cancellation_fee_cutoff,
payment_type: .quote.payment_type
}'
# {
# "cancellation_fee_amount": 25,
# "cancellation_fee_cutoff": "2026-05-22T17:30:00Z",
# "payment_type": "free"
# }
# 5) Book the slot.
# If this slot has a cancellation fee, pass --allow-cancellation-fee.
# Use --max-cancellation-fee to cap the fee amount.
# "cancellation fee cutoff" is the timestamp after which canceling can incur a fee.
# Before the cutoff: cancel is free. After: the fee may be charged.
# Use --max-cutoff-hours to require at least N hours remaining until the cutoff.
# Example: --max-cutoff-hours 12 means "do not book if cutoff is within 12 hours".
resyctl book "$SLOT_ID" --allow-cancellation-fee --yes \
| jq -r '"reservation=\(.reservation_id) token=\(.resy_token[0:12])[…] cancellation_fee_amount=\(.quote.cancellation_fee_amount) cancellation_fee_cutoff=\(.quote.cancellation_fee_cutoff)"'
# reservation=867457046 token=Ys7435rTmPAu[…] cancellation_fee_amount=25 cancellation_fee_cutoff=2026-05-22T17:30:00Z
# 6) List upcoming reservations.
resyctl reservations --upcoming \
| jq -r '.reservations
| sort_by(.day, .time_slot)
| .[:2]
| .[]
| "\(.reservation_id) | \(.day) \(.time_slot) | \(.venue.name // "?") | \(.resy_token[0:12])[…]"'
# 867250480 | 2026-04-29 18:00:00 | MOKYO | 4hRnr95|mdVS[…]
# 867248247 | 2026-05-01 18:30:00 | Antidote | A1xdLzgBrOOT[…]
# 7) Cancel the older upcoming reservation.
CANCEL_TOKEN=$(resyctl reservations --upcoming \
| jq -r '.reservations | sort_by(.day, .time_slot) | .[0].resy_token')
resyctl cancel "$CANCEL_TOKEN" --yes \
| jq '{canceled, refund: .result.payment.transaction.refund}'
# {
# "canceled": true,
# "refund": 1
# }- All command output is JSON.
resyctl bookenforces cancellation-fee guardrails by default.- Use
resyctl payment-methodsto inspect available payment method IDs. - Pass
--rawif the normalized fields are insufficient — it includes the full underlying Resy response under a top-levelrawkey.