Webhooks Quickstart
PLEASE NOTE:
Webhooks are available for PodPlay clients on the Pro or Autonomous plan. Webhooks are not available to PodPlay clients on the Basic or Basic+ plan.
This is a living document we continue to update as we iterate on our Webhooks.
Overview
PodPlay webhooks provide real-time notifications about important events in our system. This document describes all available webhook events, their payloads, and integration requirements.
Where to Find Webhook Schemas
Swagger UI: Open your PodPlay API documentation at /apis/v2 and go to the Schemas section (scroll down). You’ll find all PodPlay trigger schemas including:
PodPlay Trigger Headers:
PodplayTriggerHeaders- HTTP headers for trigger requestsComplete Payload Structures:
PodplayTriggerUserCreatedPayload(referencesUserDto,AreaDto)PodplayTriggerUserUpdatedPayload(referencesUserDto)PodplayTriggerPurchaseCreatedPayload(referencesOrderDto)PodplayTriggerBookingCreatedPayload(referencesOrderDto)PodplayTriggerMembershipEnrollmentCreatedPayload(referencesMembershipEnrollmentDto)PodplayTriggerEventCreatedPayload(referencesEventDto)PodplayTriggerEventUpdatedPayload(referencesEventDto)PodplayTriggerEventSignupCreatedPayload(referencesEventSignupDto,EventDto)PodplayTriggerEventSignupCanceledPayload(referencesEventSignupDto,EventDto)
Code Reference: All PodPlay trigger schemas are defined in webhook-schemas.ts and automatically injected into Swagger’s components.schemas.
Webhook Configuration
Delivery Settings
Method: POST
Content-Type: application/json
Timeout: 30 seconds
Retry Logic: Up to 3 retries with exponential backoff (5s, 10s, 15s)
Max Retries: 3
Response Requirements
Success: Return HTTP 200 status code
Failure: Any non-200 status will trigger retry logic
Webhook Headers
Every webhook request includes the following headers:
Header | Description | Example |
|---|---|---|
| Always |
|
| PodPlay webhook identifier |
|
| Unique webhook configuration ID |
|
| The type of event being sent |
|
| Unique identifier for this event instance |
|
| The tenant where the event occurred |
|
Available Webhook Events
📚 Detailed Documentation: For comprehensive field references and examples, see the detailed documentation files in the docs/ directory.
1. User Events
user.created
Triggered when: A new user account is created in the system
Payload Structure:
{
"event": {
"type": "user.created",
"timestamp": "2025-08-13T02:10:22.432Z",
"tenantId": "penguinstaging",
"data": {
"user": {
"id": "s0BdN8S6qWTmj5IPQdyqtte0a9N2",
"email": "[email protected]",
"firstName": "Marcelo Fer",
"lastName": "Admin",
"roles": ["PODPLAY_ADMIN", "ADMIN"]
},
"creator": {
"id": "s0BdN8S6qWTmj5IPQdyqtte0a9N2"
},
"areas": []
}
},
"webhook": {
"id": "43f36627-ac4a-4c05-9b7f-949a467c68d0",
"displayName": "mock application",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
user.updated
Triggered when: A user profile is updated
Payload Structure:
{
"event": {
"type": "user.updated",
"timestamp": "2025-08-13T02:10:22.432Z",
"tenantId": "penguinstaging",
"data": {
"user": {
"id": "s0BdN8S6qWTmj5IPQdyqtte0a9N2",
"email": "[email protected]",
"firstName": "Marcelo Fer",
"lastName": "Admin",
"roles": ["PODPLAY_ADMIN", "ADMIN"]
}
}
},
"webhook": {
"id": "43f36627-ac4a-4c05-9b7f-949a467c68d0",
"displayName": "mock application",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
2. Purchase Events
purchase.created
Triggered when: A confirmed purchase is completed (monetary transaction)
Business Logic: Only fired for orders that meet these criteria:
Has monetary value (
subtotal > 0ortotal > 0)Source is one of:
POD_SHOP,VENDING_MACHINE,CUSTOMER_SERVICE,REPLAY,COACHING,CHECK_IN,RESCHEDULEStatus is
CONFIRMED
Payload Structure:
{
"event": {
"type": "purchase.created",
"timestamp": "2025-08-13T02:04:16.252Z",
"tenantId": "penguinstaging",
"data": {
"order": {
"id": "b29321fb-e571-4f3e-b29f-be8a81c9025c",
"code": "9795O6",
"status": "CONFIRMED",
"source": "CUSTOMER_SERVICE",
"total": 3.8,
"items": [
{
"name": "Gatorade test01",
"quantity": 1,
"price": 3.8,
"total": 3.8
}
]
}
}
},
"webhook": {
"id": "983ec1c3-1b32-4dda-a65a-ee7a9c029649",
"displayName": "mock app2",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
3. Booking Events
booking.created
Triggered when: A booking is confirmed (court/pod reservation)
Business Logic: Only fired for orders that meet these criteria:
Source is
BOOKINGStatus is
CONFIRMED
Payload Structure:
{
"event": {
"type": "booking.created",
"timestamp": "2025-08-13T02:45:54.711Z",
"tenantId": "penguinstaging",
"data": {
"order": {
"id": "0e95ce5d-1dbe-4614-986a-4d11dfaef0fb",
"code": "E7BWJR",
"status": "CONFIRMED",
"source": "BOOKING",
"total": 0,
"event": {
"id": "834281a8-e6d4-4c22-ad3f-6022b6e64efb"
},
"pod": {
"id": "hudson-multi"
}
}
}
},
"webhook": {
"id": "983ec1c3-1b32-4dda-a65a-ee7a9c029649",
"displayName": "mock app2",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
4. Membership Events
membership-enrollment.created
Triggered when: A user enrolls in a membership plan
Payload Structure:
{
"event": {
"type": "membership-enrollment.created",
"timestamp": "2025-08-13T02:10:22.432Z",
"tenantId": "penguinstaging",
"data": {
"membershipEnrollment": {
"id": "enrollment-id",
"status": "ACTIVE",
"startDate": "2025-08-13T00:00:00.000Z",
"endDate": "2026-08-13T00:00:00.000Z"
}
}
},
"webhook": {
"id": "43f36627-ac4a-4c05-9b7f-949a467c68d0",
"displayName": "mock application",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
5. Event Events
event.created
Triggered when: A new event is created
Documentation: See Event Events Documentation for complete details
Payload Structure:
{
"event": {
"type": "event.created",
"timestamp": "2025-08-13T02:10:48.085Z",
"tenantId": "penguinstaging",
"data": {
"event": {
"id": "9e17bc71-a3a6-4444-9c47-8fde110985b9",
"subtype": null,
"customType": "Private",
"name": null,
"description": "",
"startTime": "2025-08-13T02:00:00.000Z",
"endTime": "2025-08-13T02:30:00.000Z",
"status": "DRAFT",
"bookingMode": "USER_BOOKED"
}
}
},
"webhook": {
"id": "43f36627-ac4a-4c05-9b7f-949a467c68d0",
"displayName": "mock application",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
event.updated
Triggered when: An existing event is modified
Payload Structure: Similar to event.created but with updated event data
Documentation: See Event Events Documentation for complete details
6. Event Signup Events
event-signup.created
Triggered when: A user signs up for an event
Payload Structure:
{
"event": {
"type": "event-signup.created",
"timestamp": "2025-08-13T02:10:22.432Z",
"tenantId": "penguinstaging",
"data": {
"eventSignup": {
"id": "19de5456-66c7-4813-9eb9-b1ed765839f1",
"status": "PENDING",
"mode": "USER_BOOKED"
},
"event": {
"id": "834281a8-e6d4-4c22-ad3f-6022b6e64efb",
"name": "Open Play",
"startTime": "2025-08-08T17:30:00.000Z"
}
}
},
"webhook": {
"id": "43f36627-ac4a-4c05-9b7f-949a467c68d0",
"displayName": "mock application",
"url": "<https://ecfa5d427026.ngrok-free.app/webhook>"
}
}
event-signup.canceled
Triggered when: A user cancels their event signup
Payload Structure: Similar to event-signup.created but with canceled status
Integration Examples
Node.js/Express Example
app.post('/webhook', (req, res) => {
const { event, webhook } = req.body;
console.log(`Received ${event.type} event for tenant ${event.tenantId}`);
// Process the event based on type
switch (event.type) {
case 'user.created':
handleUserCreated(event.data);
break;
case 'purchase.created':
handlePurchaseCreated(event.data);
break;
case 'booking.created':
handleBookingCreated(event.data);
break;
// ... handle other event types
}
// Always return 200 to acknowledge receipt
res.status(200).json({ success: true });
});
function handleUserCreated(data) {
const { user, creator, areas } = data;
console.log(`New user created: ${user.email}`);
// Your business logic here
}
Python/Flask Example
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
data = request.get_json()
event = data['event']
webhook = data['webhook']
print(f"Received {event['type']} event for tenant {event['tenantId']}")
# Process the event based on type
if event['type'] == 'user.created':
handle_user_created(event['data'])
elif event['type'] == 'purchase.created':
handle_purchase_created(event['data'])
elif event['type'] == 'booking.created':
handle_booking_created(event['data'])
# ... handle other event types
# Always return 200 to acknowledge receipt
return jsonify({'success': True}), 200
def handle_user_created(data):
user = data['user']
print(f"New user created: {user['email']}")
# Your business logic here
Best Practices
1. Idempotency
Webhook events may be delivered multiple times due to retry logic
Implement idempotency using the
X-Event-IDheaderStore processed event IDs to avoid duplicate processing
2. Error Handling
Always return HTTP 200 for successful receipt
Log errors but don’t return error status codes (this triggers retries)
Implement proper error handling for your business logic
3. Security
Validate the
X-Tenant-IDheader matches your expected tenantConsider implementing webhook signature verification if needed
Use HTTPS for webhook endpoints
4. Performance
Process webhooks asynchronously when possible
Keep webhook processing time under 30 seconds
Implement proper logging and monitoring
Troubleshooting
Common Issues
Webhooks not being received
Check your endpoint is accessible from the internet
Verify your endpoint returns HTTP 200
Check webhook configuration in PodPlay admin
Webhooks being retried repeatedly
Ensure your endpoint returns HTTP 200
Check for errors in your webhook processing logic
Verify your endpoint can handle the payload size
Missing event data
Check the event type matches what you’re subscribed to
Verify the tenant ID matches your configuration
Review the webhook payload structure
Monitoring
Monitor webhook delivery success rates
Track processing times
Set up alerts for webhook failures
Log all webhook events for debugging
Support
For webhook-related issues or questions:
Check the PodPlay admin dashboard for webhook status
Review webhook delivery logs
Contact PodPlay support with specific error details
