Reservations
With the reservations API a partner manages bookings for a location of your organization: padel courts, car wash bays, treatment rooms or staff members. You describe what can be booked (resources), how it is booked (services), when it is open (schedules and closures), and then look up free slots and create, change, cancel and check in reservations. Every change can be pushed to your system with webhooks.
All times are in the time zone Europe/Amsterdam. Responses always carry the UTC offset of that moment (+02:00 in summer, +01:00 in winter), so parse them as ISO 8601 with offset.
Quickstart: your first reservation in 4 calls
- Create a resource at one of your locations, for example a padel court:
POST /reservations/resources. - Create a service that this resource delivers, for example a 90 minute padel match for up to 4 players:
POST /reservations/serviceswithresourcesset to the court. - Look up free slots:
GET /reservations/availability. Without a schedule of its own the court follows the opening hours of the location. - Book a slot:
POST /reservationswith one of the returnedstarts_atvalues.
The token needs reservations.manage for steps 1 and 2, and reservations.get (or reservations.manage) plus reservations.store (or reservations.manage) for steps 3 and 4. See Abilities.
1. Resource
curl -X POST https://api.walletapp.co/reservations/resources \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"location":"0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d","name":"Court 1","type":"court"}'
2. Service
curl -X POST https://api.walletapp.co/reservations/services \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name":"Padel 90 minutes","duration_minutes":90,"max_party_size":4,
"resources":["3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601"]}'
3. Availability
curl -G https://api.walletapp.co/reservations/availability \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d service=8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a \
-d location=0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d \
-d from=2026-10-17 \
-d party_size=4
4. Reservation
curl -X POST https://api.walletapp.co/reservations \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 2b7f4e1c-9a3d-4c5e-8f6a-1d2c3b4a5e6f" \
-d '{"service":"8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"location":"0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"starts_at":"2026-10-17T10:30:00+02:00","party_size":4,
"customer":{"name":"Alex Jansen","email":"alex@example.com"},
"external_reference":"club-booking-1042"}'
Abilities
Request a token with the abilities below via POST /oauth/token. A token without the required ability gets an HTTP 403. Everything is scoped to the organizations of the token: a resource, service, location or reservation of another organization answers 404, exactly like one that does not exist.
- Name
reservations.get- Type
- ability
- Description
Read: list and inspect resources, services, schedules, closures and reservations, and look up availability.
- Name
reservations.store- Type
- ability
- Description
Book: create reservations, change them with
PUT /reservations/:idand cancel them (within the cancellation cut-off). This ability does not include reading: with onlyreservations.storetheGETendpoints answer403, so combine it withreservations.getif your integration also reads.
- Name
reservations.manage- Type
- ability
- Description
Everything: all of the above, plus creating, changing and deleting resources, services, schedules and closures, the status endpoints
confirm,decline,check-in,no-showandcomplete, and cancelling after the cut-off withforce.
Which endpoint needs which ability:
- Name
reservations.get or reservations.manage- Type
- Description
GET /reservations/resources,GET /reservations/resources/:id,GET /reservations/resources/:id/schedule,GET /reservations/services,GET /reservations/services/:id,GET /reservations/exceptions,GET /reservations/availability,GET /reservations,GET /reservations/:id.
- Name
reservations.store or reservations.manage- Type
- Description
POST /reservations,PUT /reservations/:id,POST /reservations/:id/cancel(withoutforce).
- Name
reservations.manage- Type
- Description
POST,PUTandDELETEon/reservations/resources,/reservations/servicesand/reservations/exceptions,PUT /reservations/resources/:id/schedule,POST /reservations/:id/confirm,/decline,/check-in,/no-show,/complete, andPOST /reservations/:id/cancelwithforce: true.
Resources
A resource is the thing that is booked: a court, a lane, a wash bay, a room or a staff member. A resource belongs to exactly one location. Its capacity is the number of reservations that may run at the same time on that resource in one slot. It is not the number of seats or players. A padel court or a wash bay takes one booking at a time, so its capacity is 1 (the default), even though four people play on the court. Only use a higher value when the resource really admits several separate bookings at once, for example a sauna that admits 8 independent bookings (capacity 8).
The group size of a reservation is its party_size, limited by the max_party_size of the service (a padel service with max_party_size: 4 lets one booking bring four players to a court with capacity 1). Availability reports remaining per resource as capacity minus the number of overlapping active reservations, regardless of their party size.
Services
A service is what a customer books: "Padel 90 minutes", "Wash program Gold", "Massage 60 minutes". It defines the duration, the buffers before and after (cleaning, changeover), the slot grid, how far ahead and how late one can book, the maximum party size, the cancellation cut-off and whether a new reservation needs to be confirmed first. A service is linked to the resources that can deliver it; a resource can deliver several services.
Schedules and closures
Every resource has a weekly schedule: one or more opening blocks per weekday (1 = Monday … 7 = Sunday), for example 09:00-12:00 and 13:00-22:00. A resource without a schedule of its own follows the opening hours of its location. Replacing the schedule with an empty list switches back to the location hours.
Exceptions (closures) override the schedule for one date, for the whole location or for one resource: closed all day (a holiday, maintenance), or open with different hours. On a date, any closed exception closes the resource; otherwise deviating hours of the resource win over deviating hours of the location, which win over the weekly schedule.
Availability
GET /reservations/availability returns the slots that can be booked for a service at a location. A slot is offered when:
- it fits in an opening block of the date, including the buffers of the service (the buffer before must start after opening, the end plus the buffer after must be before closing);
- it is on the grid of the service: slots start at opening time and then every
slot_interval_minutes(default: the duration); - it starts at least
min_notice_minutesfrom now and at mostmax_advance_daysahead; - the resource still has capacity: overlapping reservations that are
pending,confirmedorchecked_in(including their buffers) are counted;cancelled,declined,no_showandcompletedreservations free the slot; - the party size is at most
max_party_sizeof the service.
Slots are calculated in real elapsed time, so a day with a daylight saving switch has no missing or doubled slot.
Reservations and their lifecycle
A reservation books one service on one resource for one start time. A new reservation is confirmed right away, or pending when the service has requires_confirmation: true. From there it moves through these statuses:
Status machine
new reservation ──► pending (service with requires_confirmation)
new reservation ──► confirmed (all other services)
pending ───confirm───► confirmed ───check-in───► checked_in ───complete───► completed
│ │ │
│ cancel │ └───no-show───► no_show
▼ │
cancelled ◄───cancel────┘
pending ───decline───► declined
- Name
pending- Type
- status
- Description
Waiting for confirmation. Can move to
confirmed,cancelledordeclined. Holds capacity.
- Name
confirmed- Type
- status
- Description
Booked. Can move to
checked_in,no_showorcancelled. Holds capacity.
- Name
checked_in- Type
- status
- Description
The customer has arrived (sets
checked_in_at). Can only move tocompleted. Holds capacity.
- Name
completed- Type
- status
- Description
Finished (sets
completed_at). Final.
- Name
cancelled- Type
- status
- Description
Cancelled by the partner or the location (sets
cancelled_atandcancel_reason). Final.
- Name
no_show- Type
- status
- Description
The customer did not come. Final.
- Name
declined- Type
- status
- Description
The location refused a pending reservation with
decline. Frees the slot. Final.
Any other transition, for example checking in a pending reservation or cancelling a checked_in one, is refused with 409 invalid_transition and nothing changes. Only pending and confirmed reservations can be changed with PUT /reservations/:id.
Customers, participants and members
A reservation has one customer (the booker: name plus at least an e-mail address, phone number or member id) and optionally a list of participants, for example the four players of a padel match. There can be at most party_size participants; you may include the customer among them.
Customers and participants are linked to existing members of the organization that owns the service, so loyalty can follow later:
- with
member_id(the member UUID): it must be a member of this organization, otherwise422(Unknown member.); - with
email: when the e-mail address belongs to a member of this organization, the reservation is linked to that member; otherwise it stays unlinked, without an error.
Members of other organizations are never linked and never revealed: an unknown member id and a member of another organization give the exact same 422, and an e-mail address of someone outside your organization simply stays unlinked. A new member is only created when you pass create_member: true and member_opt_in: true (the explicit consent of the customer) together with an e-mail address, and only when that e-mail address is not known anywhere on the platform yet. The response tells you with member_created.
Idempotency
Two mechanisms protect you against double bookings when you retry:
external_reference(business idempotency): your own booking id, unique per organization. Creating a reservation with a reference that already exists in the organization returns the existing reservation with HTTP200andwas_duplicate: true, whatever else is in the request and whatever its status. No second reservation is made and no webhook is sent.Idempotency-Keyheader (transport idempotency): accepted on everyPOSTandPUT. Repeating the exact same request with the same key replays the first response with the headerIdempotent-Replayed: true. See Limits and retries for the rules.
Use external_reference when you have a booking id of your own; it also protects you across different tokens and after the 24 hours an idempotency key is kept.
Errors
Validation errors are a 422 in the usual format (message plus errors per field). Business errors carry a machine readable code:
- Name
404- Type
- not found
- Description
{"error": true, "message": "Reservation not found"}(orResource,Service,Location,Exception). Also for anything of another organization.
- Name
409 slot_unavailable- Type
- conflict
- Description
The slot cannot be booked.
reasonisnot_available(full, closed, off the grid, too soon or too far ahead) orno_resource(no active resource at this location delivers the service, or the requested resource does not).
- Name
409 invalid_transition- Type
- conflict
- Description
The status change is not allowed. The body contains
from,toandallowed(the statuses that are possible fromfrom).
- Name
409 not_editable- Type
- conflict
- Description
PUT /reservations/:idon a reservation that is notpendingorconfirmed.
- Name
409 has_future_reservations- Type
- conflict
- Description
Deleting a resource or service (or moving a resource to another location) while it still has future
pending,confirmedorchecked_inreservations. Cancel or move them first.
- Name
422 cancellation_window_passed- Type
- unprocessable
- Description
The cancellation cut-off of the service has passed. The body contains
cancellation_cutoff_minutes. Cancel withforce: true(needsreservations.manage) to override.
- Name
403 force_requires_manage- Type
- forbidden
- Description
force: trueon a cancellation by a token withoutreservations.manage.
- Name
401- Type
- unauthorized
- Description
No valid token, or an
organization_idyour token has no access to.
409 slot_unavailable
{
"error": true,
"code": "slot_unavailable",
"reason": "not_available",
"message": "The requested slot is not available."
}
409 invalid_transition
{
"error": true,
"code": "invalid_transition",
"message": "Cannot change a reservation from 'checked_in' to 'cancelled'.",
"from": "checked_in",
"to": "cancelled",
"allowed": ["completed"]
}
Lists and pagination
All list endpoints are paginated with page (default 1) and per_page (default 25, at most 100; more is a 422) and return a meta object. The order is always deterministic. When your token has access to several organizations, all of them are listed; pass organization_id (the organization identifier) to narrow the list to one.
meta
"meta": {
"current_page": 1,
"per_page": 25,
"total": 3,
"last_page": 1
}
The resource model
- Name
id- Type
- string
- Description
Unique identifier of the resource (a UUID).
- Name
location- Type
- string
- Description
Identifier of the location the resource belongs to.
- Name
name- Type
- string
- Description
Name, for example
Court 1.
- Name
type- Type
- string
- Description
One of
lane,bay,court,staff,room,other. Defaultother.
- Name
capacity- Type
- integer
- Description
Number of reservations that may run at the same time on this resource in one slot (1 to 10000). Default
1, which is right for a court, lane or wash bay. This is not the number of people: group size is the reservation'sparty_size, limited by the service'smax_party_size.
- Name
active- Type
- boolean
- Description
Inactive resources are not offered in availability and cannot be booked. Default
true.
- Name
sort- Type
- integer
- Description
Sort order (0 to 1000000). When you book without a resource, the first free resource in
sort, then creation order, is chosen. Default0.
- Name
external_id- Type
- string|null
- Description
Your own id for the resource (max 255 characters).
- Name
services- Type
- array
- Description
UUIDs of the services this resource delivers.
- Name
created_at- Type
- string
- Description
ISO 8601 timestamp.
- Name
updated_at- Type
- string
- Description
ISO 8601 timestamp.
List resources
Returns the resources of your organization, ordered by sort and then creation order. Ability reservations.get or reservations.manage.
Query parameters
- Name
location- Type
- string
- Description
Only resources of this location (identifier). A location of another organization answers
404.
- Name
type- Type
- string
- Description
Only resources of this type.
- Name
active- Type
- boolean
- Description
1for active,0for inactive resources.
- Name
organization_id- Type
- string
- Description
Only this organization (when your token has access to several).
- Name
page, per_page- Type
- integer
- Description
Pagination, see Lists and pagination.
Request
curl -G https://api.walletapp.co/reservations/resources \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d location=0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d
Response
{
"message": "Request successful",
"data": [
{
"id": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"name": "Court 1",
"type": "court",
"capacity": 1,
"active": true,
"sort": 1,
"external_id": "court-1",
"services": ["8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a"],
"created_at": "2026-10-10T14:00:00+02:00",
"updated_at": "2026-10-10T14:00:00+02:00"
}
],
"meta": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 }
}
Create a resource
Creates a resource at a location. The organization and brand are taken from the location. Ability reservations.manage.
Body
- Name
location- Type
- string
- Description
Identifier of one of your locations. Another organization's location answers
404.
- Name
name- Type
- string
- Description
Max 255 characters.
- Name
type- Type
- string
- Description
lane,bay,court,staff,roomorother(default).
- Name
capacity- Type
- integer
- Description
Concurrent reservations per slot, not seats or players: 1 to 10000, default
1(a padel court or wash bay). Group size is set per reservation withparty_size, up to the service'smax_party_size.
- Name
active- Type
- boolean
- Description
Default
true.
- Name
sort- Type
- integer
- Description
0 to 1000000, default
0.
- Name
external_id- Type
- string
- Description
Max 255 characters.
- Name
services- Type
- array
- Description
Up to 200 service UUIDs of the same organization. An unknown or foreign service is a
422onservices.N.
Request
curl -X POST https://api.walletapp.co/reservations/resources \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"location":"0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d","name":"Court 1","type":"court",
"capacity":1,"sort":1,"external_id":"court-1"}'
Response
{
"message": "Request successful",
"data": {
"id": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"name": "Court 1",
"type": "court",
"capacity": 1,
"active": true,
"sort": 1,
"external_id": "court-1",
"services": [],
"created_at": "2026-10-10T14:00:00+02:00",
"updated_at": "2026-10-10T14:00:00+02:00"
}
}
Inspect a resource
Returns one resource (see the resource model). Ability reservations.get or reservations.manage. 404 for an unknown resource or one of another organization.
Request
curl https://api.walletapp.co/reservations/resources/3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601 \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Update a resource
Changes only the fields you send; the fields and limits are the same as for creating a resource. Ability reservations.manage.
servicesreplaces the full list of linked services ([]unlinks all).locationmust be a location of the same organization. Moving a resource that still has future reservations to another location answers409 has_future_reservations.
Request
curl -X PUT https://api.walletapp.co/reservations/resources/3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601 \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name":"Court 1 (covered)","services":["8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a"]}'
Delete a resource
Deletes a resource. Past reservations keep referring to it. Ability reservations.manage. A resource with future pending, confirmed or checked_in reservations answers 409 has_future_reservations.
Request
curl -X DELETE https://api.walletapp.co/reservations/resources/3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601 \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Response
{
"message": "Request successful",
"data": { "id": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601", "deleted": true }
}
409
{
"error": true,
"code": "has_future_reservations",
"message": "This item has future reservations that block it. Cancel or move them first."
}
Get a schedule
Returns the weekly schedule of a resource. Ability reservations.get or reservations.manage.
- Name
resource- Type
- string
- Description
The resource UUID.
- Name
source- Type
- string
- Description
resourcewhen the resource has its own schedule,location_timeswhen it follows the opening hours of its location.
- Name
schedule- Type
- array
- Description
Opening blocks ordered by weekday and time:
weekday(1= Monday …7= Sunday),opens_atandcloses_at(HH:MM, local time).
Request
curl https://api.walletapp.co/reservations/resources/3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601/schedule \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Response
{
"message": "Request successful",
"data": {
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601",
"source": "location_times",
"schedule": [
{ "weekday": 1, "opens_at": "09:00", "closes_at": "17:00" },
{ "weekday": 6, "opens_at": "10:00", "closes_at": "14:00" }
]
}
}
Replace a schedule
Replaces the whole weekly schedule of a resource. Ability reservations.manage. Send an empty list to go back to the opening hours of the location.
Body
- Name
schedule- Type
- array
- Description
Up to 70 blocks (the key must be present, an empty list is allowed).
- Name
schedule.N.weekday- Type
- integer
- Description
1(Monday) to7(Sunday).
- Name
schedule.N.opens_at- Type
- string
- Description
HH:MM.
- Name
schedule.N.closes_at- Type
- string
- Description
HH:MM, afteropens_at(a block cannot cross midnight).
Several blocks per weekday are allowed (for a lunch break), but they must not overlap; touching blocks (09:00-12:00 and 12:00-14:00) are fine. An invalid schedule is a 422 and keeps the existing schedule.
Request
curl -X PUT https://api.walletapp.co/reservations/resources/3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601/schedule \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"schedule":[
{"weekday":6,"opens_at":"09:00","closes_at":"12:00"},
{"weekday":6,"opens_at":"13:00","closes_at":"22:00"},
{"weekday":7,"opens_at":"09:00","closes_at":"18:00"}
]}'
Response
{
"message": "Request successful",
"data": {
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601",
"source": "resource",
"schedule": [
{ "weekday": 6, "opens_at": "09:00", "closes_at": "12:00" },
{ "weekday": 6, "opens_at": "13:00", "closes_at": "22:00" },
{ "weekday": 7, "opens_at": "09:00", "closes_at": "18:00" }
]
}
}
The service model
- Name
id- Type
- string
- Description
Unique identifier of the service (a UUID).
- Name
name- Type
- string
- Description
Name, for example
Padel 90 minutes.
- Name
description- Type
- string|null
- Description
Free text, max 5000 characters.
- Name
duration_minutes- Type
- integer
- Description
Length of a reservation, 5 to 1440.
- Name
buffer_before_minutes- Type
- integer
- Description
Time blocked before the start (0 to 1440, default
0).
- Name
buffer_after_minutes- Type
- integer
- Description
Time blocked after the end (0 to 1440, default
0).
- Name
slot_interval_minutes- Type
- integer|null
- Description
Step between possible start times (5 to 1440).
null(default) uses the duration.
- Name
min_notice_minutes- Type
- integer
- Description
How long before the start a slot can still be booked (0 to 525600, default
0).
- Name
max_advance_days- Type
- integer
- Description
How many days ahead can be booked (1 to 730, default
60).
- Name
cancellation_cutoff_minutes- Type
- integer|null
- Description
Until how many minutes before the start a reservation can be cancelled without
force(0 to 525600).null(default) means always.
- Name
max_party_size- Type
- integer
- Description
Maximum
party_sizeof one reservation (1 to 10000, default1).
- Name
price_cents- Type
- integer|null
- Description
Informational price in cents.
- Name
currency- Type
- string
- Description
ISO 4217 code, stored in upper case (default
EUR).
- Name
requires_confirmation- Type
- boolean
- Description
true: new reservations start aspendingand needconfirm. Defaultfalse.
- Name
active- Type
- boolean
- Description
An inactive service cannot be booked (
422). Defaulttrue.
- Name
external_id- Type
- string|null
- Description
Your own id for the service (max 255 characters).
- Name
resources- Type
- array
- Description
UUIDs of the resources that deliver this service.
- Name
created_at- Type
- string
- Description
ISO 8601 timestamp.
- Name
updated_at- Type
- string
- Description
ISO 8601 timestamp.
List services
Returns the services of your organization in creation order. Ability reservations.get or reservations.manage.
Query parameters
- Name
active- Type
- boolean
- Description
1for active,0for inactive services.
- Name
organization_id- Type
- string
- Description
Only this organization.
- Name
page, per_page- Type
- integer
- Description
Pagination, see Lists and pagination.
Request
curl -G https://api.walletapp.co/reservations/services \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d active=1
Create a service
Creates a service. Ability reservations.manage.
Body
- Name
name- Type
- string
- Description
Max 255 characters.
- Name
duration_minutes- Type
- integer
- Description
5 to 1440.
- Name
organization_id- Type
- string
- Description
Organization identifier. Required only when your token has access to more than one organization (otherwise
422); an organization outside your token is a401.
- Name
brand_id- Type
- string
- Description
Identifier of a brand of the organization (optional, a foreign brand is a
422).
- Name
resources- Type
- array
- Description
Up to 500 resource UUIDs of the same organization. An unknown or foreign resource is a
422.
- Name
description, buffer_before_minutes, buffer_after_minutes, slot_interval_minutes, min_notice_minutes, max_advance_days, cancellation_cutoff_minutes, max_party_size, price_cents, currency, requires_confirmation, active, external_id- Type
- Description
Optional, with the limits from the service model.
The response of this call only contains the values you sent; a field you left out may show as null even though the default (for example currency: "EUR" or max_party_size: 1) applies. GET /reservations/services/:id shows the stored values.
Request
curl -X POST https://api.walletapp.co/reservations/services \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Padel 90 minutes",
"description": "Indoor court, rackets available at the desk.",
"duration_minutes": 90,
"buffer_before_minutes": 0,
"buffer_after_minutes": 0,
"slot_interval_minutes": 30,
"min_notice_minutes": 60,
"max_advance_days": 14,
"cancellation_cutoff_minutes": 1440,
"max_party_size": 4,
"price_cents": 4000,
"currency": "EUR",
"requires_confirmation": false,
"active": true,
"external_id": "padel-90",
"resources": [
"3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601",
"3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602"
]
}'
Response
{
"message": "Request successful",
"data": {
"id": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"name": "Padel 90 minutes",
"description": "Indoor court, rackets available at the desk.",
"duration_minutes": 90,
"buffer_before_minutes": 0,
"buffer_after_minutes": 0,
"slot_interval_minutes": 30,
"min_notice_minutes": 60,
"max_advance_days": 14,
"cancellation_cutoff_minutes": 1440,
"max_party_size": 4,
"price_cents": 4000,
"currency": "EUR",
"requires_confirmation": false,
"active": true,
"external_id": "padel-90",
"resources": [
"3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601",
"3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602"
],
"created_at": "2026-10-10T14:05:00+02:00",
"updated_at": "2026-10-10T14:05:00+02:00"
}
}
Inspect a service
Returns one service (see the service model). Ability reservations.get or reservations.manage. 404 for an unknown service or one of another organization.
Request
curl https://api.walletapp.co/reservations/services/8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Update a service
Changes only the fields you send, with the same limits as creating a service. resources replaces the full list of linked resources. Changing the duration or buffers does not move existing reservations; it applies to new bookings and to availability. Ability reservations.manage.
Request
curl -X PUT https://api.walletapp.co/reservations/services/8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"price_cents":4400,"cancellation_cutoff_minutes":720}'
Delete a service
Deletes a service. Ability reservations.manage. A service with future pending, confirmed or checked_in reservations answers 409 has_future_reservations.
Request
curl -X DELETE https://api.walletapp.co/reservations/services/8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Response
{
"message": "Request successful",
"data": { "id": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a", "deleted": true }
}
The exception model
- Name
id- Type
- integer
- Description
Identifier of the exception.
- Name
location- Type
- string
- Description
Identifier of the location.
- Name
resource- Type
- string|null
- Description
Resource UUID, or
nullwhen the exception applies to the whole location.
- Name
date- Type
- string
- Description
The date,
YYYY-MM-DD(local).
- Name
closed- Type
- boolean
- Description
true: closed all day.false: open with the hours below instead of the weekly schedule.
- Name
opens_at- Type
- string|null
- Description
HH:MM, only whenclosedisfalse.
- Name
closes_at- Type
- string|null
- Description
HH:MM, only whenclosedisfalse.
- Name
reason- Type
- string|null
- Description
Free text, for example
Christmas.
List exceptions
Returns closures and deviating hours, ordered by date. Ability reservations.get or reservations.manage.
Query parameters
- Name
location- Type
- string
- Description
Only exceptions of this location.
- Name
resource- Type
- string
- Description
Only exceptions of this resource (location-wide exceptions are not included).
- Name
from- Type
- string
- Description
YYYY-MM-DD, inclusive.
- Name
to- Type
- string
- Description
YYYY-MM-DD, inclusive, not beforefrom.
- Name
organization_id- Type
- string
- Description
Only this organization.
- Name
page, per_page- Type
- integer
- Description
Pagination, see Lists and pagination.
Request
curl -G https://api.walletapp.co/reservations/exceptions \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d location=0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d \
-d from=2026-12-01 -d to=2026-12-31
Response
{
"message": "Request successful",
"data": [
{
"id": 41,
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"resource": null,
"date": "2026-12-25",
"closed": true,
"opens_at": null,
"closes_at": null,
"reason": "Christmas"
},
{
"id": 42,
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"resource": null,
"date": "2026-12-31",
"closed": false,
"opens_at": "09:00",
"closes_at": "16:00",
"reason": "New Year's Eve"
}
],
"meta": { "current_page": 1, "per_page": 25, "total": 2, "last_page": 1 }
}
Create an exception
Closes a location or resource on a date, or sets different hours. Ability reservations.manage. There is one exception per location, resource (or none) and date: posting again for the same scope and date replaces it and keeps its id.
Body
- Name
location- Type
- string
- Description
Identifier of one of your locations.
- Name
resource- Type
- string
- Description
A resource UUID at that location. Leave out for the whole location.
- Name
date- Type
- string
- Description
YYYY-MM-DD.
- Name
closed- Type
- boolean
- Description
Default
true. Withtrue,opens_atandcloses_atare ignored.
- Name
opens_at- Type
- string
- Description
HH:MM. Required whenclosedisfalse.
- Name
closes_at- Type
- string
- Description
HH:MM, afteropens_at. Required whenclosedisfalse.
- Name
reason- Type
- string
- Description
Max 255 characters.
Existing reservations on that date are not cancelled automatically; an exception only affects availability and new bookings.
Request
curl -X POST https://api.walletapp.co/reservations/exceptions \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"location":"0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d","date":"2026-12-31",
"closed":false,"opens_at":"09:00","closes_at":"16:00","reason":"New Year'"'"'s Eve"}'
Response
{
"message": "Request successful",
"data": {
"id": 42,
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"resource": null,
"date": "2026-12-31",
"closed": false,
"opens_at": "09:00",
"closes_at": "16:00",
"reason": "New Year's Eve"
}
}
Delete an exception
Removes an exception; the weekly schedule applies again on that date. Ability reservations.manage.
Request
curl -X DELETE https://api.walletapp.co/reservations/exceptions/42 \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Response
{
"message": "Request successful",
"data": { "id": 42, "deleted": true }
}
Get availability
Returns the bookable slots of a service at a location, see Availability for the rules. Ability reservations.get or reservations.manage.
Query parameters
- Name
service- Type
- string
- Description
Service UUID.
- Name
location- Type
- string
- Description
Location identifier, of the same organization as the service.
- Name
from- Type
- string
- Description
First date,
YYYY-MM-DD(local).
- Name
to- Type
- string
- Description
Last date,
YYYY-MM-DD, inclusive, defaultfrom. The range may span at most 31 days.
- Name
resource- Type
- string
- Description
Only slots of this resource.
- Name
party_size- Type
- integer
- Description
Default
1. Larger thanmax_party_sizeof the service is a422.
Response
- Name
from, to- Type
- string
- Description
The requested range as instants: the start of
fromand the start of the day afterto, both local.
- Name
timezone- Type
- string
- Description
Always
Europe/Amsterdam.
- Name
slots- Type
- array
- Description
Ascending by start:
starts_at,ends_at(start plus duration, without buffers) andresources, the resources that are free at that time in booking order, each withremaining(how many more reservations fit).
No slots (closed, fully booked or no resource for the service) is an empty slots list, not an error.
Request
curl -G https://api.walletapp.co/reservations/availability \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d service=8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a \
-d location=0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d \
-d from=2026-10-17 \
-d party_size=4
Response
{
"message": "Request successful",
"data": {
"service": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"party_size": 4,
"timezone": "Europe/Amsterdam",
"from": "2026-10-17T00:00:00+02:00",
"to": "2026-10-18T00:00:00+02:00",
"slots": [
{
"starts_at": "2026-10-17T09:00:00+02:00",
"ends_at": "2026-10-17T10:30:00+02:00",
"resources": [
{ "id": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a601", "remaining": 1 },
{ "id": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602", "remaining": 1 }
]
},
{
"starts_at": "2026-10-17T10:30:00+02:00",
"ends_at": "2026-10-17T12:00:00+02:00",
"resources": [
{ "id": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602", "remaining": 1 }
]
}
]
}
}
The reservation model
This is the shape of every reservation in API responses. Webhook payloads carry only a subset (ids and status, no personal data).
- Name
id- Type
- string
- Description
Unique identifier of the reservation (a UUID).
- Name
status- Type
- string
- Description
pending,confirmed,checked_in,completed,cancelled,no_showordeclined. See the lifecycle.
- Name
service- Type
- string
- Description
Service UUID.
- Name
resource- Type
- string
- Description
UUID of the booked resource.
- Name
location- Type
- string
- Description
Location identifier.
- Name
starts_at- Type
- string
- Description
Start, ISO 8601 with the Europe/Amsterdam offset.
- Name
ends_at- Type
- string
- Description
End (start plus the service duration, without buffers).
- Name
party_size- Type
- integer
- Description
Number of people.
- Name
customer- Type
- object
- Description
name,email(lower case),phoneandmember_id(the UUID of the linked member, ornull).
- Name
participants- Type
- array
- Description
Each with
name,email(lower case) andmember_id(ornull).
- Name
source- Type
- string
- Description
Where the reservation came from:
apifor this API.
- Name
external_reference- Type
- string|null
- Description
Your own booking id, unique per organization.
- Name
notes- Type
- string|null
- Description
Free text.
- Name
metadata- Type
- object|null
- Description
Your own key-value data, returned as sent.
- Name
payment_status- Type
- string
- Description
none(default),pending,paidorrefunded. Informational: no payment is taken by this API.
- Name
amount_cents- Type
- integer|null
- Description
Informational amount in cents.
- Name
cancelled_at- Type
- string|null
- Description
When the reservation was cancelled.
- Name
cancel_reason- Type
- string|null
- Description
The
reasongiven when cancelling.
- Name
checked_in_at- Type
- string|null
- Description
When the customer was checked in.
- Name
completed_at- Type
- string|null
- Description
When the reservation was completed.
- Name
created_at- Type
- string
- Description
ISO 8601 timestamp.
- Name
updated_at- Type
- string
- Description
ISO 8601 timestamp.
Create a reservation
Books a slot. The slot is checked and claimed under a lock, so two partners booking the last free court at the same moment can never both succeed: the second one gets 409 slot_unavailable. Ability reservations.store or reservations.manage.
Body
- Name
service- Type
- string
- Description
Service UUID. An inactive service is a
422.
- Name
location- Type
- string
- Description
Location identifier, of the same organization as the service.
- Name
starts_at- Type
- string
- Description
The start, exactly one of the
starts_atvalues from availability. Send it with an offset (2026-10-17T10:30:00+02:00); without an offset it is read as Europe/Amsterdam local time.
- Name
resource- Type
- string
- Description
Book this resource. Without it, the first free resource that delivers the service (by
sort) is chosen.
- Name
party_size- Type
- integer
- Description
Default
1, at mostmax_party_sizeof the service.
- Name
customer.name- Type
- string
- Description
Max 255 characters.
- Name
customer.email- Type
- string
- Description
Max 191 characters. One of
customer.email,customer.phoneorcustomer.member_idis required.
- Name
customer.phone- Type
- string
- Description
Max 64 characters.
- Name
customer.member_id- Type
- string
- Description
UUID of a member of the organization. Unknown or foreign:
422.
- Name
customer.create_member- Type
- boolean
- Description
Create a member for a customer who is not one yet. Requires
customer.member_opt_in: trueandcustomer.email. See Customers, participants and members.
- Name
customer.member_opt_in- Type
- boolean
- Description
The customer explicitly agreed to become a member.
- Name
participants- Type
- array
- Description
At most
party_size(and at most 100) entries, each with optionalname,emailandmember_id, linked to members like the customer.
- Name
external_reference- Type
- string
- Description
Your booking id, max 191 characters. See Idempotency.
- Name
notes- Type
- string
- Description
Max 5000 characters.
- Name
metadata- Type
- object
- Description
Your own key-value data.
- Name
payment_status- Type
- string
- Description
none(default),pending,paidorrefunded.
- Name
amount_cents- Type
- integer
- Description
0 or more.
Response
201 with the reservation plus was_duplicate (false) and member_created. When the external_reference already exists: 200 with the existing reservation and was_duplicate: true.
Errors
404 for an unknown or foreign service, location or resource; 409 slot_unavailable when the slot cannot be booked; 422 for validation errors (too many participants, party size, unknown member, inactive service).
Request
curl -X POST https://api.walletapp.co/reservations \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 2b7f4e1c-9a3d-4c5e-8f6a-1d2c3b4a5e6f" \
-d '{
"service": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"starts_at": "2026-10-17T10:30:00+02:00",
"party_size": 4,
"customer": {
"name": "Alex Jansen",
"email": "Alex@Example.com",
"phone": "+31600000000"
},
"participants": [
{ "name": "Alex Jansen", "email": "alex@example.com" },
{ "name": "Sam de Boer", "member_id": "9a1c2d3e-4f50-4a6b-8c7d-0e1f2a3b4c5d" },
{ "name": "Robin Smit", "email": "robin@example.org" },
{ "name": "Kim Visser" }
],
"external_reference": "club-booking-1042",
"notes": "Bring extra balls",
"metadata": { "court_lights": true },
"payment_status": "paid",
"amount_cents": 4000
}'
Response (201)
{
"message": "Request successful",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "confirmed",
"service": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"starts_at": "2026-10-17T10:30:00+02:00",
"ends_at": "2026-10-17T12:00:00+02:00",
"party_size": 4,
"customer": {
"name": "Alex Jansen",
"email": "alex@example.com",
"phone": "+31600000000",
"member_id": null
},
"participants": [
{ "name": "Alex Jansen", "email": "alex@example.com", "member_id": null },
{ "name": "Sam de Boer", "email": null, "member_id": "9a1c2d3e-4f50-4a6b-8c7d-0e1f2a3b4c5d" },
{ "name": "Robin Smit", "email": "robin@example.org", "member_id": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8" },
{ "name": "Kim Visser", "email": null, "member_id": null }
],
"source": "api",
"external_reference": "club-booking-1042",
"notes": "Bring extra balls",
"metadata": { "court_lights": true },
"payment_status": "paid",
"amount_cents": 4000,
"cancelled_at": null,
"cancel_reason": null,
"checked_in_at": null,
"completed_at": null,
"created_at": "2026-10-10T15:12:00+02:00",
"updated_at": "2026-10-10T15:12:00+02:00",
"was_duplicate": false,
"member_created": false
}
}
Duplicate (200)
{
"message": "Request successful",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "confirmed",
"external_reference": "club-booking-1042",
"...": "the existing reservation",
"was_duplicate": true,
"member_created": false
}
}
Inspect a reservation
Returns one reservation (see the reservation model). Ability reservations.get or reservations.manage. 404 for an unknown reservation or one of another organization.
Request
curl https://api.walletapp.co/reservations/d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
List reservations
Returns reservations ordered by starts_at (then creation order). Ability reservations.get or reservations.manage.
Query parameters
- Name
from- Type
- string
- Description
YYYY-MM-DD: reservations starting on or after this local date.
- Name
to- Type
- string
- Description
YYYY-MM-DD: reservations starting on or before this local date (inclusive), not beforefrom.
- Name
status- Type
- string
- Description
One status or a comma separated list, for example
pending,confirmed. An unknown status is a422.
- Name
location- Type
- string
- Description
Location identifier (foreign:
404).
- Name
service- Type
- string
- Description
Service UUID (foreign:
404).
- Name
resource- Type
- string
- Description
Resource UUID (foreign:
404).
- Name
member_id- Type
- string
- Description
Reservations whose customer is this member. An unknown member simply returns an empty list.
- Name
customer_email- Type
- string
- Description
Exact customer e-mail address (case insensitive).
- Name
external_reference- Type
- string
- Description
Your booking id.
- Name
order- Type
- string
- Description
asc(default) ordesc.
- Name
organization_id- Type
- string
- Description
Only this organization.
- Name
page, per_page- Type
- integer
- Description
Pagination, see Lists and pagination.
Request
curl -G https://api.walletapp.co/reservations \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d from=2026-10-17 -d to=2026-10-17 \
-d status=pending,confirmed \
-d per_page=50
Response
{
"message": "Request successful",
"data": [
{
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "confirmed",
"service": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"starts_at": "2026-10-17T10:30:00+02:00",
"ends_at": "2026-10-17T12:00:00+02:00",
"party_size": 4,
"customer": { "name": "Alex Jansen", "email": "alex@example.com", "phone": "+31600000000", "member_id": null },
"participants": [],
"source": "api",
"external_reference": "club-booking-1042",
"notes": null,
"metadata": null,
"payment_status": "paid",
"amount_cents": 4000,
"cancelled_at": null,
"cancel_reason": null,
"checked_in_at": null,
"completed_at": null,
"created_at": "2026-10-10T15:12:00+02:00",
"updated_at": "2026-10-10T15:12:00+02:00"
}
],
"meta": { "current_page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}
Update a reservation
Reschedules or edits a pending or confirmed reservation; send only what changes. Ability reservations.store or reservations.manage. Any other status answers 409 not_editable.
Body
- Name
starts_at- Type
- string
- Description
New start (a slot on the grid, see Create a reservation). The new time is checked under a lock; the reservation's own old interval does not count as taken, so moving 30 minutes within an overlapping window works.
- Name
resource- Type
- string
- Description
Move to this resource. Without it the reservation stays on its current resource; when that resource is taken at the new time you get
409 slot_unavailable, even if another resource is free.
- Name
party_size- Type
- integer
- Description
At most
max_party_size, and not below the number of participants.
- Name
customer- Type
- object
- Description
name,email,phone,member_id. Amember_id(or an e-mail address of a member) links the reservation to that member; an existing link is never removed through this call.
- Name
participants- Type
- array
- Description
Replaces the full list (max
party_size).
- Name
notes, metadata, payment_status, amount_cents- Type
- Description
As in Create a reservation.
A request that changes nothing returns the reservation unchanged and sends no webhook. 404 for an unknown reservation or resource, 409 slot_unavailable when the new slot is taken (the reservation keeps its old time), 422 for validation errors.
Request
curl -X PUT https://api.walletapp.co/reservations/d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"starts_at":"2026-10-17T09:00:00+02:00","notes":"Moved to the morning"}'
Response
{
"message": "Request successful",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "confirmed",
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602",
"starts_at": "2026-10-17T09:00:00+02:00",
"ends_at": "2026-10-17T10:30:00+02:00",
"notes": "Moved to the morning",
"...": "the other fields of the reservation"
}
}
409 not_editable
{
"error": true,
"code": "not_editable",
"message": "Only pending or confirmed reservations can be changed (status is checked_in)."
}
Cancel a reservation
Cancels a pending or confirmed reservation; the slot becomes free right away. Ability reservations.store or reservations.manage.
Body
- Name
reason- Type
- string
- Description
Max 255 characters, returned as
cancel_reason.
- Name
force- Type
- boolean
- Description
Cancel even when the cancellation cut-off has passed. Needs
reservations.manage(otherwise403 force_requires_manage). A forced cancellation is recorded as such.
When the service has a cancellation_cutoff_minutes and the start is closer than that, the call answers 422 cancellation_window_passed. Cancelling a reservation in any other status is 409 invalid_transition.
Request
curl -X POST https://api.walletapp.co/reservations/d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a/cancel \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"reason":"Rain"}'
Response
{
"message": "Request successful",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "cancelled",
"cancelled_at": "2026-10-16T08:00:00+02:00",
"cancel_reason": "Rain",
"...": "the other fields of the reservation"
}
}
422 cancellation_window_passed
{
"error": true,
"code": "cancellation_window_passed",
"message": "This reservation can no longer be cancelled (cut-off is 1440 minutes before the start). Use force:true with reservations.manage to override.",
"cancellation_cutoff_minutes": 1440
}
Confirm, check in, no-show and complete
Four status endpoints for the location, without a body. Ability reservations.manage. Each returns the updated reservation, or 409 invalid_transition when the current status does not allow it.
- Name
POST /reservations/:id/confirm- Type
- Description
pendingtoconfirmed.
- Name
POST /reservations/:id/check-in- Type
- Description
confirmedtochecked_in, setschecked_in_at.
- Name
POST /reservations/:id/no-show- Type
- Description
confirmedtono_show.
- Name
POST /reservations/:id/complete- Type
- Description
checked_intocompleted, setscompleted_at.
Request
curl -X POST https://api.walletapp.co/reservations/d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a/check-in \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"
Response
{
"message": "Request successful",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "checked_in",
"checked_in_at": "2026-10-17T08:55:00+02:00",
"...": "the other fields of the reservation"
}
}
Decline a pending reservation
Refuses a reservation that is waiting for confirmation (pending to declined); the slot becomes free right away. Ability reservations.manage. Optional body field reason (string, at most 255 characters), kept in the reservation's history. Returns the updated reservation. Any other status answers 409 invalid_transition and nothing changes; 404 for an unknown reservation or one of another organization. A decline sends a reservation.updated webhook with status: declined and previous_status: pending.
Request
curl -X POST https://api.walletapp.co/reservations/d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a/decline \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-d reason="Court under maintenance"
Response
{
"message": "Request successful",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "declined",
"...": "the other fields of the reservation"
}
}
Webhooks
Subscribe to the events below with POST /webhook-subscriptions. Deliveries use the same envelope, signature and retries as all webhooks. A webhook is only sent after the change is committed: a failed, rolled back, duplicate (was_duplicate) or no-op request sends nothing.
- Name
reservation.created- Type
- Description
A reservation was created.
- Name
reservation.updated- Type
- Description
A reservation was rescheduled or edited (
PUT), or confirmed, declined, checked in, marked as no-show or completed. A decline is areservation.updatedwithstatus: declined.
- Name
reservation.cancelled- Type
- Description
A reservation was cancelled.
data carries ids and status only, no personal data, like every other Walnut webhook: never a customer or participant name, e-mail address, phone number, notes, metadata or cancel reason. Fetch the details with GET /reservations/:id when you need them.
- Name
id- Type
- string
- Description
The reservation UUID.
- Name
status- Type
- string
- Description
The status after the change.
- Name
service, resource, location- Type
- string
- Description
The service UUID, the resource UUID and the location identifier.
- Name
starts_at, ends_at- Type
- string
- Description
The booked interval (ISO 8601).
- Name
party_size- Type
- integer
- Description
Number of people.
- Name
external_reference- Type
- string
- Description
Your own booking id, if you sent one.
- Name
member_id- Type
- string
- Description
Member UUID of the customer (the booker), or
nullwhen not linked to a member.
- Name
participant_member_ids- Type
- array
- Description
Member UUIDs of the participants that are linked to a member.
- Name
cancelled_at, checked_in_at, completed_at- Type
- string
- Description
Only present once set.
- Name
event_id- Type
- integer
- Description
Id of the reservation change that caused the webhook. This is not the delivery id: de-duplicate deliveries on the envelope
id(WLLT-Message-Id).
- Name
previous_status- Type
- string
- Description
The status before the change. For
reservation.createdand for an edit it equalsstatus.
- Name
changed- Type
- array
- Description
Only for an edit with
PUT: the names of the fields that changed (not their values), fromstarts_at,resource,party_size,customer.name,customer.email,customer.phone,customer.member_id,participants,notes,metadata,payment_status,amount_cents.
- Name
previous_starts_at- Type
- string
- Description
Only when the start changed: the old start.
- Name
previous_resource- Type
- string
- Description
Only when the resource changed: the old resource UUID.
reservation.updated (reschedule)
{
"id": "5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c",
"type": "reservation.updated",
"created_at": "2026-10-11T09:20:00+02:00",
"organization_id": "5b2f0c3e-1111-4222-8333-444455556666",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "confirmed",
"service": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"starts_at": "2026-10-17T09:00:00+02:00",
"ends_at": "2026-10-17T10:30:00+02:00",
"party_size": 4,
"external_reference": "club-booking-1042",
"member_id": null,
"participant_member_ids": [
"9a1c2d3e-4f50-4a6b-8c7d-0e1f2a3b4c5d",
"4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8"
],
"event_id": 1873,
"previous_status": "confirmed",
"changed": ["starts_at", "notes"],
"previous_starts_at": "2026-10-17T10:30:00+02:00"
}
}
reservation.cancelled
{
"id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
"type": "reservation.cancelled",
"created_at": "2026-10-16T08:00:00+02:00",
"organization_id": "5b2f0c3e-1111-4222-8333-444455556666",
"data": {
"id": "d4c3b2a1-9e8f-4d7c-b6a5-0f9e8d7c6b5a",
"status": "cancelled",
"service": "8e2d4c6a-1b3f-4a5e-9c7d-0f1e2d3c4b5a",
"resource": "3f6c1a2e-7b8d-4e9f-a0b1-c2d3e4f5a602",
"location": "0c9e7b1a-4d2f-4a8e-9b3c-5e6f7a8b9c0d",
"starts_at": "2026-10-17T10:30:00+02:00",
"ends_at": "2026-10-17T12:00:00+02:00",
"party_size": 4,
"external_reference": "club-booking-1042",
"member_id": null,
"participant_member_ids": [],
"cancelled_at": "2026-10-16T08:00:00+02:00",
"event_id": 1902,
"previous_status": "confirmed"
}
}
