Skip to main content
The Lindo platform provides webhook notifications to inform your application about important events happening in your workspace. This allows you to build integrations and automate workflows based on real-time events.

Setting Up Webhooks

To receive webhook notifications, configure a webhook URL in your workspace settings. This URL should be a publicly accessible endpoint that can receive POST requests. Requirements:
  • The endpoint must accept POST requests
  • The endpoint must accept application/json content type
  • The endpoint should respond with a 2xx status code to acknowledge receipt

Webhook Format

All webhooks are sent as HTTP POST requests to your configured webhook URL.

Headers

Content-Type: application/json
User-Agent: LindoAI-Webhooks/1.0

Payload Structure

{
  "event": "event.type",
  "timestamp": "2025-10-15T12:00:00.000Z",
  "data": {
    // Event-specific data
  }
}

Supported Events

website.created

Triggered when a new website is created in your workspace.
{
  "event": "website.created",
  "timestamp": "2025-10-15T12:00:00.000Z",
  "data": {
    "website_id": "web_abc123xyz",
    "domain": "https://example.lindo.agency",
    "name": "My Business Website",
    "created_at": "2025-10-15T12:00:00.000Z",
    "activated": false
  }
}
website_id
string
Unique identifier for the website
domain
string
The preview URL of the website
name
string
Business name / website name
created_at
string
ISO 8601 timestamp of creation
activated
boolean
Whether the website is activated

website.deleted

Triggered when a website is deleted from your workspace.
{
  "event": "website.deleted",
  "timestamp": "2025-10-15T13:30:00.000Z",
  "data": {
    "website_id": "web_abc123xyz",
    "domain": "https://example.lindo.agency",
    "name": "My Business Website",
    "deleted_at": "2025-10-15T13:30:00.000Z"
  }
}
website_id
string
Unique identifier for the deleted website
domain
string
The preview URL that was associated with the website
name
string
Business name / website name
deleted_at
string
ISO 8601 timestamp of deletion

client.created

Triggered when a new client is added to your workspace.
{
  "event": "client.created",
  "timestamp": "2025-10-15T14:00:00.000Z",
  "data": {
    "client_id": "cli_xyz789abc",
    "full_name": "John Doe",
    "email": "[email protected]",
    "website_limit": 5,
    "created_at": "2025-10-15T14:00:00.000Z"
  }
}
client_id
string
Unique identifier for the client
full_name
string
Full name of the client
email
string
Email address of the client
website_limit
number
Maximum number of websites the client can create
created_at
string
ISO 8601 timestamp of creation

client.deleted

Triggered when a client is removed from your workspace.
{
  "event": "client.deleted",
  "timestamp": "2025-10-15T15:45:00.000Z",
  "data": {
    "client_id": "cli_xyz789abc",
    "full_name": "John Doe",
    "email": "[email protected]",
    "deleted_at": "2025-10-15T15:45:00.000Z"
  }
}
client_id
string
Unique identifier for the deleted client
full_name
string
Full name of the client
email
string
Email address of the client
deleted_at
string
ISO 8601 timestamp of deletion

Implementation Examples

Node.js / Express

const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
  const { event, timestamp, data } = req.body;

  console.log(`Received webhook: ${event} at ${timestamp}`);

  switch (event) {
    case 'website.created':
      handleWebsiteCreated(data);
      break;
    case 'website.deleted':
      handleWebsiteDeleted(data);
      break;
    case 'client.created':
      handleClientCreated(data);
      break;
    case 'client.deleted':
      handleClientDeleted(data);
      break;
    default:
      console.log(`Unknown event type: ${event}`);
  }

  // Always return 200 to acknowledge receipt
  res.status(200).json({ received: true });
});

function handleWebsiteCreated(data) {
  console.log(`New website created: ${data.name} (${data.website_id})`);
  // Your custom logic here
}

function handleWebsiteDeleted(data) {
  console.log(`Website deleted: ${data.name} (${data.website_id})`);
  // Your custom logic here
}

function handleClientCreated(data) {
  console.log(`New client added: ${data.full_name} (${data.email})`);
  // Your custom logic here
}

