Video chapters
- Welcome to API Reference
- Find the built-in business operation
- Open the reference for your deployment
- Search and select the right caller route
- Read access, headers and JSON requirements
- Search and inspect contracts from a tool
- Obtain definition and service identifiers
- Request available times and read their timezone
- Create the booking and verify the saved result
- Choose by actor, identifiers and deployment
- Markdown, OpenAPI, Postman and generators
- Interpret responses and recover lookup errors
- Continue your AppEngine implementation
Written guide
Companion source code
Get the course source code on GitHub. The folder includes the supplied examples and setup instructions. Keep credentials in your own environment; use the maintained API reference when adapting recorded request examples.
Video ready for review — 7:17, 13 chapters. Watch the API Reference course. Includes the hosted reference, actual discovery requests, available times, an authenticated customer booking and saved readback. Postman/generator import is explained from the OpenAPI contract; no external notification delivery is claimed.
Why this course
AppEngine has about three thousand operations. When you sit down to build a feature you need four or five of them, with their exact bodies and answers — and you need to pick the one that runs the business rules, not a lookalike that just writes a record. Every AppEngine server publishes its own reference for this: a page you can browse, a search you can call, and the full OpenAPI file. You don't guess URLs and you don't read source code.
In this course you discover and test the API calls behind a booking page for Juniper Yoga, the organization from Foundations. Mira, the customer you signed in there, should be able to see the classes, pick an open time, book it, and see her bookings. You know what you want; this course shows you how to find which routes do it, read their contracts, and call them.
For discovery, you only need the API's public address: https://appengine.appmint.io. The search and reference calls need no account. For the booking exercise, obtain API, ORG, a completed customer session (TOKEN) and that customer's actual name/email from Foundations, organization setup and customer sign-in. Keep all values on the same deployment.
You also need an active bookable reservation_definition in that organization. Follow Reservations: Create a booking definition and configure hosts, locations and resources. Obtain its saved ID, an exact service name, duration, working days, hours, timezone and capacity. Use an organization you may test and a future date with availability. This reference lesson does not configure the whole reservations feature. An empty definition list means setup is still required; it is not permission to invent an ID.
The hosted reference and the controlled training deployment can run different versions. Discover on the same server you will call. The examples below teach the request shape; replace illustrative IDs, dates and contact details with your own returned values. A successful training call is not a claim that the hosted service has the same release.
1. What is already built for bookings
Before searching, know what you're looking for. AppEngine already has the bookings feature; you are wiring a page to it.
Built-in data types
| Data type | What it is | A few of its real fields (data.…) |
|---|---|---|
reservation_definition | A bookable thing — "Yoga classes" — with its services, hours and rules | name, title, status, services[] (name, duration, price), workDays, officeHours.timezone, spots, hosts |
reservation | One booking | name (the 8-character reservation number), status (new, confirmed, rescheduled, cancelled, used, no_show …), reservationDefinitionId, service, startTime, endTime, timezone, customer (name, email, phone) |
customer | The person booking | email, firstName, lastName, phone |
Built-in actions
| You call | AppEngine does for you |
|---|---|
GET crm/reservations/definitions | lists the business's active booking types with their services |
POST crm/reservations/slots | works out the open times for one service on one day, in the business's timezone, leaving out closed days, blocked time and full slots |
POST client-data/reservations | with a customer session, validates the booking, associates the customer, applies configured host rules, gives it a number and saves it; notification and reminder work runs separately |
GET client-data/reservations | lists the signed-in customer's own bookings |
POST crm/reservations/reschedule/{id} | moves a booking after availability and actor checks; customer ownership and staff permissions differ |
DELETE crm/reservations/cancel/{email}/{reservationNumber} | matches a booking by contact and number, preserves the cancelled record and requests reminder cancellation and notices; authentication is not an ownership guarantee |
The rule. Use these actions. A reservation is also an ordinary record, so the generic record route PUT repository/create will happily save one — and skip everything in the right-hand column: no booking-service availability check, generated reservation number or booking notification/reminder work. Use the built-in reservation type for bookings and add fields to it if you need more. Use generic Repository routes for supported record operations where no dedicated business action fits; the Data Records course covers those.
The rest of this course is how you would have found that table yourself.
2. What the server publishes about itself
Every AppEngine server serves its reference from the same address as the API. None of these need an orgid or a token.
| Address | Use it for |
|---|---|
/documentation | The reference page in your browser — grouped, searchable |
/documentation/search?q=… | Search by words; returns matching operations as JSON |
/documentation/endpoint?method=…&path=… | One operation in full: parameters, body, answers, description |
/documentation/tag/<section>.md | One section (for example all of CRM reservations) as Markdown |
/documentation.md | Every operation, one line each |
/llms.txt | A short map of the above, for AI tools |
/openapi.json (also /documentation-json) | The full OpenAPI document, for code generators and Postman |
/api-docs, /docs and /swagger redirect to /documentation.
Two terms you'll use in this section:
- Operation — one method plus one path, such as
POST /crm/reservations/slots. The same path with a different method is a different operation. - Path template — a path with placeholders in braces, such as
/crm/reservations/reschedule/{id}. The reference always shows templates; you fill in real values only when you call the operation.
3. Search for what you want to do
Mira's first screen shows open times. Search for that:
curl -sS --get "$API/documentation/search" \
--data-urlencode 'q=reservations slots' --data-urlencode 'limit=5'The captured response below is abbreviated; count describes all five returned hits.
{
"query": "reservations slots",
"count": 5,
"results": [
{
"method": "POST",
"path": "/crm/reservations/slots",
"summary": "Generate appointment slots",
"tag": "CRM · Reservations",
"group": "crm-reservations",
"detail": "https://appengine.appmint.io/documentation/endpoint?method=POST&path=%2Fcrm%2Freservations%2Fslots",
"section": "https://appengine.appmint.io/documentation/tag/crm-reservations.md"
},
{
"method": "POST",
"path": "/client-data/reservations/available-slots",
"summary": "Find available slots",
"tag": "Client Account",
"group": "client-account",
"…": "…"
}
]
}Each hit gives you the method, the path template, a one-line summary, and two ready-made links: detail for this operation, section for everything near it. Follow those links rather than building URLs by hand. The first two hits answer the same question — the second is the customer-portal copy of the same slot engine — but their access differs: CRM slots is public with orgid; the client-data route requires a bearer session. This course uses CRM slots for availability and the authenticated client-data route to create the booking. The search API scores words in the path, summary and section name. The browser search currently uses substring matching: type reservations there, then select the slots operation; a spaced phrase such as reservations slots can return no browser hits. For the search API, short nouns work well: reservations, reschedule, refund, checkin, lead.
The included inspect-api.mjs script does the same search and prints the first hit's full contract. It needs Node 20 or newer and no credentials:
node inspect-api.mjs "$API" reservations slots4. Read the contract
Open the detail for the operation you picked:
curl -sS --get "$API/documentation/endpoint" \
--data-urlencode 'method=POST' \
--data-urlencode 'path=/crm/reservations/slots'You get Markdown. Add --data-urlencode 'format=json' for the same thing as JSON. Read it for six things, and write them down for each operation your page uses:
| Question | Where you find it |
|---|---|
| Who may call it? | Public, signed-in customer, staff with a permission, or an API key |
| Which organization? | The orgid header — almost every business operation needs it |
| What goes in? | Path parameters, query, JSON body and which fields are required |
| What comes back? | One record, a page of records (total, data[]), or a plain object |
| What does it change? | Nothing, a saved record, an email or a queued job |
| How does it fail? | Status codes, plus the message, code or reason in the body |
Some operations carry important details in the description. Reschedule explains its startTime body, optional service/reason, actor-dependent override, and 409 with reason: "slot_unavailable" and problems[]. Read those rules along with the schema. A bearer requirement alone does not tell you which customer owns the record or which staff roles may act.
The hosted customer contract also lists x-client-info. Supply the client metadata it requests; the demonstrated client label is appengine-api-reference-course. This header does not replace the bearer token or grant permissions.
For the slots operation, send reservationDefinitionId (which booking type), serviceName (an exact service in it) and serviceDate (YYYY-MM-DD in the business timezone). The first and third are required; an omitted service name selects the first service. The hosted reference currently has an example that omits its own required definition/date fields. Use the required schema and the corrected body below; an example is not a substitute for required-field checks. If the schema and observed response disagree, keep the actual error and request and ask the deployment owner for the supported contract before building on it.
5. Make the calls
Get the booking types first, to find the IDs:
curl -sS "$API/crm/reservations/definitions" -H "orgid: $ORG"On the current training implementation the answer is a page whose data array holds the active reservation_definition records. Take the sk of "Yoga classes" as DEF and a service name from its data.services, such as Morning flow. The hosted schema currently describes an array here, while the training implementation returns a page: inspect your actual response rather than silently assuming either envelope.
Ask for open times:
curl -sS -X POST "$API/crm/reservations/slots" \
-H "orgid: $ORG" -H 'Content-Type: application/json' \
--data "{\"reservationDefinitionId\":\"$DEF\",\"serviceName\":\"Morning flow\",\"serviceDate\":\"2026-10-20\"}"{
"reservationDefinition": { "sk": "66f2…", "data": { "name": "yoga-classes", "…": "…" } },
"service": { "name": "Morning flow", "duration": 60 },
"slots": [
{ "startTime": "2026-10-20T14:00:00.000Z", "endTime": "2026-10-20T15:00:00.000Z", "spotsAvailable": 8, "businessTimezone": "America/Los_Angeles" },
{ "startTime": "2026-10-20T16:00:00.000Z", "endTime": "2026-10-20T17:00:00.000Z", "spotsAvailable": 3, "businessTimezone": "America/Los_Angeles" }
]
}Times are UTC; show them in businessTimezone. On a day the studio is closed you get slots: [], closed: true and an error sentence you can show as is.
Select an available startTime from that response, without editing the UTC offset. For Mira's signed-in page, look up POST /client-data/reservations, then send the customer bearer and same organization. Use her actual profile email/name; a token should not be paired with someone else's contact details. The following body is illustrative:
curl -sS -X POST "$API/client-data/reservations" \
-H "orgid: $ORG" -H "Authorization: Bearer $TOKEN" \
-H 'x-client-info: appengine-api-reference-course' \
-H 'Content-Type: application/json' \
--data "{\"reservationDefinitionId\":\"$DEF\",\"service\":\"Morning flow\",\"startTime\":\"2026-10-20T14:00:00.000Z\",\"customer\":{\"name\":\"Mira Chen\",\"email\":\"[email protected]\"}}"{
"sk": "66f3…",
"datatype": "reservation",
"data": {
"name": "Q7K2M9XA",
"status": "new",
"reservationDefinitionId": "66f2…",
"service": "Morning flow",
"startTime": "2026-10-20T14:00:00.000Z",
"endTime": "2026-10-20T15:00:00.000Z",
"timezone": "America/Los_Angeles",
"customer": { "id": "66f1…", "name": "Mira Chen", "email": "[email protected]" }
},
"notices": [
{ "audience": "customer", "channel": "email", "to": ["[email protected]"], "templates": ["…"], "status": "queued" }
]
}data.name is the reservation number to show Mira. endTime was filled in from the service's duration. notices reports requested background notification work. status: "queued" does not establish provider acceptance, delivery or receipt. Check the configured provider and the message result separately before claiming a confirmation arrived. Reminder work is also asynchronous. Judge the exercise by the saved booking and read-back; external notification delivery needs separate evidence.
The current source also defines public POST /crm/reservations/book, requiring contact name and email or phone, but that route is absent from the captured hosted OpenAPI. It is not interchangeable with the authenticated route just because both concern bookings. Do not copy a source-only route onto another deployment; use the operation your server publishes and validates. The hosted client-data creation example also uses older field names; the body above follows the current training implementation, so verify it against your deployment before use.
6. Check the result
Mira's "My bookings" screen:
curl -sS "$API/client-data/reservations" \
-H "orgid: $ORG" -H "Authorization: Bearer $TOKEN"The answer is a page — total, page, pageSize, hasNext and a data array — and you should locate the returned booking ID in it. Then use GET client-data/reservations/{reservationId} with that sk and the same customer session. Compare its customer ID/email, service and start time with the create result. The current source checks customer ownership on this detail route, returning 404 for missing or inaccessible records. A positive same-customer read alone does not prove cross-customer isolation; that separate check belongs in Authentication.
7. Handle the next case
The time was taken while she was choosing. The booking service re-checks, and answers 400 with code: "SLOT_TAKEN" and a message that names the service and time in the business's timezone. Show the message and ask for slots again.
She wants to move it. Search reschedule and inspect method, path and access. The hosted reference exposes POST /crm/reservations/reschedule/{id}. Its contract permits a signed-in customer to move their own booking, while staff access depends on permissions; override is a staff option. Send the returned booking sk, the same customer session and a new available startTime. A refused new time returns 409 with reason: "slot_unavailable" and problems[]. Re-read the saved record after a successful move; do not infer success from the request alone.
The current source additionally has POST /crm/reservations/reschedule/{email}/{reservationNumber}. It is public and relies on the contact-plus-number lookup, rather than a customer token's ownership. It is absent from the captured hosted reference. This is a deployment difference to resolve, not a second interchangeable endpoint to guess.
She cancels. Inspect DELETE /crm/reservations/cancel/{email}/{reservationNumber}. The current route requires authentication but its service matches by contact and reservation number; it does not receive the signed-in actor for an ownership check. Do not expose it as proof that a customer token can cancel only its owner's records. A customer application needs a verified ownership boundary before allowing this action. When authorized cancellation succeeds, check the retained record's status: "cancelled"; notification/reminder work remains separately verifiable. Cancellation implementation is a follow-on reservations task, not required to finish this reference exercise.
A route returns a success status but something is off. Some answers carry a condition in the body: sign-in may require verification, or a slots response may have closed: true. A POST can return 201 even where an older annotation says 200. Check the actual status and body, rather than accepting every success response as a completed business outcome.
8. Keep your integration in step with the server
- Generate a client. Point an OpenAPI generator or Postman at
$API/openapi.json. Identify operations by method plus path; a fewoperationIds repeat across modules. - After every AppEngine upgrade, search your operations again and compare method, path, body and answer with your notes, then re-run one real call each.
- When the reference is not ready, document/index routes can return
503"API documentation is not ready yet". Search may instead return no hits and endpoint detail may return 404 before its index exists. Check/openapi.jsonor/documentation/index.json, then retry when the reference is ready; do not conclude an operation was removed from one empty search. - When an endpoint lookup fails,
400means method or path is missing, and404"No documented endpoint for …" means the template doesn't match — copy it from a search hit. - When a helper, example and contract disagree, compare the deployed reference, required schema and an authorized request/response. Record the deployment and ask its owner for clarification if they conflict. Use your client's generic HTTP method only after confirming the supported contract; a generated example can be stale too.
9. Where to go next
- Authentication — run this page behind your own server, with an app token and Mira's token, and prove another customer can't read her bookings.
- CRM APIs — the staff side: booking types, hosts, leads and pipelines.
- Customer Portal — the rest of Mira's account: orders, tickets, messages.
- Data Records — your own collections, through the generic Repository routes, when nothing built-in fits.