Skip to main content

Webhooks

A webhook lets SpotHero send reservation updates to an HTTPS address on your system. It can deliver updates sooner and reduce the number of API requests you make by letting you poll less often.

If you request a higher API request limit, SpotHero may ask you to implement a webhook first. This can reduce the number of requests your integration needs.

Webhooks and reservation sync​

A webhook can deliver updates sooner and help reduce how often you poll. It does not replace reservation sync. Keep syncing periodically to find changes that were missed while your endpoint was unavailable. See the Reservation Sync guide for how to do this.

1. Webhook overview​

This guide shows how to register a webhook, receive reservation updates, verify that each message came from SpotHero, and manage the webhook later. Your endpoint must use HTTPS and accept HTTP POST requests with a JSON body.

The delivery has two important steps:

In staging, you can register the webhook before your endpoint is ready or exists. This can help you test the setup. In production, make sure your endpoint is ready to receive and verify requests.

SpotHero's webhook type for reservation updates is VALIDATION_API_RESERVATION_UPDATES.

2. Register your endpoint​

Send a POST request to /v2/webhooks with your endpoint's HTTPS address and webhook type. This example registers a webhook in staging:

POST https://integrations.staging.spothero.com/v2/webhooks
Authorization: Bearer <staging_access_token>
Content-Type: application/json

{
"uri": "https://example.com/spothero/webhooks",
"type": "VALIDATION_API_RESERVATION_UPDATES"
}

Save the webhook secret​

A successful registration returns a webhook id and a secret, along with the webhook settings. Save the id and store the secret securely. The secret is returned only once. You need it to verify future messages. If you lose it, the API will not return it again.

Example registration response:

{
"id": "i_wh_01hgeb24jxfn092zyey1ws6w3a",
"uri": "https://example.com/spothero/webhooks",
"type": "VALIDATION_API_RESERVATION_UPDATES",
"active": true,
"secret": "<secret-returned-only-once>"
}
Replace a lost or exposed secret

You cannot retrieve or change a webhook's secret. If you forget it or need to rotate it, delete the webhook and register it again. The new webhook has a new secret. Save that secret securely when it is returned.

3. Receive a reservation update​

SpotHero sends an HTTP POST to your registered URI. The request body has a data list. Usually, the list contains one reservation. Your endpoint must still handle multiple items in one message. A reservation can also appear more than once in the list with different facility_id values. Process every item and keep its facility_id with its reservation data; do not deduplicate items using only reservation_id.

Messages can contain new reservations and changes to reservations you have received before. A test reservation in staging will typically produce one reservation item. Do not expect the usual test flow to create a multi-item message or repeated reservation items with different facility IDs.

Example body:

{
"data": [
{
"reservation_id": "3589785",
"facility_id": "cf_01hgh41phpfk8sbdkknw90601s",
"start_date": "2026-06-05T14:36:00Z",
"end_date": "2026-06-10T14:36:00Z",
"status": "VALID",
"last_modified": "2026-06-05T14:36:12.753623Z",
"payment_info": {
"price": 1200
}
}
]
}

The request also includes these headers:

HeaderWhat it tells you
SpotHero-Webhook-IdWhich registered webhook sent the message.
SpotHero-Reservation-VersionThe reservation version for this delivery. Use it to ignore duplicate or older deliveries.
SpotHero-Webhook-TimestampWhen SpotHero created the delivery, in ISO 8601 format.
SpotHero-Webhook-SignatureA value you check to confirm that the request came from SpotHero and was not changed.

4. Verify the message signature​

Always verify the signature before using the request body. The signature is an HMAC-SHA-384 value. Calculate it with the shared secret from registration:

  1. Keep the raw request body exactly as received. Do not parse and re-create the JSON first. Even a change to spaces or field order changes the signature.
  2. Join these values in this order, with a | between them: webhook ID, reservation version, webhook timestamp, and raw request body.
  3. Encode the joined message as UTF-8.
  4. Calculate HMAC-SHA-384 using the webhook secret. Encode the result as lowercase hexadecimal and add sha384= at the start.
  5. Compare your result with SpotHero-Webhook-Signature using a constant-time comparison function from your language or security library.

Only process the JSON body after the signature check succeeds. Replace headers, request_body_bytes, and <webhook_secret> with values from your web framework and secure secret storage.

For example, these header values:

SpotHero-Webhook-Id: i_wh_2x4y6z8a0b1c2d3e4f5g6h7j8k
SpotHero-Reservation-Version: 1000000000
SpotHero-Webhook-Timestamp: 2026-09-29T20:24:08Z

and this exact request body:

{"data":[]}

make this message to sign (the last part is the raw body):

i_wh_2x4y6z8a0b1c2d3e4f5g6h7j8k|1000000000|2026-09-29T20:24:08Z|{"data":[]}

Python example​

This example uses Python's built-in hmac and hashlib modules. Get raw_body from your web framework before it parses the JSON. The header values below must be the exact values received in the request.

import hashlib
import hmac

secret = "<webhook_secret>"
webhook_id = headers["SpotHero-Webhook-Id"]
reservation_version = headers["SpotHero-Reservation-Version"]
timestamp = headers["SpotHero-Webhook-Timestamp"]
signature = headers["SpotHero-Webhook-Signature"]
raw_body = request_body_bytes # Exact request body bytes, not parsed JSON

signed_message = b"|".join(
[
webhook_id.encode("utf-8"),
reservation_version.encode("utf-8"),
timestamp.encode("utf-8"),
raw_body,
]
)

digest = hmac.new(
secret.encode("utf-8"),
signed_message,
hashlib.sha384,
).hexdigest()
expected_signature = f"sha384={digest}"

if not hmac.compare_digest(expected_signature, signature):
raise ValueError("Invalid webhook signature")

5. Process the update and respond​

Deliveries can arrive more than once or out of order. For each reservation item, save the latest SpotHero-Reservation-Version you have processed. Track it with that item's facility_id and reservation_id, because the same reservation can appear with different facility IDs. Process an item only if its version is greater than the saved version for that facility and reservation. Ignore it if its version is the same or lower. This prevents an older delivery from replacing newer data.

After you verify and safely accept the delivery, return any HTTP 2xx status. The response body does not matter. A non-2xx response is an error, and SpotHero may retry the delivery. Make your processing safe to repeat, because a retry may send a delivery you have already received.

6. Manage your registered webhooks​

Use your bearer access token for each management request:

ActionRequest
List your webhooksGET /v2/webhooks
Get one webhookGET /v2/webhooks/{id}
Change webhook settingsPOST /v2/webhooks/{id}
Delete a webhookDELETE /v2/webhooks/{id}

You can change the webhook settings. You cannot change the endpoint URI, secret, type, or active state. You can have only one webhook of each type registered at a time. To change the endpoint URI, prepare the new endpoint, delete the existing webhook, and then register the replacement. Because the two webhooks cannot overlap, keep using reservation sync to check for updates during the change. See the optional settings section below to request extra reservation fields.

Optional: include extra reservation fields​

By default, webhook messages contain the basic reservation fields. You can ask for extra fields by adding an includes list to the webhook's settings. Your account must have access to each projection. If it does not, the API ignores that projection. The available projections are listed in Optional reservation fields.

For example, update the webhook to include vehicle and license plate details:

POST https://integrations.staging.spothero.com/v2/webhooks/i_wh_01hgeb24jxfn092zyey1ws6w3a
Authorization: Bearer <staging_access_token>
Content-Type: application/json

{
"settings": {
"includes": ["vehicle", "license_plate"]
}
}

For full request and response details, see the webhook API reference.