Video chapters
- Welcome: build a connected staff client
- Prerequisites and starting the project
- Checkpoint 0: runnable shell
- Checkpoint 1: authentication and request helper
- Connect pending, success and error states
- Checkpoint 2: branch-readiness handler
- Run the branch reads
- Independent checkpoints and one connected session
- Arrival and customer identity
- Checkpoint 3: build the walk-in handler
- Run the arrival handler
- Build stand assignment and readback
- Run stand assignment
- Handle an occupied-stand conflict
- Run the conflict and recovery
- Keep client state within one visit
- Checkpoint 4: create a customer-linked work order
- Run work-order creation
- Assign a mechanic and read the saved work order
- Run assignment and start the work
- Append time and materials, then reload totals
- Run job time and materials
- Complete the work and preserve a refusal
- Run completion and the refused append
- One request ID for one tab-opening intent
- Checkpoint 5: build idempotent tab opening
- Run the same opening intent twice
- Select catalogue items and load server totals
- Run the catalogue-priced sale
- Build tender and unpaid-close handling
- Run unpaid close, training cash and paid close
- Render receipt lines and clear the stand
- Run receipt rendering and stand release
- Use the TypeScript, Python, curl and Postman contracts
- Trace results back to implementation
Written guide
This is an implementation course. You will connect a custom staff interface to AppEngine Check-in, Work Orders and POS, then follow the same customer through the saved records. Each chapter changes a specific piece of the client and immediately checks its effect. AppEngine provides the business operations; your client supplies interaction, request context and result handling.
Video ready for review: the 10-minute implementation course follows actual source changes with their AppEngine results. It includes 35 chapters, optional captions and six runnable source checkpoints.
Runnable checkpoints on GitHub · Training branch and catalogue setup · Complete companion and portable clients · Existing endpoint reference and alternatives · Maintained AppEngine API reference
1. What you will build, and what AppEngine already supplies
Northside Bikes needs a staff counter with four views: branch setup, arrivals and stands, repair work, and checkout. A customer enters once. The arrival returns a customer identity; the repair uses that identity; the tab uses it again. The arrival task, work order and sale remain separate records.
AppEngine supplies the queue, stand assignment checks, work-order lifecycle, catalogue pricing, tax, payment records and receipt template. Your code connects those operations and shows the returned state. Completing a repair does not create a sale, job hours do not become payroll hours, and a receipt preview does not prove paper was printed.
Explain visually: animate three distinct saved records around one customer, then show the four actual client views as a brief destination preview. Do not hold the finished interface while introducing unrelated capabilities.
Follow these steps
open the published source. Identify the progressive checkpoints/00-project-shell through checkpoints/05-tab-payment-receipt folders and their actual server.mjs, index.html, app.js, api.mjs, auth.mjs and style.css files. Open operations.json and the portable clients only to establish where the equivalent calls live. We will implement the browser flow first, using actual changes in app.js; a language comparison must explain a meaningful difference rather than repeat every request.
2. Start the client and define the server boundary
Use Appmint Studio to access your organization. Follow API Foundations for the assigned AppEngine origin and organization, and Authentication for staff sessions and credentials. This course continues from those working prerequisites, not from an unexplained token.
The supplied application uses browser JavaScript and fetch; it does not use an Appmint SDK wrapper. The Node server is a local development proxy. Its environment supplies the AppEngine origin; it forwards only allowed application paths, organization context and authorization. It does not contain an embedded privileged identity.
git clone https://github.com/JacLight/appmint-examples.git
cd appmint-examples/appengine-businessmade-apis/checkpoints/00-project-shell
export APPENGINE_API_ORIGIN='https://YOUR_ASSIGNED_APPENGINE_ORIGIN'
PORT=4109 npm startBuild demonstration: start with the static shell, then open the proxy’s /api/ branch. Show how a browser request to /api/checkin/queue becomes a request to the assigned backend’s /checkin/queue. Show the explicit allowlist and the loopback listener. Run the client and confirm the shell loads. Development addresses belong in the downloadable setup instructions; the training capture focuses on the application and assigned public references, not a browser bar showing localhost.
Each checkpoint is an independent runnable source snapshot. Reloading or switching folders clears this sample’s in-memory session and visit; AppEngine records already saved remain. For incremental practice, sign in and repeat the preceding setup inside the chosen folder using distinct practice references and an available stand. For the connected integration check, run checkpoint five once and complete the visit in one browser session. The film identifies this complete client with its checkpoint-five badge. It does not imply that the earlier folder restored its memory.
This is a staff application. Do not place a manager token in a public kiosk or customer bundle. A deployed consumer needs the supported authentication, origin and server boundary appropriate to its audience; the local helper is not a production gateway.

