Move a booking
const url = 'https://api.keptslot.com/v1/public/bookings/example/reschedule';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"starts_at":"2026-09-02T14:00:00+01:00"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.keptslot.com/v1/public/bookings/example/reschedule \ --header 'Content-Type: application/json' \ --data '{ "starts_at": "2026-09-02T14:00:00+01:00" }'This endpoint moves the booking the token identifies to a new time. The token is the only credential.
The service’s cancel notice applies here too. A booking that is not confirmed cannot move. Read can_reschedule first.
The new time must be an open slot. Read the open slots from GET /v1/availability. Pass the id of the booking as exclude_booking_id. If you do not, the slot that the booking holds now counts as busy.
Rate limited at 30 requests a minute for one caller address, shared
across every route under /v1/public.
Parameters
Section titled “ Parameters ”Path Parameters
Section titled “ Path Parameters ”Request Body required
Section titled “ Request Body required ”object
An RFC3339 timestamp. The new start time.
Example
{ "starts_at": "2026-09-02T14:00:00+01:00"}Responses
Section titled “ Responses ”The booking is moved.
object
When the booking last changed. Send it back as updated_since on
GET /v1/bookings to read everything that changed after it.
The customer’s manage link token. It is present only on the response that creates the booking. Store it then. No later response repeats it.
When this booking stops holding its slot. It is present only while the booking is on a hold, and absent otherwise.
Show this time rather than counting a fixed number of minutes from now. The window differs between a booking waiting on a code and one waiting on a card, and it is longer for a card because a 3DS redirect goes to the customer’s bank and back.
Present only when the service takes a payment, and only on the response that creates the booking. The key is absent for a service that takes no payment. It is never null, so a client can test for the key itself.
Use these fields to mount Stripe’s Payment Element on the connected account and confirm the payment. The client secret works once, for this booking.
object
What this booking costs, in the smallest unit of the currency. It is a snapshot taken when the booking was made. Changing the service’s price later never changes it.
The business’s Stripe account. Pass it as stripeAccount when you
construct Stripe.js.
The Stripe publishable key to construct Stripe.js with. It is not
your Keptslot pk_ key.
The extras booked with the service, in the order the business lists them. Empty when there are none.
One extra on a booking, as it was when the booking was made. A later change to the service does not change it.
object
The price of the whole visit when it was booked: the service plus every extra. It is null when none of them had a price.
The price of the service alone when it was booked, without its extras. It is null when the service had no price.
The record sent with POST /v1/bookings. It is {} when none was
sent, never null.
object
Example generated
{ "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "staff_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "service_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "customer_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "starts_at": "2026-04-15T12:00:00Z", "ends_at": "2026-04-15T12:00:00Z", "status": "example", "updated_at": "2026-04-15T12:00:00Z", "access_token": "example", "hold_expires_at": "2026-04-15T12:00:00Z", "payment": { "client_secret": "example", "amount_cents": 1, "currency": "example", "connected_account_id": "example", "stripe_publishable_key": "example" }, "extras": [ { "service_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example", "duration_minutes": 1, "price_cents": 1 } ], "price_cents": 1, "service_price_cents": 1, "metadata": { "additionalProperty": "example" }}There is no such resource in this workspace.
object
object
A stable machine-readable code. Match on this.
A sentence for a person. Do not match on this.
The field that caused the refusal, if one field did.
The plan limit that refused the request. It is present only on a 402, and only for a secret key or an account key. See the errors guide.
The current count for that dimension. It is present only on a 402, and only for an account key. It is absent when the refusal has no count to report. Absent and zero are different answers.
The cap for that dimension. It is present only on a 402, and only for an account key. A cap of zero is a real value.
Example generated
{ "error": { "type": "example", "message": "example", "param": "example", "dimension": "example", "used": 1, "limit": 1 }}Refusals here are told apart by error.type.
slot_unavailable means the new time is not available.
bookings_paused means the business has paused online bookings.
The booking does not move. Cancelling with the same token still
works.
object
object
A stable machine-readable code. Match on this.
A sentence for a person. Do not match on this.
The field that caused the refusal, if one field did.
The plan limit that refused the request. It is present only on a 402, and only for a secret key or an account key. See the errors guide.
The current count for that dimension. It is present only on a 402, and only for an account key. It is absent when the refusal has no count to report. Absent and zero are different answers.
The cap for that dimension. It is present only on a 402, and only for an account key. A cap of zero is a real value.
Example generated
{ "error": { "type": "example", "message": "example", "param": "example", "dimension": "example", "used": 1, "limit": 1 }}The request body or the query is not valid.
object
object
A stable machine-readable code. Match on this.
A sentence for a person. Do not match on this.
The field that caused the refusal, if one field did.
The plan limit that refused the request. It is present only on a 402, and only for a secret key or an account key. See the errors guide.
The current count for that dimension. It is present only on a 402, and only for an account key. It is absent when the refusal has no count to report. Absent and zero are different answers.
The cap for that dimension. It is present only on a 402, and only for an account key. A cap of zero is a real value.
Example generated
{ "error": { "type": "example", "message": "example", "param": "example", "dimension": "example", "used": 1, "limit": 1 }}This endpoint’s request budget is spent. The type field is
rate_limited.
The budget is a token bucket, not a calendar-minute counter: it
refills CONTINUOUSLY as time passes, a few tokens’ worth every
second, rather than resetting all at once at the top of the minute.
There is no wall-clock boundary to wait for. A REFUSED request costs
nothing – the check happens before a token is spent, so hammering
this endpoint while refused does not delay when it starts working
again. What you get back is Retry-After: seconds until the bucket
has refilled enough to admit one more request, computed from exactly
how empty the bucket is right now. Retrying before then just gets
refused again; retrying at or after Retry-After succeeds. See the
endpoint’s own description for the actual numbers – they differ by
endpoint and, for the publishable-key routes, by whether the budget
is shared by the whole workspace or is one caller’s own share of it.
The response also carries RateLimit-Remaining (requests left in
the bucket – always 0 on this response, since a refusal only
happens once it reaches zero). Both headers are ADDITIVE: type
alone is enough to act correctly, and a client that cannot read
response headers loses nothing. Access-Control-Expose-Headers
names both, so a browser-based integration on a publishable key can
read them too, the same as it reads type from the body.
object
object
A stable machine-readable code. Match on this.
A sentence for a person. Do not match on this.
The field that caused the refusal, if one field did.
The plan limit that refused the request. It is present only on a 402, and only for a secret key or an account key. See the errors guide.
The current count for that dimension. It is present only on a 402, and only for an account key. It is absent when the refusal has no count to report. Absent and zero are different answers.
The cap for that dimension. It is present only on a 402, and only for an account key. A cap of zero is a real value.
Example generated
{ "error": { "type": "example", "message": "example", "param": "example", "dimension": "example", "used": 1, "limit": 1 }}Headers
Section titled “ Headers ”Seconds until this bucket admits another request.
Requests left in the bucket. Always 0 on this response.
A fault on our side. Do not retry the same request in a loop. The
type field is internal_error.
object
object
A stable machine-readable code. Match on this.
A sentence for a person. Do not match on this.
The field that caused the refusal, if one field did.
The plan limit that refused the request. It is present only on a 402, and only for a secret key or an account key. See the errors guide.
The current count for that dimension. It is present only on a 402, and only for an account key. It is absent when the refusal has no count to report. Absent and zero are different answers.
The cap for that dimension. It is present only on a 402, and only for an account key. A cap of zero is a real value.
Example generated
{ "error": { "type": "example", "message": "example", "param": "example", "dimension": "example", "used": 1, "limit": 1 }}Two writes contended and one was rolled back. The request did nothing.
Retry it. The type field is transient_failure.
object
object
A stable machine-readable code. Match on this.
A sentence for a person. Do not match on this.
The field that caused the refusal, if one field did.
The plan limit that refused the request. It is present only on a 402, and only for a secret key or an account key. See the errors guide.
The current count for that dimension. It is present only on a 402, and only for an account key. It is absent when the refusal has no count to report. Absent and zero are different answers.
The cap for that dimension. It is present only on a 402, and only for an account key. A cap of zero is a real value.
Example generated
{ "error": { "type": "example", "message": "example", "param": "example", "dimension": "example", "used": 1, "limit": 1 }}