Destinations API
Destinations are delivery targets for webhooks. Hookbase supports HTTP endpoints and warehouse destinations (S3, R2, GCS, Azure Blob).
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/destinations | List destinations |
| POST | /api/destinations | Create destination |
| GET | /api/destinations/{id} | Get destination |
| PATCH | /api/destinations/{id} | Update destination |
| DELETE | /api/destinations/{id} | Delete destination |
| POST | /api/destinations/{id}/test | Test destination |
| GET | /api/destinations/export | Export destinations as JSON |
| POST | /api/destinations/import | Import destinations from JSON |
| DELETE | /api/destinations/bulk | Bulk delete destinations |
Destination Object
HTTP Destination
{
"id": "dst_xyz789",
"name": "Production API",
"type": "http",
"url": "https://api.yourapp.com/webhooks",
"method": "POST",
"headers": {
"X-Custom-Header": "value"
},
"authType": "bearer",
"authConfig": {
"token": "sk_live_xxx"
},
"timeoutMs": 30000,
"rateLimitPerMinute": 500,
"retryPolicy": {
"maxRetries": 5,
"initialDelay": 1000,
"maxDelay": 60000,
"backoffMultiplier": 2
},
"enabled": true,
"successRate": 98.5,
"avgLatency": 245,
"deliveriesCount": 5230,
"lastDeliveryAt": "2024-01-15T10:30:00Z",
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}Warehouse Destination (S3 example)
{
"id": "dst_wh_abc123",
"name": "Analytics Data Lake",
"type": "s3",
"url": "",
"config": {
"bucket": "my-data-lake",
"region": "us-east-1",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"secretAccessKey": "••••EKEY"
},
"fieldMapping": [
{ "source": "$.payload.type", "target": "event_type", "type": "string" },
{ "source": "$.payload.data.amount", "target": "amount", "type": "number" }
],
"enabled": true,
"createdAt": "2026-02-21T10:30:00Z",
"updatedAt": "2026-02-21T10:30:00Z"
}Credential Redaction
Sensitive fields (secretAccessKey, serviceAccountKey, accountKey) are returned as •••• followed by the last 4 characters. Credentials are encrypted at rest using AES-256-GCM.
List Destinations
GET /api/destinationsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| pageSize | number | Items per page (default: 20, max: 100) |
| enabled | boolean | Filter by enabled status |
| search | string | Search by name or URL |
Example
curl https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key"const response = await fetch('https://api.hookbase.app/api/destinations', {
method: 'GET',
headers: {
'Authorization': 'Bearer whr_your_api_key',
},
});
const { data, pagination } = await response.json();import requests
response = requests.get(
'https://api.hookbase.app/api/destinations',
headers={
'Authorization': 'Bearer whr_your_api_key',
},
)
result = response.json()
data = result['data']Response
{
"data": [
{
"id": "dst_xyz789",
"name": "Production API",
"type": "http",
"url": "https://api.yourapp.com/webhooks",
"enabled": true,
"successRate": 98.5,
"avgLatency": 245,
"deliveriesCount": 5230
},
{
"id": "dst_wh_abc123",
"name": "Analytics Data Lake",
"type": "s3",
"url": "",
"config": {
"bucket": "my-data-lake",
"region": "us-east-1",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"secretAccessKey": "••••EKEY"
},
"enabled": true
}
],
"pagination": {
"total": 5,
"page": 1,
"pageSize": 20
}
}Create Destination
POST /api/destinationsCommon Fields
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name |
| type | string | No | http (default), s3, r2, gcs, or azure_blob |
| enabled | boolean | No | Active status (default: true) |
HTTP-Specific Fields
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Delivery URL (HTTPS recommended) |
| method | string | No | HTTP method: POST, PUT, PATCH (default: POST) |
| headers | object | No | Custom headers to include |
| authType | string | No | Authentication type (see below) |
| authConfig | object | No | Authentication configuration |
| timeoutMs | number | No | Request timeout in milliseconds (default: 30000, max: 60000) |
| rateLimitPerMinute | number | No | Max deliveries per minute (null = unlimited) |
| retryPolicy | object | No | Retry configuration |
Warehouse-Specific Fields
| Field | Type | Required | Description |
|---|---|---|---|
| config | object | Yes | Provider-specific configuration (see below) |
| fieldMapping | array | No | Field mapping rules for structured output |
Plan Requirement
Warehouse destinations (s3, r2, gcs, azure_blob) require a Pro or Business plan.
Authentication Types (HTTP)
| Type | Description | Auth Config |
|---|---|---|
none | No authentication | -- |
basic | HTTP Basic Auth | { "username": "...", "password": "..." } |
bearer | Bearer token | { "token": "..." } |
api_key | API key in header | { "headerName": "X-API-Key", "key": "..." } |
custom_header | Custom auth header | { "headerName": "...", "headerValue": "..." } |
Warehouse Config: S3
{
"bucket": "my-data-lake",
"region": "us-east-1",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"prefix": "webhooks/",
"fileFormat": "jsonl",
"partitionBy": "date"
}| Field | Type | Required | Description |
|---|---|---|---|
| bucket | string | Yes | S3 bucket name (3-63 chars) |
| region | string | Yes | AWS region |
| accessKeyId | string | Yes | AWS access key ID |
| secretAccessKey | string | Yes | AWS secret access key |
| prefix | string | No | Path prefix |
| fileFormat | string | No | jsonl (default) or json |
| partitionBy | string | No | date (default), hour, or source |
Warehouse Config: R2
{
"bucket": "my-webhook-archive",
"prefix": "webhooks/",
"fileFormat": "jsonl",
"partitionBy": "date"
}| Field | Type | Required | Description |
|---|---|---|---|
| bucket | string | Yes | R2 bucket name (3-63 chars) |
| prefix | string | No | Path prefix |
| fileFormat | string | No | jsonl (default) or json |
| partitionBy | string | No | date (default), hour, or source |
Warehouse Config: GCS
{
"bucket": "my-gcs-bucket",
"projectId": "my-gcp-project",
"serviceAccountKey": "{...}",
"prefix": "webhooks/",
"fileFormat": "jsonl",
"partitionBy": "date"
}| Field | Type | Required | Description |
|---|---|---|---|
| bucket | string | Yes | GCS bucket name (3-63 chars) |
| projectId | string | Yes | Google Cloud project ID |
| serviceAccountKey | string | Yes | Service account key JSON |
| prefix | string | No | Path prefix |
| fileFormat | string | No | jsonl (default) or json |
| partitionBy | string | No | date (default), hour, or source |
Warehouse Config: Azure Blob
{
"accountName": "myaccount",
"accountKey": "base64-encoded-key",
"containerName": "webhook-data",
"prefix": "webhooks/",
"fileFormat": "jsonl",
"partitionBy": "date"
}| Field | Type | Required | Description |
|---|---|---|---|
| accountName | string | Yes | Storage account name (3-24 chars) |
| accountKey | string | Yes | Base64-encoded account key |
| containerName | string | Yes | Blob container name (3-63 chars) |
| prefix | string | No | Path prefix |
| fileFormat | string | No | jsonl (default) or json |
| partitionBy | string | No | date (default), hour, or source |
Field Mapping
| Field | Type | Required | Description |
|---|---|---|---|
| source | string | Yes | JSONPath expression (e.g., $.payload.amount) |
| target | string | Yes | Output column name |
| type | string | Yes | string, number, boolean, timestamp, or json |
| default | string | No | Default value if source path is missing |
Maximum 50 field mappings per destination.
Example: Create HTTP Destination
curl -X POST https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Production API",
"type": "http",
"url": "https://api.yourapp.com/webhooks/handler",
"headers": {
"Authorization": "Bearer sk_live_xxx"
},
"retryPolicy": {
"maxRetries": 5,
"initialDelay": 1000,
"maxDelay": 300000
}
}'const response = await fetch('https://api.hookbase.app/api/destinations', {
method: 'POST',
headers: {
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Production API',
type: 'http',
url: 'https://api.yourapp.com/webhooks/handler',
headers: {
'Authorization': 'Bearer sk_live_xxx',
},
retryPolicy: {
maxRetries: 5,
initialDelay: 1000,
maxDelay: 300000,
},
}),
});
const data = await response.json();import requests
response = requests.post(
'https://api.hookbase.app/api/destinations',
headers={
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
json={
'name': 'Production API',
'type': 'http',
'url': 'https://api.yourapp.com/webhooks/handler',
'headers': {
'Authorization': 'Bearer sk_live_xxx',
},
'retryPolicy': {
'maxRetries': 5,
'initialDelay': 1000,
'maxDelay': 300000,
},
},
)
data = response.json()Example: Create S3 Warehouse Destination
curl -X POST https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Analytics Data Lake",
"type": "s3",
"config": {
"bucket": "my-data-lake",
"region": "us-east-1",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"prefix": "webhooks/stripe/",
"fileFormat": "jsonl",
"partitionBy": "date"
},
"fieldMapping": [
{ "source": "$.payload.type", "target": "event_type", "type": "string" },
{ "source": "$.payload.data.amount", "target": "amount", "type": "number" },
{ "source": "$.payload.created", "target": "created_at", "type": "timestamp" }
]
}'const response = await fetch('https://api.hookbase.app/api/destinations', {
method: 'POST',
headers: {
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Analytics Data Lake',
type: 's3',
config: {
bucket: 'my-data-lake',
region: 'us-east-1',
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
prefix: 'webhooks/stripe/',
fileFormat: 'jsonl',
partitionBy: 'date',
},
fieldMapping: [
{ source: '$.payload.type', target: 'event_type', type: 'string' },
{ source: '$.payload.data.amount', target: 'amount', type: 'number' },
{ source: '$.payload.created', target: 'created_at', type: 'timestamp' },
],
}),
});
const data = await response.json();import requests
response = requests.post(
'https://api.hookbase.app/api/destinations',
headers={
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
json={
'name': 'Analytics Data Lake',
'type': 's3',
'config': {
'bucket': 'my-data-lake',
'region': 'us-east-1',
'accessKeyId': 'AKIAIOSFODNN7EXAMPLE',
'secretAccessKey': 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
'prefix': 'webhooks/stripe/',
'fileFormat': 'jsonl',
'partitionBy': 'date',
},
'fieldMapping': [
{'source': '$.payload.type', 'target': 'event_type', 'type': 'string'},
{'source': '$.payload.data.amount', 'target': 'amount', 'type': 'number'},
{'source': '$.payload.created', 'target': 'created_at', 'type': 'timestamp'},
],
},
)
data = response.json()Response
{
"id": "dst_wh_new456",
"name": "Analytics Data Lake",
"type": "s3",
"config": {
"bucket": "my-data-lake",
"region": "us-east-1",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"secretAccessKey": "••••EKEY"
},
"fieldMapping": [
{ "source": "$.payload.type", "target": "event_type", "type": "string" },
{ "source": "$.payload.data.amount", "target": "amount", "type": "number" },
{ "source": "$.payload.created", "target": "created_at", "type": "timestamp" }
],
"enabled": true,
"createdAt": "2026-02-21T10:30:00Z",
"updatedAt": "2026-02-21T10:30:00Z"
}Get Destination
GET /api/destinations/{id}Example
curl https://api.hookbase.app/api/destinations/dst_xyz789 \
-H "Authorization: Bearer whr_your_api_key"const response = await fetch('https://api.hookbase.app/api/destinations/dst_xyz789', {
method: 'GET',
headers: {
'Authorization': 'Bearer whr_your_api_key',
},
});
const data = await response.json();import requests
response = requests.get(
'https://api.hookbase.app/api/destinations/dst_xyz789',
headers={
'Authorization': 'Bearer whr_your_api_key',
},
)
data = response.json()Response
Returns the full destination object. Sensitive credential fields are redacted.
Update Destination
PATCH /api/destinations/{id}Request Body
All fields are optional. Only provided fields are updated.
| Field | Type | Description |
|---|---|---|
| name | string | Display name |
| url | string | Delivery URL (HTTP destinations) |
| headers | object | Custom headers (replaces existing) |
| retryPolicy | object | Retry configuration |
| config | object | Warehouse configuration |
| fieldMapping | array | Field mapping rules |
| enabled | boolean | Active status |
Updating Credentials
When updating a warehouse destination, redacted credential values (e.g., ••••EKEY) are automatically preserved. Only provide a new value if you are rotating keys.
Example
curl -X PATCH https://api.hookbase.app/api/destinations/dst_xyz789 \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.yourapp.com/webhooks/v2/handler",
"retryPolicy": {
"maxRetries": 10
}
}'const response = await fetch('https://api.hookbase.app/api/destinations/dst_xyz789', {
method: 'PATCH',
headers: {
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://api.yourapp.com/webhooks/v2/handler',
retryPolicy: {
maxRetries: 10
}
}),
});
const data = await response.json();import requests
response = requests.patch(
'https://api.hookbase.app/api/destinations/dst_xyz789',
headers={
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
json={
'url': 'https://api.yourapp.com/webhooks/v2/handler',
'retryPolicy': {
'maxRetries': 10
}
},
)
data = response.json()Response
Returns the updated destination object.
Delete Destination
DELETE /api/destinations/{id}WARNING
Deleting a destination removes it from all routes. Deliveries are preserved for history.
Example
curl -X DELETE https://api.hookbase.app/api/destinations/dst_xyz789 \
-H "Authorization: Bearer whr_your_api_key"const response = await fetch('https://api.hookbase.app/api/destinations/dst_xyz789', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer whr_your_api_key',
},
});
// Returns 204 No Contentimport requests
response = requests.delete(
'https://api.hookbase.app/api/destinations/dst_xyz789',
headers={
'Authorization': 'Bearer whr_your_api_key',
},
)
# Returns 204 No ContentResponse
204 No ContentTest Destination
Send a test webhook (HTTP) or upload a test file (warehouse) to verify the destination is configured correctly.
POST /api/destinations/{id}/testRequest Body (Optional)
| Field | Type | Description |
|---|---|---|
| payload | object | Custom test payload (default: sample webhook event) |
Example
curl -X POST https://api.hookbase.app/api/destinations/dst_xyz789/test \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"event": "test",
"timestamp": "2024-01-15T10:30:00Z"
}
}'const response = await fetch('https://api.hookbase.app/api/destinations/dst_xyz789/test', {
method: 'POST',
headers: {
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
payload: {
event: 'test',
timestamp: '2024-01-15T10:30:00Z'
}
}),
});
const data = await response.json();import requests
response = requests.post(
'https://api.hookbase.app/api/destinations/dst_xyz789/test',
headers={
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
json={
'payload': {
'event': 'test',
'timestamp': '2024-01-15T10:30:00Z'
}
},
)
data = response.json()HTTP Destination Response
{
"success": true,
"statusCode": 200,
"latency": 245,
"responseBody": "{\"received\": true}",
"message": "Test webhook delivered successfully"
}Warehouse Destination Response
{
"success": true,
"result": {
"key": "hookbase/2026-02-21/test-1740150000000.jsonl",
"size": 142,
"count": 1
},
"message": "Test file uploaded successfully"
}Error Response
{
"success": false,
"statusCode": 503,
"latency": 30012,
"error": "Connection timeout after 30000ms",
"message": "Test webhook delivery failed"
}Destination Health
Get health metrics for a destination:
GET /api/destinations/{id}/healthResponse
{
"successRate": 98.5,
"avgLatency": 245,
"p50Latency": 180,
"p95Latency": 450,
"p99Latency": 890,
"totalDeliveries": 5230,
"successfulDeliveries": 5152,
"failedDeliveries": 78,
"lastDeliveryAt": "2024-01-15T10:30:00Z",
"lastSuccessAt": "2024-01-15T10:30:00Z",
"lastFailureAt": "2024-01-15T09:15:00Z",
"status": "healthy"
}Status Values
| Status | Description |
|---|---|
healthy | Success rate > 95% |
degraded | Success rate 80-95% |
failing | Success rate < 80% |
unknown | No recent deliveries |
Export Destinations
Export destinations as a JSON file for backup or migration to another organization.
GET /api/destinations/exportQuery Parameters
| Parameter | Type | Description |
|---|---|---|
| ids | string | Comma-separated destination IDs to export (optional, exports all if not specified) |
| includeSensitive | boolean | Include auth credentials in export (default: false) |
Example
# Export all destinations (auth credentials redacted)
curl https://api.hookbase.app/api/destinations/export \
-H "Authorization: Bearer whr_your_api_key"
# Export specific destinations with credentials
curl "https://api.hookbase.app/api/destinations/export?ids=dst_1,dst_2&includeSensitive=true" \
-H "Authorization: Bearer whr_your_api_key"const response = await fetch('https://api.hookbase.app/api/destinations/export', {
method: 'GET',
headers: {
'Authorization': 'Bearer whr_your_api_key',
},
});
const data = await response.json();import requests
response = requests.get(
'https://api.hookbase.app/api/destinations/export',
headers={
'Authorization': 'Bearer whr_your_api_key',
},
)
data = response.json()Response
{
"version": "1.0",
"exportedAt": "2024-01-15T10:30:00Z",
"organizationSlug": "myorg",
"destinations": [
{
"name": "Production API",
"slug": "production-api",
"type": "http",
"url": "https://api.yourapp.com/webhooks",
"method": "POST",
"headers": {
"X-Custom-Header": "value"
},
"authType": "bearer",
"authConfig": "***REDACTED***",
"timeoutMs": 30000,
"rateLimitPerMinute": null,
"isActive": true
}
]
}TIP
When includeSensitive is false (default), authentication credentials are replaced with ***REDACTED***. You'll need to reconfigure auth settings after importing.
Import Destinations
Import destinations from a JSON export file.
POST /api/destinations/importRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
| destinations | array | Yes | Array of destination objects to import |
| conflictStrategy | string | Yes | How to handle slug conflicts: skip, rename, or overwrite |
| validateOnly | boolean | No | If true, validates without importing (default: false) |
Conflict Strategies
| Strategy | Description |
|---|---|
skip | Skip destinations that already exist (by slug) |
rename | Auto-rename conflicting destinations (e.g., api -> api-1) |
overwrite | Update existing destinations with imported data |
Example
curl -X POST https://api.hookbase.app/api/destinations/import \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"destinations": [
{
"name": "Staging API",
"slug": "staging-api",
"type": "http",
"url": "https://staging.yourapp.com/webhooks",
"method": "POST",
"timeoutMs": 30000,
"isActive": true
}
],
"conflictStrategy": "skip"
}'const response = await fetch('https://api.hookbase.app/api/destinations/import', {
method: 'POST',
headers: {
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
destinations: [
{
name: 'Staging API',
slug: 'staging-api',
type: 'http',
url: 'https://staging.yourapp.com/webhooks',
method: 'POST',
timeoutMs: 30000,
isActive: true
}
],
conflictStrategy: 'skip'
}),
});
const data = await response.json();import requests
response = requests.post(
'https://api.hookbase.app/api/destinations/import',
headers={
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
json={
'destinations': [
{
'name': 'Staging API',
'slug': 'staging-api',
'type': 'http',
'url': 'https://staging.yourapp.com/webhooks',
'method': 'POST',
'timeoutMs': 30000,
'isActive': True
}
],
'conflictStrategy': 'skip'
},
)
data = response.json()Response
{
"success": true,
"summary": {
"imported": 3,
"skipped": 1,
"overwritten": 0,
"failed": 0
},
"details": {
"imported": ["Staging API", "Development API", "QA Endpoint"],
"skipped": ["Production API"],
"overwritten": [],
"failed": []
}
}Bulk Delete Destinations
Delete multiple destinations in a single request.
DELETE /api/destinations/bulkRequest Body
{
"ids": ["dst_abc123", "dst_def456", "dst_ghi789"]
}Example
curl -X DELETE https://api.hookbase.app/api/destinations/bulk \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{"ids": ["dst_abc123", "dst_def456"]}'const response = await fetch('https://api.hookbase.app/api/destinations/bulk', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
ids: ['dst_abc123', 'dst_def456']
}),
});
const data = await response.json();import requests
response = requests.delete(
'https://api.hookbase.app/api/destinations/bulk',
headers={
'Authorization': 'Bearer whr_your_api_key',
'Content-Type': 'application/json',
},
json={
'ids': ['dst_abc123', 'dst_def456']
},
)
data = response.json()Response
{
"success": true,
"deleted": 2
}WARNING
Deleting destinations removes them from all routes. Delivery history is preserved.
Error Responses
400 Bad Request
Invalid URL:
{
"error": "Bad Request",
"message": "Invalid URL format",
"code": "INVALID_URL"
}Invalid warehouse config:
{
"error": "Bad Request",
"message": "Invalid warehouse configuration: bucket must be 3-63 characters"
}403 Forbidden
Plan restriction:
{
"error": "Forbidden",
"message": "Warehouse destinations require Pro or Business plan."
}404 Not Found
Destination not found:
{
"error": "Not Found",
"message": "Destination with ID dst_xyz not found",
"code": "RESOURCE_NOT_FOUND"
}