3. Implement sign-in, requests and visible waiting states
The login form submits the organization header and the staff email/password to POST /profile/user/signin. The successful response supplies a token. The existing client keeps {org, token} in browser memory and resets the form. If the server requests another sign-in challenge, complete that supported flow; do not label it a connected session just because an HTTP request returned.
Build demonstration: wire the login form’s submit handler, sign in with a controlled account without showing credentials, and inspect only the connected state. Next implement the shared request helper. Show the method, route and body as separate arguments; authorization belongs in headers, never in the business payload.
// Every business action uses the current staff identity and organization.
export class AppEngineError extends Error {
constructor(status, body) {
super(`${status} · ${body.message || body.error || "Request failed"}`);
this.status = status;
this.body = body;
}
}
export function createApi(getSession, onResult = () => {}) {
return async function request(method, path, body) {
const session = getSession();
if (!session)
throw new Error("Sign in with your authorized staff account first.");
const response = await fetch("/api" + path, {
method,
headers: {
orgid: session.org,
Authorization: "Bearer " + session.token,
"Content-Type": "application/json",
},
...(body === undefined ? {} : { body: JSON.stringify(body) }),
});
const result = await response.json();
onResult({ method, path, status: response.status, result });
if (!response.ok) throw new AppEngineError(response.status, result);
return result;
};
}This is the companion’s JSON-route helper, not a universal decoder for file downloads. The exact published implementation also writes a developer response inspector. Keep credentials out of that inspector.
Wrap form actions in try/catch/finally: disable duplicate submission while pending, show returned errors, render after success, and restore controls when finished. Render permissions from real responses; a visible button grants no authority. Demonstrate one failed request and a corrected request so the learner sees what the error path preserves.

4. Load the branch and render readiness
Prepare one training location, two service points, an active employee and catalogue service products. The companion’s checkpoints/SETUP.md supplies the concrete preparation requests and readback checks; use distinct training identifiers and controlled delivery. The location uses data.name for businessLocationId; service points and employee references use their saved sk. These identifiers are not interchangeable. Service-point records must carry the location value expected by the current Check-in implementation.
Build demonstration: wire chooseLocation to read POS readiness, query catalogue products for the chosen branch, load employees, then call refreshFloor(). Bind the returned arrays to the actual stand cards and catalogue choices. Keep the readiness reasons visible when allowed is false.
async function refreshFloor() {
const location = encodeURIComponent(app.location);
app.points = await request('GET',
'/checkin/service-point?businessLocationId=' + location);
app.queue = await request('GET',
'/checkin/queue?businessLocationId=' + location);
}Show the maintained API reference before using a response shape. Then run the change: an empty shell becomes two real stands and a real readiness result. The learner should see which code produced the new view, not only the finished view.

5. Turn an arrival form into saved customer and queue state
The form collects name, email and visit notes. POST /checkin/walk-in uses businessLocationId, customer, partySize and the optional visit details. AppEngine finds or creates the customer and returns the arrival task. Retain the task’s sk and data.payload.customer; do not use the entered email as a substitute for its returned customer ID.
Build demonstration: add the walkIn handler in the editor, submit the form, read the saved task, then inspect the queue-position request made with its ID. Map the returned fields to the arrival card. Show the request and the card together only long enough to establish the mapping; return to a readable full app view for the result.
app.arrival = await request('POST', '/checkin/walk-in', {
businessLocationId: app.location,
customer: { name: values.name, email: values.email },
partySize: 1,
notes: values.notes
});
app.position = await request('GET',
'/checkin/queue/position/' + app.arrival.sk);
await refreshFloor();Required state behavior: selecting a different arrival must switch its related job/tab state deliberately. A previous customer’s tab or retry ID must not silently carry into the next customer. The progressive companion implements resetVisitState when the staff session, branch or arrival changes. That clears the previous work order, tab and opening request ID. It protects this single-visit teaching client from carrying another customer’s sale into a new visit; a production multi-visit client also needs explicit saved-visit restoration.

6. Assign a stand and recover from a real conflict
The select option value is the service point’s sk, while its label shows the stand name and status. Submit that saved ID to /checkin/:taskId/assign. After success, use the returned assignment, refresh the queue and floor, and read the new queue position.
Build demonstration: add the assignment handler and refresh sequence. Assign Dana to a free stand. In a second controlled arrival, attempt the now-occupied stand. Show the actual conflict response, preserve the current arrival and reload availability. The action wrapper recognizes HTTP409, refreshes the floor, renders the current availability, and then restores the original error and failed-request trace. It never retries the failed write automatically. The learner sees the remaining valid choice before making another assignment.
Assignment can invoke a configured ready notification. Training contacts must use controlled delivery. The example does not establish an external message was received.

