Quickstart

Not a developer? You can set up webhooks without writing any code using the Deel Developer Center. This guide is for developers who want to integrate webhooks programmatically.

What You’ll Learn

This quickstart guide will walk you through:

  1. Creating a webhook endpoint to receive events
  2. Verifying webhook signatures for security
  3. Subscribing to specific events
  4. Testing your webhook integration

Time to complete: ~15 minutes

Prerequisites

Before you begin, ensure you have:

  • API Token – Get your API token from the Deel Developer Center
  • Development Environment – Node.js, Python, Go, or PHP installed locally
  • Testing Tool – ngrok or a similar tool to expose localhost

Step 1: Create a Webhook Endpoint

First, create a simple HTTP endpoint that can receive POST requests from Deel.

Choose your language: We’ll show examples in multiple languages. Pick the one you’re most comfortable with!

1// webhook-server.js
2const express = require('express');
3const crypto = require('crypto');
4
5const app = express();
6
7// Middleware to capture raw body for signature verification
8app.use(express.json({
9 verify: (req, res, buf) => {
10 req.rawBody = buf.toString();
11 }
12}));
13
14// Webhook endpoint
15app.post('/webhooks/deel', (req, res) => {
16 console.log('✅ Webhook received!');
17 console.log('Event type:', req.body.data?.meta?.event_type);
18
19 // Always respond with 200 immediately
20 res.status(200).send('OK');
21});
22
23app.listen(3000, () => {
24 console.log('🚀 Webhook server running on http://localhost:3000');
25});

Start your server and make sure it’s running on port 3000. We’ll expose it publicly in the next step.

Step 2: Expose Your Local Server

Use ngrok to create a public HTTPS URL for your local server:

1

Install ngrok

Download and install ngrok from ngrok.com

$# Or install via homebrew (macOS)
>brew install ngrok
2

Start ngrok tunnel

Run ngrok to expose port 3000:

$ngrok http 3000

You’ll see output like:

Forwarding https://abc123.ngrok.io -> http://localhost:3000
3

Copy your HTTPS URL

Copy the https:// URL (e.g., https://abc123.ngrok.io)

Your webhook URL will be: https://abc123.ngrok.io/webhooks/deel

Keep ngrok running in a separate terminal window while testing. If you restart ngrok, you’ll get a new URL and need to update your webhook subscription.

Step 3: Create a Webhook Subscription

Now subscribe to webhook events using the Deel API.

Prefer a visual interface? You can also create webhooks through the Deel Developer Center without writing code. This guide uses the API for learning purposes.

$curl -X POST 'https://api.letsdeel.com/rest/v2/webhooks' \
> -H 'Authorization: Bearer YOUR_API_TOKEN' \
> -H 'Content-Type: application/json' \
> -d '{
> "url": "https://abc123.ngrok.io/webhooks/deel",
> "events": ["contract.created", "contract.signed"],
> "description": "My first webhook"
> }'

Save the signing key! The API response includes a signing_key that you’ll need for signature verification in the next step. Store it securely as an environment variable.

Don’t know which events to subscribe to? List all available events first:

$curl 'https://api.letsdeel.com/rest/v2/webhooks/events' \
> -H 'Authorization: Bearer YOUR_API_TOKEN'

Step 4: Add Signature Verification

Now add security to your webhook endpoint by verifying signatures:

1// webhook-server.js
2const express = require('express');
3const crypto = require('crypto');
4
5const app = express();
6const SIGNING_KEY = process.env.DEEL_WEBHOOK_SECRET; // From Step 3
7
8app.use(express.json({
9 verify: (req, res, buf) => {
10 req.rawBody = buf.toString();
11 }
12}));
13
14function verifySignature(req) {
15 const signature = req.headers['x-deel-signature'];
16 const expectedSignature = crypto
17 .createHmac('sha256', SIGNING_KEY)
18 .update('POST' + req.rawBody)
19 .digest('hex');
20
21 return crypto.timingSafeEqual(
22 Buffer.from(signature),
23 Buffer.from(expectedSignature)
24 );
25}
26
27app.post('/webhooks/deel', (req, res) => {
28 // Verify signature
29 if (!verifySignature(req)) {
30 console.log('❌ Invalid signature!');
31 return res.status(401).send('Invalid signature');
32 }
33
34 console.log('✅ Signature verified!');
35 console.log('Event:', req.body.data.meta.event_type);
36
37 res.status(200).send('OK');
38});
39
40app.listen(3000, () => {
41 console.log('🚀 Secure webhook server running');
42});

