Global WatchGlobal Watch Docs
API Reference

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:

  1. Go to SettingsWebhooks
  2. Click Add Webhook
  3. Enter your endpoint URL
  4. Select the events to subscribe to
  5. 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

EventDescription
project.createdA new project was created
project.updatedA project was updated
project.deletedA project was deleted
project.archivedA project was archived
project.restoredAn 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

EventDescription
asset.createdA new asset was created
asset.updatedAn asset was updated
asset.deletedAn asset was deleted
asset.status_changedAn 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

EventDescription
member.invitedA new member was invited
member.joinedA member accepted an invitation
member.updatedA member's role was updated
member.removedA 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

EventDescription
subscription.createdA new subscription was created
subscription.updatedA subscription was updated
subscription.canceledA subscription was canceled
invoice.paidAn invoice was paid
invoice.failedAn invoice payment failed

Usage Events

EventDescription
usage.threshold_reachedUsage threshold was reached (75%, 90%, 100%)
usage.limit_exceededUsage 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"
  }
}
FieldTypeDescription
idstringUnique event identifier
typestringEvent type
created_atstringISO 8601 timestamp
dataobjectEvent-specific payload
accountobjectAccount 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:

  1. Extract the signature from the header
  2. Compute the expected signature using your webhook secret
  3. 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

  1. Respond quickly - Return a 2xx response within 5 seconds
  2. Process asynchronously - Queue events for background processing
  3. Handle duplicates - Events may be delivered more than once
  4. Verify signatures - Always validate webhook authenticity
  5. 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:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
624 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_at timestamp 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

  1. Use the test endpoint - Send test events to verify your handler
  2. Check webhook logs - Review delivery status in the dashboard
  3. Log raw payloads - Temporarily log incoming requests for debugging
  4. Use a tunnel - Tools like ngrok can expose local endpoints for testing

Security Considerations

  1. Always verify signatures - Never process unverified webhooks
  2. Use HTTPS - Only register HTTPS endpoints
  3. Rotate secrets - Periodically rotate webhook secrets
  4. Limit IP access - If possible, allowlist Global Watch IP ranges
  5. Validate data - Don't trust webhook data blindly

On this page