7. Build the linked work-order screen
Create the work order with customerId: app.arrival.data.payload.customer.id, a unique order number, title and initial work items. Save the returned sk. The mechanic selection carries an employee sk; it is not the staff login token or employee badge code.
Build demonstration: implement creation, then assignment and status: 'in_progress'. Add the shared readback helper and use it after each mutation. The current detail route returns the work order within data[0]; inspect that actual contract instead of assuming every route returns one top-level record.
async function workRead() {
const result = await request('GET',
'/business-made/work-orders/' + app.work.sk);
app.work = result.data[0];
}Wire hours to /time-entries using the selected employee ID, date, numeric hours and description. Wire materials to /materials using material identity, quantity and cost. Add a note to the saved work-order ID. Read the server-calculated hours and material totals, then bind those results to the display. In the recorded continuation, the saved job has 1.25 hours and $10 material cost. The chain’s $18 catalogue sale price is a separate value used later at checkout.
Complete the job with the status action and read completedAt. Attempt an additional time entry and show the refusal. Explain the supported reopen/status alternative before appending any legitimate correction. Do not automatically retry time/material appends after an uncertain timeout: they do not share the tab-opening retry contract.
Job time and material cost are internal records. They do not consume stock, create payroll time or determine the customer’s sale price by themselves. Link Workforce and the inventory/commerce lesson for those separate sequences.


8. Open one tab and preserve its retry identity
Opening a POS tab is a business operation. Supply the saved branch name, stand ID, returned customer and a stable requestId. Generate that request ID once for this opening intent; reuse it on retry. A second click must not create a different request ID. A different customer needs a different opening intent.
Build demonstration: create the per-arrival opening state, implement the call, save the returned order, then retry the same request. Display or compare the two actual returned IDs. This is where retry protection belongs; a decorative “idempotent” label is not evidence.
app.requestId ||= crypto.randomUUID();
app.tab = await request('POST', '/storefront/pos/tab', {
businessLocationId: app.location,
servicePointId: app.standId,
customer: app.arrival.data.payload.customer,
requestId: app.requestId
});Checkpoint five retains requestId while retrying the same opening intent and clears it when resetVisitState changes the visit. The same identifier survives a retry within this session. It is held in memory, so this checkpoint does not claim recovery after a page reload; production clients should restore the saved order or opening identity for that visit before retrying. A network timeout is not a reason to create a new sale.

9. Send catalogue choices and render server totals
The item-selection form sends SKU and quantity. The POS applies catalogue pricing and the location’s tax. A browser total is a display, not the authority for payment.
Build demonstration: turn selected checkboxes into {sku, quantity} entries, post them to /storefront/order/:id/items, then implement breakdown(). Bind subtotal, tax, paid and balance due to the returned breakdown. Change the selected lines and show the server result change.
async function breakdown() {
app.breakdown = await request('GET',
'/storefront/pos/tab/' + app.tab.sk + '/breakdown');
app.tab = app.breakdown.order;
}The detail contains both the current order and the calculated breakdown. Preserve the order returned by this read, not an older status from the opening response. Service charges and work-order material costs remain separate; do not copy a cost into a price without a deliberate catalogue decision.

10. Record tender, close, render receipt and clear the stand
Build demonstration: first wire close and show that the server refuses a tab with money due. Then implement cash recording: fetch the current breakdown and submit that balance with the manual-cash method. Preserve a server refusal if the amount is no longer payable; the portable TypeScript helper also refuses locally when nothing is due. Refresh the order and payment totals. Manual cash records an internal training tender; it is not a card gateway simulation or proof of external funds.
After the permitted tender is recorded, call close, read the resulting saved order and inspect the stand state. In the verified deployment, the stand still requires an explicit clear action. Keep clearing separate and show its final available state.
Fetch /receipt-payload and render its real primitives: escape text, preserve spacing, respect bold/feed entries, and handle unsupported primitives explicitly. Show the small renderer being added before showing the finished receipt. Printer dispatch and physical output require the Events and Devices course; the browser preview does not claim paper was printed.
For card payments, split tender, void/refund and kitchen/device dispatch, use the exact maintained contracts and linked Finance, Headless Storefront and device lessons. These are reference alternatives, not demonstrations completed in this sequence.


11. Carry the implementation to another client
The JavaScript/Python runners and Postman collection execute one named operation from operations.json. The TypeScript client provides typed request and sequence helpers. For the portable exercise, call readiness from the Python runner using environment variables and compare its returned allowed/reasons with the browser’s view model. The video shows the Python request implementation and typed recordCash() helper to show that it reads the current balance before composing the mutation. Do not replay every API in every language.
The same backend operations can support another web framework or a mobile client; this package is plain browser JavaScript, not a claimed React or Flutter implementation. Framework-specific session storage, navigation and native printing belong in their dedicated integration lessons.

12. Finish with a reproducible result
A learner has completed this course when they can run the source with their own authorized training organization; check readiness; submit and assign an arrival; create and complete the linked work order; open one retry-safe tab; add catalogue lines; handle a refused close; record a permitted tender; read the receipt; and clear the stand. They should also be able to point to the code that performs each step and the saved ID it carries forward.
The connected checkpoint-five walkthrough has been recorded and freshly read back from AppEngine: the customer is shared by the arrival, work order and tab; the work is completed with 1.25 hours and a 10-dollar internal material cost; the sale totals 84.36 dollars with zero balance; and the stand is available again. The returned receipt contains 21 rendering entries. These are controlled training outcomes, with no card charge or physical printing claim. The downloadable checkpoints are published at commit 96b824b13b9aaa48c34df85071bfde5ab26b10f0.