Before you start
- A VelBEE workspace and the settings permission, to create a source under Connect.
- The source’s address and, for a key or secret source, the key or secret (shown once).
- Any language that can send an HTTPS request.
The request
POST https://api.velbee.com/v1/ingest/{source}
Content-Type: application/jsonA source belongs to one company workspace, so the lead always lands there. The company is found from the source alone and never from what the request contains.
Authentication
| Source type | How the caller proves itself |
|---|---|
| Website form, form page, landing page | Nothing secret: the address is public. You can limit it to your websites in Settings. |
| WordPress, automation tools, portals | A key: header Authorization: Bearer vbt_… (or X-Velbee-Token). |
| Your own software | A signature: header X-Velbee-Signature: sha256=…, the HMAC-SHA256 of the raw body with your secret vbs_…. |
Warning
Keys and secrets are shown once. Never put one in a web page or mobile app: use a public source type there. You can renew a key or secret at any time and the old one stops at once.
The body
JSON, plain text containing JSON, or an ordinary form post. Names are recognised in many spellings, for example name, full_name or your-name; phone, mobile or contact_no; email; message, comments or enquiry; interested_in, property or project; the five utm_* fields; page_url; referrer; and consent. First and last name are joined. One level of nesting under data, fields or form_data is read. Anything else is saved in the lead’s note, so nothing is lost. A phone number or an email is required. A field called website must be left empty: it catches bots. Fields named like passwords, cards or tokens are ignored.
curl -X POST https://api.velbee.com/v1/ingest/src_xxxx \
-H "Authorization: Bearer vbt_xxxx" \
-H "Content-Type: application/json" \
-d '{"name":"Ravi Kumar","phone":"+91 98765 43210","email":"ravi@example.com","message":"Interested in a 3BHK","utm_campaign":"diwali"}'Responses
| Code | Meaning |
|---|---|
201 | Saved. duplicate: true means the person already had an open lead and the enquiry was added to it. |
202 | Accepted and held, because lead intake is switched off for the workspace. It can be processed again later. |
401 | Wrong key or signature. |
403 | The website is not allowed, or the source is paused. |
404 | Unknown source. |
422 | No phone number or email, or a required agreement was not given. errors says which. |
429 | Too many requests (about 300 a minute per source and 120 per address). |
Troubleshooting
| What you see | Likely cause | What to do |
|---|---|---|
401 | Wrong key or signature. | Check the key, or sign the raw body exactly as sent. Keys and secrets that were renewed stop working at once. |
403 | The website is not allowed, or the source is paused. | Check the allowed websites and the pause switch in the source’s Settings. |
404 | Unknown source. | Copy the address again from Connect. |
422 | No phone number or email, or a required agreement was not given. | The errors field says which. A field called website must be empty. |
202 instead of 201 | Lead intake is switched off for the workspace, so the request is held. | It can be processed again once intake is on. |
201 with duplicate: true | The person already had an open lead and the enquiry was added to it. | Nothing to fix. |
429 | About 300 requests a minute per source and 120 per address were exceeded. | Wait and retry more slowly. |
Signing a request
import crypto from "node:crypto";
const body = JSON.stringify({ name: "Ravi Kumar", phone: "+91 98765 43210" });
const sig = crypto.createHmac("sha256", process.env.VELBEE_SECRET).update(body).digest("hex");
await fetch("https://api.velbee.com/v1/ingest/src_xxxx", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Velbee-Signature": "sha256=" + sig },
body,
});$body = json_encode(["name" => "Ravi Kumar", "phone" => "+91 98765 43210"]);
$sig = hash_hmac("sha256", $body, getenv("VELBEE_SECRET"));
$ch = curl_init("https://api.velbee.com/v1/ingest/src_xxxx");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json", "X-Velbee-Signature: sha256=$sig"]]);
curl_exec($ch);import hmac, hashlib, json, os, requests
body = json.dumps({"name": "Ravi Kumar", "phone": "+91 98765 43210"})
sig = hmac.new(os.environ["VELBEE_SECRET"].encode(), body.encode(), hashlib.sha256).hexdigest()
requests.post("https://api.velbee.com/v1/ingest/src_xxxx", data=body,
headers={"Content-Type": "application/json", "X-Velbee-Signature": "sha256=" + sig})The browser kit
For public sources: the script at https://api.velbee.com/v1/embed/{source} (add ` to show the form, or data-velbee-attach on an existing form), a hosted form page at https://api.velbee.com/v1/forms/{source} (add ?embed=1 for an iframe), and in custom code VelBEE.submit({…})`.
What this API does not do yet
It sends leads in. Reading leads, updating them, and message sending through the API are not available yet. Outbound webhooks are built: they send signed events to your own tools, and are included in the Growth and Scale plans (see the webhooks guide). WhatsApp and Instagram messages arrive through their own connections once those are live. See what is next.
Questions
Can I read or update leads through the API?
Not yet. The API sends leads in. To be told when something changes, use webhooks.
Can a property portal use this?
Portals use a simpler secret address. See the sheet for portals.