Skip to content

Routes API

Routes connect sources to destinations.

Endpoints

MethodPathDescription
GET/api/routesList routes
POST/api/routesCreate route
GET/api/routes/{id}Get route
PATCH/api/routes/{id}Update route
DELETE/api/routes/{id}Delete route
GET/api/routes/{id}/circuit-statusGet circuit breaker status
POST/api/routes/{id}/reset-circuitReset circuit breaker
PATCH/api/routes/{id}/circuit-configUpdate circuit breaker config
GET/api/routes/exportExport routes as JSON
POST/api/routes/importImport routes from JSON
POST/api/routes/bulk-pausePause/resume multiple routes
POST/api/routes/bulk-deleteDelete multiple routes

Route Object

json
{
  "id": "rte_abc123",
  "name": "GitHub to Slack",
  "description": "Forward GitHub events to Slack",
  "sourceId": "src_xyz789",
  "destinationIds": ["dst_slack1", "dst_slack2"],
  "transformId": "tfm_format123",
  "filterId": "flt_pushonly456",
  "priority": 10,
  "filterConditions": null,
  "filterLogic": "AND",
  "failoverDestinationIds": ["dst_backup1"],
  "failoverAfterAttempts": 3,
  "circuitState": "closed",
  "circuitCooldownSeconds": 300,
  "circuitFailureThreshold": 5,
  "circuitProbeSuccessThreshold": 2,
  "notifyOnFailure": true,
  "notifyOnSuccess": false,
  "notifyOnRecovery": true,
  "notifyEmails": "[email protected]",
  "expectedResponse": null,
  "enabled": true,
  "source": {
    "id": "src_xyz789",
    "name": "GitHub"
  },
  "destinations": [
    {"id": "dst_slack1", "name": "Slack #dev"},
    {"id": "dst_slack2", "name": "Slack #alerts"}
  ],
  "transform": {
    "id": "tfm_format123",
    "name": "Slack Formatter"
  },
  "filter": {
    "id": "flt_pushonly456",
    "name": "Push Events Only"
  },
  "eventsRouted": 1523,
  "lastEventAt": "2024-01-15T10:30:00Z",
  "createdAt": "2024-01-01T00:00:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}

List Routes

http
GET /api/routes

Query Parameters

ParameterTypeDescription
pagenumberPage number (default: 1)
pageSizenumberItems per page (default: 20, max: 100)
enabledbooleanFilter by enabled status
sourceIdstringFilter by source
destinationIdstringFilter by destination

Example

bash
curl https://api.hookbase.app/api/routes \
  -H "Authorization: Bearer whr_your_api_key"
javascript
const response = await fetch('https://api.hookbase.app/api/routes', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
  },
});
const { data, pagination } = await response.json();
python
import requests

response = requests.get(
    'https://api.hookbase.app/api/routes',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
    },
)
result = response.json()
data = result['data']

Response

json
{
  "data": [
    {
      "id": "rte_abc123",
      "name": "GitHub to Slack",
      "sourceId": "src_xyz789",
      "destinationIds": ["dst_slack1"],
      "enabled": true,
      "eventsRouted": 1523,
      "source": {"id": "src_xyz789", "name": "GitHub"},
      "destinations": [{"id": "dst_slack1", "name": "Slack #dev"}]
    }
  ],
  "pagination": {
    "total": 5,
    "page": 1,
    "pageSize": 20
  }
}

Create Route

http
POST /api/routes

Request Body

FieldTypeRequiredDescription
namestringYesDisplay name
sourceIdstringYesSource ID to listen to
destinationIdsstring[]YesArray of destination IDs
descriptionstringNoOptional description
transformIdstringNoTransform to apply
filterIdstringNoFilter to evaluate
prioritynumberNoRoute priority — higher values run first (default: 0)
filterConditionsarrayNoInline filter conditions (alternative to filterId). See Filters
filterLogicstringNoLogic for inline filters: AND or OR (default: AND)
enabledbooleanNoActive status (default: true)

Failover Configuration

