Skip to content

Brief for an AI assistant

Building a studio’s site with an AI assistant works well — if the assistant knows exactly what Mr Charles is, what it must not reinvent, and where the three values it needs come from. Copy the block below into the conversation before the first prompt about the site, and fill in the four placeholders.

The same text is served as plain text at /llms.txt for tools that read it automatically.

# Mr Charles integration brief (for the assistant building this site)
You are building a website for a music studio / teacher whose bookings run on
Mr Charles. Do NOT build a booking system, calendar, OTP verification, or
payment flow yourself: all of that exists behind a public API and a JavaScript
SDK, and the site only has to call it. Prices, availability, conflicts, codes
and card payments are decided by the server; the site never computes them.
## Values provided by the site owner (never hardcode, never guess)
- STUDIO_SLUG = <slug> # the studio's slug on Mr Charles
- TEACHER_USER_ID = <uuid or "none"> # the teacher's user id, if lessons are offered
- MC_ENDPOINTS: apiBase=<url> appBase=<url> sitesBase=<host>
Put these in environment variables (e.g. PUBLIC_MC_API_BASE,
PUBLIC_MC_APP_BASE, PUBLIC_MC_SITES_BASE, PUBLIC_MC_STUDIO_SLUG,
PUBLIC_MC_TEACHER_ID) and read them at build time. Do not commit them.
If an endpoint value is missing, the SDK defaults to production.
## The SDK
Browser bundle (no build step):
<script src="https://developers.mrcharles.app/sdk/v0/mrcharles.min.js"></script>
const mc = MrCharles.createClient({ apiBase, appBase, sitesBase });
ES module / npm:
import { createClient } from '@mrcharles/sdk'; // or /sdk/v0/mrcharles.esm.js
Zero dependencies, TypeScript types included. Reference:
https://developers.mrcharles.app/sdk/reference
Languages: createClient({ locale }) with the page's language ('es', 'ca',
'en', 'fr', 'pt', 'it', 'de' or any BCP-47 tag). It sets the hosted screen's
language, the guest's SMS/email, and gives you mc.t(), mc.format.* and
mc.pick() for your own labels, prices, dates and payload texts. For a
multilingual site call mc.setLocale() from the language switcher. Do not
hardcode lang: 'es' — pass the current page language.
## What the site must do
1. Rooms page — read the studio payload at build time or on request:
const site = await mc.sites.get(STUDIO_SLUG);
site.studio (name, city, timezone), site.rooms[] (id, name, hourlyCents,
currency, bookable, minMinutes, maxMinutes, stepMinutes, paymentModes,
depositPercent, cancelNoticeHours), site.site.sandbox (boolean).
Render prices from hourlyCents/currency. Only rooms with bookable=true
get a booking button.
2. Booking — the simplest correct integration is the embedded screen:
mc.mountRoomBooking('#book', { slug: STUDIO_SLUG, roomId,
onBooked: ({ reference, manageUrl }) => { /* thank-you + analytics */ } });
(language comes from createClient({ locale }); pass lang only to override)
It is an iframe of the hosted booking page; it handles availability, the
12-minute hold, the one-time code (SMS/email), card deposits (Stripe) or
pay-at-studio, legal texts and the manage link. It resizes itself.
Alternative: a plain link to mc.links.bookRoom({ slug, roomId }).
Only build a custom UI (mc.roomBooking() headless flow) if the owner asks
for it explicitly; see https://developers.mrcharles.app/integrate/own-frontend
3. Lessons (only if TEACHER_USER_ID is set):
const profile = await mc.teachers.profile(TEACHER_USER_ID);
const offers = await mc.teachers.offers(TEACHER_USER_ID); // published only
const slots = await mc.teachers.slots({ instructorUserId: TEACHER_USER_ID,
offerId, date: 'YYYY-MM-DD', days: 7 });
Show offers (title, instrument, durationMinutes, priceCents, currency,
mode) and real bookable times; the booking itself happens in the app:
mc.links.bookLesson({ instructorId: TEACHER_USER_ID, offerId })
Students need an account there — do not try to book a lesson from the site.
4. Manage link — show/email the manageUrl you receive on booking; it is the
guest's way to see or cancel. Never implement cancellations yourself.
## Rules
- No API key exists; the slug is the only identifier. Public endpoints are
rate-limited; writes on live sites require a Turnstile captcha token — the
embedded screen handles it, a custom UI must include the widget.
- Never cache availability or slots; fetch when the user opens the picker.
The site payload may be cached for 60 s.
- All times are in the studio's/teacher's time zone (site.studio.timezone).
- Errors come as { success:false, error:{ code, message } }; the SDK throws
MrCharlesError with .code, .slotIsGone, .holdIsDead, .isTransient.
- Do not store guest phone numbers or emails on the site.
- Keep the site static (Astro is ideal). The SDK runs in the browser; the
payload reads can also run at build time in Node (pass fetch if needed).
## Develop against the sandbox, then switch the slug
While building, use slug "demo-studio" (always in sandbox mode: verification
codes come back in the response as devCode, nothing is sent or charged,
bookings are purged after 7 days). Switching to STUDIO_SLUG is the whole
go-live. Before that, go through
https://developers.mrcharles.app/getting-started/go-live
## Suggested structure (Astro)
src/lib/mrcharles.ts createClient() from env; getSite(); getTeacher()
src/pages/index.astro hero, rooms grid (prices from the payload), CTA
src/pages/salas/[id].astro one room: details + <div id="book"> + mount script
src/pages/clases.astro teacher offers + next free slots + "Book in the app"
src/components/Book.astro the script tag + mountRoomBooking call
## Acceptance
- Rooms and prices on the site match https://<slug>.<sitesBase>/ exactly.
- A booking made on the site appears in the studio's Mr Charles inbox.
- The sandbox banner/devCode never appears once STUDIO_SLUG is live.
- Lighthouse: no layout shift from the iframe (it reports its height).
Full docs: https://developers.mrcharles.app — API: /reference/api
ValueWhere the owner finds it
STUDIO_SLUGDashboard → Studio → Web del estudio; also the hosted URL slug.sites.mrcharles.app
TEACHER_USER_IDThe teacher’s public profile URL in the app: app.mrcharles.app/instructors/<id>
MC_ENDPOINTSOnly if you were given a non-production environment. Otherwise omit — the SDK defaults to production.
Room idsNot needed up front: the site reads them from mc.sites.get(slug)