Skip to main content
VendorPad VendorPad

Developer API

JSON REST API. Bearer-token auth with scoped keys. Predictable rate limits. HMAC-signed outbound webhooks with retries.

Base URL

https://api.vendorpad.app/api/v1/developer

Authentication

Every request carries a Bearer token. Keys look like vp_live_… (or vp_test_… for test environments). Create keys in the Members app under Integrations → API Keys.

curl https://api.vendorpad.app/api/v1/developer/leads \
  -H "Authorization: Bearer vp_live_AbCdEfGh1234..."

Create a lead

Scope required: leads:write. first_name and email are required.

POST /api/v1/developer/leads
Content-Type: application/json
Authorization: Bearer vp_live_...

{
  "first_name": "Sam",
  "last_name": "Taylor",
  "email": "sam@example.com",
  "phone": "07700 900000",
  "event_type": "Wedding",
  "event_date": "2026-09-14",
  "brand_id": 1,
  "utm_source": "google",
  "utm_campaign": "summer_wedding"
}

Returns { lead_id, visitor_id, duplicate }. If a lead with the same email already exists in your business, duplicate: true and the existing lead_id is returned (no duplicate created).

Record an event

Scope required: events:write. Used for pageviews, conversions, or custom telemetry.

POST /api/v1/developer/events
{
  "visitor_id": "uuid-v4-string",
  "event_type": "conversion",
  "event_name": "booking_confirmed",
  "event_props": { "value_pence": 50000 }
}

Rate limits

TierPer minutePer day
Free605 000
Pro600100 000
Enterprise3 0001 000 000

Outbound webhooks

Register a URL to receive HMAC-signed POSTs when events fire (lead.created, lead.status_changed, lead.won, lead.lost, …). Delivered within 1 minute via retry queue. Verify the signature:

// Node.js
const crypto = require('crypto');
const timestamp = req.headers['x-vendorpad-timestamp'];
const sig = req.headers['x-vendorpad-signature']; // "sha256=..."
const expected = 'sha256=' + crypto
  .createHmac('sha256', process.env.VP_WEBHOOK_SECRET)
  .update(timestamp + '.' + rawBody)
  .digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) throw new Error('bad sig');
// Reject timestamps older than 5 minutes to prevent replay
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) throw new Error('replay');
# PHP
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_VENDORPAD_TIMESTAMP'];
$sig = $_SERVER['HTTP_X_VENDORPAD_SIGNATURE']; // "sha256=..."
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($sig, $expected)) http_response_code(401);
# Python
import hmac, hashlib, time
ts = request.headers['X-VendorPad-Timestamp']
sig = request.headers['X-VendorPad-Signature']
expected = 'sha256=' + hmac.new(secret.encode(), (ts + '.' + body).encode(), hashlib.sha256).hexdigest()
assert hmac.compare_digest(sig, expected)
assert abs(time.time() - int(ts)) < 300