function handleClientDeleted(data) {
  console.log(`Client removed: ${data.full_name} (${data.email})`);
  // Your custom logic here
}

app.listen(3000, () => {
  console.log('Webhook receiver listening on port 3000');
});

Python / Flask

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.get_json()

    event = payload.get('event')
    timestamp = payload.get('timestamp')
    data = payload.get('data')

    print(f"Received webhook: {event} at {timestamp}")

    if event == 'website.created':
        handle_website_created(data)
    elif event == 'website.deleted':
        handle_website_deleted(data)
    elif event == 'client.created':
        handle_client_created(data)
    elif event == 'client.deleted':
        handle_client_deleted(data)
    else:
        print(f"Unknown event type: {event}")

    # Always return 200 to acknowledge receipt
    return jsonify({'received': True}), 200

def handle_website_created(data):
    print(f"New website created: {data['name']} ({data['website_id']})")
    # Your custom logic here

def handle_website_deleted(data):
    print(f"Website deleted: {data['name']} ({data['website_id']})")
    # Your custom logic here

def handle_client_created(data):
    print(f"New client added: {data['full_name']} ({data['email']})")
    # Your custom logic here

def handle_client_deleted(data):
    print(f"Client removed: {data['full_name']} ({data['email']})")
    # Your custom logic here

if __name__ == '__main__':
    app.run(port=3000)

Best Practices

Your webhook endpoint should respond with a 2xx status code as quickly as possible (ideally within 5 seconds). If you need to perform time-consuming operations, queue them for background processing.
app.post('/webhook', async (req, res) => {
  // Acknowledge receipt immediately
  res.status(200).json({ received: true });

  // Process in background
  processWebhookAsync(req.body).catch(err => {
    console.error('Background processing failed:', err);
  });
});
For production environments, consider implementing additional security measures:
  • Use HTTPS endpoints only
  • Validate the User-Agent header (LindoAI-Webhooks/1.0)
  • Consider implementing IP allowlisting if Lindo provides static IP addresses
Your endpoint should be resilient to:
  • Duplicate webhook deliveries
  • Out-of-order webhook deliveries
  • Missing or malformed data
Design your webhook handlers to be idempotent, as you may receive the same webhook multiple times:
async function handleWebsiteCreated(data) {
  const exists = await checkIfWebsiteExists(data.website_id);

  if (exists) {
    console.log(`Website ${data.website_id} already processed, skipping`);
    return;
  }

  // Process the new website...
  await createWebsiteInDatabase(data);
}
Implement comprehensive logging and monitoring:
  • Log all received webhooks
  • Monitor webhook processing failures
  • Set up alerts for repeated failures

Testing Webhooks

Local Development

For local development and testing, you can use tools like:
  • ngrok - Create a public URL for your local server
  • Webhook.site - Test webhook payloads without writing code
  • RequestBin - Another tool for inspecting webhook requests
ngrok http 3000

Manual Testing

You can manually test your webhook endpoint using curl:
curl -X POST http://your-webhook-url/webhook \
  -H "Content-Type: application/json" \
  -H "User-Agent: LindoAI-Webhooks/1.0" \
  -d '{
    "event": "website.created",
    "timestamp": "2025-10-15T12:00:00.000Z",
    "data": {
      "website_id": "web_test123",
      "domain": "https://test.lindo.agency",
      "name": "Test Website",
      "created_at": "2025-10-15T12:00:00.000Z",
      "activated": false
    }
  }'

Troubleshooting

  • Check webhook URL configuration in your workspace settings
  • Verify endpoint accessibility (not behind a firewall)
  • Check for HTTPS requirement
  • Review server logs
If your endpoint returns non-2xx status codes:
  • Webhooks may be retried (implementation dependent)
  • Check your endpoint’s error handling and logging
  • Ensure your endpoint can handle the payload structure
If you’re receiving duplicate webhooks:
  • Implement idempotency in your webhook handlers
  • Use unique identifiers (like website_id, client_id) to detect duplicates
  • Store processed webhook IDs to prevent reprocessing