Skip to content

Getting Started

This guide will walk you through integrating TibiHooks into your SaaS application. By the end, you'll be able to create Applications for your customers, send events when things happen in their accounts, and let your customers manage their own webhooks through dedicated portals.

Prerequisites

  • A TibiHooks account with a management API key
  • Your TibiHooks instance URL
  • A SaaS application or platform with customers who need webhook notifications
  • Customer database to store TibiHooks Application IDs and API keys

Step 1: Create Applications for Your Customers

Applications represent a piece of context - typically, each of your customers. When a customer signs up, you create an Application for them via the TibiHooks API using your management API key.

bash
# Create an application for a customer using your management API key
curl -X POST https://your-tibihooks-instance.com/api/application \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_MANAGEMENT_API_KEY" \
  -d '{"name": "Customer: Acme Corp", "enabled": true}'

# Response: {"id": "uuid", "name": "Customer: Acme Corp", "apiKey": "tbh_..."}

Important: Store each customer's Application ID and API key securely in your database. You'll use the customer's API key when sending events for that specific customer. The API key is only shown once when the application is created!

Understanding Applications

Each Application provides:

  • Isolation: Customers' webhooks and events are completely separated
  • API Key: Unique authentication for sending events to that customer's webhooks
  • Portal Access: Each customer gets their own portal to manage their webhooks
  • Independent Configuration: Each customer can have their own webhook endpoints

Step 2: Send Events for Your Customers

When something happens in a customer's account (order created, payment received, etc.), send an event to TibiHooks using that customer's API key. TibiHooks will then deliver the event to all webhooks registered by that customer.

PHP Example

php
// In your application code - send events for a specific customer
function notifyCustomerWebhooks(Customer $customer, array $eventData): void
{
    // Get this customer's TibiHooks API key from your database
    $apiKey = $customer->getTibiHooksApiKey();
    $tibiHooksUrl = $_ENV['TIBIHOOKS_URL'];

    $ch = curl_init("{$tibiHooksUrl}/api/event");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            "X-API-KEY: {$apiKey}"
        ],
        CURLOPT_POSTFIELDS => json_encode($eventData)
    ]);

    $response = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($statusCode !== 201) {
        // Handle error (log, retry, alert)
        error_log("Failed to send webhook event: {$response}");
    }
}

// Usage example: When a customer's order is created
$customer = getCurrentCustomer();
notifyCustomerWebhooks($customer, [
    'event_type' => 'order.created',
    'order_id' => 12345,
    'total' => 99.99,
    'items' => [
        ['sku' => 'WIDGET-1', 'quantity' => 2]
    ]
]);

Node.js Example

javascript
// In your application code - send events for a specific customer
async function notifyCustomerWebhooks(customer, eventData) {
    // Get this customer's TibiHooks API key from your database
    const apiKey = customer.tibiHooksApiKey;
    const tibiHooksUrl = process.env.TIBIHOOKS_URL;

    try {
        const response = await fetch(`${tibiHooksUrl}/api/event`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-API-KEY': apiKey
            },
            body: JSON.stringify(eventData)
        });

        if (!response.ok) {
            console.error('Failed to send webhook event:', await response.text());
        }

        return await response.json();
    } catch (error) {
        console.error('Error sending webhook event:', error);
    }
}

// Usage example: When a user registers for a customer
const customer = await getCurrentCustomer();
await notifyCustomerWebhooks(customer, {
    event_type: 'user.registered',
    user_id: 67890,
    email: 'newuser@example.com',
    registered_at: new Date().toISOString()
});

Python Example

python
import os
import requests

def notify_customer_webhooks(customer, event_data):
    # Get this customer's TibiHooks API key from your database
    api_key = customer.tibihooks_api_key
    tibihooks_url = os.environ['TIBIHOOKS_URL']

    try:
        response = requests.post(
            f'{tibihooks_url}/api/event',
            headers={
                'Content-Type': 'application/json',
                'X-API-KEY': api_key
            },
            json=event_data
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f'Failed to send webhook event: {e}')