FieldTypeDefaultDescription
failoverDestinationIdsstring[][]Backup destination IDs (max 3). See Failover Guide
failoverAfterAttemptsnumber3Switch to failover after this many failed attempts

Circuit Breaker Configuration

FieldTypeDefaultDescription
circuitCooldownSecondsnumber300Seconds to wait in open state before probing
circuitFailureThresholdnumber5Consecutive failures to trip the circuit
circuitProbeSuccessThresholdnumber2Successful probes required to close circuit

See Circuit Breaker Guide for details.

Notification Configuration

FieldTypeDefaultDescription
notifyOnFailurebooleanfalseSend notification when delivery fails
notifyOnSuccessbooleanfalseSend notification when delivery succeeds
notifyOnRecoverybooleanfalseSend notification when route recovers from failure
notifyEmailsstringnullComma-separated email addresses for notifications

Response Validation

FieldTypeDescription
expectedResponseobjectValidate destination response. { "statusCodes": [200, 201], "bodyContains": "ok" }

Example

bash
curl -X POST https://api.hookbase.app/api/routes \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GitHub to Slack",
    "sourceId": "src_xyz789",
    "destinationIds": ["dst_slack1", "dst_slack2"],
    "transformId": "tfm_format123",
    "filterId": "flt_pushonly456"
  }'
javascript
const response = await fetch('https://api.hookbase.app/api/routes', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'GitHub to Slack',
    sourceId: 'src_xyz789',
    destinationIds: ['dst_slack1', 'dst_slack2'],
    transformId: 'tfm_format123',
    filterId: 'flt_pushonly456'
  }),
});
const data = await response.json();
python
import requests

response = requests.post(
    'https://api.hookbase.app/api/routes',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'name': 'GitHub to Slack',
        'sourceId': 'src_xyz789',
        'destinationIds': ['dst_slack1', 'dst_slack2'],
        'transformId': 'tfm_format123',
        'filterId': 'flt_pushonly456'
    },
)
data = response.json()

Response

json
{
  "id": "rte_new123",
  "name": "GitHub to Slack",
  "sourceId": "src_xyz789",
  "destinationIds": ["dst_slack1", "dst_slack2"],
  "transformId": "tfm_format123",
  "filterId": "flt_pushonly456",
  "enabled": true,
  "eventsRouted": 0,
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}

Get Route

http
GET /api/routes/{id}

Example

bash
curl https://api.hookbase.app/api/routes/rte_abc123 \
  -H "Authorization: Bearer whr_your_api_key"
javascript
const response = await fetch('https://api.hookbase.app/api/routes/rte_abc123', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
  },
});
const data = await response.json();
python
import requests

response = requests.get(
    'https://api.hookbase.app/api/routes/rte_abc123',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
    },
)
data = response.json()

Response

Returns the full route object with expanded source, destinations, transform, and filter.

Update Route

http
PATCH /api/routes/{id}

Request Body

All fields are optional. Only provided fields are updated.

FieldTypeDescription
namestringDisplay name
destinationIdsstring[]Array of destination IDs
descriptionstringOptional description
transformIdstringTransform ID (null to remove)
filterIdstringFilter ID (null to remove)
enabledbooleanActive status

WARNING

The sourceId cannot be changed after creation. Create a new route instead.

Example

bash
curl -X PATCH https://api.hookbase.app/api/routes/rte_abc123 \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": false,
    "filterId": null
  }'
javascript
const response = await fetch('https://api.hookbase.app/api/routes/rte_abc123', {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    enabled: false,
    filterId: null
  }),
});
const data = await response.json();
python
import requests

response = requests.patch(
    'https://api.hookbase.app/api/routes/rte_abc123',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'enabled': False,
        'filterId': None
    },
)
data = response.json()

Response

Returns the updated route object.

Delete Route

http
DELETE /api/routes/{id}

INFO

Deleting a route does not delete the source, destinations, transform, or filter.

Example

bash
curl -X DELETE https://api.hookbase.app/api/routes/rte_abc123 \
  -H "Authorization: Bearer whr_your_api_key"
