Reporting API
A read-only HTTP API that returns ride times, pick-up and drop-off points, which buggy ran each ride, and the wait figures behind them. Three endpoints, one API key, JSON. Your property's admin creates the key in the console.
Version 1 · Last updated: 2026-08-27
The key, and where it goes
An administrator at the property creates a key in Settings → Reporting API, gives it a label ("Power BI — finance"), and chooses what it may read. The key is shown once. A lost key is rotated: a new one is issued and the old one stops working immediately.
A key carries only the permissions chosen when it is created:
rides:read
for reporting, and handoff:mint
for opening the buggy screen from the property's own guest app
(documented separately).
The key travels in the Authorization header, never in a query string. A request with the key in a query string is refused with its own error code.
curl -H "Authorization: Bearer rb_live_xxxxxxxx_..." \ https://app.resortbuggy.com/api/partner/v1/meta
The three endpoints
GET /partner/v1/meta
Requires: any live key
The property this key is bound to, its timezone, its own wait target, and what the key is allowed to read. Confirms the key works.
GET /partner/v1/rides/daily
Requires: rides:read
One row per calendar day: rides requested, completed, cancelled and timed out, plus the median and 90th-percentile wait and how many rides met the property's own target.
GET /partner/v1/rides
Requires: rides:read
One row per ride: its times, where it started and finished, how long the guest waited, how long the ride took, how many people rode, and which buggy.
Both ride endpoints take ?from=YYYY-MM-DD&to=YYYY-MM-DD,
inclusive at both ends, as calendar dates on the
property's own clock. With no dates you get the last seven days. Ask for
at most 31 days at a time and page through longer
periods.
A day is not final until the day is over. A ride still running when you pull will be reported with the status it had at that moment; re-pull the day afterwards to settle it.
GET /partner/v1/meta
{
"apiVersion": "1",
"resort": {
"name": "Kunaavashi",
"timezone": "Indian/Maldives",
"waitTargetMinutes": 15
},
"scopes": ["rides:read"],
"keyPrefix": "a1b2c3d4",
"deprecation": { "noticeMonths": 12, "sunsetAt": null }
} GET /partner/v1/rides/daily
{
"from": "2026-07-01",
"to": "2026-07-03",
"timezone": "Indian/Maldives",
"waitTargetMinutes": 15,
"backfilledExcluded": 0,
"days": [
{
"date": "2026-07-01",
"ridesRequested": 42, "ridesCompleted": 39,
"ridesCancelled": 2, "ridesTimedOut": 1,
"boardedRides": 39, "withinTargetRides": 34,
"medianWaitMinutes": 8.5, "p90WaitMinutes": 19.9
}
]
} - date
- A calendar day on the property's own clock, not UTC.
- ridesRequested
- Guest rides asked for on that day.
- ridesCompleted · ridesCancelled · ridesTimedOut
- What became of them. These need not add up to ridesRequested: a ride asked for at 23:55 may still have been running at midnight.
- boardedRides
- Guest rides that actually boarded that day — the denominator.
- withinTargetRides
- How many of those met the property's own wait target — the numerator.
- medianWaitMinutes · p90WaitMinutes
- Null on a day nobody boarded.
GET /partner/v1/rides
- rideId
- Stable identifier for the ride.
- status
- COMPLETED, CANCELLED, TIMED_OUT, or a live status if the ride was still running when you pulled.
- scheduledFor
- The promised time, if the guest pre-booked. Null for an on-demand ride.
- requestedAt
- When the guest asked.
- dispatchedAt · acceptedAt · arrivedAt · boardedAt · completedAt
- The ride's stamps, in ISO 8601 UTC. Any of them can be null — a cancelled ride never reached most of them.
- waitMinutes
- Request to boarding, one decimal. Null for a pre-booked ride — the guest did not wait from booking time to pick-up.
- rideMinutes
- Boarding to drop-off, as recorded.
- passengerCount
- How many people rode.
- pickup · dropoff
- The names of the two points, as the property named them.
- vehicleLabel
- The buggy's call-sign. Null if no buggy ever took the ride.
Making your numbers agree with the property's
We give you the numerator and the denominator, never a
percentage. A daily "% met target" cannot be averaged across a
month — a Tuesday with four rides would count as much as a Saturday with
ninety. Sum withinTargetRides
and boardedRides
over whatever period you want and divide once.
Two clocks in one row. The counts (requested, completed, cancelled, timed out) bucket by the minute the guest asked. The wait figures bucket by the minute the guest boarded — the same clock the property's own wait card uses.
Every ride-bearing response carries
backfilledExcluded:
rides entered after the fact from a radio log, excluded from published
figures here and in the property's own reports.
Staff errands are not guest rides and are excluded.
What is not on this API
Version 1 of this API reports rides, hours and utilisation per buggy. Per-driver figures are not part of version 1.
Driver productivity is reported in the ops dashboard.
Any guest, in any form
No name, no room or villa number, no reservation reference, no contact detail, and no opaque identifier that would let rides be grouped by person. There is no guest field on any row, so there is nothing to join a guest onto.
Any member of staff, in any form
No driver name, no staff identifier, no shift, no sign-on or sign-off time — and no opaque driver id either.
Messages, notes and ratings
Guest and host messages, booking notes, cancellation reasons and star ratings do not cross this boundary at any granularity.
Location traces
A ride is reported as two named points and a set of timestamps — no GPS trace crosses this API.
A request for one of these returns the reason above at a machine-readable
"code": "not_offered".
A query parameter that asks either ride endpoint to group by a person is
refused, not ignored.
What happens when a guest's data is deleted
We delete a departed guest's name, contact details and messages. We do not delete the ride. A ride record — its times, its pick-up and drop-off points, its duration and its outcome — is retained as operational history and, after deletion, is attached to nobody.
Nothing you have already pulled is affected, and nothing you pull afterwards changes.
Versioning, and what we promise
The version is in the path. Everything documented here is version 1, at
/partner/v1.
If version 1 is ever withdrawn, we will publish the date at least
twelve months in advance, and version 1 will keep working throughout
those twelve months. The date is machine-readable at
/partner/v1/meta as
deprecation.sunsetAt,
which is null while version 1 is current.
Within version 1 we may add a field. We will not remove one, rename one, or change what an existing one means — so a report that reads the fields on this page keeps working. Parse leniently: ignore fields you do not recognise rather than failing on them.
Excel and Power BI
Paste this into Power Query, with the key in the header. The same snippet with the key already filled in is shown in the console at the moment the key is created.
let
Source = Json.Document(
Web.Contents(
"https://app.resortbuggy.com/api/partner/v1/rides/daily",
[Headers = [#"Authorization" = "Bearer rb_live_xxxxxxxx_..."]]
)
),
Days = Table.FromRecords(Source[days])
in
Days Rate limits are per key, not per address: 60 reporting requests an hour, bursting to 10 a minute.
To request a field or an endpoint, contact us.