Security tip: The signature is computed as HMAC-SHA256(signing_key, "POST" + raw_body). Always prefix with “POST” before hashing!

Step 5: Test It Out!

Now trigger a webhook event to see everything working:

1

Ensure everything is running

Make sure you have:

  • ✅ Your webhook server running on port 3000
  • ✅ ngrok tunnel active
  • ✅ Webhook subscription created
2

Trigger an event in Deel Sandbox

Log into your Deel Sandbox and perform an action that triggers your subscribed event:

  • Create a new contract (triggers contract.created)
  • Sign a contract (triggers contract.signed)
  • Complete a payment (triggers payment.completed)
3

Check your server logs

You should see output like:

✅ Signature verified!
Event: contract.created
4

Verify in ngrok dashboard

Open the ngrok web interface at http://localhost:4040 to see the webhook request details

Webhook not arriving? Deel retries failed deliveries with exponential backoff. If your endpoint was down, the webhook will be retried automatically up to 10 times.

What’s in a Webhook Payload?

Here’s what you’ll receive when an event occurs:

1{
2 "data": {
3 "meta": {
4 "event_type": "contract.created",
5 "organization_id": "c2f26732-e747-4776-8a21-b31c379f2356"
6 },
7 "resource": [
8 {
9 "contract_id": "123abc1",
10 "worker_email": "contractor@example.com",
11 "status": "pending",
12 "created_at": "2025-02-05T15:39:38.070Z"
13 }
14 ]
15 },
16 "timestamp": "2025-02-05T15:39:38.070Z"
17}

Key fields:

  • data.meta.event_type - The type of event that occurred
  • data.meta.organization_id - Your organization ID
  • data.resource - Event-specific data (varies by event type)
  • timestamp - When the event occurred (ISO 8601 format)

Common Issues & Solutions

Check:

  • Is your server running and accessible via ngrok?
  • Did you use the correct ngrok HTTPS URL when creating the subscription?
  • Are you triggering the right events you subscribed to?

Quick test:

$curl -X POST https://abc123.ngrok.io/webhooks/deel \
> -H "Content-Type: application/json" \
> -d '{"test": "data"}'

Common mistakes:

  • Not prefixing with “POST” before hashing
  • Using parsed JSON instead of raw body
  • Wrong signing key (check your environment variables)

Debug it:

1console.log('Received signature:', req.headers['x-deel-signature']);
2console.log('Raw body:', req.rawBody);
3console.log('Signing key:', SIGNING_KEY);

Possible causes:

  • Invalid or expired API token
  • Using sandbox token for production API (or vice versa)

Solution: Verify your token:

$curl 'https://api.letsdeel.com/rest/v2/organizations' \
> -H 'Authorization: Bearer YOUR_API_TOKEN'

Learn More

You can manage webhook subscriptions via API or through the Developer Center:

Via API:

  • GET /webhooks - List all your webhooks
  • GET /webhooks/{id} - Get webhook details
  • PATCH /webhooks/{id} - Update webhook URL or events
  • DELETE /webhooks/{id} - Delete webhook

Via Developer Center: Visit the Managing Webhooks guide to learn how to create, view, edit, test, and monitor webhooks through the visual interface.

Deel retries failed webhooks up to 10 times with exponential backoff:

  • 1st retry: 1 minute
  • 2nd retry: 2 minutes
  • 3rd retry: 4 minutes
  • 4th-9th retries: Continue with exponential backoff
  • 10th attempt: After 16 hours, webhook is disabled if it fails

After 10 failures, the webhook is automatically disabled and you’ll need to re-enable it.

Before going live, make sure you:

  • Always verify signatures with constant-time comparison
  • Respond within 30 seconds (ideally under 5 seconds)
  • Use HTTPS with a valid SSL certificate
  • Log webhook deliveries for debugging
  • Monitor for failures and retries
  • Implement graceful error handling
  • Set up alerts for webhook failures

Additional Resources