meta data for this page
  •  

This is an old revision of the document!


API Cookbook — Webhook Patterns

Receiving a Webhook

LEAST sends a POST request to your endpoint with Content-Type: application/json. Respond with HTTP 200 within 10 seconds to acknowledge receipt. Process asynchronously if your handler takes longer.

Minimal PHP receiver:

<?php
// webhook-receiver.php
$payload   = json_decode(file_get_contents('php://input'), true);
$signature = $_SERVER['HTTP_X_LEAST_SIGNATURE'] ?? '';
$secret    = 'your-webhook-secret';
 
// Verify signature
$expected = 'sha256=' . hash_hmac('sha256', file_get_contents('php://input'), $secret);
if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}
 
// Acknowledge immediately
http_response_code(200);
echo 'ok';
 
// Process asynchronously (queue the payload, don't block here)
file_put_contents('/tmp/webhook_queue.jsonl',
    json_encode($payload) . "\n", FILE_APPEND);

Minimal Node.js Receiver

const express = require('express');
const crypto  = require('crypto');
const app     = express();
 
app.use(express.raw({ type: 'application/json' }));
 
app.post('/webhook', (req, res) => {
    const secret    = 'your-webhook-secret';
    const signature = req.headers['x-least-signature'];
    const expected  = 'sha256=' + crypto.createHmac('sha256', secret)
                                        .update(req.body).digest('hex');
 
    if (signature !== expected) return res.status(401).send('Bad signature');
 
    const payload = JSON.parse(req.body);
    console.log('Event:', payload.event, 'Record:', payload.record_id);
 
    res.status(200).send('ok');
    // process payload asynchronously...
});
 
app.listen(3000);

Routing Events by Type

switch ($payload['event']) {
    case 'record.created':
        // New record — send a Slack notification, create a Jira ticket, etc.
        notify_slack("New item: " . $payload['data']['strD5_1_1']);
        break;
    case 'record.action':
        // Status change — check which action was taken
        if ($payload['action_id'] === 8) { // Action 8 = Complete
            close_external_ticket($payload['record_id']);
        }
        break;
    case 'record.archived':
        archive_in_crm($payload['record_id']);
        break;
}

Zapier / Make (Integromat)

Both Zapier and Make have a “Webhooks” trigger that can receive LEAST webhook deliveries:

  1. Create a new Zap / Scenario
  2. Add a Webhook trigger → copy the generated URL
  3. In LEAST collection admin → Webhooks → add the Zapier/Make URL as the target
  4. Trigger a test event in LEAST → the webhook fires → Zapier/Make catches it
  5. Build downstream steps: create a Google Sheets row, send a Slack message, create a HubSpot deal, etc.

Signature verification in Zapier: not natively supported in the free tier. Use a Code step to verify manually if security is required.