How this phone reaches your Google Sheet, and what proves you are the owner. The Owner key is the one that everything hangs on — get it wrong and the money screens stay empty, every money action is refused, and your captains' PINs never reach the server, so they cannot sign in.
This is the address THIS phone uses to reach your Google Sheet: Reports, Log, expenses, day-close, backups, confirming an InstaPay payment and settling a captain's cash all go through it, and if it is wrong those screens on this phone stay empty or refuse to save. It does not travel to anyone else — captains' and residents' apps each carry their own copy, so what you type here moves only this phone and cannot stop a captain signing in. Moving the whole business onto a new Sheet takes a new version of the app, not this box: change the address here and this phone will be reading a new, empty Sheet while every booking is still being written to the old one — so keep the address it came with unless you know exactly why you are changing it.
Residents' one-time phone-verification code is checked against this address, which is a different Apps Script deployment from the Bookings webhook. Leave it blank to keep the address already in use — only change it if you re-deploy the bot's Apps Script, because a wrong address means nobody can verify a phone number and the app just says "no network". A new address travels to residents' phones over the live sync channel below, so that has to be set for the change to reach them.
This name is how the admin, driver and resident apps talk to each other live — new rides, cart positions, and every price or hours change you save. Anyone who knows the name can read it, so keep it secret, and if you change it, change it on every device at the same moment or those devices stop seeing each other.
This one secret locks your revenue and driver-cash figures to your device and authorises every money action: confirming InstaPay, settling captain cash, expenses, day-close and backups. It also carries your captains' sign-in PINs. If it does not match the REPORT_KEY on the server — including leaving it blank when the server has one — every one of those is refused, and the prices you save here stop reaching the WhatsApp bot. Press Check key after any change.
🧑✈️ Captains & shifts
Who drives, who takes the resident's call, and who may sign in to the driver app. The list below is the master record — the shift boxes underneath are filled from it.
🧑✈️ Drivers (captains) — one row per captain
Each captain gets a row: name, mobile, cart number, sign-in PIN and shift. Residents only see a captain's name and call button while he is actually sharing his live location from the Driver page.
Name — exactly as the captain will see it. This name is how the app files the captain's cash and how he signs in on his phone. If you change the spelling later his PIN comes with him — he signs in with the same code under the new name — but his phone signs itself out and he must pick the new name again, and the rides he already did stay filed under the old spelling, as if they belonged to another captain. Mobile (01xxxxxxxxx). The number residents dial while this captain is on shift or sharing his location — type it as 01xxxxxxxxx, the app adds the 20 itself. Leave it empty and residents on his shift get the other captain's number, or no call button at all. Cart number. Give two captains who share the same vehicle the same cart number, so the app counts one cart, not two. Get this wrong and bookings stay open while the only cart is sitting on the charger. PIN (needs the Owner key). A 4–6 digit code the captain types to sign in on his phone. It only reaches him if the Owner key in Connection & keys is set — one Save is enough, key and PINs together. Set a PIN with no Owner key and the captain is locked out of his own dashboard. Shift (this fills the Shift boxes below). Choosing Shift 1 or Shift 2 here puts this captain's name and mobile into the Shift boxes below, and that is the number residents call during that shift. Only one captain per shift — giving a shift to someone else takes it away from the first.
🛺 The fleet — which carts are working
One row per vehicle, not per captain. A cart listed here but switched off is fully
configured and completely silent: its driver can install the app, sign in with his PIN and look
around, and the cart still appears on no board, is offered no rides, is counted by no booking gate,
and tells the WhatsApp bot nothing. Turning it on is the only change needed on the day it
starts work. Hours are that cart's own, not the compound's. Leave both blank and the cart follows the
service's opening hours. Outside its hours a cart behaves exactly as if it were switched off, and
its captain sees a plain note on his dashboard saying so — so nobody sits waiting for a ride that
was never going to be offered to him. Riders is how many PASSENGERS the cart carries — not counting the captain. This is not
the number on the vehicle's spec sheet: a four-seater with the captain in one of those seats
carries three. Get it wrong in this box and dispatch gets it wrong all day.
Dispatch obeys it: a booking for five is never offered to a three-rider cart, its captain cannot
take it by mistake, and his card says why — otherwise he drives to the pick-up, cannot fit
everyone, and the group waits for a cart nobody told about them. Leave blank to use the
compound-wide capacity above.
📞 Who residents call, by time of day
Two captains can work the same shift, and that is set in the driver list above — not here. A captain who works split periods — say 6am–9am and 3pm–9pm — is set there too, in the Hours box on his own row (write whole hours: 6-9, 15-21). Those hours override his Shift in both directions, which is the one thing a half-of-the-day cannot express. Mark each man's own row Shift 1 or Shift 2; both men on a shift are offered to a resident who has not yet had his ride taken, so he can reach either one. These two boxes hold one captain each and cannot express that. They are now only a fallback: a number to ring for that shift when nobody's phone is reporting in and the driver list has nobody on it. To remove a captain you must clear his name here as well as deleting his row — deleting the row alone leaves residents still calling him.
Normally leave this alone: it is filled from the captain you marked Shift 1 above. If you type a name here that is not in the driver list, he gets no cart number and no PIN until you open Settings again and Save.
The number residents dial before the changeover hour. If it is blank or wrong, their calls go to the Shift 2 captain instead — or the Call driver button disappears entirely.
Normally leave this alone: it is filled from the captain you marked Shift 2 above. A name typed only here has no cart number and no PIN until you reopen Settings and Save.
The number residents dial from the changeover hour onward. If it is blank or wrong, evening calls go to the Shift 1 captain instead.
Before this hour residents call Shift 1, from this hour Shift 2 — and the Log tab splits the day's trips and cash at the same hour, so changing it re-cuts figures you have already read. If the on-shift captain has no mobile saved, the app calls the other one instead.
💵 Fares & payment
What every ride costs. These are the prices residents are quoted in the app the moment you save, so a slip here is money lost on every trip. WhatsApp quotes them too, but only once the Owner key in Connection & keys is set — that key is what carries a saved price to the bot.
Single rider — price by ride distance
These three prices are only ever charged when exactly one person rides; two or more always get the flat group price below. "Distance" is the route distance the app measures between the two points, not a straight line.
A one-rider trip this distance or shorter pays the short-ride price. Clearing the box does not remove the band — it keeps the distance that is already live.
From this distance upward a one-rider trip pays the long-ride price; below it, the medium price. Cleared, it keeps the distance already live.
What a single rider pays for a short trip.
For a trip between the two distances above.
Longest trips — and whenever the distance cannot be measured.
Every price you type here is live on the app and the captains' screens the moment you Save, and on WhatsApp as well while the Owner key is set. Clearing a box does not delete a price — it keeps the one already live.
2 or more riders — ONE total for the whole ride
These are totals, not per person and not by distance: the amount covers the whole trip to any point. Used automatically as soon as a booking has 2 riders or more.
The whole fare a 2-rider booking pays, any distance.
The only 3-rider price the app, the drivers and WhatsApp use. Ignore the old "Group 3-5" column in your Sheet.
One total for any group of 4 or more. Raise "Seats in the cart" above 5 and groups of 6–9 still pay only this, so raise it too.
Premium / Home pickup — price per person
These prices only apply while "Home pickup (Premium)" is switched on, and they are charged per person, not per ride. A resident who chooses Premium pays these instead of the flat group price, so keep them above the group totals.
One person, door pickup, any distance.
Each of the two pays this, so the booking totals double.
Every rider pays it: five riders pay five times this.
Payment
Residents tap "Pay now" and this link opens in their banking app, where they type the fare themselves. Leave it blank and the InstaPay choice disappears for every resident — all rides become cash only.
🕒 Hours & scheduling
When residents may ride now, and how far ahead they may book. Set these too narrow and the app turns people away while your cart is still on the road.
Let residents book a ride for later
Off means residents may only ride right now, inside working hours — outside those hours the app shows "closed" instead of a time picker. Big groups can only ever schedule, so with this off they cannot book at all.
When residents can start asking for a cart right now; before it they can only book a time. Half hours are allowed — type 6.5 for 06:30, which is what you want if your morning captain arrives on the half hour: opening before he does means the only cart in service is still on the charger, and booking shuts for everyone until he unplugs. It must be a smaller number than "open until" — a window that crosses midnight leaves residents with no bookable time at all.
The hour ride-now closes (24 = midnight); after it residents can still book, but only for the next opening. Set it at or below "open from" and residents can never ride now and the time picker has no valid slot.
الجمعة. Blank = same as every other day.
Blank = same as every other day.
Take rides after hours — while a captain is still out
Outside the hours above, residents can normally only book a time. With this on they may ride right away too — but only while at least one captain's app is reporting in, so it opens and closes itself as your crew signs on and off and can never take a 3 AM ride with nobody there to drive it. Those rides reach the captain marked "🌙 After hours · not scheduled", because no time was ever promised. WhatsApp stays closed outside hours either way — the app is the only after-hours door.
How far ahead a booked time must be, and how many minutes before closing the app stops offering today. Leave it at 15: the resident's error message always says "15 minutes", so any other value refuses them with the wrong instruction.
Book the ride back with the ride out and BOTH rides are this much cheaper — on the resident screen, on your Add-a-trip form, and on every standing booking with a ride back. 0 switches it off. Paste the latest Code.gs first: until then residents pay the lower fare but the Sheet does not record the rate, and the nightly fare check blames the captain.
Groups of this many riders or more cannot ride now — they must pick a time, and they are the only bookings allowed to choose a future day rather than today. Keep it above "Seats in the cart" to switch the rule off; if scheduling is turned off, these groups cannot book at all.
Inside working hours residents can ride now or pick a time later today; outside them only booking a time is offered, starting at the next opening hour (Friday included). If scheduling is switched off, the app is simply closed outside working hours.
🗺 Pick-up points & route
The places a resident can be collected from, and the metres between them. Those metres decide which price band a single-rider trip falls into.
Tap a pin to rename, reorder (⬆⬇) or delete a point · drag a pin onto its exact spot. Tap a + to add a road corner and drag it onto the road. Rides between points follow the sequence through everything in between (1→2→3→…), in either direction; the hub goes direct.
Each row's metres is the road distance to the next point on the loop, and the last one closes back to point 1. If you type a distance by hand it stays fixed until you move that point again — wrong metres push single-rider trips into the wrong price band, so they get over- or under-charged.
#Name→ next (m)
The hub is the one point that connects straight to every other point instead of following the loop order. Enter a number that does not exist and the app quietly uses the last point; changing it changes every ride distance, so single-rider prices can jump a band.
🛰 Live tracking & ETA
How the arrival time a waiting resident sees is worked out, and when a waiting ride is flagged to you. Promise a cart faster than it can drive and every ride looks late.
The cart's live position comes from the captain's phone (Driver page → “Share my live location”). The resident then sees the 🚗 and the ETA on the booking maps.
To watch the whole fleet, open the Bookings tab — every cart reports there (🟢 on the road · 🔌 charging · ⚫ app open with location off), with what it is carrying and the waiting backlog.
Only used to work out the "arriving in X min" a resident sees while a driver is sharing live location. Set it too high and residents are promised a cart sooner than it can possibly arrive.
Extra minutes added to the "arriving in X min" shown to the resident, so the estimate is not over-optimistic (empty counts as 0). Too small and the cart always looks late; too big and residents stop trusting the ETA.
On the driver's screen a ride that has waited this long or less shows a green ⏱ badge; beyond it turns yellow, then red. It only changes colours — no fare, ETA or booking is affected.
A ride waiting longer than this turns yellow on the driver's screen and counts as late on your Bookings board (red at double it). Anything under 5 minutes is ignored by the late alarms, which always use at least 5.
🏠 What residents see
Everything a resident reads, taps or dials — starting with the switch that can stop the whole service.
Accepting bookings (turn off to pause the service)
Off blocks every new booking, in the app and on WhatsApp, and shows your paused message instead; rides already running are not affected. Residents only see that message after they tap Book, and the WhatsApp bot only learns you paused if an Owner key is set.
What residents read once bookings are paused — e.g. "مغلق اليوم للصيانة، نعود غدًا الساعة ٦ صباحًا". Blank shows the standard closed text.
Home pickup (Premium) — offer it to residents
On adds the Premium "cart comes to your door" option to the booking screen and makes home-pickup links and QR codes work, priced from the Premium boxes in Fares. Off hides the option and any home-pickup link tells the resident "Home pickup is currently closed".
Make residents forward the booking to WhatsApp
On, the resident's phone jumps out of the app into WhatsApp with the booking typed out and they have to press Send — nothing is sent automatically. Leave it off: the booking already reaches the driver dashboard and your Google Sheet on its own.
Changes the language of this phone or browser only — the same as the EN / عربي button at the top of the screen. It does not set the language for residents or drivers; each of their devices chooses its own and starts in Arabic.
The largest number of riders one booking can request — set it to the cart's real seat count (5). Set it above 5 and groups of 6 or more are still charged the flat 4-or-more group price, and anything above 9 is ignored by the admin ride forms and the data checks.
The name shown at the top of the app, on the home banner and in the WhatsApp messages sent to drivers. Leave it blank to keep the built-in name; anything you type here reaches every resident and driver device when you press Save.
Only the text printed next to each price — it does not convert anything, so the numbers in the fare boxes stay exactly as you typed them. Leave it blank to use EGP / ج.م.
The number residents call or WhatsApp for help, and the number written onto every booking row. Get it wrong or leave it blank and the Call and WhatsApp Help buttons vanish from the app — it does not change the complaints line or the "Call driver" button, which use the driver shifts.
Both the "Book on WhatsApp" button and the resident verification-code request open a chat with this number, so it must be the automated bot line. If it is blank it falls back to the office number, and neither WhatsApp booking nor phone verification will work.
The number residents reach from the "Complaints & suggestions" button and the safety card's call button — this is not the office number above. Leave it blank to keep the number already in use.
Replaces the big line on the resident home screen, and whatever you type is what both Arabic and English residents see — the app will not translate it. Leave it blank to keep the built-in bilingual text; cart emojis (🛺 🚗) are removed automatically because the logo already shows one.
The small line under the title on the resident home screen, shown to Arabic and English residents exactly as you type it. Leave it blank to keep the built-in bilingual text.
📈 Costs & break-even
What a ride and a day actually cost you. Leave both at zero and the Profit and break-even cards in Reports flatter you.
Work the cost out automatically
On, the two boxes below stop being things you type and become things the books work out: the expenses you actually logged over the basis window, plus depreciation on everything in "What you bought", plus the expected costs you list under it — all divided by the rides that window really carried. It refreshes every time Reports loads, and it refuses to state a figure at all when the window is too short, the rides too few, or nothing is configured; in any of those cases whatever you typed keeps standing. The full workings are printed on the Reports tab under 🧮 What a ride costs.
What one trip costs you — the app uses it for the break-even count and for judging every package price. With the switch above on, this is computed for you and typing here does nothing. With it off, leave it at 0 and the Profit figure on your dashboard will read higher than the real one.
Driver pay, charging and upkeep for one day — it decides how many rides a day you must do to break even. Computed for you when the switch above is on.
How far back the automatic cost looks — always ending today, and deliberately not the period you are browsing in Reports: flicking to "today" to check the morning's takings must never re-price every package you sell. 30 is right for most operations. Shorter reacts faster to a wage rise and is noisier; below 7 the app refuses to compute anything.
⚖️ When the app judges, and when it refuses
Four numbers that decide what the app is willing to say — not what it computes. Clear a box to go back to the shipped value; a number outside its range is treated as empty rather than obeyed.
A package that earns, but keeps less than this share of what the resident paid, is labelled thin rather than earns under 💰 What each package earns. Raise it to be told about more of them. It never changes a price or a margin — only the word beside it. Shipped: 20.
On Reports → 🧮 What a ride costs → Compare every period, a long period this far from the window you price on raises "your basis window may be wrong". Lower it to be warned sooner. It only ever recommends — it never changes your cost. Shipped: 20.
Below this many days the app refuses to say what a ride costs, because a short window is a week of weather rather than an operating cost. Lowering this also lowers how short your basis window may be — set both to 1 and you can price the whole business off a single day's receipts, which is the mistake the fixed basis window exists to prevent. Shipped: 7.
Below this many rides in the window it refuses too — dividing real costs by a handful of rides produces a per-ride figure that would condemn every package you sell. The daily cost is still computed and still used. Shipped: 20.
These four stay on this device. They decide what your own screens say, so they are never sent to a resident's phone — the same rule the cost switch, your assets and your expected costs already follow.
🛒 What you bought — carts, batteries, chargers
Things you paid for once and use for years. These never belong in Expenses — a 60,000 cart logged there would wipe out one month and flatter every month after it. Listed here instead, each one is written off in equal daily slices over its working life (4 years unless you say otherwise), so every ride carries its fair share of the vehicle that drove it. An asset past its life charges nothing: it is paid for. Leave the date blank and it is treated as already in service. Columns: what it is · what it cost · date bought · life in years.
📅 Expected costs not in the ledger
Real money you will pay that the Expenses tab cannot see yet — a licence, insurance, a yearly battery fund, a retainer. Enter each as a monthly figure: an annual 6,000 licence goes in as 500, and then it is in every month instead of ambushing one. Do not list anything you already log as an expense — that money is counted once, from the ledger. Columns: what it is · per month.
📊 Dashboards — rider levels & risk thresholds
Loyalty tiers on every resident's "My rides" dashboard (rides needed · emoji · Arabic · English). They reach every device when you Save.
Risk-register limits (amber · red) on the Reports tab. Collections has one red limit (% of period money).
InstaPay pending red ≥ %
Driver float (days)
No-show %
Cancellations %
Expense ratio %
🔐 Admin access
The lock on this panel. It is asked once per device and never again, so treat it as the key to the whole business.
Unlocks the whole owner panel — bookings, money and settings. Type one here and Save,
and it is stored on your server, not in this app: from then on every device must ask
the server to let it in, and nobody can open the panel without it. Until you set one, this panel has no real lock. It used to ship with a built-in
password printed inside the app itself, which meant anyone who looked could read it —
that has been removed, and a password is now something you choose rather than something
we ship. Reports → 🩺 System check tells you which state you are in.
Four characters minimum; six digits is meaningfully harder to guess than four. Five wrong
tries lock that phone out for ten minutes — the phone doing the guessing, never yours.
Leaving the box empty changes nothing. Clearing the password entirely (so the lock comes
off again) is a deliberate act: see RUNBOOK §0-S.
Revenue, cash-vs-InstaPay reconciliation, per-driver cash to settle, no-show rate and KPIs — computed live from your Bookings sheet. Profit = collected revenue − (cost per ride × rides) − expenses.
Loading…
🏦 الخزنة — The vault
What is actually in your cash box, what is in the bank, and the two added up — the money you hold in total. The box is worked out from the cash your captains handed over, less what you spent out of your own float, then adjusted by the movements you log here; the bank is every InstaPay you confirmed, less what you paid out of the account, plus what you banked. Banking cash is not a loss: it moves from the box to the bank and your total does not change. Both balances can be checked against the real world — count the box with your hands, read the bank in your banking app — and neither check ever changes a balance: it records what you found beside what was expected and leaves the gap visible.
Loading…
💸 Expenses
Log what you spend — fuel, salary, charging, maintenance. Every line is written to the shared Expenses tab and read back from it, so each row shows whether it is already in the Sheet or still queued on this device, and every admin device sees the same ledger. Removing a line posts a reversal, so the audit trail survives. Profit and break-even are calculated from these.
👷 Salaries
Everyone you pay a wage, their salary per month, and what each is owed for the days actually worked: due = salary per month ÷ days basis × days worked. The roster lives in the Salaries tab of your Sheet, so every admin device shows the same one and nothing is lost if this browser is cleared. Nothing here is a cost until you post it — Post the month writes one Expenses line per person, tagged 👷 Salary and noting the name, the month, the days and the monthly salary, so Profit, the vault and the daily close pick them up the same way they pick up fuel. Who has already been paid is read back out of the Expenses tab itself, so a month is never paid twice and voiding a salary line there puts it back to unpaid. Change an amount after posting and the old line is reversed before the new one goes in, so the audit trail survives.
Days a FULL month's wage is paid over — 30 unless you pay a six-day week (26). It is the divisor: someone who works all of them is owed the whole month.
The day the money leaves. That is the day the books count it, whichever month the wage is for.
Only matters with "Paid from cash": a captain who pays a wage out of the notes he collected owes that much less.
➕ Record a lump sum for a month (no roster)
For wages already paid as one figure — a month settled before this panel existed, a bonus, a round-figure settlement. It posts one Expenses line tagged 👷 Salary against the payroll month you choose, and shows up in the history like any other wage. Use the roster above for a payroll you are actually running: it is the only way the per-person detail exists at all.
🧾 What is posted for this month
📅 Month by month
🛠 Data, backups & system
Housekeeping. The nightly cycles already run these on their own — use them when you want one now.
📮 Waiting on you
The badge on this tab counts exactly what is in this pane and the next. Nothing here happens on its own.
📝 Booking requests — contact first
Residents can request a lesson or household package here only after you publish the catalogue. A request does not activate rides, unlimited access or wallet credit; contact the resident, confirm the appointment/household details and payment, then mark it handled.
Set an Owner key in Settings → Connection & keys to load these.
💳 Transfers — confirm the money
Residents who asked to buy and transferred. Confirming turns their trips on — do it only once you can see the money. Void cancels a request that should not have been made; it is refused once any trip has been ridden.
Set an Owner key in Settings → Connection & keys to load these.
🔁 Standing bookings — the one approval that commits a cart
The same trip on every chosen day, for a stated period. Approving is not like ticking a package
request: it puts a real ride on the board every matching day until the period ends, without anyone
looking again. Each row shows how full that half-hour already is — that is a warning and never a
refusal, because an oversubscribed slot is exactly the evidence that a second run is needed.
You can add one yourself with ➕ — a parent who rang, a package you arranged by hand — and it is
live at once. An arrangement with a ride back makes both rides each morning, at the round-trip rate
from Settings. Ending one stops tomorrow's rides; today's ride, once made, is an ordinary booking
and is cancelled from the Bookings tab like any other.
Set an Owner key in Settings → Connection & keys to load these.
📒 Prepaid balances — what you owe in rides
Every resident with a balance, biggest first, and what you still owe them in rides.
Read the headline figure as a debt, not as takings — money for a trip nobody has driven is
already in your account while the cart still has to go out. A trip is valued at the price of the
bundle that resident bought, so changing your prices later never rewrites an existing balance.
Package sales are not counted in the Reports revenue figures. The sale price is recorded against the resident but no report reads it back, so your InstaPay account runs ahead of the revenue screens by exactly what you have sold. This roster is what reconciles the two by eye.
Set an Owner key in Settings → Connection & keys to load these.
🏆 Your best riders — and how to thank them
Ranked from the Residents tab, which rebuilds nightly. 🎁 Free rides puts trips in
their balance at no charge; 🏷️ Special offer gives them a lower rate on every trip for a set
number of days. Both are recorded in the Sheet under your name with your reason — and the resident
is told in the app, with whatever message you write for them.
Set an Owner key in Settings → Connection & keys to load these.
🚌 Where the compound travels — how to group the trips
What residents told us about the trips they take every week, from the 🗺️ My regular trips
card on their booking screen. None of these is a booking — nothing is reserved and nobody is
expecting a cart to arrive. They are what lets you put one cart on a run several neighbours are
already making separately, which is where the waiting time actually goes.
Set an Owner key in Settings → Connection & keys to load these.
📦 The catalogue — what residents can buy
Edit each package below. Nothing you change reaches a resident until you press Publish to residents.
Editing here publishes nothing. Every change below is kept on this device as a draft until you press Publish to residents. Each package keeps a permanent ID — the key a resident's request points at — so retire a package you have stopped selling rather than deleting it.
Advanced: the raw catalogue (JSON)
The whole document in one box — useful for pasting a revised sheet in one go, or reaching a field the cards above do not cover. Applying it replaces the draft; it still publishes nothing.
🕘 Catalogue revision history
No server history loaded.
🗺️ Student package zones
Every student in one group package must be inside the same zone. Edit the building boundaries below; the ranges must remain continuous from building 1 through 376, with no gaps or overlaps.
💰 What each package earns
Your published prices against your own fares and your cost per ride. A bundle is judged on what
it earns per cart movement; an unlimited package on how many rides it survives; a rate pass
on how long before it costs you; a wallet on what serving the whole credit costs.
Optional, and separate from the catalogue above: plain prepaid-trip bundles, where a resident buys N interchangeable trips for one price. Keep this empty unless you want to sell those as well. A row with no trips or no price is never published.
These rows are saved by the same Save settings button up beside the launch switch.
📖 How packages work
Nothing appears to residents until you switch it on and save. The lesson/household catalogue
is already drafted above; the simple trip-bundle list may stay empty. Switching it on publishes the
catalogue to every resident phone on its next sync, so Save asks you to confirm the first time.
How the money works. Buying only records a request; it grants no trips and counts as no
revenue. The resident transfers by InstaPay and you confirm receipt under Inbox — that is the
moment the trips become usable and the moment the money is yours. One trip is deducted when a ride is
completed, never when it is booked, so a cancelled ride costs the resident nothing.
What the captain sees. A package ride shows “🎟️ Prepaid — collect nothing” and books at
zero, so it never lands in his drawer at day-close.
Round trips. A resident who books the ride back with the ride out pays the round-trip rate from
Settings on both rides, and so does every standing booking with a ride back. Price a round-trip
lesson route from its one-way twin with the 🔁 button in the editor, or the package costs more than
paying cash both ways.
What you fix by hand. There is deliberately no way to type a new balance into this screen —
an editable balance is invented money. In the Sheet’s Packages tab: change
Expires to extend a package, or Used to correct a count, and write why in
Note.
Package sales are not yet counted in the Reports revenue figures. The sale price is recorded
against the resident but no report reads it back, so your InstaPay account will run ahead of the
revenue screens by exactly what you have sold. The Customers pane is what reconciles the two by eye.
All install and share QR codes use the official https://gogolfy.net/ release: one resident app, plus the separate Driver and Admin apps.
On the live gogolfy.net site this is fixed and cannot be edited — every Save restores it. It is only typeable while testing a copy of the app somewhere else.
WhatsApp auto-reply / share link
Paste this as your WhatsApp Business greeting or away message. Anyone who messages you instantly gets the link to open the app, see the map, and make a reservation.
📊 Today at a glance
🚨 Needs attention now
Longest wait first, each labelled with why it is stuck — a cart was on charge, its slot went past, or nobody picked it up. Amber past your ETA target, red past double it.
Loading…
🛰 Fleet — where is each captain
Each driver app checks in every 60s: 🟢 on the road, 🔌 charging, ⚫ open but no location. A cart drops off 5 minutes after its last check-in.
Waiting for a captain to open the driver app…
👁 Through their eyes
Opens the resident app, or any captain's dashboard, exactly as it is running right now — real fares, real hours, real waiting rides. Nothing you tap in there is sent: no booking, no claim, no completion, no location. It cannot change your own settings either.
📣 Message a captain
Puts a message on his dashboard that he has to tap to dismiss, and tells you when he did. It asks — it cannot act: nobody can switch on his location, his cart switch or his GPS from here; those need his own tap, which is exactly why this exists.
📅 Scheduled trips — next appointments
Soonest first, with the exact appointment, booking time, lead time, live countdown, route, claim state, source, payment and package marker. Amber inside 30 minutes; once due, it moves to Waiting and keeps its appointment time for audit.
Loading…
🔎 Every trip today
App, WhatsApp, office and driver-added rides, with live status and how each is paid. Refreshes every 30s. The Scheduled filter keeps the original appointment visible after a ride becomes due, completed or cancelled.
The chips are today. Cash is real the moment the captain has it. An InstaPay transfer is not revenue and never enters the day's cash until you confirm you have seen it in the account — claimed means the resident pressed “I paid”, which is a promise, not a deposit. The list underneath is every unconfirmed transfer, of any date, read straight from the Sheet — so nothing ages out of sight. Confirm one, tick several, or clear the lot; a multi-selection is a single write.
Loading…
👤 Close the day with each captain
Cash to collect is what that captain is physically holding. InstaPay sits beside it and is never added in — that money went to the bank, not his pocket. These are today's trips as seen by this device, so open the panel during the day. Take the payment itself in 📊 Reports — that figure is the server's running balance and includes earlier unsettled days.
Loading…
➕ Add a trip from the office
Walk-ups and phone calls you take yourself. Written to the Sheet and pushed to every driver dashboard instantly, chime and all — exactly like an app booking.
New trip
1
Fare: —
🔄 Just changed
Every status change this device has seen today, newest first, with the minute it happened.
Loading…
📱 This device
Bookings made from this device only — the Sheet above is the real record.
GoGolfy production is hosted only on Cloudflare Pages at https://gogolfy.net/.
Current hosting — Cloudflare Pages
Test changes in the canonical GoGolfy workspace.
Push the verified files to the main branch.
The workflow stamps one BUILD across the website and every installed PWA.
Use Cloudflare deployment rollback if a release must be reversed.
Do not generate public links from GitHub, Netlify, or preview domains; they install a separate PWA that cannot receive the production release.