Skip to content

Getting Started

This guide walks you through integrating Hookbase into your application to provide webhooks to your customers.

Overview

Hookbase provides two main packages for integration:

  1. @hookbase/sdk - Server-side SDK for sending webhooks and managing resources
  2. @hookbase/portal - Embeddable React components for customer-facing webhook management

Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Your App      │────▶│    Hookbase     │────▶│  Customer's     │
│   (Backend)     │     │      API        │     │   Endpoint      │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │                       │
        │                       │
        ▼                       ▼
┌─────────────────┐     ┌─────────────────┐
│   Your App      │     │   Hookbase      │
│   (Frontend)    │────▶│    Portal       │
└─────────────────┘     └─────────────────┘

Quick Start

1. Install the SDK

bash
npm install @hookbase/sdk

2. Initialize the Client

typescript
import { Hookbase } from '@hookbase/sdk';

const hookbase = new Hookbase({
  apiKey: process.env.HOOKBASE_API_KEY,
});

3. Create an Application for Your Customer

typescript
// When a new customer signs up
const app = await hookbase.applications.create({
  name: customer.name,
  uid: customer.id, // Your internal customer ID
  metadata: {
    plan: customer.plan,
    email: customer.email,
  },
});

4. Define Event Types

typescript
// Set up the event types your customers can subscribe to
await hookbase.eventTypes.create({
  name: 'order.created',
  displayName: 'Order Created',
  description: 'Triggered when a new order is placed',
  category: 'Orders',
});

await hookbase.eventTypes.create({
  name: 'order.shipped',
  displayName: 'Order Shipped',
  description: 'Triggered when an order ships',
  category: 'Orders',
});

await hookbase.eventTypes.create({
  name: 'payment.completed',
  displayName: 'Payment Completed',
  description: 'Triggered when payment is received',
  category: 'Payments',
});

5. Send Webhooks

typescript
// When an event occurs in your application
await hookbase.messages.send(customer.hookbaseAppId, {
  eventType: 'order.created',
  payload: {
    orderId: order.id,
    amount: order.total,
    currency: order.currency,
    items: order.items,
    customer: {
      id: order.customerId,
      email: order.customerEmail,
    },
  },
  eventId: `order_created_${order.id}`, // For deduplication
});

Embed the Portal

1. Install the Portal Package

bash
npm install @hookbase/portal

2. Generate a Portal Token

typescript
// Backend API endpoint
app.get('/api/webhook-portal-token', async (req, res) => {
  const customer = await getCustomerFromSession(req);

  const token = await hookbase.portalTokens.create(customer.hookbaseAppId, {
    expiresIn: 3600, // 1 hour
  });

  res.json({ token: token.token });
});

3. Embed the Portal

tsx
import { useState, useEffect } from 'react';
import {
  HookbasePortal,
  EndpointList,
  EndpointForm,
  SubscriptionManager,
  MessageLog,
} from '@hookbase/portal';
import '@hookbase/portal/styles.css';

function WebhookSettings() {
  const [portalToken, setPortalToken] = useState<string | null>(null);
  const [selectedEndpoint, setSelectedEndpoint] = useState(null);

  useEffect(() => {
    fetch('/api/webhook-portal-token')
      .then((res) => res.json())
      .then((data) => setPortalToken(data.token));
  }, []);

  if (!portalToken) return <div>Loading...</div>;

  return (
    <HookbasePortal token={portalToken}>
      <div className="grid grid-cols-2 gap-6">
        <div>
          <h2 className="text-xl font-semibold mb-4">Endpoints</h2>
          <EndpointForm
            onSuccess={(endpoint, secret) => {
              // Show the secret to the user - it won't be available again
              alert(`Save this secret: ${secret}`);
            }}
          />
          <EndpointList
            onEndpointClick={setSelectedEndpoint}
          />
        </div>

        <div>
          {selectedEndpoint ? (
            <>
              <h2 className="text-xl font-semibold mb-4">Subscriptions</h2>
              <SubscriptionManager endpointId={selectedEndpoint.id} />
            </>
          ) : (
            <p>Select an endpoint to manage subscriptions</p>
          )}
        </div>
      </div>

      <div className="mt-8">
        <h2 className="text-xl font-semibold mb-4">Delivery History</h2>
        <MessageLog
          limit={25}
          refreshInterval={30000}
        />
      </div>
    </HookbasePortal>
  );
}

Verify Incoming Webhooks

When your customers receive webhooks, they need to verify the signature.

Share This Code with Your Customers

typescript
import { createWebhook } from '@hookbase/sdk';

// Create a verifier with the endpoint's signing secret
const webhook = createWebhook(process.env.WEBHOOK_SECRET);

// Express.js example
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    const payload = webhook.verify(req.body.toString(), req.headers);

    // Process the webhook
    switch (payload.type) {
      case 'order.created':
        handleOrderCreated(payload.data);
        break;
      case 'payment.completed':
        handlePaymentCompleted(payload.data);
        break;
    }

    res.status(200).send('OK');
  } catch (error) {
    console.error('Webhook verification failed:', error);
    res.status(401).send('Invalid signature');
  }
});

Best Practices

1. Use Idempotency Keys

Always include an eventId when sending webhooks to prevent duplicate deliveries:

typescript
await hookbase.messages.send(appId, {
  eventType: 'order.created',
  payload: orderData,
  eventId: `order_created_${order.id}`,
});

2. Use Application UIDs

Map your internal customer IDs to Hookbase applications:

typescript
// Store the Hookbase app ID in your database
const app = await hookbase.applications.getOrCreate(customer.id, {
  name: customer.name,
});

// Later, use the UID to find the app
const app = await hookbase.applications.getByUid(customer.id);

3. Handle Delivery Failures

Monitor failed deliveries and alert your customers:

typescript
// List failed messages
const { data: failures } = await hookbase.messages.listAllOutbound(appId, {
  status: 'exhausted',
});

// Notify customer about failures
for (const failure of failures) {
  await notifyCustomer(appId, {
    message: `Webhook delivery failed to ${failure.endpointUrl}`,
    eventType: failure.eventType,
    error: failure.lastError,
  });
}

4. Implement Retry Logic

When your customers' endpoints fail, Hookbase automatically retries with exponential backoff. You can also manually retry:

typescript
// Retry a specific failed delivery
await hookbase.messages.retry(appId, outboundMessageId);

// Resend to all endpoints
await hookbase.messages.resend(appId, messageId);

5. Use Event Categories

Organize your event types into categories for better UX:

typescript
await hookbase.eventTypes.create({
  name: 'order.created',
  category: 'Orders',
});

await hookbase.eventTypes.create({
  name: 'order.shipped',
  category: 'Orders',
});

await hookbase.eventTypes.create({
  name: 'invoice.created',
  category: 'Billing',
});

Webhook Payload Format

Webhooks are delivered with the following structure:

json
{
  "type": "order.created",
  "data": {
    "orderId": "ord_123",
    "amount": 99.99,
    "currency": "USD"
  },
  "timestamp": "2024-01-15T10:30:00Z",
  "webhookId": "msg_abc123"
}

Headers

HeaderDescription
webhook-idUnique identifier for this delivery attempt
webhook-timestampUnix timestamp when the webhook was sent
webhook-signatureHMAC-SHA256 signature for verification
Content-Typeapplication/json

Rate Limits

ResourceLimit
API Requests1000/minute
Webhook Sends10000/minute
Portal Tokens100/hour per application

Rate limit headers are included in all API responses:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

Support

Released under the MIT License.