Endpoints

Endpoints are HTTP URLs belonging to your customers that receive webhook events. Each endpoint subscribes to specific event types and has its own secret for signature verification.

The Endpoint Object

{
  "id": "ep_4Bz5Z0sR1tM7oP3xD",
  "application_id": "app_2Zy3X8qP9rK5mN1vB",
  "url": "https://api.customer.com/webhooks",
  "status": "active",
  "event_types": ["user.created", "user.updated", "invoice.paid"],
  "headers": {
    "X-Customer-ID": "cust_123",
    "X-Environment": "production"
  },
  "rate_limit_rps": 100,
  "timeout_ms": 30000,
  "secret": "whsec_a1b2c3d4e5f6...",
  "secret_version": 1,
  "failure_count": 0,
  "disabled_at": null,
  "created_at": "2026-01-18T10:30:00Z",
  "updated_at": "2026-01-20T15:30:00Z"
}
AttributeTypeDescription
idstringUnique identifier (KSUID format)
application_idstringApplication this endpoint belongs to
urlstringHTTPS endpoint URL (max 2048 chars)
statusenumactive, paused, or disabled
event_typesarrayEvent types this endpoint subscribes to
headersobjectCustom headers sent with each webhook
rate_limit_rpsintegerMax requests per second (1-10000)
timeout_msintegerRequest timeout in milliseconds (1000-120000)
secretstringSigning secret (64 hex characters)
failure_countintegerRecent consecutive failures (circuit breaker)
Security: The secret field is only returned when creating or retrieving a specific endpoint. It's hidden from list operations for security.

Create Endpoint

Create a new endpoint to receive webhook events. The endpoint must use HTTPS and will be automatically assigned a unique signing secret.

POST /v1/endpoints

Request Body

{
  "application_id": "app_2Zy3X8qP9rK5mN1vB",
  "url": "https://api.customer.com/webhooks",
  "event_types": ["user.created", "user.updated"],
  "headers": {
    "X-Customer-ID": "cust_123"
  },
  "rate_limit_rps": 50,
  "timeout_ms": 15000
}
ParameterRequiredDescription
application_idYesApplication ID
urlYesHTTPS URL (no localhost/private IPs)
event_typesNoArray of event type names (default: [])
headersNoCustom headers object
rate_limit_rpsNoRequests per second (default: 100)
timeout_msNoTimeout in milliseconds (default: 30000)

Response

{
  "id": "ep_4Bz5Z0sR1tM7oP3xD",
  "application_id": "app_2Zy3X8qP9rK5mN1vB",
  "url": "https://api.customer.com/webhooks",
  "status": "active",
  "event_types": ["user.created", "user.updated"],
  "headers": {
    "X-Customer-ID": "cust_123"
  },
  "rate_limit_rps": 50,
  "timeout_ms": 15000,
  "secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
  "secret_version": 1,
  "failure_count": 0,
  "created_at": "2026-01-20T15:30:00Z"
}
Save the secret! The secret is only returned once during creation. Store it securely - your customer will need it to verify webhook signatures.

Code Examples

Node.js
const response = await fetch('https://api.hookmesh.com/v1/endpoints', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    application_id: 'app_2Zy3X8qP9rK5mN1vB',
    url: 'https://api.customer.com/webhooks',
    event_types: ['user.created', 'user.updated'],
    headers: {
      'X-Customer-ID': 'cust_123'
    },
    rate_limit_rps: 50,
    timeout_ms: 15000
  })
});

const endpoint = await response.json();
console.log('Endpoint created:', endpoint.id);
console.log('Secret:', endpoint.secret);
Python
import requests
import os

response = requests.post(
    'https://api.hookmesh.com/v1/endpoints',
    headers={'Authorization': f'Bearer {os.environ.get("HOOKMESH_API_KEY")}'},
    json={
        'application_id': 'app_2Zy3X8qP9rK5mN1vB',
        'url': 'https://api.customer.com/webhooks',
        'event_types': ['user.created', 'user.updated'],
        'headers': {
            'X-Customer-ID': 'cust_123'
        },
        'rate_limit_rps': 50,
        'timeout_ms': 15000
    }
)

