Rental Pass API
Rental Pass API — Integration Guide
Connect your booking or property-management software so guest reservations automatically create rental passes that can be printed or redeemed at the community kiosk. One HTTPS endpoint: send reservation data, receive a pass ID and serial number.
Overview
Base URL
https://<project-ref>.functions.supabase.co/ingest-rental-pass
<project-ref> with the
value the Palmetto Guard team provides when you receive your API token.1 · Authentication
Every request must include your API token as a Bearer token:
Authorization: Bearer pg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Getting a token
Your community administrator issues your token from within Palmetto Guard. A token:
| Is shown only once at creation — store it securely; it cannot be retrieved again. |
| Is tied to your company within one community. If you manage units in multiple Palmetto Guard communities, you receive a separate token per community. |
| Can be revoked or regenerated at any time. If regenerated, the old token stops working immediately and you must update your configuration. |
2 · Making a request
Send a POST with a JSON body and these headers:
POST https://<project-ref>.functions.supabase.co/ingest-rental-pass
Authorization: Bearer pg_live_xxx...
Content-Type: application/json
The body may be a single pass object, an array of pass objects, or an object with a
passes array: { "passes": [ ... ] }. A single request may
contain up to 200 passes.
Request fields
| Field | Type | Req | Description |
|---|---|---|---|
passcode | string | Yes | Owner/unit passcode identifying the rental unit. Same code used on the unit's title record. |
renter_name | string | Yes | Guest name printed on the pass. Max 100 chars. |
start_date | string | Yes | Check-in date, YYYY-MM-DD. |
end_date | string | Yes | Check-out date, YYYY-MM-DD. Must be after start_date. |
renter_email | string | No | Guest email. Max 100 chars. |
notes | string | No | Free-text notes (e.g. number of vehicles). |
quantity | integer | No | Identical passes to create (e.g. one per vehicle). 1–50. Default 1. Each gets its own serial. |
external_reservation_id | string | No | Your reservation ID. Strongly recommended — enables safe retries. |
Computed for you
You do not send pricing or night counts — Palmetto Guard derives these and ignores any values you send for them:
| Nights — calculated from the dates. |
| Charge amount — from the community's pricing model, if configured. |
| Serial number — generated per pass. |
| Status — pass is created "issued", ready to print or redeem. |
3 · The passcode
The passcode tells Palmetto Guard which unit a reservation belongs
to. It must match a current unit title in the community your token is associated with.
The passcode determines the property the pass attaches to and confirms the unit is
managed by your company.
If a passcode isn't found in your community, or belongs to a unit not managed by your company, that item is rejected with a clear error — and the rest of the batch still processes.
4 · Idempotency
Booking systems retry. To prevent a retried webhook from creating duplicate passes,
include external_reservation_id with your own stable reservation ID.
When you send a pass whose external_reservation_id already produced a pass
in that community, Palmetto Guard does not create a new one — it returns the
existing pass with a duplicate status. Re-sending the same
reservation is always safe.
external_reservation_id.
It's the simplest way to keep data clean across retries and reconnects.5 · Responses
| HTTP | Meaning |
|---|---|
200 | Every pass succeeded (or was a known duplicate). |
207 | Processed, but at least one item failed validation — inspect results. |
4xx / 5xx | The whole request was rejected — see Error reference. |
Response body
{
"ok": true,
"created": 2,
"failed": 0,
"duplicates": 0,
"results": [
{
"index": 0,
"status": "ok",
"property_name": "Ocean Dunes 14B",
"quantity": 2,
"passes": [
{ "id": "f1c2...", "serial_number": "A1B2C3D4E5F6" },
{ "id": "9b7e...", "serial_number": "G7H8J9K0L1M2" }
]
}
]
}
Top-level fields
| Field | Description |
|---|---|
ok | true if no items failed. |
created | Total pass records created. |
failed | Items that failed validation. |
duplicates | Items skipped as duplicates. |
results | One entry per input item, in the order sent. |
Per-item results
Each results entry has a status of
okerrorduplicate:
| Status | Shape |
|---|---|
ok | { index, status, property_name, quantity, passes:[{id, serial_number}] } |
error | { index, status, errors:["..."] } |
duplicate | { index, status, external_reservation_id, passes:[{id, serial_number}] } |
index matches the position of the item you submitted, so you can map
results back to your source data.
6 · Error reference
Request-level (rejects the whole request)
| HTTP | Message | Cause |
|---|---|---|
| 401 | Missing bearer token | No Authorization header. |
| 401 | Invalid token | Token not recognized. |
| 401 | Token revoked | Token revoked or regenerated. |
| 400 | Invalid JSON body | Body isn't valid JSON. |
| 400 | Body must be an object or array of passes | Unexpected body shape. |
| 400 | No passes provided | Empty array. |
| 400 | Too many passes (max 200) | Over 200 items. |
| 405 | Method not allowed | Any method other than POST. |
| 500 | Auth lookup failed / Insert failed | Transient server error — retry with backoff. |
Item-level (appear in a result's errors, HTTP 207)
| Message | Cause |
|---|---|
passcode required | Missing passcode. |
renter_name required | Missing renter_name. |
start_date required (YYYY-MM-DD) | Missing/malformed start date. |
end_date required (YYYY-MM-DD) | Missing/malformed end date. |
end_date must be after start_date | Dates out of order or equal. |
quantity must be 1-50 | quantity out of range. |
passcode "X" not found | No current unit with that passcode in your community. |
passcode "X" is not managed by your company | Unit exists but isn't assigned to your company. |
7 · Best practices
Always send external_reservation_id so retries are safe. |
Retry 5xx responses with exponential backoff (e.g. 1s, 5s, 30s) — they're transient. |
Don't retry 4xx responses without fixing the request first. |
Handle 207 explicitly: read each item's errors and re-queue only those. |
| Send in batches (up to 200) to reduce overhead, but small enough to inspect failures. |
| One token per community — route each reservation to the correct community's token. |
8 · Example
Create passes (cURL)
curl -X POST "https://<project-ref>.functions.supabase.co/ingest-rental-pass" \
-H "Authorization: Bearer pg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"passes": [
{
"passcode": "1234",
"renter_name": "Smith Family",
"renter_email": "smith@example.com",
"start_date": "2026-07-01",
"end_date": "2026-07-08",
"notes": "Two vehicles",
"quantity": 2,
"external_reservation_id": "RES-558831"
}
]
}'
A partial-failure response (HTTP 207)
{
"ok": false,
"created": 1,
"failed": 1,
"duplicates": 0,
"results": [
{
"index": 0,
"status": "ok",
"property_name": "Ocean Dunes 14B",
"quantity": 1,
"passes": [{ "id": "f1c2...", "serial_number": "A1B2C3D4E5F6" }]
},
{
"index": 1,
"status": "error",
"errors": ["passcode \"9999\" not found"]
}
]
}
Support
Questions or need a token? Contact the Palmetto Guard integrations team at info@palmettoguard.com.