Developers
Your centre's data,on your terms.
A REST API for your centre's own website, spreadsheet or systems. Read lessons, rolls, families, invoices and enquiries, and send new enquiries from your own form. You make the keys, choose what each one may do, and revoke one in a click.
The API is switched on centre by centre. Ask us to switch it on for yours, then make a key under Settings, Connections, API keys.
$ curl https://yourcentre.xtutio.com/api/v1/lessons?limit=2 \
-H "Authorization: Bearer xtk_..."{
"data": [
{
"id": "1a2b3c4d-...",
"starts_at": "2026-11-02T06:00:00Z",
"status": "scheduled",
"subject": "maths",
"delivery": "centre",
"campus_id": "3f1c2a9e-..."
}
],
"next_cursor": "eyJpZCI6...",
"has_more": true
}Every answer
- endpoints
- 21
- permissions a key can carry
- 17
- requests a minute, at most
- 600
- webhook events they match
- 8
Keys
You make the keys. You decide what each may do.
Settings, Connections, API keys. Name a key, tick its permissions, and copy it once. Only someone who can manage your centre's connections, signed in with two-step sign-in, can make or revoke one.
Shown once, stored scrambled
A key is shown the moment you make it and never again. We keep only a scrambled copy, so a database copy cannot be turned back into a working key.
Permissions per key
Each key carries only the permissions you tick, from 17: read lessons but not invoices, or send enquiries and nothing else.
Your addresses and websites
Lock a key to your office's internet address, and name the websites allowed to call it from a browser. Anything else is refused.
Revoke in a click
A revoked key stops working on the next request. Every key made or revoked is written to your centre's audit log.
API keys
Make a key- Website enquiry formenquiries:write, campuses:readUsed todayActive
- Finance spreadsheetinvoices:read, families:readUsed yesterdayActive
- Old booking widgetlessons:readRevoked 2 OctRevoked
A new key is shown once. Copy it now: it cannot be shown again.
Your own enquiry form
Send enquiries from your own website.
Keep the form your website already has, and send what a family types straight into your enquiries. It lands with its source and campaign, ready for the front desk.
The same checks as your form
The same fields and limits, your own required questions, your own campuses, and the family's marketing choice respected.
No surprise messages
The API emails and texts nobody. Your admins see the enquiry in the portal, as they do from the form.
Duplicates caught
The same family twice in a day is acknowledged without a second enquiry, and one parent email can send at most 3 an hour.
$ curl -X POST https://yourcentre.xtutio.com/api/v1/enquiries \
-H "Authorization: Bearer xtk_..." \
-H "Content-Type: application/json" \
-d '{
"parent_full_name": "Sam Example",
"parent_email": "sam@example.com",
"students": [{ "full_name": "Leo Example",
"year_level": "Year 4" }],
"consent": true
}'HTTP/1.1 201 Created
{ "data": { "id": "4d5e6f70-...", "received": true } }Privacy
What never leaves, whatever a key may do.
Never leaves
Home addresses and home notes, a child's own contact details, safety records, AI conversations, lesson notes, custom field values and payment ids. No endpoint returns them, whatever a key may do.
Only your centre
The key decides the centre, never the request. Every query names it, so one centre's key can never read another centre's rows.
Contact details on purpose
An enquiry's parent email and phone come back only with their own permission, which no preset ticks. Students come back as names, year and campus only.
Reads, and one write
The only thing the API can write is a new enquiry, checked exactly as your own enrolment form checks it. Lessons, the roll and anything to do with money are changed in the portal, never through the API.
Permissions
17 permissions. Tick only what a key needs.
Each one reads one kind of record, and one sends enquiries. A key that tries anything else is answered 403 scope_missing.
campuses:readCampuses
services:readServices and prices
tutors:readTutors (first name, subjects, campus)
families:readFamilies (name and campus)
students:readStudents (names, year, campus)
lessons:readLessons
invoices:readInvoices
enquiries:readEnquiries
enquiries:writeSend new enquiries
enquiry_contacts:readEnquiry contact details (the parent's name, email and phone they sent)
Contact details: never in a preset.
attendance:readAttendance (the roll for each lesson)
subjects:readSubjects the centre teaches
tags:readTags on families and students
tasks:readStaff tasks (title, status, due date; never the notes)
waitlist:readWaitlist (what each family is waiting for; no contact details)
classes:readGroup classes (times, seats and enrolment counts)
reviews:readTerm progress reviews (only those released to families)
Reference
Every endpoint.
Under /api/v1 on your centre's own address. 7 of them read a feature that is switched on separately, and answer 501 feature_not_enabled until it is. The full description, with every field and example, is the OpenAPI document.
GET
/meThe key making the request: its name, centre and permissions.
any keyGET
/campusesThe centre's campuses.
campuses:readGET
/servicesThe services the centre sells, with their prices in cents (AUD).
services:readGET
/tutorsActive tutors: first name, subjects and campus.
tutors:readGET
/familiesFamilies: name, campus and number of students.
families:readGET
/studentsStudents: first and last name, year level, family and campus.
students:readGET
/lessonsLessons in date order. Defaults to lessons from today; give from and to (ISO dates) to change that.
lessons:readGET
/lessons/{id}One lesson.
lessons:readGET
/invoicesInvoices, newest first, with totals in cents (AUD).
invoices:readPOST
/enquiriesSend an enquiry, as the centre's own enrolment form does (same checks, same limits). Returns the enquiry's id.
enquiries:writeGET
/enquiriesEnquiries, newest first: status, source, campus, the children's first names and years, and answers to the centre's own questions.
enquiries:readGET
/enquiries/{id}One enquiry. The id is the one enquiry.created webhooks carry.
enquiries:readGET
/attendanceThe roll for each lesson, in date order: who was expected, present, absent or late.
attendance:readGET
/subjectsThe subjects the centre teaches.
subjects:readGET
/tagsThe centre's tags.
Available once tags and groups is switched on.
tags:readGET
/tags/{id}/membersThe families and students carrying a tag.
Available once tags and groups is switched on.
tags:readGET
/tasksStaff tasks: title, status, due date and what each is about. Never the notes or comments.
Available once staff tasks is switched on.
tasks:readGET
/waitlistFamilies waiting for a place: the subject, days, times and campus they want. No contact details.
Available once the waitlist is switched on.
waitlist:readGET
/classesGroup classes with their times, capacity, seats left and how many are waiting. Published classes unless status is given.
Available once group classes is switched on.
classes:readGET
/classes/{id}One group class.
Available once group classes is switched on.
classes:readGET
/progress-reviewsTerm progress reviews released to families, exactly as the family reads them.
Available once term progress reviews is switched on.
reviews:read
Limits and pages
Plain limits. Plain errors.
Each key has its own limit, from 10 to 600 requests a minute (60 unless you choose otherwise), and every answer says how many are left. Lists come 25 at a time, up to 100, with a cursor for the next page.
Every error is JSON with a code and a sentence you can show a person. It never carries the database's words.
$ curl https://yourcentre.xtutio.com/api/v1/lessons?limit=2 \
-H "Authorization: Bearer xtk_..."HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
{ "error": { "code": "rate_limited",
"message": "Too many requests. Slow down and try again." } }Errors
The codes you will meet.
| Status | Code | What it says |
|---|---|---|
| 400 | invalid_request | The request is not valid. |
| 401 | unauthorised | Send a valid API key as 'Authorization: Bearer <key>'. |
| 401 | key_revoked | This API key has been revoked. |
| 403 | scope_missing | This key does not have the permission this needs. |
| 403 | address_not_allowed | This key may not be used from this address. |
| 404 | not_found | Not found. |
| 413 | payload_too_large | The request is too large. |
| 429 | rate_limited | Too many requests. Slow down and try again. |
| 501 | feature_not_enabled | This feature is not switched on for this centre yet. |
| 500 | server_error | Something went wrong on our side. Try again shortly. |
With webhooks
Hear about it. Then read it.
Where your centre has outgoing webhooks switched on, each event carries an id. These are the endpoints that read the thing each event is about.
enquiry.createdGET /enquiries/{id}family.createdGET /familiesstudent.createdGET /studentslesson.bookedGET /lessons/{id}lesson.cancelledGET /lessons/{id}lesson.completedGET /attendance?lesson_id={id}invoice.issuedGET /invoicespayment.receivedGET /invoices
In short
Developers
XTutio has a REST API for a centre's own website, spreadsheet or systems: it reads lessons, rolls, families, invoices and enquiries, and its one write is a new enquiry, checked as the enrolment form checks it. A centre makes its own keys, chooses what each may do and can revoke one at any time. It is switched on centre by centre.
Questions centres ask
Can the API change lessons or money?
No. Its only write is a new enquiry. Lessons, the roll and anything to do with money are changed in the portal.
Can one centre's key read another centre's data?
No. The key decides the centre, and every query names it.
Is the API on for every centre?
No. It is switched on centre by centre; then the centre makes a key under Settings, Connections, API keys.
