Webhooks Quickstart

Edited

PLEASE NOTE:

  1. 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.

  2. 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 requests

  • Complete Payload Structures:

    • PodplayTriggerUserCreatedPayload (references UserDto, AreaDto)

    • PodplayTriggerUserUpdatedPayload (references UserDto)

    • PodplayTriggerPurchaseCreatedPayload (references OrderDto)

    • PodplayTriggerBookingCreatedPayload (references OrderDto)

    • PodplayTriggerMembershipEnrollmentCreatedPayload (references MembershipEnrollmentDto)

    • PodplayTriggerEventCreatedPayload (references EventDto)

    • PodplayTriggerEventUpdatedPayload (references EventDto)

    • PodplayTriggerEventSignupCreatedPayload (references EventSignupDto, EventDto)

    • PodplayTriggerEventSignupCanceledPayload (references EventSignupDto, 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

Content-Type

Always application/json

application/json

User-Agent

PodPlay webhook identifier

PodPlay-Webhooks/1.0

X-Webhook-ID

Unique webhook configuration ID

43f36627-ac4a-4c05-9b7f-949a467c68d0

X-Event-Type

The type of event being sent

user.updated

X-Event-ID

Unique identifier for this event instance

15123835660943403

X-Tenant-ID

The tenant where the event occurred

penguinstaging

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 > 0 or total > 0)

  • Source is one of: POD_SHOP, VENDING_MACHINE, CUSTOMER_SERVICE, REPLAY, COACHING, CHECK_IN, RESCHEDULE

  • Status 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 BOOKING

  • Status 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-ID header

  • Store 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-ID header matches your expected tenant

  • Consider 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

  1. Webhooks not being received

    • Check your endpoint is accessible from the internet

    • Verify your endpoint returns HTTP 200

    • Check webhook configuration in PodPlay admin

  2. 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

  3. 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