OpheliaDocs
Search docs⌘K
DashboardCreate an account
Verticals · Dining

Resy

Card at some venues · no Resy password · OTP unless the end_user has an active session

Search Resy by passing providers: ["resy"]; a dining search without providers searches OpenTable only. No Resy password is ever needed: Resy texts a one-time code to customer.phone_number, and the booking returns requires_action with next_action.type "otp". If that phone already has a Resy account, customer.email must match the account's email, or the booking fails with error_code: "account_email_mismatch". If it has none, a Resy account is created during the booking. party_size is required (1–20).

Some Resy venues need a card. Check card_required on the POST /v1/availability/search response: false means no card is needed; true means send metadata.payment (the same shape as on OpenTable) or have the card authorized through AgentCard. A booking that reaches Resy's card form without a card fails with error_code: "card_required". window_minutes on availability keeps only slots within that many minutes of the requested datetime.

Each Resy slot from availability, and from a search with a datetime, carries terms: the cancellation fee, deposit and deadlines Resy states for it. Resy gives these as numbers with no currency or unit, so neither is filled in. A confirmed Resy booking carries confirmation.terms with the venue's cancellation sentence word for word, read just before the reservation is made. See Restaurant terms.

curl
# Search (Resy). Without "providers", a dining search searches OpenTable only.
curl -X POST https://api.opheliaos.com/v1/venues/search \
-H "Authorization: Bearer YOUR_LIVE_KEY" \
-H "Content-Type: application/json" \
-d '{
"vertical": "dining",
"providers": ["resy"],
"term": "sushi",
"location": "New York, NY",
"datetime": "2026-11-15T02:15:00Z",
"party_size": 2
}'
# → venues[].image = "https://img.resy.com/..."
curl
# Book (use a ven_ id from the search response).
# No Resy password: Resy texts the diner a code. Add metadata.payment only at a
# venue whose availability answered card_required: true.
curl -X POST "https://api.opheliaos.com/v1/bookings" \
-H "Authorization: Bearer YOUR_LIVE_KEY" \
-H "Idempotency-Key: dining-002" \
-H "Content-Type: application/json" \
-d '{
"venue_id": "ven_12345678",
"datetime": "2026-11-15T02:15:00Z",
"party_size": 2,
"customer": {
"name": "Jane Smith",
"phone_number": "+15555555555",
"email": "jane.smith.diner@gmail.com"
}
}'
# → "status": "requires_action", next_action.type "otp" (the diner gets an SMS code)
curl
# Submit the OTP the diner received — returns "status": "confirmed"
curl -X POST "https://api.opheliaos.com/v1/bookings/bkg_12345678/continue" \
-H "Authorization: Bearer YOUR_LIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "otp_code": "123456" }'

End-user sessions

Send end_user, your stable id for the diner, on POST /v1/bookings. Once a booking signs the diner in with the code, Ophelia keeps that Resy sign-in for them, and their later Resy bookings with the same end_user are made signed in as them, with no code. If Resy stops accepting the session, it is marked expired and the next booking goes through the code again. To connect a diner ahead of a booking, list or disconnect sessions, or check whether their Resy account has a card on file, see Sessions.

Cancelling or modifying

A confirmed Resy booking cannot be cancelled or modified through Ophelia. POST /v1/bookings/{id}/cancel leaves it confirmed with error_code: "cancellation_unsupported", and the diner cancels with Resy directly. A booking still waiting on its code can be cancelled.

Remember when testing

If you're testing against a real Resy account, the customer fields you submit (email, phone number) must match the info tied to that account, or authentication will fail.