endpoint = response.json()
print(f'Endpoint created: {endpoint["id"]}')
print(f'Secret: {endpoint["secret"]}')
Go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func main() {
    payload := map[string]interface{}{
        "application_id": "app_2Zy3X8qP9rK5mN1vB",
        "url":            "https://api.customer.com/webhooks",
        "event_types":    []string{"user.created", "user.updated"},
        "headers": map[string]string{
            "X-Customer-ID": "cust_123",
        },
        "rate_limit_rps": 50,
        "timeout_ms":     15000,
    }

    body, _ := json.Marshal(payload)
    req, _ := http.NewRequest("POST",
        "https://api.hookmesh.com/v1/endpoints",
        bytes.NewBuffer(body))

    req.Header.Set("Authorization", "Bearer "+os.Getenv("HOOKMESH_API_KEY"))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    fmt.Printf("Endpoint: %s\nSecret: %s\n", result["id"], result["secret"])
}
PHP
<?php

$ch = curl_init('https://api.hookmesh.com/v1/endpoints');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . getenv('HOOKMESH_API_KEY'),
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'application_id' => 'app_2Zy3X8qP9rK5mN1vB',
    'url' => 'https://api.customer.com/webhooks',
    'event_types' => ['user.created', 'user.updated'],
    'headers' => [
        'X-Customer-ID' => 'cust_123',
    ],
    'rate_limit_rps' => 50,
    'timeout_ms' => 15000,
]));

$response = curl_exec($ch);
$endpoint = json_decode($response, true);
echo "Endpoint: {$endpoint['id']}\n";
echo "Secret: {$endpoint['secret']}\n";

curl_close($ch);

List Endpoints

Retrieve all endpoints for an application with filtering options.

GET /v1/endpoints?application_id={id}&status={status}

Query Parameters

ParameterRequiredDescription
application_idYesFilter by application
statusNoFilter by status (active, paused, disabled)
pageNoPage number (default: 1)
per_pageNoItems per page (default: 25, max: 100)

Response

{
  "data": [
    {
      "id": "ep_4Bz5Z0sR1tM7oP3xD",
      "application_id": "app_2Zy3X8qP9rK5mN1vB",
      "url": "https://api.customer.com/webhooks",
      "status": "active",
      "event_types": ["user.created", "user.updated"],
      "failure_count": 0,
      "created_at": "2026-01-20T15:30:00Z"
    }
  ],
  "pagination": {
    "total": 5,
    "page": 1,
    "per_page": 25,
    "total_pages": 1
  }
}
Note: The secret field is not included in list responses for security. Retrieve individual endpoints to view secrets.

Retrieve Endpoint

Get detailed information about a specific endpoint, including its secret.

GET /v1/endpoints/{endpoint_id}

Response

{
  "id": "ep_4Bz5Z0sR1tM7oP3xD",
  "application_id": "app_2Zy3X8qP9rK5mN1vB",
  "url": "https://api.customer.com/webhooks",
  "status": "active",
  "event_types": ["user.created", "user.updated"],
  "headers": {
    "X-Customer-ID": "cust_123"
  },
  "rate_limit_rps": 50,
  "timeout_ms": 15000,
  "secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
  "secret_version": 1,
  "failure_count": 0,
  "disabled_at": null,
  "created_at": "2026-01-20T15:30:00Z",
  "updated_at": "2026-01-20T15:30:00Z"
}

Update Endpoint

Update an endpoint's configuration. All fields are optional.

PATCH /v1/endpoints/{endpoint_id}

Request Body

{
  "url": "https://api.customer.com/webhooks/v2",
  "event_types": ["user.created", "user.updated", "user.deleted"],
  "status": "paused",
  "rate_limit_rps": 75,
  "timeout_ms": 20000
}

Status Values

StatusDescription
activeEndpoint receives all subscribed webhooks
pausedTemporarily disabled by user (no deliveries)
disabledDisabled by circuit breaker (automatic)

Delete Endpoint

Soft delete an endpoint. No new webhooks will be delivered, but the endpoint can be restored.

DELETE /v1/endpoints/{endpoint_id}

Response

{
  "id": "ep_4Bz5Z0sR1tM7oP3xD",
  "deleted": true,
  "deleted_at": "2026-01-20T16:50:00Z"
}

