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. 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. 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. 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. 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. 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%20femme

2. 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/v1

Authentication

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