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

Protocol
HTTPS · JSON
Method
POSTonly
Auth
Bearer token
Batch
Up to 200 / request
Idempotent
Yes

Base URL

https://<project-ref>.functions.supabase.co/ingest-rental-pass
Note: Replace <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.
Keep it secret. Treat the token like a password. Send it only over HTTPS, never embed it in client-side code or public repositories, and ask your administrator to regenerate it if you suspect exposure.

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

FieldTypeReqDescription
passcodestringYes Owner/unit passcode identifying the rental unit. Same code used on the unit's title record.
renter_namestringYes Guest name printed on the pass. Max 100 chars.
start_datestringYes Check-in date, YYYY-MM-DD.
end_datestringYes Check-out date, YYYY-MM-DD. Must be after start_date.
renter_emailstringNo Guest email. Max 100 chars.
notesstringNo Free-text notes (e.g. number of vehicles).
quantityintegerNo Identical passes to create (e.g. one per vehicle). 1–50. Default 1. Each gets its own serial.
external_reservation_idstringNo 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.

Recommendation: always send external_reservation_id. It's the simplest way to keep data clean across retries and reconnects.

5 · Responses

HTTPMeaning
200Every pass succeeded (or was a known duplicate).
207Processed, but at least one item failed validation — inspect results.
4xx / 5xxThe 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

FieldDescription
oktrue if no items failed.
createdTotal pass records created.
failedItems that failed validation.
duplicatesItems skipped as duplicates.
resultsOne entry per input item, in the order sent.

Per-item results

Each results entry has a status of okerrorduplicate:

StatusShape
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)

HTTPMessageCause
401Missing bearer tokenNo Authorization header.
401Invalid tokenToken not recognized.
401Token revokedToken revoked or regenerated.
400Invalid JSON bodyBody isn't valid JSON.
400Body must be an object or array of passesUnexpected body shape.
400No passes providedEmpty array.
400Too many passes (max 200)Over 200 items.
405Method not allowedAny method other than POST.
500Auth lookup failed / Insert failedTransient server error — retry with backoff.

Item-level (appear in a result's errors, HTTP 207)

MessageCause
passcode requiredMissing passcode.
renter_name requiredMissing 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_dateDates out of order or equal.
quantity must be 1-50quantity out of range.
passcode "X" not foundNo current unit with that passcode in your community.
passcode "X" is not managed by your companyUnit 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.