Rotate Secret

Generate a new signing secret for an endpoint. The old secret remains valid during a grace period to allow zero-downtime rotation.

POST /v1/endpoints/{endpoint_id}/rotate-secret

Response

{
  "id": "ep_4Bz5Z0sR1tM7oP3xD",
  "secret": "whsec_x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0r1s2t3u4v5w6",
  "secret_version": 2,
  "old_secret_expires_at": "2026-01-21T16:00:00Z"
}

Rotation Process

// Step 1: Rotate the secret
const rotateResponse = await fetch(
  'https://api.hookmesh.com/v1/endpoints/ep_xyz/rotate-secret',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  }
);

const { secret: newSecret, old_secret_expires_at } = await rotateResponse.json();

// Step 2: Update your verification code to try both secrets
function verifySignature(payload, signature, timestamp) {
  // Try new secret first
  if (verify(payload, signature, timestamp, newSecret)) {
    return true;
  }
  // Fall back to old secret during transition period
  if (verify(payload, signature, timestamp, oldSecret)) {
    return true;
  }
  return false;
}

// Step 3: After 24 hours, remove old secret from your code
// The old secret is automatically invalidated

Send Test Webhook

Send a test webhook to an endpoint to verify it's working correctly.

POST /v1/endpoints/{endpoint_id}/test

Request Body (Optional)

{
  "payload": {
    "type": "test",
    "message": "Testing webhook delivery",
    "timestamp": "2026-01-20T15:30:00Z"
  }
}

Response

{
  "job_id": "job_5nM8pQ1rK3vL9xB",
  "status": "queued",
  "message": "Test webhook queued for delivery"
}
Rate limits: Test webhooks are limited to 10 per endpoint per hour to prevent abuse.

Reset Circuit Breaker

Manually reset the circuit breaker for a disabled endpoint after fixing the issue.

POST /v1/endpoints/{endpoint_id}/reset-circuit

Response

{
  "id": "ep_4Bz5Z0sR1tM7oP3xD",
  "status": "active",
  "failure_count": 0,
  "message": "Circuit breaker reset, endpoint reactivated"
}
When to use: After your customer fixes their endpoint, reset the circuit breaker to resume webhook deliveries immediately instead of waiting for automatic recovery.

Common Patterns

Endpoint Subscription Management

Allow your customers to choose which events they want to receive:

async function updateCustomerWebhookSubscriptions(customerId, selectedEvents) {
  // Find the customer's endpoint
  const endpoints = await fetch(
    `https://api.hookmesh.com/v1/endpoints?application_id=${appId}`,
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  ).then(r => r.json());

  const endpoint = endpoints.data.find(ep =>
    ep.headers['X-Customer-ID'] === customerId
  );

  // Update their event subscriptions
  await fetch(`https://api.hookmesh.com/v1/endpoints/${endpoint.id}`, {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      event_types: selectedEvents
    })
  });
}

Custom Headers for Context

Use custom headers to pass additional context with each webhook:

{
  "headers": {
    "X-Customer-ID": "cust_123",
    "X-Tenant-ID": "tenant_abc",
    "X-Environment": "production",
    "X-Region": "us-west-2",
    "X-Account-Tier": "enterprise"
  }
}

These headers are sent with every webhook, making it easy for your customers to route and process events.

Rate Limiting Strategy

// Set conservative rate limits for new customers
const newCustomerEndpoint = {
  rate_limit_rps: 10,
  timeout_ms: 30000
};

// Increase limits for enterprise customers
const enterpriseEndpoint = {
  rate_limit_rps: 500,
  timeout_ms: 15000 // Faster timeout for reliable endpoints
};

Best Practices

  • Always use HTTPS: Hook Mesh enforces HTTPS for security
  • Set appropriate timeouts: Match your customer's response time (15-30 seconds typical)
  • Use custom headers: Include customer/tenant IDs for easier routing
  • Test before production: Use the test endpoint feature to verify configuration
  • Rotate secrets regularly: Update secrets every 90 days for compliance
  • Monitor failure counts: High failure counts indicate customer endpoint issues
  • Document the secret: Ensure customers save their secret during endpoint creation

Next Steps