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
| Tier | Per minute | Per day |
|---|---|---|
| Free | 60 | 5 000 |
| Pro | 600 | 100 000 |
| Enterprise | 3 000 | 1 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