javascript
const response = await fetch('https://api.hookbase.app/api/routes/rte_abc123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
  },
});
// Returns 204 No Content
python
import requests

response = requests.delete(
    'https://api.hookbase.app/api/routes/rte_abc123',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
    },
)
# Returns 204 No Content

Response

204 No Content

Route Statistics

Get statistics for a route:

http
GET /api/routes/{id}/stats

Query Parameters

ParameterTypeDescription
periodstringTime period: 1h, 24h, 7d, 30d

Response

json
{
  "eventsRouted": 1523,
  "eventsFiltered": 234,
  "deliveriesSucceeded": 1489,
  "deliveriesFailed": 34,
  "avgLatency": 245,
  "period": "24h"
}

Circuit Breaker Status

Get the current circuit breaker state for a route.

http
GET /api/routes/{id}/circuit-status

Response

json
{
  "circuitState": "closed",
  "failureCount": 2,
  "failureThreshold": 5,
  "cooldownSeconds": 300,
  "lastFailureAt": "2024-01-15T10:25:00Z",
  "lastSuccessAt": "2024-01-15T10:30:00Z",
  "openedAt": null,
  "probeSuccessCount": 0,
  "probeSuccessThreshold": 2
}

Circuit States

StateDescription
closedNormal operation — deliveries are flowing
openCircuit tripped — deliveries are paused, failover activated
half_openProbing — sending test deliveries to check recovery

Reset Circuit Breaker

Manually reset the circuit breaker to closed state.

http
POST /api/routes/{id}/reset-circuit

Example

bash
curl -X POST https://api.hookbase.app/api/routes/rte_abc123/reset-circuit \
  -H "Authorization: Bearer whr_your_api_key"
javascript
const response = await fetch('https://api.hookbase.app/api/routes/rte_abc123/reset-circuit', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
  },
});
const data = await response.json();
python
import requests

response = requests.post(
    'https://api.hookbase.app/api/routes/rte_abc123/reset-circuit',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
    },
)
data = response.json()

Response

json
{
  "message": "Circuit breaker reset to closed",
  "circuitState": "closed"
}

Update Circuit Breaker Config

Update the circuit breaker configuration for a route without modifying other route settings.

http
PATCH /api/routes/{id}/circuit-config

Request Body

FieldTypeDescription
circuitCooldownSecondsnumberSeconds to wait before probing
circuitFailureThresholdnumberFailures to trip the circuit
circuitProbeSuccessThresholdnumberSuccessful probes to close circuit

Example

bash
curl -X PATCH https://api.hookbase.app/api/routes/rte_abc123/circuit-config \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "circuitFailureThreshold": 10,
    "circuitCooldownSeconds": 600
  }'
javascript
const response = await fetch('https://api.hookbase.app/api/routes/rte_abc123/circuit-config', {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    circuitFailureThreshold: 10,
    circuitCooldownSeconds: 600
  }),
});
const data = await response.json();
python
import requests

response = requests.patch(
    'https://api.hookbase.app/api/routes/rte_abc123/circuit-config',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'circuitFailureThreshold': 10,
        'circuitCooldownSeconds': 600
    },
)
data = response.json()

Response

json
{
  "circuitCooldownSeconds": 600,
  "circuitFailureThreshold": 10,
  "circuitProbeSuccessThreshold": 2,
  "message": "Circuit breaker configuration updated"
}

Export Routes

Export routes as a portable JSON file. Uses slugs instead of IDs for cross-environment compatibility.

http
GET /api/routes/export

Query Parameters

ParameterTypeDescription
idsstringComma-separated route IDs to export (optional, exports all if omitted)

Example

bash
# Export all routes
curl https://api.hookbase.app/api/routes/export \
  -H "Authorization: Bearer whr_your_api_key"

# Export specific routes
curl "https://api.hookbase.app/api/routes/export?ids=rte_abc,rte_def" \
  -H "Authorization: Bearer whr_your_api_key"
javascript
// Export all routes
const response1 = await fetch('https://api.hookbase.app/api/routes/export', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
  },
});
const data1 = await response1.json();

