Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.h0p.co/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks send HTTP requests to your server when events occur in H0p. Use them to sync data with your systems, trigger automations, or build real-time integrations.
Webhooks are a Premium feature.

How webhooks work

1

Event occurs

Something happens in H0p (link created, clicked, etc.)
2

H0p sends request

An HTTP POST request is sent to your webhook URL with event data.
3

Your server responds

Your server processes the event and returns a 2xx status code.

Available events

EventDescriptionTrigger
link.createdNew short link createdDashboard or API
link.updatedShort link modifiedDashboard or API
link.deletedShort link removedDashboard or API
link.clickedSomeone clicked a linkLink visitor

Creating a webhook

1

Go to Webhooks

Navigate to Webhooks in the sidebar under Developer Tools.
2

Click Create Webhook

Click the Create Webhook button to open the configuration form.
3

Enter your endpoint URL

Provide the URL where H0p should send events:
https://your-server.com/api/webhooks/h0p
Your endpoint must use HTTPS. HTTP endpoints are not supported.
4

Select events

Choose which events should trigger this webhook:
  • Check specific events (recommended)
  • Or select all events
Webhook Creation Form
5

Save the webhook

Click Create to save. H0p will provide a webhook secret for signature verification.
Store the webhook secret securely. You’ll need it to verify incoming requests.
Webhook Creation Form

Event payloads

All webhook payloads follow this structure:
{
  "id": "evt_abc123",
  "type": "link.created",
  "createdAt": "2024-01-15T10:30:00Z",
  "data": {
    // Event-specific data
  }
}
Sent when a new short link is created:
{
  "id": "evt_abc123",
  "type": "link.created",
  "createdAt": "2024-01-15T10:30:00Z",
  "data": {
    "link": {
      "id": "link_xyz789",
      "slug": "my-promo",
      "shortUrl": "https://links.company.com/my-promo",
      "destination": {
        "type": "link",
        "value": "https://example.com/landing"
      },
      "createdAt": "2024-01-15T10:30:00Z"
    }
  }
}
Sent when a short link is modified:
{
  "id": "evt_def456",
  "type": "link.updated",
  "createdAt": "2024-01-15T11:00:00Z",
  "data": {
    "link": {
      "id": "link_xyz789",
      "slug": "my-promo",
      "shortUrl": "https://links.company.com/my-promo",
      "destination": {
        "type": "link",
        "value": "https://example.com/new-landing"
      },
      "updatedAt": "2024-01-15T11:00:00Z"
    }
  }
}
Sent when a short link is deleted:
{
  "id": "evt_ghi789",
  "type": "link.deleted",
  "createdAt": "2024-01-15T12:00:00Z",
  "data": {
    "link": {
      "id": "link_xyz789",
      "slug": "my-promo",
      "deletedAt": "2024-01-15T12:00:00Z"
    }
  }
}
Sent when someone clicks a short link:
{
  "id": "evt_jkl012",
  "type": "link.clicked",
  "createdAt": "2024-01-15T14:30:00Z",
  "data": {
    "link": {
      "id": "link_xyz789",
      "slug": "my-promo"
    },
    "click": {
      "country": "US",
      "city": "San Francisco",
      "region": "California",
      "device": "mobile",
      "browser": "Chrome",
      "os": "iOS",
      "referer": "https://twitter.com",
      "utmSource": "twitter",
      "utmMedium": "social",
      "utmCampaign": "spring_launch",
      "isQrCode": false,
      "clickedAt": "2024-01-15T14:30:00Z"
    }
  }
}

Verifying webhook signatures

Every webhook request includes a signature header for verification. This ensures the request came from H0p and wasn’t tampered with.

Signature header

X-H0p-Signature: sha256=abc123...

Verification example

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  const trusted = `sha256=${expectedSignature}`;
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(trusted)
  );
}

// In your webhook handler
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-h0p-signature'];
  const payload = JSON.stringify(req.body);

  if (!verifyWebhook(payload, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const event = req.body;
  console.log(`Received ${event.type} event`);

  res.status(200).send('OK');
});
Always verify webhook signatures in production. Never trust webhook data without verification.

Retry behavior

If your server doesn’t respond with a 2xx status code, H0p retries with exponential backoff:
AttemptDelay
1Immediate
212 seconds
3~2.5 minutes
4~30 minutes
5~6 hours
624 hours
After 18 consecutive failures (6 attempts x 3), the webhook is automatically disabled to prevent further failed deliveries.
You’ll receive an email notification when a webhook is disabled due to failures.

Monitoring webhooks

Delivery history

View recent webhook deliveries in the webhook details page:
  • Event type - Which event triggered the delivery
  • Status - Success or failure
  • Response code - HTTP status returned by your server
  • Timestamp - When the delivery was attempted
Webhook Delivery History

Re-enable disabled webhooks

If a webhook is disabled due to failures:
  1. Fix the issue with your endpoint
  2. Go to the webhook settings
  3. Toggle the webhook back on
  4. The failure counter resets

Best practices

Return a 2xx response as quickly as possible. Process events asynchronously in a queue.
app.post('/webhook', async (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  // Process async
  await queue.add('process-webhook', req.body);
});
Network issues can cause duplicate deliveries. Use the event id to deduplicate:
if (await hasProcessedEvent(event.id)) {
  return; // Already processed
}
Only process events you expect. Ignore unknown event types gracefully:
switch (event.type) {
  case 'link.created':
    await handleLinkCreated(event);
    break;
  case 'link.clicked':
    await handleLinkClicked(event);
    break;
  default:
    console.log(`Unknown event type: ${event.type}`);
}
Always use HTTPS endpoints. HTTP is not supported and exposes your data.
Use environment variables or a secrets manager. Never commit secrets to version control.

Testing webhooks

Local development

Use a tunneling service to expose your local server:
# Install ngrok
brew install ngrok

# Start your local server
npm run dev

# Expose it
ngrok http 3000
Use the ngrok URL (e.g., https://abc123.ngrok.io/webhook) as your webhook endpoint.

Webhook testing tools

  1. Create a webhook with a test endpoint
  2. Trigger an event (create a link)
  3. Check the delivery history for the request/response

Limits

LimitValue
Webhooks per organization50
Concurrent deliveries10
Request timeout30 seconds
Max retries6

API access

Manage webhooks programmatically:
# Create webhook
curl -X POST https://api.h0p.co/webhook \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["link.created", "link.clicked"]
  }'

# List webhooks
curl https://api.h0p.co/webhook/list \
  -H "x-api-key: YOUR_API_KEY"
See the API Reference for complete documentation.