# Usage example: When a customer receives a payment
customer = get_current_customer()
notify_customer_webhooks(customer, {
    'event_type': 'payment.received',
    'payment_id': 'pay_abc123',
    'amount': 149.99,
    'currency': 'USD',
    'status': 'completed'
})

Event Payload Guidelines

  • Any JSON structure: Send whatever data makes sense for your event
  • Max size: 1MB per event
  • Max depth: 10 levels of nested objects
  • Best practice: Include event_type field to help webhook consumers

Step 3: Let Customers Register Their Webhooks

Your customers need a way to register webhook endpoints that will receive the events. TibiHooks provides two options:

TibiHooks provides a dedicated portal for each customer (Application). This is the recommended approach as it saves you from building webhook management UI!

How it works:

  1. Each customer's Application has its own dedicated portal URL
  2. Give your customers access to their portal (link from your app, embed in iframe, etc.)
  3. Customers can register, update, and delete their webhooks themselves
  4. Customers can view delivery history and monitor webhook health
  5. You don't need to build any webhook management screens!

Portal URL:

https://your-tibihooks-instance.com/portal/{customer-application-id}

Example integration:

php
// In your application - show customer their webhook management portal
<a href="https://your-tibihooks-instance.com/portal/<?= $customer->getTibiHooksAppId() ?>"
   target="_blank">
    Manage Your Webhooks
</a>

Option B: Programmatic Registration via API

If you need to register webhooks programmatically (e.g., during customer onboarding), use the API:

bash
# Register a webhook for a specific customer using their API key
curl -X POST https://your-tibihooks-instance.com/api/webhooks \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: CUSTOMER_API_KEY" \
  -d '{"url": "https://customer-service.com/webhooks/endpoint"}'

# Response: {"id": "uuid", "url": "https://...", "enabled": true}

Use API registration when:

  • Automating webhook registration during customer onboarding
  • Integration with your own webhook management UI
  • Automated testing or deployment pipelines
  • Programmatic webhook provisioning based on customer actions

Step 4: Test Your Integration

Let's verify everything works end-to-end using webhook.site for testing.

Get a Test Webhook URL

  1. Visit webhook.site
  2. Copy your unique URL (e.g., https://webhook.site/12345678-abcd-...)

Register the Test Webhook for a Test Customer

You can register this test webhook via API using a test customer's API key:

bash
curl -X POST https://your-tibihooks-instance.com/api/webhooks \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: TEST_CUSTOMER_API_KEY" \
  -d '{"url": "https://webhook.site/YOUR-UNIQUE-ID"}'

Or access that customer's portal at https://your-tibihooks-instance.com/portal/{customer-application-id} and add it there.

Send a Test Event

From your application code or via curl using the test customer's API key:

bash
curl -X POST https://your-tibihooks-instance.com/api/event \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: TEST_CUSTOMER_API_KEY" \
  -d '{
    "event_type": "test.event",
    "message": "Hello from TibiHooks!",
    "timestamp": "2024-01-15T10:30:00Z"
  }'

# Response: {"id": "event-uuid", "status": "queued"}

Verify Delivery

  1. Go back to webhook.site
  2. You should see a POST request with your event payload
  3. Check the customer's portal (https://your-tibihooks-instance.com/portal/{customer-application-id}) to see delivery status and history

Troubleshooting:

  • No request received? Check webhook URL is correct and accessible
  • Delivery failed? Check the delivery attempts in the customer's portal for error details
  • Event not created? Verify API key is correct and application is enabled

Step 5: Your Customers Implement Webhook Receivers

This step is for your customers. They need to implement webhook endpoints in their systems to receive the events you send. Here's what they need to build:

PHP Example (Symfony/Laravel)

