Webhook Jobs
Webhook jobs represent individual webhook deliveries. Create jobs to send webhooks to your customers' endpoints.
The Webhook Job Object
{
"id": "job_5nM8pQ1rK3vL9xB",
"application_id": "app_2Zy3X8qP9rK5mN1vB",
"event_type": "user.created",
"event_id": "evt_unique_123",
"status": "succeeded",
"attempt_count": 1,
"max_retries": 6,
"payload": {
"user_id": "usr_xyz789",
"email": "alice@example.com",
"created_at": "2026-01-20T15:30:00Z"
},
"headers": {
"X-Custom-Header": "value"
},
"created_at": "2026-01-20T15:30:00Z",
"expires_at": "2026-01-22T15:30:00Z",
"completed_at": "2026-01-20T15:30:05Z"
}| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier for the job |
application_id | string | Application this job belongs to |
event_type | string | Event type name (e.g., "user.created") |
event_id | string | Optional idempotency key |
status | enum | created, executing, succeeded, awaiting_retry, discarded, archived |
payload | object | Webhook payload (max 256 KB) |
headers | object | Custom headers to send |
expires_at | timestamp | 48-hour delivery window |
Create Webhook Job
Send a webhook event to all endpoints subscribed to the specified event type.
POST /v1/webhook-jobsRequest Body
{
"application_id": "app_2Zy3X8qP9rK5mN1vB",
"event_type": "user.created",
"event_id": "evt_unique_123",
"payload": {
"user_id": "usr_xyz789",
"email": "alice@example.com",
"name": "Alice Johnson",
"created_at": "2026-01-20T15:30:00Z"
},
"headers": {
"X-User-ID": "usr_xyz789",
"X-Environment": "production"
}
}| Parameter | Required | Description |
|---|---|---|
application_id | Yes | Application ID |
event_type | Yes | Event type name |
payload | Yes | Webhook payload object |
event_id | No | Idempotency key (prevents duplicate sends) |
headers | No | Custom headers object |
Response
{
"id": "job_5nM8pQ1rK3vL9xB",
"application_id": "app_2Zy3X8qP9rK5mN1vB",
"event_type": "user.created",
"status": "created",
"created_at": "2026-01-20T15:30:00Z",
"expires_at": "2026-01-22T15:30:00Z",
"attempt_count": 0,
"max_retries": 6
}Code Examples
// JavaScript
const response = await fetch('https://api.hookmesh.com/v1/webhook-jobs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
application_id: 'app_2Zy3X8qP9rK5mN1vB',
event_type: 'user.created',
payload: {
user_id: 'usr_xyz789',
email: 'alice@example.com'
}
})
});
const job = await response.json();
console.log('Job created:', job.id);# Python
import requests
import os
response = requests.post(
'https://api.hookmesh.com/v1/webhook-jobs',
headers={'Authorization': f'Bearer {os.environ.get("HOOKMESH_API_KEY")}'},
json={
'application_id': 'app_2Zy3X8qP9rK5mN1vB',
'event_type': 'user.created',
'payload': {
'user_id': 'usr_xyz789',
'email': 'alice@example.com'
}
}
)
job = response.json()
print(f'Job created: {job["id"]}')// Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload := map[string]interface{}{
"application_id": "app_2Zy3X8qP9rK5mN1vB",
"event_type": "user.created",
"payload": map[string]string{
"user_id": "usr_xyz789",
"email": "alice@example.com",
},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST",
"https://api.hookmesh.com/v1/webhook-jobs",
bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("HOOKMESH_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var job map[string]interface{}
json.NewDecoder(resp.Body).Decode(&job)
fmt.Printf("Job created: %s\n", job["id"])
}<?php
// PHP
$ch = curl_init('https://api.hookmesh.com/v1/webhook-jobs');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HOOKMESH_API_KEY'),
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode([
'application_id' => 'app_2Zy3X8qP9rK5mN1vB',
'event_type' => 'user.created',
'payload' => [
'user_id' => 'usr_xyz789',
'email' => 'alice@example.com'
]
])
]);
$response = curl_exec($ch);
$job = json_decode($response, true);
echo "Job created: {$job['id']}\n";
curl_close($ch);Retrieve Webhook Job
GET /v1/webhook-jobs/{job_id}Response
{
"id": "job_5nM8pQ1rK3vL9xB",
"application_id": "app_2Zy3X8qP9rK5mN1vB",
"event_type": "user.created",
"status": "succeeded",
"attempt_count": 1,
"payload": {...},
"deliveries": [
{
"endpoint_id": "ep_xyz789",
"status_code": 200,
"response_body": "{\"received\":true}",
"latency_ms": 145,
"attempted_at": "2026-01-20T15:30:05Z"
}
]
}List Webhook Jobs
GET /v1/webhook-jobs?application_id={id}&status={status}Query Parameters
| Parameter | Required | Description |
|---|---|---|
application_id | Yes | Filter by application |
status | No | Filter by status |
event_type | No | Filter by event type |
endpoint_id | No | Filter by endpoint |
created_after | No | ISO 8601 timestamp |
created_before | No | ISO 8601 timestamp |
page | No | Page number (default: 1) |
per_page | No | Items per page (default: 25, max: 100) |
Response
{
"data": [
{
"id": "job_5nM8pQ1rK3vL9xB",
"event_type": "user.created",
"status": "succeeded",
"created_at": "2026-01-20T15:30:00Z"
}
],
"pagination": {
"total": 1543,
"page": 1,
"per_page": 25,
"total_pages": 62
}
}Filtering Jobs
The List endpoint supports multiple query parameters for filtering webhook jobs. Here are common filtering patterns:
Filter by Status
Retrieve all failed jobs to investigate delivery issues:
// Node.js - Get all failed jobs
const response = await fetch(
'https://api.hookmesh.com/v1/webhook-jobs?' +
`application_id=${appId}&` +
'status=failed',
{
headers: {
'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`
}
}
);
const { data: failedJobs } = await response.json();
console.log(`Found ${failedJobs.length} failed jobs`);# Python - Get all failed jobs
import requests
import os
response = requests.get(
'https://api.hookmesh.com/v1/webhook-jobs',
headers={'Authorization': f'Bearer {os.getenv("HOOKMESH_API_KEY")}'},
params={
'application_id': app_id,
'status': 'failed'
}
)
failed_jobs = response.json()['data']
print(f'Found {len(failed_jobs)} failed jobs')Filter by Date Range
Get jobs from the last 24 hours:
// Node.js - Last 24 hours
const yesterday = new Date();
yesterday.setHours(yesterday.getHours() - 24);
const response = await fetch(
'https://api.hookmesh.com/v1/webhook-jobs?' +
`application_id=${appId}&` +
`created_after=${yesterday.toISOString()}`,
{
headers: {
'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`
}
}
);
const { data: recentJobs } = await response.json();# Python - Last 24 hours
from datetime import datetime, timedelta
yesterday = datetime.now() - timedelta(hours=24)
response = requests.get(
'https://api.hookmesh.com/v1/webhook-jobs',
headers={'Authorization': f'Bearer {os.getenv("HOOKMESH_API_KEY")}'},
params={
'application_id': app_id,
'created_after': yesterday.isoformat()
}
)Filter by Event Type
Get all jobs for a specific event:
// Node.js - Filter by event type
const response = await fetch(
'https://api.hookmesh.com/v1/webhook-jobs?' +
`application_id=${appId}&` +
'event_type=user.created',
{
headers: {
'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`
}
}
);Combine Multiple Filters
Find failed payment webhooks from the last week:
// Node.js - Combined filters
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
const response = await fetch(
'https://api.hookmesh.com/v1/webhook-jobs?' +
`application_id=${appId}&` +
'event_type=payment.failed&' +
'status=failed&' +
`created_after=${lastWeek.toISOString()}&` +
'page=1&' +
'per_page=50',
{
headers: {
'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`
}
}
);
const { data: jobs, pagination } = await response.json();
console.log(`Found ${pagination.total} failed payment webhooks in the last week`);# Python - Combined filters
from datetime import datetime, timedelta
last_week = datetime.now() - timedelta(days=7)
response = requests.get(
'https://api.hookmesh.com/v1/webhook-jobs',
headers={'Authorization': f'Bearer {os.getenv("HOOKMESH_API_KEY")}'},
params={
'application_id': app_id,
'event_type': 'payment.failed',
'status': 'failed',
'created_after': last_week.isoformat(),
'page': 1,
'per_page': 50
}
)
result = response.json()
print(f'Found {result["pagination"]["total"]} failed payment webhooks')// Go - Combined filters
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"time"
)
func main() {
lastWeek := time.Now().AddDate(0, 0, -7)
params := url.Values{}
params.Add("application_id", appID)
params.Add("event_type", "payment.failed")
params.Add("status", "failed")
params.Add("created_after", lastWeek.Format(time.RFC3339))
params.Add("page", "1")
params.Add("per_page", "50")
req, _ := http.NewRequest("GET",
"https://api.hookmesh.com/v1/webhook-jobs?"+params.Encode(),
nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("HOOKMESH_API_KEY"))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
pagination := result["pagination"].(map[string]interface{})
fmt.Printf("Found %.0f failed payment webhooks\n", pagination["total"])
}<?php
// PHP - Combined filters
$lastWeek = new DateTime('-7 days');
$params = http_build_query([
'application_id' => $appId,
'event_type' => 'payment.failed',
'status' => 'failed',
'created_after' => $lastWeek->format(DateTime::ISO8601),
'page' => 1,
'per_page' => 50
]);
$ch = curl_init('https://api.hookmesh.com/v1/webhook-jobs?' . $params);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('HOOKMESH_API_KEY')
]
]);
$response = curl_exec($ch);
$result = json_decode($response, true);
echo "Found {$result['pagination']['total']} failed payment webhooks\n";
curl_close($ch);Pagination with Filters
When filtering returns many results, use pagination:
// Node.js - Paginate through filtered results
async function getAllFailedJobs(appId: string) {
const allJobs = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(
'https://api.hookmesh.com/v1/webhook-jobs?' +
`application_id=${appId}&` +
'status=failed&' +
`page=${page}&` +
'per_page=100',
{
headers: {
'Authorization': `Bearer ${process.env.HOOKMESH_API_KEY}`
}
}
);
const { data, pagination } = await response.json();
allJobs.push(...data);
hasMore = page < pagination.total_pages;
page++;
}
return allJobs;
}application_id in your filters for best performance. Combine created_after and created_before to limit the date range when searching for specific jobs.Retry Webhook Job
Manually retry a failed webhook job. Resets the job to created status and re-enters the queue.
POST /v1/webhook-jobs/{job_id}/retryResponse
{
"id": "job_5nM8pQ1rK3vL9xB",
"status": "created",
"attempt_count": 0
}Cancel Webhook Job
Cancel a pending webhook job. Only works for jobs in created or awaiting_retry status.
POST /v1/webhook-jobs/{job_id}/cancelResponse
{
"id": "job_5nM8pQ1rK3vL9xB",
"status": "discarded",
"discarded_at": "2026-01-20T15:30:00Z"
}Idempotency
Prevent duplicate webhook sends by providing an event_id. If you send the same event_id within 24 hours, Hook Mesh returns the existing job instead of creating a new one.
// First request
await fetch('https://api.hookmesh.com/v1/webhook-jobs', {
method: 'POST',
headers: {...},
body: JSON.stringify({
application_id: 'app_xyz',
event_type: 'invoice.paid',
event_id: 'inv_123_paid', // Idempotency key
payload: {...}
})
});
// Creates new job
// Second request (within 24 hours)
await fetch('https://api.hookmesh.com/v1/webhook-jobs', {
method: 'POST',
headers: {...},
body: JSON.stringify({
application_id: 'app_xyz',
event_type: 'invoice.paid',
event_id: 'inv_123_paid', // Same key
payload: {...}
})
});
// Returns existing job, doesn't create duplicateevent_id for all webhook sends to prevent duplicates from retries or network issues.