#Public API — Webhook Endpoints

TheVibeCRM exposes public webhook endpoints that external systems can call to create and manage leads. These endpoints do not require user session authentication — they use per-listener secrets instead.

#Authentication

All webhook endpoints use a secret-based authentication model:

HeaderDescription
x-webhook-secretA unique secret generated per listener. Must be included in every request.
Important

Treat your webhook secret like a password. Do not share it publicly or commit it to version control. If compromised, delete the listener and create a new one to get a fresh secret.

#Endpoint: Create Lead from External Data

POST/api/webhooks/supabase/{listenerId}

Creates a new lead from external data using the field mappings configured for this listener.

Request Body
request — json
{
  "type": "INSERT",
  "table": "contacts",
  "schema": "public",
  "record": {
    "full_name": "Jane Doe",
    "email": "jane@example.com",
    "phone": "+1234567890",
    "company": "Acme Inc"
  },
  "old_record": null
}
Response
response — json
{
  "ok": true,
  "leadId": "clxx1234567890"
}

Headers:

HeaderRequiredValue
Content-TypeYesapplication/json
x-webhook-secretYesThe listener's webhook secret

Flat record format (also supported):

flat-payload — json
{
  "full_name": "Jane Doe",
  "email": "jane@example.com",
  "phone": "+1234567890",
  "company": "Acme Inc"
}

The column names in the payload must match the field mappings configured in your listener. The listener translates them to lead fields (e.g. full_namename, companycompanyName).

Responses

  • 201 — Lead created successfully: { "ok": true, "leadId": "clxx..." }
  • 200 — Event skipped (non-INSERT): { "ok": true, "skipped": true, "reason": "..." }
  • 401 — Invalid secret: { "error": "Invalid webhook secret" }
  • 404 — Listener not found: { "error": "Listener not found" }
  • 400 — Listener inactive or bad request: { "error": "Listener is inactive" }
  • 422 — Validation error: { "ok": false, "error": "Missing required fields: name and email must be mapped" }

#Endpoint: Process Stripe Subscription Event

POST/api/webhooks/stripe-integration/{listenerId}

Processes Stripe subscription events to create or update leads.

Request Body
request — json
{
  "id": "evt_1234567890",
  "type": "customer.subscription.created",
  "data": {
    "object": {
      "id": "sub_1234567890",
      "customer": "cus_1234567890",
      "status": "active"
    }
  }
}
Response
response — json
{
  "received": true,
  "matched": 1,
  "created": 0
}

Headers: Content-Type: application/json, x-webhook-secret (required).

Supported Event Types

Stripe EventListener Event Type
customer.subscription.createdsubscription_created
customer.subscription.updatedsubscription_updated
customer.subscription.deletedsubscription_canceled

Events that don't match the listener's configured event type are silently skipped. matched = existing leads updated; created = new leads created.

Responses

  • 200 — Processed: { "received": true, "matched": 1, "created": 0 } or skipped: { "received": true, "skipped": true }
  • 401 — Invalid secret
  • 404 — Listener not found or inactive

#Endpoint: Google Calendar Push Notification

POST/api/webhooks/google-calendar

This endpoint is called automatically by Google when calendar events change. It is not intended to be called manually.

Google includes: x-goog-channel-id (identifies the watch), x-goog-resource-state (sync, exists, not_exists). TheVibeCRM verifies the channel ID against active watches.

#Using Webhooks with Custom Systems

You can use the Supabase webhook endpoint to send leads from any external system — not just Supabase. Send a POST with JSON body and the x-webhook-secret header; payload fields must match your configured field mappings.

cURL

curl — bash
curl -X POST "https://app.thevibecrm.com/api/webhooks/supabase/{listenerId}" \
  -H "Content-Type: application/json" \
  -H "x-webhook-secret: your-listener-secret" \
  -d '{
    "full_name": "John Smith",
    "email": "john@company.com",
    "phone": "+1987654321",
    "company": "Tech Corp",
    "website": "https://techcorp.com"
  }'

JavaScript (fetch)

javascript — javascript
const response = await fetch(
  "https://app.thevibecrm.com/api/webhooks/supabase/{listenerId}",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-webhook-secret": "your-listener-secret",
    },
    body: JSON.stringify({
      full_name: "John Smith",
      email: "john@company.com",
      phone: "+1987654321",
      company: "Tech Corp",
    }),
  }
);

const data = await response.json();
console.log(data); // { ok: true, leadId: "clxx..." }

Python (requests)

python — python
import requests

response = requests.post(
    "https://app.thevibecrm.com/api/webhooks/supabase/{listenerId}",
    headers={
        "Content-Type": "application/json",
        "x-webhook-secret": "your-listener-secret",
    },
    json={
        "full_name": "John Smith",
        "email": "john@company.com",
        "phone": "+1987654321",
        "company": "Tech Corp",
    },
)

data = response.json()
print(data)  # {"ok": True, "leadId": "clxx..."}