Vendra CRM Vendra Websites Why Vendra Pricing Help centre Log in Start free trial
Developers

The Vendra API and webhooks.

The same records the CRM shows, reachable with a key. Read leads, contacts, companies, deals and tasks; create leads; receive every event as a signed webhook. Built for Zapier, Make, Google Sheets and your own code.

Authentication

Create a key under CRM → Integrations → Website & API. It is shown once. Send it as a bearer token on every request.

curl https://vendraone.com/api/api.php?r=me \
  -H "Authorization: Bearer vk_live_..."

Keys are per company and carry a scope: read or read,write. Limit: 600 requests per 10 minutes per key. A revoked key stops working immediately.

Reading records

One resource per call. Filters are optional and combine.

RequestReturns
GET ?r=leadsid, name, company, email, phone, status, source, value, owner, score, next, tags, created, updated
GET ?r=contactsid, first, last, title, email, phone, mobile, companyId, tags, created, updated
GET ?r=companiesid, name, domain, industry, size, phone, email, address, city, country, vat, tags
GET ?r=dealsid, name, pipeline, stage, value, companyId, contactId, owner, close, prob, wonAt, lostAt
GET ?r=tasksid, title, type, due, time, prio, status, assignee, linkType, linkId, doneAt
GET ?r=export&entity=leadsThe same list as CSV (semicolon separated, UTF-8 with BOM, opens correctly in Excel)

Filters: q (free text), status, since (milliseconds since 1970, on the updated timestamp), limit (max 200), offset.

GET https://vendraone.com/api/api.php?r=leads&status=New&limit=50

{
  "ok": true, "entity": "leads", "total": 12, "limit": 50, "offset": 0,
  "items": [
    { "id": "l_8f2a", "name": "Anders Holm", "company": "Nordvest Solar A/S",
      "email": "anders@nordvestsolar.dk", "phone": "+45 71 99 88 77",
      "status": "New", "source": "Website", "value": 24000, "score": 72,
      "created": 1757145600000, "updated": 1757145600000 }
  ]
}

Creating a lead

A POST goes into the CRM inbox, exactly like the website form. The CRM checks it against existing phone numbers and email addresses and assigns an owner, so the answer is 202 queued, not 201 created.

POST https://vendraone.com/api/api.php?r=leads
Authorization: Bearer vk_live_...
Content-Type: application/json

{ "name": "Anders Holm", "email": "anders@nordvestsolar.dk", "phone": "+45 71 99 88 77",
  "company": "Nordvest Solar A/S", "note": "Asked for a call about the CRM", "source": "Zapier" }

Webhooks

Paste an https URL under Integrations → Chat & alerts or connect Outgoing webhooks, Zapier or Make on your account page. Every CRM event is POSTed as JSON from the Vendra server, retried after 2 minutes, 15 minutes and 2 hours if your endpoint does not answer 2xx, and logged on the same screen.

POST https://example.com/vendra-webhook
X-Vendra-Event: deal.won
X-Vendra-Delivery: 8123
X-Vendra-Timestamp: 1757145600
X-Vendra-Signature: sha256=3f1c...

{ "event": "deal.won", "ts": 1757145600123, "company_id": 42,
  "data": { "name": "CRM for 14 seats", "value": 41000, "company": "Nordvest Solar A/S" } }

Events: lead.created, deal.created, deal.stage, deal.won, ticket.created, invoice.paid, meeting.booked, and test from the Send test button.

Verifying the signature

The signature is an HMAC-SHA256 of timestamp + "." + rawBody with your signing secret (Integrations → Website & API). Reject anything older than five minutes.

// Node
const crypto = require("crypto");
function verify(req, rawBody, secret) {
  const ts = req.headers["x-vendra-timestamp"];
  const sig = (req.headers["x-vendra-signature"] || "").replace("sha256=", "");
  const mine = crypto.createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
  return Math.abs(Date.now() / 1000 - ts) < 300 && crypto.timingSafeEqual(Buffer.from(mine), Buffer.from(sig));
}

// PHP
$raw  = file_get_contents('php://input');
$ts   = $_SERVER['HTTP_X_VENDRA_TIMESTAMP'] ?? '';
$sig  = str_replace('sha256=', '', $_SERVER['HTTP_X_VENDRA_SIGNATURE'] ?? '');
$mine = hash_hmac('sha256', $ts . '.' . $raw, $secret);
$ok   = abs(time() - (int)$ts) < 300 && hash_equals($mine, $sig);

Recipes

Zapier

Out: trigger Webhooks by Zapier → Catch Hook, copy the hook URL, paste it as your Zapier connection. In: action Webhooks by Zapier → POST to /api/api.php?r=leads with the Authorization header and a JSON body.

Make

Out: a Webhooks → Custom webhook module gives you a URL to paste. In: an HTTP → Make a request module that POSTs to /api/api.php?r=leads.

Google Sheets

Extensions → Apps Script, then a time-driven trigger on this function:

function pullVendraLeads() {
  const res = UrlFetchApp.fetch("https://vendraone.com/api/api.php?r=export&entity=leads",
    { headers: { Authorization: "Bearer vk_live_..." } });
  const rows = Utilities.parseCsv(res.getContentText("UTF-8"), ";");
  const sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Leads") || SpreadsheetApp.getActiveSpreadsheet().insertSheet("Leads");
  sh.clearContents();
  sh.getRange(1, 1, rows.length, rows[0].length).setValues(rows);
}

WordPress, Webflow and any HTML form

The website form endpoint needs your site key (Integrations → Website & API) instead of an API key, because it is called from a browser. Post the fields key, name, email, phone, company, note, source to:

POST https://vendraone.com/api/public.php?a=web_lead
Content-Type: application/json

{ "key": "YOUR_SITE_KEY", "name": "…", "email": "…", "phone": "…", "company": "…", "note": "…", "source": "Website" }

Shopify and WooCommerce

Create a webhook for Order creation (Shopify: Settings → Notifications → Webhooks; WooCommerce: Settings → Advanced → Webhooks) pointing at:

https://vendraone.com/api/public.php?a=shop_order&key=YOUR_SITE_KEY

Orders land on the customer card with lifetime value; Shopify checkout webhooks show up as abandoned carts in the CRM inbox.

Good to know