// Export specific routes
const response2 = await fetch('https://api.hookbase.app/api/routes/export?ids=rte_abc,rte_def', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
  },
});
const data2 = await response2.json();
python
import requests

# Export all routes
response1 = requests.get(
    'https://api.hookbase.app/api/routes/export',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
    },
)
data1 = response1.json()

# Export specific routes
response2 = requests.get(
    'https://api.hookbase.app/api/routes/export',
    params={'ids': 'rte_abc,rte_def'},
    headers={
        'Authorization': 'Bearer whr_your_api_key',
    },
)
data2 = response2.json()

Response

json
{
  "version": "1.0",
  "exportedAt": "2026-01-30T12:00:00Z",
  "organizationSlug": "acme-corp",
  "routes": [
    {
      "name": "Stripe to Slack",
      "sourceSlug": "stripe-webhooks",
      "destinationSlug": "slack-notifications",
      "filterSlug": "payment-events",
      "transformSlug": null,
      "schemaSlug": null,
      "filterConditions": null,
      "filterLogic": "AND",
      "priority": 10,
      "isActive": true,
      "notifyOnFailure": true,
      "notifyOnSuccess": false,
      "notifyOnRecovery": true,
      "notifyEmails": "[email protected]",
      "failureThreshold": 3,
      "failoverDestinationSlugs": ["backup-webhook"],
      "failoverAfterAttempts": 3,
      "circuitCooldownSeconds": 300,
      "circuitFailureThreshold": 5,
      "circuitProbeSuccessThreshold": 2
    }
  ]
}

TIP

Export files use slugs instead of IDs. This makes them portable between environments. Set up matching source and destination slugs in your target environment before importing.

Import Routes

Import routes from a JSON export file.

http
POST /api/routes/import

Request Body

FieldTypeRequiredDescription
routesarrayYesArray of route objects from export
conflictStrategystringYesHow to handle name conflicts: skip, rename, or overwrite
validateOnlybooleanNoIf true, validates without creating routes (dry run)

Conflict Strategies

StrategyBehavior
skipKeep existing route, don't import the duplicate
renameImport with suffix: "My Route" becomes "My Route (1)"
overwriteReplace existing route with imported configuration

Example

bash
# Validate import (dry run)
curl -X POST https://api.hookbase.app/api/routes/import \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "routes": [...],
    "conflictStrategy": "skip",
    "validateOnly": true
  }'

# Import routes
curl -X POST https://api.hookbase.app/api/routes/import \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "routes": [...],
    "conflictStrategy": "rename"
  }'
javascript
// Validate import (dry run)
const response1 = await fetch('https://api.hookbase.app/api/routes/import', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    routes: [...],
    conflictStrategy: 'skip',
    validateOnly: true
  }),
});
const data1 = await response1.json();

// Import routes
const response2 = await fetch('https://api.hookbase.app/api/routes/import', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    routes: [...],
    conflictStrategy: 'rename'
  }),
});
const data2 = await response2.json();
python
import requests

# Validate import (dry run)
response1 = requests.post(
    'https://api.hookbase.app/api/routes/import',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'routes': [...],
        'conflictStrategy': 'skip',
        'validateOnly': True
    },
)
data1 = response1.json()

# Import routes
response2 = requests.post(
    'https://api.hookbase.app/api/routes/import',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'routes': [...],
        'conflictStrategy': 'rename'
    },
)
data2 = response2.json()

Response

json
{
  "imported": 3,
  "skipped": 1,
  "overwritten": 0,
  "errors": [],
  "warnings": [
    "Route 'Legacy Webhook' skipped: already exists"
  ],
  "routes": [
    {
      "id": "rte_new123",
      "name": "Stripe to Slack",
      "status": "created"
    },
    {
      "id": "rte_new456",
      "name": "GitHub to Discord",
      "status": "created"
    }
  ]
}

Validation Errors

Before importing, Hookbase validates that all referenced resources exist:

json
{
  "imported": 0,
  "skipped": 0,
  "errors": [
    "Source 'stripe-webhooks' not found",
    "Destination 'backup-webhook' not found"
  ],
  "warnings": []
}

