Developers
Integrate Daytio booking into a website
Building or managing the website of a professional who uses Daytio? Three ways to add their booking, from the simplest to the most complete.
Three ways to integrate
Step by step
- 1
Open the professional's settings
Sign in to the professional's Daytio account (they can share access, or go through these steps with you), then open Settings → Website. Everything needed for all three integrations is there.
- 2
Pick the integration
A simple button? Copy the link (section 1). Booking inside the page? Copy the widget code (section 2). A custom flow? Go to step 3.
- 3
Create an API key
Section 3 → “Create an API key”. Name it and enter the website address (https://…). The key (dtk_live_…) is shown only once: copy it straight away. If the professional is subscribed, this first key adds the API add-on to their subscription.
- 4
Check the key works
A first call should return the business name:
curl https://daytio.com/api/v1/business \ -H "Authorization: Bearer dtk_live_…" - 5
Build the flow
List the services, show free slots for the chosen one, then book. If the service requires online payment, redirect the client to the returned Stripe link: they come back to your website afterwards.
1. The booking link
Every professional has a permanent address. Add ?prestation=<service name> to preselect a service.
https://daytio.com/votre-salon
https://daytio.com/votre-salon?prestation=Coupe%20femme2. The widget
Paste these two lines where you want it. The script creates the frame and sizes it automatically — don't hand-write an <iframe>. Available blocks: reservation, horaires (opening hours), plus carte and commande for restaurants.
<div data-daytio="reservation" data-slug="votre-salon"></div>
<script src="https://daytio.com/embed.js" async></script>Framed content isn't indexed with the page that contains it: the widget drives bookings, not search rankings.
3. The API
Add-on at €25/month, activated by the first API key
Base URL
https://daytio.com/api/v1Authentication
Every request carries the key in the Authorization header (or X-Daytio-Key). The key identifies the business: no other identifier needed.
Authorization: Bearer dtk_live_…
# ou / or
X-Daytio-Key: dtk_live_…Browser or server calls
From a browser, the Origin header must match a website declared on the key, otherwise the request is refused (origin_not_allowed). A key in JavaScript is readable by every visitor: this list is what stops another site from using it. If you can, call the API from your server and keep the key server-side.
Limits
300 reads per minute and 120 bookings per hour, per key. From a browser, the Daytio page's per-device limit also applies to bookings. Beyond that: 429 rate_limited.
Response format
JSON. Success: { "data": … }. Error: { "error": { "code", "message" } }. Test the code, which is stable; the message (in French) may change.
Times and time zone
Instants are ISO 8601 (UTC). Show the client the time field, the professional's local time: it is the one that counts, as the client travels to them.
Endpoints
GET /businessThe business: name, city, time zone, currency, booking rules, online payment.GET /servicesBookable services, in the professional's order.GET /staffTeam members (Team plan). Empty list for a solo professional.GET /availabilityFree slots, 1 to 7 days. Parameters: services (comma-separated ids), from (YYYY-MM-DD), days, staff, anyStaff=1.POST /bookingsBooks a slot. The slot is re-checked at booking time.GET /payments/{sessionId}Status of a booking paid online.
GET /business
{
"data": {
"name": "Atelier Coiffure",
"slug": "atelier-coiffure",
"city": "Quimper",
"timezone": "Europe/Paris",
"currency": "EUR",
"bookingUrl": "https://daytio.com/atelier-coiffure",
"bookingOpen": true,
…
}
}GET /availability?services=3f0c…&from=2026-10-12&days=7
{
"data": {
"timezone": "Europe/Paris",
"durationMinutes": 45,
"groupClass": false,
"days": [
{
"date": "2026-10-12",
"slots": [
{ "startsAt": "2026-10-12T07:00:00.000Z", "time": "09:00" },
{ "startsAt": "2026-10-12T07:45:00.000Z", "time": "09:45" }
]
}
]
}
}Booking
The client's consent (consent: true) is mandatory: show an UNCHECKED box “I agree that my data is used to manage my appointment”. marketingConsent is a second, optional and separate box.
POST /bookings
{
"services": ["3f0c…"],
"startsAt": "2026-10-12T07:00:00.000Z",
"client": {
"firstName": "Camille",
"lastName": "Martin",
"email": "camille@example.com",
"phone": "0612345678"
},
"consent": true,
"returnUrl": "https://www.mon-salon.fr/reservation/merci"
}- Appointment created (201). The client and the professional get their confirmation email.
- The service requires online payment (200): redirect the client to checkoutUrl. Nothing is booked until they pay.
- Always handle refusal: a displayed slot may be taken in the meantime (409 booking_failed). Offer to pick another one.
// 201 — rendez-vous créé / appointment created
{ "data": { "status": "confirmed", "startsAt": "…", "endsAt": "…",
"partySize": 1, "manageUrl": "https://daytio.com/rdv/…" } }
// 200 — paiement en ligne exigé / online payment required
{ "data": { "status": "payment_required",
"checkoutUrl": "https://checkout.stripe.com/…" } }
// 409
{ "error": { "code": "booking_failed", "message": "…" } }Online payment
Pass returnUrl (and cancelUrl if needed), on a website declared on the key. After paying, the client returns to returnUrl with ?daytio_session=…; poll /payments/{id} until confirmed (a few seconds). Without returnUrl, the client ends on Daytio's confirmation page.
Statuses: confirmed (paid, booked), processing (paid, confirmation in progress), awaiting_payment, expired (abandoned), failed (slot taken meanwhile: refunded).
GET /payments/cs_live_…
{ "data": { "status": "confirmed", "startsAt": "…",
"amountPaid": 30, "currency": "EUR" } }Error codes
missing_api_keyNo key in the request.invalid_api_keyUnknown or revoked key.origin_not_allowedBrowser call from a website not declared on the key.rate_limitedToo many requests. Try again later.account_unavailableThe professional's account is deactivated.invalid_requestMissing or invalid parameter (the message says which).not_foundResource not found.booking_failedBooking refused (slot taken, notice period, missing phone…).return_url_not_allowedreturnUrl / cancelUrl outside the declared websites.unsupported_accountRestaurant account: use the widget or the link.
Full example (JavaScript)
const API = "https://daytio.com/api/v1";
const KEY = "dtk_live_…"; // de préférence côté serveur / server-side if possible
async function daytio(path, init = {}) {
const res = await fetch(API + path, {
...init,
headers: {
Authorization: "Bearer " + KEY,
"Content-Type": "application/json",
...init.headers,
},
});
const json = await res.json();
if (json.error) throw Object.assign(new Error(json.error.message), json.error);
return json.data;
}
// 1. Prestations / services
const services = await daytio("/services");
// 2. Créneaux libres sur 7 jours / free slots over 7 days
const today = new Date().toISOString().slice(0, 10);
const { days } = await daytio(
"/availability?services=" + services[0].id + "&from=" + today + "&days=7",
);
// 3. Réserver le premier créneau / book the first slot
const slot = days.find((d) => d.slots.length)?.slots[0];
try {
const booking = await daytio("/bookings", {
method: "POST",
body: JSON.stringify({
services: [services[0].id],
startsAt: slot.startsAt,
client: { firstName: "Camille", lastName: "Martin", email: "camille@example.com" },
consent: true, // case cochée par le client / box ticked by the client
returnUrl: location.origin + "/reservation/merci",
}),
});
if (booking.status === "payment_required") {
location.href = booking.checkoutUrl; // Stripe, puis retour / then back
} else {
showConfirmation(booking); // booking.startsAt, booking.manageUrl
}
} catch (e) {
if (e.code === "booking_failed") askForAnotherSlot(e.message);
else throw e;
}
// 4. Page /reservation/merci : état du paiement / payment status
const session = new URLSearchParams(location.search).get("daytio_session");
if (session) {
const payment = await daytio("/payments/" + encodeURIComponent(session));
// payment.status : confirmed | processing | awaiting_payment | expired | failed
}Restaurant accounts: the API covers appointments and classes. For tables and takeaway, use the widget or the link.
Questions?
Write to us, with the name of the business concerned. daytio.com/en/contact