Webhooks
Webhooks
Webhooks allow your application to receive real-time notifications when events occur in your Global Watch account. Instead of polling the API for changes, webhooks push data to your server as events happen.
Overview
When you configure a webhook, Global Watch sends HTTP POST requests to your specified URL whenever subscribed events occur. This enables you to:
- Sync data in real-time with external systems
- Trigger workflows based on Global Watch events
- Build integrations that respond to changes automatically
- Audit activity by logging all events
Setting Up Webhooks
1. Create a Webhook Endpoint
First, create an endpoint in your application to receive webhook events:
// Example: Express.js webhook handler
import express from 'express';
import crypto from 'crypto';
const app = express();
app.post('/webhooks/globalwatch', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-globalwatch-signature'];
const payload = req.body;
// Verify the webhook signature
if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
// Handle the event
switch (event.type) {
case 'project.created':
handleProjectCreated(event.data);
break;
case 'asset.updated':
handleAssetUpdated(event.data);
break;
// ... handle other events
}
res.status(200).send('OK');
});
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expectedSignature}`)
);
}2. Register the Webhook
Register your webhook endpoint via the API or dashboard:
Via API:
curl -X POST "https://api.global.watch/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/globalwatch",
"events": ["project.created", "project.updated", "asset.created"],
"description": "Production webhook"
}'Via Dashboard:
- Go to Settings → Webhooks
- Click Add Webhook
- Enter your endpoint URL
- Select the events to subscribe to
- Click Create Webhook
3. Store the Signing Secret
When you create a webhook, Global Watch generates a signing secret. Store this securely:
# .env.local
GLOBALWATCH_WEBHOOK_SECRET=whsec_abc123...Webhook Events
Project Events
| Event | Description |
|---|---|
project.created | A new project was created |
project.updated | A project was updated |
project.deleted | A project was deleted |
project.archived | A project was archived |
project.restored | An archived project was restored |
Example Payload: project.created
{
"id": "evt_abc123",
"type": "project.created",
"created_at": "2024-01-25T10:30:00Z",
"data": {
"id": "proj_def456",
"name": "New Conservation Area",
"slug": "new-conservation-area",
"description": "Reforestation project",
"area_hectares": 1250.5,
"status": "active",
"created_at": "2024-01-25T10:30:00Z",
"created_by": {
"id": "user_xyz789",
"email": "john@example.com"
}
},
"account": {
"id": "acc_abc123",
"name": "Acme Forest Management"
}
}Asset Events
| Event | Description |
|---|---|
asset.created | A new asset was created |
asset.updated | An asset was updated |
asset.deleted | An asset was deleted |
asset.status_changed | An asset's status changed |
Example Payload: asset.created
{
"id": "evt_def456",
"type": "asset.created",
"created_at": "2024-01-25T11:00:00Z",
"data": {
"id": "asset_ghi789",
"project_id": "proj_def456",
"name": "Monitoring Station Beta",
"type": "equipment",
"location": {
"type": "Point",
"coordinates": [-60.0217, -3.1190]
},
"status": "active",
"created_at": "2024-01-25T11:00:00Z"
},
"account": {
"id": "acc_abc123",
"name": "Acme Forest Management"
}
}Member Events
| Event | Description |
|---|---|
member.invited | A new member was invited |
member.joined | A member accepted an invitation |
member.updated | A member's role was updated |
member.removed | A member was removed |
Example Payload: member.joined
{
"id": "evt_ghi789",
"type": "member.joined",
"created_at": "2024-01-25T12:00:00Z",
"data": {
"id": "user_jkl012",
"email": "jane@example.com",
"name": "Jane Smith",
"role": "editor",
"joined_at": "2024-01-25T12:00:00Z"
},
"account": {
"id": "acc_abc123",
"name": "Acme Forest Management"
}
}Billing Events
| Event | Description |
|---|---|
subscription.created | A new subscription was created |
subscription.updated | A subscription was updated |
subscription.canceled | A subscription was canceled |
invoice.paid | An invoice was paid |
invoice.failed | An invoice payment failed |
Usage Events
| Event | Description |
|---|---|
usage.threshold_reached | Usage threshold was reached (75%, 90%, 100%) |
usage.limit_exceeded | Usage limit was exceeded |
Webhook Payload Structure
All webhook payloads follow this structure:
{
"id": "evt_abc123",
"type": "event.type",
"created_at": "2024-01-25T10:30:00Z",
"data": {
// Event-specific data
},
"account": {
"id": "acc_abc123",
"name": "Account Name"
}
}| Field | Type | Description |
|---|---|---|
id | string | Unique event identifier |
type | string | Event type |
created_at | string | ISO 8601 timestamp |
data | object | Event-specific payload |
account | object | Account information |
Verifying Webhooks
Always verify webhook signatures to ensure requests are from Global Watch.
Signature Verification
Global Watch signs all webhook payloads using HMAC-SHA256. The signature is included in the X-GlobalWatch-Signature header.
Verification Steps:
- Extract the signature from the header
- Compute the expected signature using your webhook secret
- Compare signatures using a timing-safe comparison
Node.js Example:
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
const providedSignature = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(providedSignature)
);
}Python Example:
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
provided_signature = signature.replace('sha256=', '')
return hmac.compare_digest(expected_signature, provided_signature)Handling Webhooks
Best Practices
- Respond quickly - Return a 2xx response within 5 seconds
- Process asynchronously - Queue events for background processing
- Handle duplicates - Events may be delivered more than once
- Verify signatures - Always validate webhook authenticity
- Log events - Keep records for debugging and auditing
Idempotency
Webhooks may be delivered multiple times. Use the event id to ensure idempotent processing:
async function handleWebhook(event: WebhookEvent) {
// Check if we've already processed this event
const processed = await db.webhookEvents.findUnique({
where: { eventId: event.id }
});
if (processed) {
console.log(`Event ${event.id} already processed, skipping`);
return;
}
// Process the event
await processEvent(event);
// Mark as processed
await db.webhookEvents.create({
data: { eventId: event.id, processedAt: new Date() }
});
}Error Handling
If your endpoint returns an error, Global Watch will retry the webhook:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 24 hours |
After 6 failed attempts, the webhook is marked as failed and no further retries are attempted.
Managing Webhooks
List Webhooks
curl -X GET "https://api.global.watch/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY"Update Webhook
curl -X PUT "https://api.global.watch/v1/webhooks/wh_abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["project.created", "project.updated", "project.deleted"],
"enabled": true
}'Delete Webhook
curl -X DELETE "https://api.global.watch/v1/webhooks/wh_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"Test Webhook
Send a test event to verify your endpoint:
curl -X POST "https://api.global.watch/v1/webhooks/wh_abc123/test" \
-H "Authorization: Bearer YOUR_API_KEY"View Webhook Logs
Check recent webhook deliveries:
curl -X GET "https://api.global.watch/v1/webhooks/wh_abc123/logs" \
-H "Authorization: Bearer YOUR_API_KEY"Example Response:
{
"data": [
{
"id": "log_abc123",
"event_id": "evt_def456",
"event_type": "project.created",
"status": "success",
"response_code": 200,
"response_time_ms": 150,
"delivered_at": "2024-01-25T10:30:00Z"
},
{
"id": "log_def456",
"event_id": "evt_ghi789",
"event_type": "asset.created",
"status": "failed",
"response_code": 500,
"error": "Internal Server Error",
"delivered_at": "2024-01-25T11:00:00Z",
"next_retry_at": "2024-01-25T11:05:00Z"
}
]
}Troubleshooting
Common Issues
Webhook not receiving events
- Verify the webhook is enabled
- Check that you're subscribed to the correct events
- Ensure your endpoint is publicly accessible
- Check firewall rules
Signature verification failing
- Ensure you're using the raw request body (not parsed JSON)
- Verify you're using the correct webhook secret
- Check for encoding issues
Events arriving out of order
- Use the
created_attimestamp to order events - Don't rely on delivery order for processing logic
Duplicate events
- Implement idempotency using the event
id - Store processed event IDs to detect duplicates
Debugging Tips
- Use the test endpoint - Send test events to verify your handler
- Check webhook logs - Review delivery status in the dashboard
- Log raw payloads - Temporarily log incoming requests for debugging
- Use a tunnel - Tools like ngrok can expose local endpoints for testing
Security Considerations
- Always verify signatures - Never process unverified webhooks
- Use HTTPS - Only register HTTPS endpoints
- Rotate secrets - Periodically rotate webhook secrets
- Limit IP access - If possible, allowlist Global Watch IP ranges
- Validate data - Don't trust webhook data blindly
Related Documentation
- API Overview - Introduction to the Global Watch API
- Authentication - API authentication methods
- Endpoints - Available API endpoints