GuestBan · Partner API
Version 1 reference
Three read-only reports covering yesterday's arrivals at your properties: every check-in, the guests who matched a criminal or sex-offender record, and a usage summary. Built for a nightly pull into your own systems.
- Base URL
- https://api.myguestban.com
- Auth
- X-API-Key header
- Format
- JSON
- Released
- 14 Aug 2026
Getting started
One call, and you should have yesterday's arrivals.
# Every guest who checked in yesterday, across all your properties
curl -s https://api.myguestban.com/api/public/v1/guests \
-H "X-API-Key: YOUR_API_KEY"
A successful response is the report itself — there is no wrapper object to unpack and no
success flag to check. Use the HTTP status code.
Run it once before you build anything. If your key is not yet enabled for an
endpoint you will get 403 rather than data, and that is far easier to diagnose now
than inside a scheduled job at 3am.
Authentication
Send your key in the X-API-Key header on every request. There is no token exchange
and nothing to refresh.
curl -s https://api.myguestban.com/api/public/v1/guests \
-H "X-API-Key: YOUR_API_KEY"Keys are 32 hexadecimal characters. GuestBan issues yours directly — it is never sent by email in the same message as this document.
Your key determines which properties you can see. There is no tenantId or
propertyId parameter anywhere in this API, and no request can widen your access
beyond the properties your key already covers.
Treat the key as a password. It does not expire on its own. If it is exposed, contact GuestBan support and we will issue a replacement — the old one stops working immediately, so plan a short changeover.
How it behaves
Three behaviours differ from most APIs. They are deliberate, and knowing them up front will save you a support ticket.
Always yesterday
Every endpoint reports the previous day. There is no date, no range, and no way to request history — so run your job daily and keep what you pull.
Almost no parameters
The two list endpoints accept propertyName and nothing else. The usage summary
accepts nothing at all. Anything unrecognised is rejected with 400, never
ignored.
The day is local
"Yesterday" is resolved in each property's own time zone, so a hotel's real overnight — not one shared UTC window.
Why unknown parameters are rejected
If you sent ?date=2026-01-01 and we quietly returned yesterday instead, nothing in
the response would tell you the difference — you would file a year of wrong data believing it was
right. So an unrecognised parameter fails loudly:
curl -s "https://api.myguestban.com/api/public/v1/guests?date=2026-01-01" \
-H "X-API-Key: YOUR_API_KEY"
# 400 Bad Request
{
"code": "parameter_not_supported",
"detail": "This endpoint does not accept 'date'."
}Naming a property
Applies to /v1/guests and /v1/criminal-arrivals only.
Pass propertyName to narrow one of those reports to a single property; omit it to
cover all of them. Matching tolerates casing and spacing, so WSSAddison finds
WSS Addison. It is not a partial search — a name that matches nothing returns
404 property_not_found rather than an empty day, so a typo in your config surfaces
immediately instead of looking like a quiet night.
curl -s "https://api.myguestban.com/api/public/v1/guests?propertyName=WSS%20Addison" \
-H "X-API-Key: YOUR_API_KEY"
The usage summary takes no parameter at all. It always covers every property your key reaches and
splits the figures out per property in its byProperty array, so there is nothing to
narrow.
Guest arrivals
Every guest who checked in yesterday. The whole day comes back in one response.
{
"totalCount": 2,
"propertyName": "WSS Addison",
"reportedDateLocal": "2026-08-10",
"message": null,
"items": [
{
"propertyName": "WSS Addison",
"propertyTimeZone": "Eastern Standard Time",
"checkInAtUtc": "2026-08-10T21:14:03Z",
"checkInDateLocal": "2026-08-10",
"firstName": "ANTHONY",
"middleName": null,
"lastName": "RAIA",
"dateOfBirth": "1979-04-22T00:00:00",
"gender": "MALE",
"identificationNumber": "S1234567",
"documentExpirationDate": "2029-04-22T00:00:00",
"address": "114 Mulberry St",
"city": "Newark",
"state": "NJ",
"zipCode": "07102",
"roomNumber": "412",
"isFlagged": false,
"hasCriminalRecord": false,
"isSexualOffender": false,
"matchedBanAtCheckIn": false,
"isCurrentlyBanned": false,
"source": "scan"
}
]
}Fields worth reading twice
| Field | Notes |
|---|---|
| gender |
MALE, FEMALE or UNKNOWN — or null.
UNKNOWN means a value was captured that we could not classify; null
means nothing was captured. Machine values, not display text. |
| matchedBanAtCheckIn |
Whether screening matched a ban at the desk. Never changes for a past day.
null means no screening record exists for that arrival — which is not the
same as false. |
| isCurrentlyBanned |
Whether a ban exists right now. Live, so two pulls of the same day can differ —
a guest banned after they left flips to true here. |
| isFlagged | A manual staff action, not a screening outcome. For screening use the two fields above. |
| source |
scan, scan manual entry (photographed, details typed) or
manual entry. |
Not included: vehicle details and ID scan images are deliberately excluded from this contract, as is GuestBan's internal guest identifier. Properties are identified by name throughout, so you never need to store one of our keys.
Criminal arrivals
Yesterday's arrivals that matched the criminal-records or sex-offender feed — one record per guest, however many matches they triggered.
{
"totalCount": 1,
"reportedDateLocal": "2026-08-10",
"items": [
{
"propertyName": "WSS Addison",
"checkInAtUtc": "2026-08-10T18:02:55Z",
"checkInDateLocal": "2026-08-10",
"firstName": "KAREN",
"lastName": "KLINE",
"gender": "FEMALE",
"hasCriminalRecord": true,
"isSexualOffender": false,
"crimeTypes": ["FELONY", "MISDEMEANOR"],
"offenseDescriptions": ["THEFT BY DECEPTION"],
"acknowledgedAtUtc": "2026-08-10T18:04:11Z",
"acknowledgedAtLocal": "2026-08-10 14:04:11",
"source": "scan"
}
]
}
crimeTypes and offenseDescriptions are not positionally
paired. They are two independent lists — do not zip them together. Both are empty
arrays, never null, when there are no criminal offenses.
Sex-offender matches are exposed as the isSexualOffender flag only; the associated
offense descriptions are not disclosed. A guest can be a sex-offender match with no criminal
match, in which case both offense lists are empty.
acknowledgedAtUtc is null when the alert was shown to staff but not yet
acknowledged. That is distinct from a guest with no alert at all.
Usage summary
Yesterday's screening activity as counts, covering every property your key reaches, with a breakdown per property. Calculated once after the day closes and served from storage thereafter.
{
"reportedDateLocal": "2026-08-10",
"fromUtc": "2026-08-10T04:00:00Z",
"toUtc": "2026-08-11T05:00:00Z",
"reportGeneratedAt": "2026-08-11T05:12:41Z",
"apiRequestedAt": "2026-08-11T08:15:32Z",
"totals": {
"scans": { "value": 19, "changePercent": 5.6 },
"manualEntry": { "value": 3, "changePercent": -25.0 },
"bansAdded": { "value": 1, "changePercent": null },
"criminalMatches": { "value": 1, "changePercent": 0.0 },
"sexOffenderMatches": { "value": 0, "changePercent": null }
},
"alerts": {
"banMatches": { "value": 0, "changePercent": null },
"criminal": { "value": 1, "changePercent": 0.0 },
"sexOffender": { "value": 0, "changePercent": null }
},
"warnings": {
"ageAlert": { "value": 4, "changePercent": 33.3 },
"localAlert": { "value": 6, "changePercent": -14.3 }
},
"byProperty": [
{
"propertyName": "BHS Beavercreek",
"scans": 2, "manualEntry": 0, "bansAdded": 0,
"criminalMatches": 0, "sexOffenderMatches": 0,
"banMatches": 0, "criminal": 0, "sexOffender": 0,
"ageAlert": 1, "localAlert": 0
},
{
"propertyName": "BHS Jackson MS",
"scans": 6, "manualEntry": 2, "bansAdded": 0,
"criminalMatches": 1, "sexOffenderMatches": 0,
"banMatches": 0, "criminal": 1, "sexOffender": 0,
"ageAlert": 1, "localAlert": 2
}
/* … one row per property */
],
"activity": {
"granularity": "DAILY",
"points": [ { "date": "2026-08-10T04:00:00Z", "scans": 19, /* … */ } ]
},
"message": null
}Two timestamps, and they mean different things
This report is calculated once, shortly after the reported day closes, and stored. Every request after that reads the stored copy rather than recounting.
| Field | Describes | Changes between two identical requests? |
|---|---|---|
| reportGeneratedAt | When GuestBan calculated and stored these figures. A property of the report. | No — identical every time you pull the same day. |
| apiRequestedAt | When we received your request. A property of the call, informational only. | Yes — it is the only field that moves. |
A gap of several hours between the two is normal and expected. Use it to tell how fresh the figures you are holding actually are.
Alerts and warnings are different things
This is the distinction most worth understanding before you build a report on these numbers.
| Block | Meaning | Fields |
|---|---|---|
| alerts | Something was found about the guest — they matched a ban, the criminal feed, or the sex-offender registry. | banMatches, criminal, sexOffender |
| warnings | Nothing was found. A threshold the property configured for itself was crossed — the guest is under the age limit, or lives inside the local radius. | ageAlert, localAlert |
Do not add the two blocks together. They answer different questions, and a guest who matches several sources at once contributes to each alert they matched — so the alert figures do not count guests.
bansAdded and banMatches are unrelated.
bansAdded counts ban records your staff created during the day.
banMatches counts arriving guests caught by a ban that already existed. A
property can add bans all day and match none of them.
The per-property breakdown
byProperty carries one row for every property your key reaches — including any that
recorded nothing, which appear as a row of zeros rather than disappearing. A silent property and a
removed one should not look the same.
The row fields use the same names as the headline blocks, and every column adds up across the rows to its headline figure. That is guaranteed, not incidental: the rows are the same calculation split by property. If they ever disagree, tell us.
Rows carry bare numbers with no changePercent — there is no per-property comparison
against the day before. Difference two days of this report if you need one.
Reading the numbers
| Field | Notes |
|---|---|
| scans | Guests processed at the kiosk — one per person checked in. Not a count of documents scanned. |
| manualEntry |
Of those scans, how many were typed in by staff rather than machine-read. A
subset of scans, never additional to it — adding the two
together double-counts every manual guest. |
| criminalMatches |
Overlaps alerts.criminal, which answers the same question from a different
record. They agree on recent data and can differ on older data. The same applies to
sexOffenderMatches and alerts.sexOffender. |
| changePercent |
Movement against the day before. null when there is no single comparable
baseline — including any group of properties in different time zones, since each has its
own day-before. |
| value |
null means "not recorded for this day", which is not zero. |
Errors
Failures come back as application/problem+json. Branch on code — it is
stable. The detail text is written for humans and may be reworded at any time.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"code": "parameter_not_supported",
"detail": "This endpoint does not accept 'date'.",
"correlationId": "3f9c1a4b-8e2d-47f0-a1b6-c3d5e7f80921"
}| Status | Code | What to do |
|---|---|---|
| 400 | parameter_not_supported |
Remove the parameter. The list endpoints accept only propertyName; the usage
summary accepts nothing. |
| 400 | invalid_request | The request was malformed. Check the URL and header syntax. |
| 401 | invalid_api_key | Key missing or not recognised. Confirm the X-API-Key header is being sent. |
| 403 | insufficient_scope | Key is valid but not enabled for this endpoint. Contact GuestBan support. |
| 404 | property_not_found | The name matched none of your properties. Check spelling before assuming an empty day. |
| 404 | property_name_ambiguous | Several of your properties share that name. |
| 404 | not_found | No such route. Check the path against this page. |
| 429 | rate_limit_exceeded | Too many concurrent requests. Back off and retry — see below. |
| 500 | internal_error | Our fault. Retry once, then contact support quoting the correlation id. |
Retrying
Retry 429 and 500 with exponential backoff. Do not retry
400, 401, 403 or 404 — those describe
something about the request that will not change on its own.
These reports cover a fixed past day, so they are safe to request more than once: pulling the same day twice returns the same data, apart from the handful of live fields noted above.
Support
Every response carries an X-Correlation-Id header — including failures that never
reach our application, such as an authentication rejection.
curl -sD - -o /dev/null https://api.myguestban.com/api/public/v1/guests \
-H "X-API-Key: YOUR_API_KEY" | grep -i correlation
x-correlation-id: 3f9c1a4b-8e2d-47f0-a1b6-c3d5e7f80921Log it. Quoting one lets us find your exact request in seconds instead of reconstructing it from a description — it is by far the fastest route to an answer.
Suggested integration
Run once daily, after your properties' overnight has closed in every time zone you operate in. Store what you pull: there is no way to request an earlier day, so an unrun job is a gap you cannot backfill.