php
// WebhookController.php
public function handleTibiHooksWebhook(Request $request): JsonResponse
{
    // Get the raw JSON payload
    $payload = json_decode($request->getContent(), true);

    // Log for debugging
    Log::info('Received TibiHooks webhook', ['payload' => $payload]);

    try {
        // Process the event based on type
        match ($payload['event_type'] ?? null) {
            'order.created' => $this->handleOrderCreated($payload),
            'payment.received' => $this->handlePaymentReceived($payload),
            'user.registered' => $this->handleUserRegistered($payload),
            default => Log::warning('Unknown event type', ['type' => $payload['event_type'] ?? 'none'])
        };

        // IMPORTANT: Return 2xx status code to acknowledge receipt
        return new JsonResponse(['status' => 'received'], 200);

    } catch (\Exception $e) {
        // Log error but still return 2xx to prevent retries for permanent failures
        Log::error('Webhook processing failed', ['error' => $e->getMessage()]);
        return new JsonResponse(['status' => 'error', 'message' => $e->getMessage()], 200);
    }
}

private function handleOrderCreated(array $payload): void
{
    // Your business logic here
    $orderId = $payload['order_id'];
    // Process order...
}

Node.js Example (Express)

javascript
app.post('/webhooks/tibihooks', express.json(), async (req, res) => {
    const payload = req.body;

    console.log('Received TibiHooks webhook:', payload);

    try {
        // Process based on event type
        switch (payload.event_type) {
            case 'order.created':
                await handleOrderCreated(payload);
                break;
            case 'payment.received':
                await handlePaymentReceived(payload);
                break;
            default:
                console.warn('Unknown event type:', payload.event_type);
        }

        // Return 2xx status
        res.status(200).json({ status: 'received' });

    } catch (error) {
        console.error('Webhook processing failed:', error);
        // Still return 2xx to prevent retries
        res.status(200).json({ status: 'error', message: error.message });
    }
});

Python Example (Flask)

python
@app.route('/webhooks/tibihooks', methods=['POST'])
def handle_tibihooks_webhook():
    payload = request.get_json()

    logger.info(f'Received TibiHooks webhook: {payload}')

    try:
        event_type = payload.get('event_type')

        if event_type == 'order.created':
            handle_order_created(payload)
        elif event_type == 'payment.received':
            handle_payment_received(payload)
        else:
            logger.warning(f'Unknown event type: {event_type}')

        # Return 2xx status
        return jsonify({'status': 'received'}), 200

    except Exception as e:
        logger.error(f'Webhook processing failed: {e}')
        # Still return 2xx to prevent retries
        return jsonify({'status': 'error', 'message': str(e)}), 200

Best Practices for Webhook Receivers

  1. Return 2xx Status Codes: Always return 200-299 for successful receipt, even if processing fails
  2. Process Asynchronously: Queue the webhook for background processing to respond quickly
  3. Be Idempotent: Handle duplicate deliveries gracefully (store event IDs, check before processing)
  4. Timeout Quickly: TibiHooks times out after 30 seconds, respond faster than that
  5. Log Everything: Log all webhooks for debugging and audit trails
  6. Validate Payload: Check that required fields exist before processing
  7. Handle Errors Gracefully: Don't return 5xx errors for issues that won't be fixed by retries

Next Steps

Now that you have TibiHooks integrated, consider these best practices:

  • Customer Onboarding: Automatically create Applications when customers sign up
  • Portal Integration: Link to customer portals from your dashboard so customers can manage their webhooks
  • Monitoring: Your customers can view delivery history, success rates, and failure patterns in their dedicated portals
  • Documentation: Provide documentation to your customers on what events you send and how to use the portal
  • Event Standards: Define clear event types and payload structures for consistency
  • Webhook Security: Implement signature verification (coming soon)
  • Event Type Filtering: Set up webhooks for specific event types (coming soon)

Check the API documentation at /api/docs on your TibiHooks instance for complete endpoint reference.

Common Issues

Events not being delivered to customer webhooks?

  • Ensure customer's webhook URL is publicly accessible
  • Check firewall rules allow incoming connections from TibiHooks
  • Verify customer's endpoint returns 2xx status code
  • Review delivery attempts in the customer's portal for error messages

API key not working when sending events?

  • Verify you're using the correct customer's API key
  • Check API key is passed in X-API-KEY header
  • Ensure the customer's Application is enabled

Need to update customer's Application?

  • Use your TibiHooks management API key to manage Applications
  • Customer API keys are only for sending events, not managing Applications