Skip to content

Webhooks

TurnStay sends real-time notifications to your server when events occur — payment completions, transaction updates, refunds, and more.

Configure webhook endpoints in the TurnStay Dashboard under Settings > Webhooks.

All webhooks follow this structure:

{
"type": "merchant_of_record.payment_intent.succeeded",
"data": {
"object": {
"id": "45678",
"object": "payment_intent",
"status": "PROCESSED",
"billing_amount": 100000,
"billing_currency": "ZAR",
"processing_amount": 5200,
"processing_currency": "EUR",
"merchant_reference": "BOOKING-2026-001",
"company_id": 10,
"account_id": 123
}
}
}

Your webhook endpoint must:

  • Accept POST requests with a JSON body.
  • Return HTTP 200 to acknowledge receipt.
  • Be publicly accessible (not behind a VPN).
  • Handle duplicate events gracefully (idempotency).
@app.route("/webhooks/turnstay", methods=["POST"])
def turnstay_webhook():
event = request.get_json()
if event["type"] == "merchant_of_record.payment_intent.succeeded":
intent = event["data"]["object"]
mark_booking_as_paid(
reference=intent["merchant_reference"],
amount=intent["billing_amount"],
)
return jsonify({"ok": True}), 200

If your endpoint does not return HTTP 200, TurnStay retries delivery with exponential backoff. Ensure your handler is idempotent — the same event may be delivered more than once.

When an API request fails, TurnStay returns a JSON error response:

{
"detail": "billing_amount is required"
}
HTTP statusMeaning
400Bad request — check the detail field for the cause.
401Unauthorized — invalid or missing API key.
404Resource not found.
405Method not allowed — check the HTTP method.
422Validation error — invalid field values.
500Server error — retry the request or contact support.

If you set callback_url when creating a Payment Intent, TurnStay also POSTs to {your_callback_url}/callback/payment/intent. See Payment Events for the callback payload format.

Webhooks are recommended over callbacks for new integrations.