Addresses
- Production
https://{business}.crenosync.ca/api/v1 - Staging
https://{business}.staging.crenosync.ca/api/v1
About the examples: their IDs, dates, keys and tokens are illustrations; use your business's own, and dates from today on. The example answers are in English, as asked by Accept-Language: en; with fr, names, texts and messages come in French.
Site
The business itself and its policies.
GET/site
The business
The business's public identity, languages, currency, time zone and what its bookings can involve.
Credentials publishable key or secret key
Parameters
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPGET /api/v1/site
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
Accept-Language: en
Answers
-
200
The business.
Example A business with a deposit paid online.
JSON JSON
{ "name": "Évasion Alpha", "logo_url": "https://alpha.crenosync.ca/media/logo.png", "address": "1234 Saint-Denis St., Montréal, QC H2X 3K1", "phone": "514 555-0100", "website_url": "https://www.example.ca", "booking_url": "https://alpha.crenosync.ca/en/book", "languages": [ "fr", "en" ], "default_language": "fr", "currency": "CAD", "timezone": "America/Toronto", "capabilities": { "online_payment": "deposit", "email_verification": false, "promo_codes": true, "shared_sessions": false } } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
GET/policies
The policies in force
The privacy policy and terms and conditions in force, in the requested language, with the version IDs a checkout must send back to show which texts the visitor accepted. Show both texts (or link to them) next to the acceptance boxes.
Credentials publishable key or secret key
Parameters
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPGET /api/v1/policies
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
Accept-Language: en
Answers
-
200
The policies.
Example Both texts, shortened here.
JSON JSON
{ "privacy": { "version_id": 3, "title": "Privacy policy", "html": "<h2>1. Introduction</h2><p>Évasion Alpha collects…</p>", "url": "https://alpha.crenosync.ca/en/privacy-policy", "published_at": "2026-10-19T15:56:55-04:00" }, "terms": { "version_id": 2, "title": "Terms and conditions", "html": "<p>Arrive 15 minutes before your game…</p>", "url": null, "published_at": "2026-10-19T15:56:55-04:00" } } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
Activities
What can be booked, and when.
GET/activities
List activities
The activities open for booking, in the business's display order.
Credentials publishable key or secret key
Parameters
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPGET /api/v1/activities
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
Accept-Language: en
Answers
-
200
objectThe activities.
-
dataarray ofActivityrequired -
has_morebooleanrequired -
Always false in v1; reserved for pagination.
Example The business's activities, one shown here.
JSON JSON
{ "data": [ { "id": "act_7Kq2mX9pLr", "slug": "labo-perdu", "name": "The Lost Lab", "description": "A lab abandoned overnight, an experiment still running. Find the antidote before the doors seal.", "theme": "Science", "highlights": null, "rules": "Arrive 15 minutes early.", "image_url": "https://alpha.crenosync.ca/media/rooms/labo-perdu.jpg", "display_order": 1, "duration_minutes": 60, "group": { "min": 2, "max": 6, "recommended": "3-5" }, "mode": "private", "shared": null, "difficulty": { "levels": [ { "level": 0, "name": "Easy", "success_rate": null }, { "level": 1, "name": "Classic", "success_rate": 62.5 } ] }, "success_rate": 62.5, "minimum_age": 8, "price_categories": [ { "id": "cat_Rt6yU1oP3aSd", "name": "Adults", "age_min": 16, "age_max": null, "price": { "amount": 3000, "currency": "CAD" }, "counts_toward_group": true, "must_be_accompanied": false, "can_accompany": true, "min_per_booking": null, "max_per_booking": null }, { "id": "cat_Wq3eR5tY7uIo", "name": "Youths", "age_min": 8, "age_max": 15, "price": { "amount": 2200, "currency": "CAD" }, "counts_toward_group": true, "must_be_accompanied": true, "can_accompany": false, "min_per_booking": null, "max_per_booking": null } ], "from_price": { "amount": 2200, "currency": "CAD" }, "online_payment": "deposit", "deposit": { "amount": { "amount": 2000, "currency": "CAD" }, "per_person": false }, "no_show_fee": null, "resources": [ { "id": "res_Zp4xN7cB2qLe", "type": "room", "name": "The Lost Lab" } ], "booking_url": "https://alpha.crenosync.ca/en/book?roomId=labo-perdu" } ], "has_more": false } -
-
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
GET/activities/{activity_id}
Get an activity
Credentials publishable key or secret key
Parameters
-
activity_idstringrequired path -
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPGET /api/v1/activities/act_7Kq2mX9pLr
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
Accept-Language: en
Answers
-
200
The activity.
Example A private room with two difficulty levels and a deposit.
JSON JSON
{ "id": "act_7Kq2mX9pLr", "slug": "labo-perdu", "name": "The Lost Lab", "description": "A lab abandoned overnight, an experiment still running. Find the antidote before the doors seal.", "theme": "Science", "highlights": null, "rules": "Arrive 15 minutes early.", "image_url": "https://alpha.crenosync.ca/media/rooms/labo-perdu.jpg", "display_order": 1, "duration_minutes": 60, "group": { "min": 2, "max": 6, "recommended": "3-5" }, "mode": "private", "shared": null, "difficulty": { "levels": [ { "level": 0, "name": "Easy", "success_rate": null }, { "level": 1, "name": "Classic", "success_rate": 62.5 } ] }, "success_rate": 62.5, "minimum_age": 8, "price_categories": [ { "id": "cat_Rt6yU1oP3aSd", "name": "Adults", "age_min": 16, "age_max": null, "price": { "amount": 3000, "currency": "CAD" }, "counts_toward_group": true, "must_be_accompanied": false, "can_accompany": true, "min_per_booking": null, "max_per_booking": null }, { "id": "cat_Wq3eR5tY7uIo", "name": "Youths", "age_min": 8, "age_max": 15, "price": { "amount": 2200, "currency": "CAD" }, "counts_toward_group": true, "must_be_accompanied": true, "can_accompany": false, "min_per_booking": null, "max_per_booking": null } ], "from_price": { "amount": 2200, "currency": "CAD" }, "online_payment": "deposit", "deposit": { "amount": { "amount": 2000, "currency": "CAD" }, "per_person": false }, "no_show_fee": null, "resources": [ { "id": "res_Zp4xN7cB2qLe", "type": "room", "name": "The Lost Lab" } ], "booking_url": "https://alpha.crenosync.ca/en/book?roomId=labo-perdu" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
GET/activities/{activity_id}/availability
Open times
The days and times a group can book, between two dates (at most 31 days, from today on). A time is listed only if it is in the future, not booked or held, and, for a shared activity, has enough places left for the group.
Fetch it when the visitor opens the calendar or changes the date or group size, not on a timer.
Credentials publishable key or secret key
Parameters
-
activity_idstringrequired path -
Accept-Languagestringheader -
froren. Default: the business's default language. -
fromstring(date) required query -
First day, local date. Today or later.
-
tostring(date) required query -
Last day, local date, included. At most 30 days after
from. -
group_sizeintegerquery -
People who count toward the group size (see
PriceCategory.counts_toward_group). By default, the activity'sgroup.min. It must be within the activity'sgroup.minandgroup.max; otherwise the answer is422 validation_failedwithfields.group_size, since no time could be booked for that group.
Example request
HTTPGET /api/v1/activities/act_7Kq2mX9pLr/availability?from=2026-11-14&to=2026-11-15&group_size=4
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
Accept-Language: en
Answers
-
200
The open days and times.
Example Two days of open times for a group of 4.
JSON JSON
{ "activity_id": "act_7Kq2mX9pLr", "from": "2026-11-14", "to": "2026-11-15", "group_size": 4, "timezone": "America/Toronto", "days": [ { "date": "2026-11-14", "times": [ { "time": "17:00", "starts_at": "2026-11-14T17:00:00-05:00", "places_left": null, "still_needed": null, "booking_url": "https://alpha.crenosync.ca/en/booking/widget?room=labo-perdu&date=2026-11-14&time=17%3A00&players=4" }, { "time": "19:30", "starts_at": "2026-11-14T19:30:00-05:00", "places_left": null, "still_needed": null, "booking_url": "https://alpha.crenosync.ca/en/booking/widget?room=labo-perdu&date=2026-11-14&time=19%3A30&players=4" } ] }, { "date": "2026-11-15", "times": [ { "time": "13:00", "starts_at": "2026-11-15T13:00:00-05:00", "places_left": null, "still_needed": null, "booking_url": "https://alpha.crenosync.ca/en/booking/widget?room=labo-perdu&date=2026-11-15&time=13%3A00&players=4" } ] } ] } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
422
validation_failed, with each field's messages inerror.fields; or a booking rule:group_invalid,difficulty_required,category_not_offered,promo_code_invalid,quote_hold_mismatch,return_url_not_allowed,verification_failed(witherror.attempts_left). -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
Booking
Booking sessions, quotes, holds and checkouts.
POST/booking-sessions
Start a booking session
A guest token for one visitor's booking attempt. Create it when the visitor
starts booking (not on page load), keep it for that visitor only, and send it as
CrenoSync-Guest-Token with every quote, hold and checkout. It lasts 60 minutes.
A server integration creates one per visitor too: the limits on held times apply
per guest token, and to all of the business's keys together (a second key adds no
allowance).
Credentials publishable key or secret key
Example request
HTTPPOST /api/v1/booking-sessions
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
Answers
-
201
The session.
Example A guest token for 60 minutes.
JSON JSON
{ "token": "cs_gt_4fG7hJ2kL9mN3pQ6rS8tV1wX", "expires_at": "2026-11-10T15:01:55-05:00", "limits": { "holds": 3 } } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
POST/quotes
Price a booking
The exact price CrenoSync will charge for a time, a group and an optional promotion code: each price category, the discount, the taxes, the total, and what is due now. Valid 15 minutes. A quote reserves nothing; hold the time next.
The group is checked against the activity's rules (size, each category's limits, accompaniment, difficulty level). Availability is not guaranteed by a quote.
Credentials publishable key + guest token or secret key + guest token
Parameters
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Request body
Example request
HTTPPOST /api/v1/quotes
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Accept-Language: en
Content-Type: application/json
{
"activity_id": "act_7Kq2mX9pLr",
"date": "2026-11-14",
"time": "19:30",
"participants": [
{
"category_id": "cat_Rt6yU1oP3aSd",
"quantity": 4
}
],
"difficulty_level": 1,
"promo_code": null
}
Answers
-
201
The quote.
Example The price, with a deposit due online.
JSON JSON
{ "id": "quo_2Jf9TzQk4mVw", "expires_at": "2026-11-10T14:17:11-05:00", "activity_id": "act_7Kq2mX9pLr", "date": "2026-11-14", "time": "19:30", "starts_at": "2026-11-14T19:30:00-05:00", "group_size": 4, "difficulty": { "level": 1, "name": "Classic" }, "lines": [ { "category_id": "cat_Rt6yU1oP3aSd", "name": "Adults", "quantity": 4, "unit_price": { "amount": 3000, "currency": "CAD" }, "amount": { "amount": 12000, "currency": "CAD" } } ], "subtotal": { "amount": 12000, "currency": "CAD" }, "discount": null, "taxes": [ { "name": "GST", "rate": 5, "amount": { "amount": 600, "currency": "CAD" } }, { "name": "QST", "rate": 9.9749999999999996447286321199499070644378662109375, "amount": { "amount": 1197, "currency": "CAD" } } ], "total": { "amount": 13797, "currency": "CAD" }, "due_now": { "amount": 2000, "currency": "CAD" }, "due_later": { "amount": 11797, "currency": "CAD" }, "payment": "deposit" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
422
validation_failed, with each field's messages inerror.fields; or a booking rule:group_invalid,difficulty_required,category_not_offered,promo_code_invalid,quote_hold_mismatch,return_url_not_allowed,verification_failed(witherror.attempts_left). -
413
payload_too_large: the request body is larger than the server accepts. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
POST/holds
Hold a time
Reserves the quoted time (or, for a shared activity, the group's places) for this
guest while the visitor fills in their details. The hold lasts the business's
hold time (10 minutes by default; see expires_at) and is not renewed. Starting
a checkout that needs a payment extends it for the length of the payment window,
plus 5 minutes for the payment to be confirmed. The payment window lasts 30
minutes, cut short to end 15 minutes before the activity starts, but never lasts
less than 10 minutes: for a checkout opened in the last 25 minutes before the
start, the 10 minutes win, and its window can end after the start. A time abandoned
near its start is offered again in time for a last-minute visitor, and a checkout
opened just before the start can still be paid. A checkout paid or confirmed by
code before its hold ends is booked, even once the activity has just started; a
checkout can't be opened after the start. When the payment provider's page
outlives the window, it is closed at the window's end, and the time is offered
again once the provider confirms the page is closed.
A quote_changed answer to a checkout leaves the hold as it was.
Each guest token may hold a few times at once (BookingSession.limits); holding
a new time while at that limit is refused. Release a hold the visitor gives up.
Credentials publishable key + guest token or secret key + guest token
Parameters
-
Idempotency-Keystringrequired header -
A unique value per operation you mean to perform once, e.g. a UUID. Kept 24 hours.
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Request body
-
quote_idstringrequired -
A quote of this guest, not expired.
Example request
HTTPPOST /api/v1/holds
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Idempotency-Key: 6f1c2a4e-8b3d-4e9a-9c7f-2d5b1a0e3c84
Accept-Language: en
Content-Type: application/json
{
"quote_id": "quo_2Jf9TzQk4mVw"
}
Answers
-
201
The hold.
Headers
Idempotent-ReplayedExample The time held for 10 minutes.
JSON JSON
{ "id": "hld_Vb3nP8sQw1Zx", "quote_id": "quo_2Jf9TzQk4mVw", "activity_id": "act_7Kq2mX9pLr", "date": "2026-11-14", "time": "19:30", "starts_at": "2026-11-14T19:30:00-05:00", "places": 4, "status": "active", "expires_at": "2026-11-10T14:12:30-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
422
validation_failed, with each field's messages inerror.fields; or a booking rule:group_invalid,difficulty_required,category_not_offered,promo_code_invalid,quote_hold_mismatch,return_url_not_allowed,verification_failed(witherror.attempts_left). -
413
payload_too_large: the request body is larger than the server accepts. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
DELETE/holds/{hold_id}
Release a hold
Gives the time back. Safe to repeat: a hold already released or expired answers
204 too. A hold whose checkout is being started or waiting for a payment cannot
be released (409 hold_in_checkout); cancel the checkout instead.
Credentials publishable key + guest token or secret key + guest token
Parameters
-
hold_idstringrequired path
Example request
HTTPDELETE /api/v1/holds/hld_Vb3nP8sQw1Zx
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Answers
-
204
Released.
-
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support.
POST/checkouts
Check out
Books the held time with the visitor's details and their acceptance of the
policies. quote_id is the quote the visitor saw; it must be for the hold's
exact choice (activity, date, time, participants, difficulty level), or the
answer is 422 quote_hold_mismatch.
When the price changed. The price is recomputed. If it differs from the
quote's (a price, a tax rate or the promotion changed, or the quote expired and
its price no longer holds), the answer is 409 quote_changed with a new quote
in error.quote, and nothing is charged or reserved:
- the hold is kept as it was: same time, same places, same
expires_at; - the new quote is for the hold's choice, with the same promotion code, so it can be used with the same hold;
- no promotion use was taken (a use is only taken when a checkout starts);
- show the new price, then send the checkout again with the new
quote_id, the samehold_idand a newIdempotency-Key(the body changed).
If the promotion code no longer applies at all, the answer is
422 promo_code_invalid: get a new quote without the code (the hold is kept).
If the hold ended meanwhile, the answer is 409 hold_expired: start again from
the availability. If a policy changed, the answer is 409 policy_changed with
the texts now in force; show them and ask again.
One checkout per hold. Sent again for the same hold (a retry with a new
Idempotency-Key, a second tab), the answer is the checkout already open, when
nothing changed; a checkout with other details replaces it. While another request
is still opening a checkout of the same hold, the answer is
409 checkout_in_progress with Retry-After: send the request again after it, and
the checkout it opened comes back. Never two payment pages for one hold.
The answer's status says what happens next: confirmed,
awaiting_verification (email code) or awaiting_payment (send the visitor to
next_action.url).
Credentials publishable key + guest token or secret key + guest token
Parameters
-
Idempotency-Keystringrequired header -
A unique value per operation you mean to perform once, e.g. a UUID. Kept 24 hours.
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Request body
Example request
HTTPPOST /api/v1/checkouts
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Idempotency-Key: 6f1c2a4e-8b3d-4e9a-9c7f-2d5b1a0e3c84
Accept-Language: en
Content-Type: application/json
{
"hold_id": "hld_Vb3nP8sQw1Zx",
"quote_id": "quo_2Jf9TzQk4mVw",
"customer": {
"name": "Camille Tremblay",
"email": "camille@example.com",
"phone": "514 555-0199",
"language": "en"
},
"consent": {
"privacy_version_id": 3,
"privacy_accepted": true,
"terms_version_id": 2,
"terms_accepted": true
},
"return_url": "https://www.example.ca/booking/thanks",
"attribution": {
"source": "newsletter",
"medium": "email",
"campaign": "fall-2026"
},
"external_reference": "order-1042"
}
Answers
-
201
The checkout.
Headers
Idempotent-ReplayedExample A deposit is due: send the visitor to the payment page.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "awaiting_payment", "next_action": { "type": "redirect", "url": "https://alpha.crenosync.ca/en/checkout/chk_Hd5rL2kTy9Mc?expires=1794339340&signature=3f9c0e…", "expires_at": "2026-11-10T14:35:40-05:00" }, "amount_due_now": { "amount": 2000, "currency": "CAD" }, "expires_at": "2026-11-10T14:35:40-05:00", "booking": null, "created_at": "2026-11-10T14:05:40-05:00" }Example Nothing to pay online, the business asks for the emailed code.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "awaiting_verification", "next_action": { "type": "verify_email", "url": "https://alpha.crenosync.ca/en/checkout/chk_Hd5rL2kTy9Mc?expires=1794337950&signature=8b21d4…", "expires_at": "2026-11-10T14:12:30-05:00", "email_hint": "c•••••@example.com", "attempts_left": 5 }, "amount_due_now": { "amount": 0, "currency": "CAD" }, "expires_at": "2026-11-10T14:12:30-05:00", "booking": null, "created_at": "2026-11-10T14:05:40-05:00" }Example Nothing to pay online and no code: booked at once.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "confirmed", "next_action": null, "amount_due_now": { "amount": 0, "currency": "CAD" }, "expires_at": null, "booking": { "reference": "PB7K2QXA-20261114" }, "created_at": "2026-11-10T14:05:40-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
422
validation_failed, with each field's messages inerror.fields; or a booking rule:group_invalid,difficulty_required,category_not_offered,promo_code_invalid,quote_hold_mismatch,return_url_not_allowed,verification_failed(witherror.attempts_left). -
413
payload_too_large: the request body is larger than the server accepts. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
GET/checkouts/{checkout_id}
Get a checkout
A checkout's current state. After a CrenoSync page (payment or email code), read it with the return
token from your return_url. While the status is processing, ask again after
a few seconds, waiting longer each time (2, 4, 8 seconds…), for at most a few
minutes: the payment is being confirmed with the provider. If it is still
processing after that, show the visitor that the booking is being confirmed
and that an email will follow; never ask them to pay again.
Credentials publishable key + guest token or secret key + guest token or publishable key + return token or secret key
Parameters
-
checkout_idstringrequired path -
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPGET /api/v1/checkouts/chk_Hd5rL2kTy9Mc
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Accept-Language: en
Answers
-
200
The checkout.
Example Booked after the payment.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "confirmed", "next_action": null, "amount_due_now": { "amount": 2000, "currency": "CAD" }, "expires_at": null, "booking": { "reference": "PB7K2QXA-20261114" }, "created_at": "2026-11-10T14:05:40-05:00" }Example The payment is being confirmed: read it again in a few seconds.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "processing", "next_action": null, "amount_due_now": { "amount": 2000, "currency": "CAD" }, "expires_at": "2026-11-10T14:35:40-05:00", "booking": null, "created_at": "2026-11-10T14:05:40-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support.
POST/checkouts/{checkout_id}/verify-email
Confirm with the email code
For a checkout awaiting_verification: the 6-digit code CrenoSync emailed to the
visitor. The right code confirms the booking. Five wrong codes cancel the checkout
and release the time (409 verification_locked). The code works until the hold
ends.
Credentials publishable key + guest token or secret key + guest token
Parameters
-
checkout_idstringrequired path -
Accept-Languagestringheader -
froren. Default: the business's default language.
Request body
-
codestringrequired
Example request
HTTPPOST /api/v1/checkouts/chk_Hd5rL2kTy9Mc/verify-email
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Accept-Language: en
Content-Type: application/json
{
"code": "482913"
}
Answers
-
200
The booking is confirmed.
Example The right code: booked.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "confirmed", "next_action": null, "amount_due_now": { "amount": 0, "currency": "CAD" }, "expires_at": null, "booking": { "reference": "PB7K2QXA-20261114" }, "created_at": "2026-11-10T14:05:40-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
422
validation_failed, with each field's messages inerror.fields; or a booking rule:group_invalid,difficulty_required,category_not_offered,promo_code_invalid,quote_hold_mismatch,return_url_not_allowed,verification_failed(witherror.attempts_left). -
413
payload_too_large: the request body is larger than the server accepts. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support.
POST/checkouts/{checkout_id}/resend-code
Send the email code again
At most three codes per checkout, one a minute.
Credentials publishable key + guest token or secret key + guest token
Parameters
-
checkout_idstringrequired path -
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPPOST /api/v1/checkouts/chk_Hd5rL2kTy9Mc/resend-code
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Accept-Language: en
Answers
-
202
A new code was sent.
Example A new code is on its way.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "awaiting_verification", "next_action": { "type": "verify_email", "url": "https://alpha.crenosync.ca/en/checkout/chk_Hd5rL2kTy9Mc?expires=1794337950&signature=8b21d4…", "expires_at": "2026-11-10T14:12:30-05:00", "email_hint": "c•••••@example.com", "attempts_left": 5 }, "amount_due_now": { "amount": 0, "currency": "CAD" }, "expires_at": "2026-11-10T14:12:30-05:00", "booking": null, "created_at": "2026-11-10T14:05:40-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support. -
503
business_unavailable(suspended, under maintenance or still being set up),payment_unavailable,email_unavailable.Headers
Retry-After
POST/checkouts/{checkout_id}/cancel
Cancel a checkout
The visitor gave up before the booking was confirmed: the checkout ends and its
time and promotion use are released. Safe to repeat. A checkout whose payment
may already be under way cannot be cancelled this way (409 checkout_processing):
CrenoSync finishes checking it with the payment provider first.
Credentials publishable key + guest token or secret key + guest token
Parameters
-
checkout_idstringrequired path
Example request
HTTPPOST /api/v1/checkouts/chk_Hd5rL2kTy9Mc/cancel
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Answers
-
200
The cancelled checkout.
Example Given up: the time is free again.
JSON JSON
{ "id": "chk_Hd5rL2kTy9Mc", "status": "cancelled", "next_action": null, "amount_due_now": { "amount": 0, "currency": "CAD" }, "expires_at": null, "booking": null, "created_at": "2026-11-10T14:05:40-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
409
The request is valid but the situation doesn't allow it:
time_unavailable,activity_unavailable,quote_expired,quote_changed(witherror.quote),policy_changed(witherror.policies),hold_expired,hold_in_checkout,hold_already_used,checkout_in_progress,checkout_processing,checkout_closed,verification_locked,idempotency_key_reused,idempotency_in_progress. The two…_in_progressanswers carryRetry-After: send the same request again after it.Headers
Retry-After -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support.
Bookings
Confirmed bookings.
GET/bookings/{reference}
Get a booking
A confirmed booking made through this API. A guest token or return token reads only the booking of its own checkout; a secret key reads only bookings made through that key. There is no search by name, email or phone.
Credentials publishable key + guest token or secret key + guest token or publishable key + return token or secret key
Parameters
-
referencestringrequired path -
The booking number.
-
Accept-Languagestringheader -
froren. Default: the business's default language.
Example request
HTTPGET /api/v1/bookings/PB7K2QXA-20261114
Host: alpha.crenosync.ca
Authorization: Bearer cs_pk_…
CrenoSync-Guest-Token: cs_gt_…
Accept-Language: en
Answers
-
200
The booking.
Example A booking with its deposit paid.
JSON JSON
{ "reference": "PB7K2QXA-20261114", "status": "confirmed", "activity": { "id": "act_7Kq2mX9pLr", "name": "The Lost Lab" }, "date": "2026-11-14", "time": "19:30", "starts_at": "2026-11-14T19:30:00-05:00", "group_size": 4, "difficulty": { "level": 1, "name": "Classic" }, "lines": [ { "name": "Adults", "quantity": 4, "amount": { "amount": 12000, "currency": "CAD" } } ], "price": { "subtotal": { "amount": 12000, "currency": "CAD" }, "discount": null, "taxes": [ { "name": "GST", "amount": { "amount": 600, "currency": "CAD" } }, { "name": "QST", "amount": { "amount": 1197, "currency": "CAD" } } ], "total": { "amount": 13797, "currency": "CAD" } }, "paid": { "amount": 2000, "currency": "CAD" }, "balance": { "amount": 11797, "currency": "CAD" }, "customer": { "name": "Camille Tremblay", "email": "camille@example.com", "phone": "514 555-0199", "language": "en" }, "confirmation_url": "https://alpha.crenosync.ca/en/booking/confirmation?booking_number=PB7K2QXA-20261114&token=Gl22GGQx…", "channel": { "type": "api", "key_name": "Our website" }, "attribution": { "source": "newsletter", "medium": "email", "campaign": "fall-2026" }, "external_reference": "order-1042", "created_at": "2026-11-10T14:05:40-05:00" } -
401
missing_key,invalid_key(also a key of another business: keys exist only on their own business's host),key_revoked,missing_guest_token,invalid_guest_token(unknown, or made with another key),guest_token_expired,invalid_return_token(unknown, made with another key, replaced by a newer one, or past its 30 minutes). Too many refused keys from one address in a minute answers429 rate_limitedfor a while, even with a valid key. -
403
not_in_plan(the business's plan doesn't include the API),integration_disabled,origin_not_allowed,secret_key_in_browser,not_allowed(this credential can't do that). -
404
not_found: no such record for this credential. Records of another guest, key or business answer the same. -
429
rate_limited,too_many_holds,business_busy,too_many_codes. Wait forRetry-After. A page can read these answers from any website. An address over its limit also gets429on browser preflights, which browsers report as a network error: wait before trying again.Headers
Retry-After -
500
internal_error: a failure on CrenoSync's side. Quoterequest_idwhen contacting support.
Error answers
The answers several endpoints share, mostly refusals, each with examples. Every error has the same shape: test its code, never its message.
401 Unauthenticated
missing_key, invalid_key (also a key of another business: keys exist only on
their own business's host), key_revoked, missing_guest_token,
invalid_guest_token (unknown, or made with another key), guest_token_expired,
invalid_return_token (unknown, made with another key, replaced by a newer one, or
past its 30 minutes). Too many refused keys from one
address in a minute answers 429 rate_limited for a while, even with a valid key.
Example A key this business doesn't know.
JSON{
"error": {
"code": "invalid_key",
"message": "This API key is not valid for this business.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
403 Forbidden
not_in_plan (the business's plan doesn't include the API), integration_disabled,
origin_not_allowed, secret_key_in_browser, not_allowed (this credential can't
do that).
Example A web page on a website not registered with the key.
JSON{
"error": {
"code": "origin_not_allowed",
"message": "This website is not registered with this API key.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
404 NotFound
not_found: no such record for this credential. Records of another guest, key or business answer the same.
Example Nothing with that ID for this key or token.
JSON{
"error": {
"code": "not_found",
"message": "Not found.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
409 Conflict
The request is valid but the situation doesn't allow it:
time_unavailable, activity_unavailable, quote_expired, quote_changed (with
error.quote), policy_changed (with error.policies), hold_expired,
hold_in_checkout, hold_already_used, checkout_in_progress, checkout_processing,
checkout_closed, verification_locked, idempotency_key_reused,
idempotency_in_progress. The two …_in_progress answers carry Retry-After: send
the same request again after it.
Example The time was taken meanwhile: choose another one.
JSON{
"error": {
"code": "time_unavailable",
"message": "This time slot is no longer available.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
Example The price changed (checkout): show the new quote, then check out with it.
JSON{
"error": {
"code": "quote_changed",
"message": "The price changed since this quote. Show the new quote, then check out with it.",
"request_id": "req_01JBQ7Z5K3W8",
"quote": {
"id": "quo_8Nc3Vx6Bm1Qa",
"expires_at": "2026-11-10T14:20:40-05:00",
"activity_id": "act_7Kq2mX9pLr",
"date": "2026-11-14",
"time": "19:30",
"starts_at": "2026-11-14T19:30:00-05:00",
"group_size": 4,
"difficulty": {
"level": 1,
"name": "Classic"
},
"lines": [
{
"category_id": "cat_Rt6yU1oP3aSd",
"name": "Adults",
"quantity": 4,
"unit_price": {
"amount": 3000,
"currency": "CAD"
},
"amount": {
"amount": 12000,
"currency": "CAD"
}
}
],
"subtotal": {
"amount": 12000,
"currency": "CAD"
},
"discount": null,
"taxes": [
{
"name": "GST",
"rate": 5,
"amount": {
"amount": 600,
"currency": "CAD"
}
},
{
"name": "QST",
"rate": 9.9749999999999996447286321199499070644378662109375,
"amount": {
"amount": 1197,
"currency": "CAD"
}
}
],
"total": {
"amount": 13797,
"currency": "CAD"
},
"due_now": {
"amount": 2000,
"currency": "CAD"
},
"due_later": {
"amount": 11797,
"currency": "CAD"
},
"payment": "deposit"
}
}
}
Example The same hold's checkout is being started: wait for Retry-After, then send it again.
JSON{
"error": {
"code": "checkout_in_progress",
"message": "A checkout for this time is already being started. Wait a moment, then try again.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
422 ValidationFailed
validation_failed, with each field's messages in error.fields; or a booking
rule: group_invalid, difficulty_required, category_not_offered,
promo_code_invalid, quote_hold_mismatch, return_url_not_allowed,
verification_failed (with error.attempts_left).
Example Fields to correct, each with its messages.
JSON{
"error": {
"code": "validation_failed",
"message": "The data sent is not valid.",
"request_id": "req_01JBQ7Z5K3W8",
"fields": {
"customer.email": [
"The email address is not valid."
],
"time": [
"The time field is required."
]
}
}
}
Example A wrong code, when checking the emailed code.
JSON{
"error": {
"code": "verification_failed",
"message": "This code is not right. Check the latest email we sent and try again.",
"request_id": "req_01JBQ7Z5K3W8",
"attempts_left": 4
}
}
429 RateLimited
rate_limited, too_many_holds, business_busy, too_many_codes. Wait for
Retry-After. A page can read these answers from any website. An address over its
limit also gets 429 on browser preflights, which browsers report as a network
error: wait before trying again.
Example Too many requests: wait for Retry-After.
JSON{
"error": {
"code": "rate_limited",
"message": "Too many requests. Please try again later.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
503 Unavailable
business_unavailable (suspended, under maintenance or still being set up), payment_unavailable, email_unavailable.
Example The business is under maintenance.
JSON{
"error": {
"code": "business_unavailable",
"message": "This site is under maintenance. Please try again in a few minutes.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
413 PayloadTooLarge
payload_too_large: the request body is larger than the server accepts.
Example A body over the limit.
JSON{
"error": {
"code": "payload_too_large",
"message": "The request is too large.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
500 InternalError
internal_error: a failure on CrenoSync's side. Quote request_id when contacting support.
Example A failure on CrenoSync's side: quote the request_id.
JSON{
"error": {
"code": "internal_error",
"message": "Something went wrong on our side. Please try again later.",
"request_id": "req_01JBQ7Z5K3W8"
}
}
Objects
Error object
-
errorobjectrequired -
error.codestringrequired -
Stable, machine-readable. New codes may be added.
-
error.messagestringrequired -
For people, in the request's language. May change; never test it.
-
error.request_idstringrequired -
error.fieldsobject -
For
validation_failed, each invalid field (dotted path) and its messages. -
error.attempts_leftinteger -
For
verification_failed. -
error.quoteQuote -
For
quote_changed, the new quote, for the same hold's choice and promotion code. -
error.policiesPolicies -
For
policy_changed, the texts now in force.
Money object
-
amountintegerrequired -
In the currency's smallest unit (cents).
-
currencystringrequired -
ISO 4217.
LocalizedText string | null
Plain text in the request's language (line breaks kept, no HTML), or null when empty.
Site object
-
namestringrequired -
logo_urlstring|null(uri) -
addressstring|null -
phonestring|null -
website_urlstring(uri) -
The business's hosted CrenoSync site.
-
booking_urlstring(uri) required -
The hosted booking page, as a fallback link.
-
languagesarray ofstringrequired -
Open list.
-
default_languagestringrequired -
Open list.
-
currencystringrequired -
timezonestringrequired -
IANA time zone of every local date and time.
-
capabilitiesobjectrequired -
capabilities.online_paymentstringrequired -
none: everything is paid at the venue.deposit: activities may ask for a deposit online.full: activities ask for the full price online.guarantee: activities save the visitor's card as a no-show guarantee, with nothing paid online.mixed: it depends on the activity (itsonline_payment). -
capabilities.email_verificationbooleanrequired -
Bookings with nothing to pay online are confirmed with an emailed code.
-
capabilities.promo_codesbooleanrequired -
capabilities.shared_sessionsbooleanrequired -
Some activities let several groups share a session.
Policies object
Policy object
-
version_idintegerrequired -
titlestringrequired -
htmlstringrequired -
Sanitized HTML (headings, paragraphs, lists, links, emphasis).
-
urlstring|null(uri) -
The same text on the hosted site, when it has its own page (the privacy policy does; the terms are shown at booking).
-
published_atstring(date-time) required
Activity object
-
idstringrequired -
slugstringrequired -
Human-readable, for your own URLs. May change; store
id. -
namestringrequired -
descriptionLocalizedText -
themeLocalizedText -
highlightsLocalizedText -
rulesLocalizedText -
image_urlstring|null(uri) -
display_orderinteger -
duration_minutesintegerrequired -
How long the activity lasts.
-
groupobjectrequired -
Counts only people in categories that count toward the group.
-
group.minintegerrequired -
group.maxinteger|nullrequired -
group.recommendedstring|null -
modestringrequired -
private: one group per time.shared: several groups share a session, up to its places. Open list. -
sharedobject|null -
For a shared activity.
-
shared.places_per_sessioninteger -
shared.min_to_playinteger|null -
People the session needs to take place; below it the business may cancel the session.
-
difficultyobject|null -
Present when the visitor must choose a level.
-
difficulty.levelsarray ofobjectrequired -
difficulty.levels[].levelintegerrequired -
difficulty.levels[].namestringrequired -
difficulty.levels[].success_ratenumber|null -
success_ratenumber|null -
The activity's single success rate, when it has no levels.
-
minimum_ageinteger|null -
price_categoriesarray ofPriceCategoryrequired -
from_priceMoney|null -
The lowest non-zero price per person, before taxes.
-
online_paymentstringrequired -
What booking it asks to pay online:
none,deposit(seedeposit),full, the whole price with taxes once a promotion comes off, orguarantee: nothing paid, the visitor's card saved for the no-show fee (seeno_show_fee). -
depositobject|null -
The deposit due online, before any promotion; null when no deposit is paid online (nothing, or the full price).
-
deposit.amountMoney -
deposit.per_personboolean -
no_show_feeobject|null -
The no-show fee of an activity that saves the visitor's card (
online_payment: guarantee), at mostamount(per person whenper_person), charged only if no one comes or the booking is canceled less thanfree_cancellation_hoursbefore its start; null otherwise. -
no_show_fee.amountMoney -
no_show_fee.per_personboolean -
no_show_fee.free_cancellation_hoursinteger -
resourcesarray ofResourcerequired -
What is booked for the activity. In v1, one room. Later, people such as trainers.
-
booking_urlstring(uri) required -
This activity on the hosted booking page.
PriceCategory object
-
idstringrequired -
namestringrequired -
age_mininteger|null -
age_maxinteger|null -
priceMoneyrequired -
Per person, before taxes.
-
counts_toward_groupbooleanrequired -
Whether these people count toward the group size and the places (an infant may not).
-
must_be_accompaniedbooleanrequired -
can_accompanybooleanrequired -
min_per_bookinginteger|null -
max_per_bookinginteger|null
Resource object
-
idstringrequired -
typestringrequired -
Open list.
roomin v1; new types such aspersonwill be added. Show a type you don't know by itsname. -
namestringrequired
Availability object
-
activity_idstringrequired -
fromstring(date) required -
tostring(date) required -
group_sizeintegerrequired -
timezonestringrequired -
daysarray ofobjectrequired -
Only days with at least one open time.
-
days[].datestring(date) required -
days[].timesarray ofOpenTimerequired
OpenTime object
-
timestringrequired -
starts_atstring(date-time) required -
places_leftinteger|null -
For a shared session, the places still free; null for a private time.
-
still_neededinteger|null -
For a shared session with a minimum, the people it still needs to take place.
-
booking_urlstring(uri) required -
This time on the hosted booking page, for sites that hand the booking over to CrenoSync.
BookingSession object
-
tokenstringrequired -
expires_atstring(date-time) required -
limitsobjectrequired -
limits.holdsintegerrequired -
Times this session may hold at once.
ParticipantInput object
-
category_idstringrequired -
quantityintegerrequired
QuoteRequest object
-
activity_idstringrequired -
datestring(date) required -
timestringrequired -
participantsarray ofParticipantInputrequired -
difficulty_levelinteger|null -
Required when the activity has
difficulty. -
promo_codestring|null
Quote object
-
idstringrequired -
expires_atstring(date-time) required -
activity_idstringrequired -
datestring(date) required -
timestringrequired -
starts_atstring(date-time) required -
group_sizeintegerrequired -
difficultyobject|null -
difficulty.levelinteger -
difficulty.namestring -
linesarray ofobjectrequired -
lines[].category_idstringrequired -
lines[].namestringrequired -
lines[].quantityintegerrequired -
lines[].unit_priceMoneyrequired -
lines[].amountMoneyrequired -
subtotalMoneyrequired -
discountobject|null -
discount.codestring -
discount.labelstring -
discount.amountMoney -
taxesarray ofobjectrequired -
taxes[].namestringrequired -
taxes[].ratenumberrequired -
Percent.
-
taxes[].amountMoneyrequired -
totalMoneyrequired -
due_nowMoneyrequired -
Paid online at checkout (the deposit, or the full price), possibly 0.
-
due_laterMoneyrequired -
Paid at the venue.
-
paymentstringrequired -
What the checkout asks to pay online:
none,deposit,full, the whole price, orguarantee, nothing paid but the visitor's card saved for the no-show fee. Anything butnonesends the visitor to the payment page.
Hold object
-
idstringrequired -
quote_idstringrequired -
activity_idstringrequired -
datestring(date) required -
timestringrequired -
starts_atstring(date-time) required -
placesintegerrequired -
For a shared session, the places held; for a private time, the group size.
-
statusstringrequired -
Open list.
-
expires_atstring(date-time) required
Customer object
-
namestringrequired -
emailstring(email) required -
phonestringrequired -
languagestring -
The language of the emails sent to the customer; by default, the request's language.
CheckoutRequest object
-
hold_idstringrequired -
quote_idstringrequired -
The quote shown to the visitor, for the hold's exact choice. After
quote_changed, the new quote's ID. -
customerCustomerrequired -
notesstring|null -
attributionAttribution|null -
Where the visitor came from, for the business's reports. Optional.
-
external_referencestring|null -
Your own reference for this booking (an order number in your system), returned on the booking. Not checked for uniqueness. Never put personal data in it.
-
consentobjectrequired -
The versions shown to the visitor (from
GET /policies) and their explicit acceptance. -
consent.privacy_version_idintegerrequired -
consent.privacy_acceptedbooleanrequired -
consent.terms_version_idinteger|null -
Required when the business has terms.
-
consent.terms_acceptedboolean|null -
Required (true) when the business has terms.
-
consent.no_show_fee_acceptedboolean|null -
Required (true) when the quote's
paymentisguarantee: the visitor accepted the activity's no-show fee (no_show_fee). -
return_urlstring|null(uri) -
Required when the quote's
paymentis notnone; optional otherwise. Where the visitor comes back after a CrenoSync page ofnext_action.url(payment, email code); CrenoSync addscheckout_idandreturn_tokenafter#(https://www.example.ca/thanks#checkout_id=chk_…&return_token=cs_rt_…), its own query string kept. Without one, that page ends on the hosted confirmation.The address is parsed, never compared as text, and must match one of the return addresses registered with the key (
422 return_url_not_allowedotherwise):httpsonly;httpis accepted only for a local development host (localhost,127.0.0.1,*.localhost,*.test), and never in production;- the scheme, host and port equal the registered address's, exactly (no wildcard, no subdomain of it);
- the path equals the registered path, or continues below it after a
/(registered/bookingallows/booking/done, never/booking-done); - no user name or password, and no fragment (
#…).
Browsers never send the part after
#to your server, its logs or another site. Read it in the page's script, then take it out of the address (history.replaceState) before any analytics or other outside script runs.
Checkout object
-
idstringrequired -
statusstringrequired -
Open list: treat a status you don't know as not confirmed yet, and read it again later.
awaiting_verification: waiting for the emailed code.awaiting_payment: waiting for the visitor on the payment page.processing: the payment is being confirmed with the provider.confirmed: booked; seebooking.expired: the time ran out before the code or the payment.cancelled: given up, or too many wrong codes.attention_required: paid, but the booking could not be made automatically. The business was alerted and will contact the customer. Never ask them to pay again.
-
next_actionNextAction|null -
What the visitor must do next for this checkout, or null.
-
amount_due_nowMoneyrequired -
expires_atstring|null(date-time) -
When an awaiting checkout ends: the end of the payment window (30 minutes, cut short to end 15 minutes before the start, but never less than 10 minutes, so it can end after the start for a checkout opened in the last 25 minutes), or of the hold for an email code. Paid or confirmed by then, it is booked.
-
bookingBookingSummary|null -
Once
confirmed. -
created_atstring(date-time) required
NextAction object
A step the visitor must complete for this checkout. Every action, whatever its
type, has a url: CrenoSync's page where the visitor completes that step for this
same checkout. The page ends on your return_url, or on the hosted confirmation
when the checkout has none.
Handle the types you know your own way. For a type you don't know, send the
visitor to url. Never send them to booking_url or start a new quote or hold:
this checkout already holds the time, and may already have a payment under way.
-
typestringrequired -
Open list.
verify_email: ask for the 6-digit code emailed to the visitor and send it withPOST /checkouts/{id}/verify-email, or send the visitor tourlto type it on CrenoSync's page.redirect: send the visitor tourl, CrenoSync's payment page.
-
urlstring(uri) required -
CrenoSync's page for this step, on the business's own host. Open it in the whole window, never in a frame. It is a signed link for this checkout only, valid until
expires_at: don't store, share or log it. The first browser that opens it and loads the page keeps the checkout and completes the step there (it can reload, and comes back there from the payment); the address loses its signature at once. Another browser opening the same link is then refused, so send it only to the visitor's own browser. -
expires_atstring(date-time) required -
When the step can no longer be completed (the end of the hold or of the payment window; see
Checkout.expires_at). -
email_hintstring -
For
verify_email, the address the code went to, partly hidden. -
attempts_leftinteger -
For
verify_email, the wrong codes still allowed.
BookingSummary object
-
referencestringrequired
Booking object
-
referencestringrequired -
statusstringrequired -
Open list.
-
activityobjectrequired -
activity.idstringrequired -
activity.namestringrequired -
datestring(date) required -
timestringrequired -
starts_atstring(date-time) required -
group_sizeintegerrequired -
difficultyobject|null -
difficulty.levelinteger -
difficulty.namestring -
linesarray ofobjectrequired -
lines[].namestringrequired -
lines[].quantityintegerrequired -
lines[].amountMoneyrequired -
priceobjectrequired -
price.subtotalMoneyrequired -
price.discountMoney|null -
price.taxesarray ofobjectrequired -
price.taxes[].namestringrequired -
price.taxes[].amountMoneyrequired -
price.totalMoneyrequired -
paidMoneyrequired -
balanceMoneyrequired -
Left to pay at the venue.
-
customerobject -
With a guest token or the secret key:
name,email,phoneandlanguage. With a return token:name,email_hintandlanguageonly. -
customer.namestring -
customer.emailstring -
customer.email_hintstring -
The email, masked, e.g.
c••••••@example.com(return token reads). -
customer.phonestring -
customer.languagestring -
Open list.
-
confirmation_urlstring|null(uri) -
The customer's own confirmation page on the hosted site (private link). Null on a read with the return token.
-
channelobject -
How the booking was made, recorded by CrenoSync.
-
channel.typestringrequired -
apifor every booking this endpoint returns. Open list (see Conventions). -
channel.key_namestring|null -
The name the owner gave the key in Settings › API.
-
attributionAttribution|null -
external_referencestring|null -
created_atstring(date-time) required
Attribution object
Campaign information, as in utm_source, utm_medium and utm_campaign. Each
value is at most 100 characters of letters, digits, spaces and ._-+:/. Never put
personal data in them.
-
sourcestring|null -
mediumstring|null -
campaignstring|null