WARNING

All referenced sources, destinations, filters, transforms, and schemas must exist in the target organization before importing.

Bulk Pause Routes

Pause or resume multiple routes at once.

http
POST /api/routes/bulk-pause

Request Body

FieldTypeRequiredDescription
routeIdsstring[]YesArray of route IDs to update
isActivebooleanYesSet to false to pause, true to resume

Example

bash
# Pause multiple routes
curl -X POST https://api.hookbase.app/api/routes/bulk-pause \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "routeIds": ["rte_abc123", "rte_def456", "rte_ghi789"],
    "isActive": false
  }'

# Resume multiple routes
curl -X POST https://api.hookbase.app/api/routes/bulk-pause \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "routeIds": ["rte_abc123", "rte_def456"],
    "isActive": true
  }'
javascript
// Pause multiple routes
const response1 = await fetch('https://api.hookbase.app/api/routes/bulk-pause', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    routeIds: ['rte_abc123', 'rte_def456', 'rte_ghi789'],
    isActive: false
  }),
});
const data1 = await response1.json();

// Resume multiple routes
const response2 = await fetch('https://api.hookbase.app/api/routes/bulk-pause', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    routeIds: ['rte_abc123', 'rte_def456'],
    isActive: true
  }),
});
const data2 = await response2.json();
python
import requests

# Pause multiple routes
response1 = requests.post(
    'https://api.hookbase.app/api/routes/bulk-pause',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'routeIds': ['rte_abc123', 'rte_def456', 'rte_ghi789'],
        'isActive': False
    },
)
data1 = response1.json()

# Resume multiple routes
response2 = requests.post(
    'https://api.hookbase.app/api/routes/bulk-pause',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'routeIds': ['rte_abc123', 'rte_def456'],
        'isActive': True
    },
)
data2 = response2.json()

Response

json
{
  "updated": 3,
  "routes": [
    { "id": "rte_abc123", "name": "Stripe to Slack", "isActive": false },
    { "id": "rte_def456", "name": "GitHub to Discord", "isActive": false },
    { "id": "rte_ghi789", "name": "Shopify to CRM", "isActive": false }
  ]
}

Bulk Delete Routes

Delete multiple routes at once.

http
POST /api/routes/bulk-delete

Request Body

FieldTypeRequiredDescription
routeIdsstring[]YesArray of route IDs to delete

Example

bash
curl -X POST https://api.hookbase.app/api/routes/bulk-delete \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "routeIds": ["rte_abc123", "rte_def456", "rte_ghi789"]
  }'
javascript
const response = await fetch('https://api.hookbase.app/api/routes/bulk-delete', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer whr_your_api_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    routeIds: ['rte_abc123', 'rte_def456', 'rte_ghi789']
  }),
});
const data = await response.json();
python
import requests

response = requests.post(
    'https://api.hookbase.app/api/routes/bulk-delete',
    headers={
        'Authorization': 'Bearer whr_your_api_key',
        'Content-Type': 'application/json',
    },
    json={
        'routeIds': ['rte_abc123', 'rte_def456', 'rte_ghi789']
    },
)
data = response.json()

Response

json
{
  "deleted": 3,
  "routes": [
    { "id": "rte_abc123", "name": "Stripe to Slack" },
    { "id": "rte_def456", "name": "GitHub to Discord" },
    { "id": "rte_ghi789", "name": "Shopify to CRM" }
  ]
}

DANGER

Bulk delete is irreversible. Consider exporting routes before deleting as a backup.

Error Responses

400 Bad Request

Invalid source or destination:

json
{
  "error": "Bad Request",
  "message": "Source with ID src_invalid not found",
  "code": "INVALID_SOURCE"
}

404 Not Found

Route not found:

json
{
  "error": "Not Found",
  "message": "Route with ID rte_xyz not found",
  "code": "RESOURCE_NOT_FOUND"
}

409 Conflict

Duplicate route:

json
{
  "error": "Conflict",
  "message": "A route with this source and destinations already exists",
  "code": "DUPLICATE_ROUTE"
}

Released under the MIT License.