Sending leads into Saudaflow.
A signed webhook, a shared secret and a JSON body. This page is the whole contract — the URL shape, how the signature is computed, what a duplicate delivery does, what comes back, and what happens when your server sends something we cannot read.
The straight answer
Saudaflow has no public API and no OpenAPI specification. There is no tenant API key to issue, no Settings › API keys screen, no bearer token for a program, and no openapi.yaml at any address. An older version of this page said a spec was "in the works" and pointed at a marketing page for the endpoint list; a sibling page went further and rendered an interactive explorer for a spec URL that has never resolved. Both are gone.
There is also no way to read data out over HTTP — no outbound webhooks, no events pushed to your server, no query endpoint. If you are scoping an integration, scope it as one-way in. Data comes out through exports, which are covered at the bottom of this page.
What is real, in production, and documented below for the first time: a signed inbound endpoint per integration, with idempotency, a retry ladder, dead letters and a delivery log you can read. If your job is "get our enquiries into Saudaflow", you can build it today.
Nine sources, one endpoint.
Every source below posts to the same route. Each carries its own signing scheme and header, and its own map of where the buyer's details sit inside its payload — so nothing downstream ever sees a portal-shaped body. Adding a source is one registry entry, not a new endpoint.
| Source | Proves itself with | Header | Notes |
|---|---|---|---|
| 99acres | HMAC-SHA256 | x-99acres-signature | One lead per delivery. |
| MagicBricks | HMAC-SHA256 | x-mb-signature | camelCase payload. |
| Housing.com | HMAC-SHA256 | x-housing-signature | Buyer nested under user, listing under listing. |
| Facebook / Instagram Lead Ads | Meta SHA-256 | x-hub-signature-256 | Batched; fields arrive as {name, values[]}. |
| Google Ads lead forms | Shared key | google-key | Key in the header or the body; column names in SCREAMING_CASE. |
| Website form | HMAC-SHA256 | x-saudaflow-signature | Any hand-built form; UTM parameters carry attribution. |
| QR / landing page | HMAC-SHA256 | x-saudaflow-signature | Hoarding and print codes; placement records which one produced the scan. |
| Custom partner webhook | HMAC-SHA256 | x-saudaflow-signature | The one to build against. Field paths are remappable per workspace with no code change. |
| WhatsApp Business (Meta Cloud API) | Meta SHA-256 | x-hub-signature-256 | Routed to the WhatsApp module, not straight to lead intake. |
Two honest qualifications. The portal-branded rows are adapters on our side — the delivery still has to be switched on inside your own 99acres, MagicBricks or Housing account, and what those portals will push varies by the contract you hold with them. And WhatsApp Business is not live for anyone: the module is complete end to end but we have no live Meta credentials yet, so no workspace is sending or receiving on it today.
What is live and unconditional is the endpoint itself, and therefore the custom partner webhook and the website form. Those are yours to point at whatever you like — your own site, a marketing automation tool, a partner's CRM, a script on a cron.
The contract, in full.
Create it in the product first: Integrations › Connect an integration › Custom partner webhook. The screen hands you three things — the URL, the header name and the signing secret. Copy them from there; do not build the URL by hand. The endpoint slug is sixteen random bytes and is deliberately not derived from anything guessable, and the secret is shown once — only its hash is kept, so a secret you lose has to be rotated, not recovered.
POST /api/v1/inbound/<endpoint-slug> HTTP/1.1
Host: app.saudaflow.in
Content-Type: application/json
X-Saudaflow-Signature: <hex>
Idempotency-Key: <your own id for this enquiry> # optional, recommended
{"name":"Rohit Sharma","phone":"+919876543210",
"email":"rohit@example.com","project":"Skyline Heights",
"city":"Pune","message":"2BHK, site visit this weekend"}
- The signature
- hex(HMAC-SHA256(secret, raw_request_body)), lowercase, in the header the screen names. Compute it over the exact bytes you send — re-serialising your JSON first will change key order or whitespace and the signature will not match. Base64 is accepted for this scheme too, and a leading sha256= is stripped if your library adds one. The compare is constant-time.
- The body
- JSON, up to 1,000,000 bytes. Anything larger is refused at the door with nothing buffered and nothing written. The keys above are the defaults; a workspace admin can remap the paths to whatever your system already emits, without anyone writing code.
- The idempotency key
- Send one per enquiry and we will believe you. Without it we use the lead id your payload carries; without that, a hash of the source, the phone, the e-mail, a ten-minute time bucket and a digest of the body. That bucket is the point: the same body redelivered four minutes later is a retry, the same buyer enquiring again next week is a new lead.
- Signing is not optional in production
- A sandbox integration may run unsigned while you wire it up. A production integration with no signing scheme refuses every delivery, because otherwise anyone who learned the URL could inject leads into a live pipeline.
# shell
BODY='{"name":"Rohit Sharma","phone":"+919876543210"}'
SIG=$(printf '%s' "$BODY" \
| openssl dgst -sha256 -hmac "$SECRET" -hex \
| sed 's/^.* //')
curl -sS "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-H "X-Saudaflow-Signature: $SIG" \
-H "Idempotency-Key: enquiry-88213" \
--data-raw "$BODY"
# node
const sig = require('crypto')
.createHmac('sha256', secret)
.update(body, 'utf8') // body is the STRING you POST
.digest('hex');
200 {"ok":true,"data":{
"received":true,
"eventId":"…",
"status":"PROCESSED", # or DUPLICATE, FAILED
"outcome":"…",
"duplicate":false}}
401 signature verification failed
404 unknown or disabled endpoint
422 body unreadable, or over the size limit
{"ok":false,"error":{"code":"…","message":"…",
"details":{…}}}
Every response uses the product-wide envelope: success is {ok:true, data}, failure is {ok:false, error:{code, message, details}}. An unknown endpoint and a disabled one answer identically on purpose — the endpoint must not become a way to find out which workspaces are live.
Create the integration as a sandbox one first. A sandbox integration has a Send a test delivery action that puts a sample payload through the real pipeline — the same mapping, the same dedup engine, the same delivery log — so you can confirm your field mapping produces the lead you expect before a live enquiry depends on it. Signing is optional in sandbox and mandatory in production, so wire the signature up while you are still in sandbox and switch the environment over once it verifies.
A 200 means "recorded", not "processed".
Senders retry on anything that is not a 2xx, so we answer the moment the delivery is on disk and then do the work. That is why a failure on our side is still a 200 to you — and why nothing you send can be lost quietly.
- 01
Recorded
The delivery is written down with its headers, its source IP and its raw body — before anything is interpreted.
- 02
Signature checked
A forged or missing signature is the one thing that gets a 401. It is recorded as Rejected with the reason, and your workspace is notified.
- 03
Deduplicated by key
A second delivery with the same idempotency key is marked Duplicate and stops there. You still get a 200.
- 04
Mapped and normalised
Batched payloads are split, the fields are read out of wherever that source keeps them, and the phone number is normalised.
- 05
Through the dedup engine
Never a direct write. The same engine a walk-in and a manual entry go through decides whether this is a new lead or a touch on an existing one.
- 06
Assigned and stamped
If the integration auto-assigns, an owner is chosen; the event is stamped Processed with the lead id and how long it took.
The same buyer, twice
- This is the part worth reading twice if you send from more than one source. A phone number that already exists inside the attribution window does not become a second owned lead. It is recorded as a duplicate touch on the original owner's lead, and both parties are notified.
- The window is a workspace setting and defaults to 45 days. Outside it, the enquiry is claimable fresh.
- Every one of those decisions is written to the audit trail, because lead ownership is the thing builders and channel partners actually argue about.
- What this means for your integration: send everything. You do not need to check for duplicates first, and you should not try — deduplication is a property of the workspace's rules, not of your sender.
When something goes wrong
- A body we cannot parse is kept, not dropped. The bytes you sent are stored with the reason, the refusal names the record id, and it shows up on the delivery log so a human can rescue the enquiry and tell you what to fix. Form-encoded and truncated bodies land here.
- A processing failure retries at one minute, five, fifteen and an hour — five attempts. After that the event becomes a dead letter and waits for a person, because silently dropping a portal lead is the single outcome this whole subsystem exists to prevent.
- Anything can be replayed by hand once the cause is fixed; the original event is preserved beside the replay.
- Health is scored, not guessed. Each integration carries a 0-100 score from its failures, dead letters, signature rejections and latency: healthy at 90 and above, degraded from 70, failing below that, and idle after 48 hours with no delivery at all.
Watching it from the product: Integrations › your integration › the delivery log. Every event carries one of eight states — Received, Processing, Processed, Duplicate, Rejected, Failed, Dead letter, Replayed — with its latency and, where one was created, the lead it became. Timestamps are IST.
GET, for Meta's subscription check.
Only relevant if you are wiring a Meta product. Meta calls the same URL with hub.mode=subscribe, hub.verify_token and hub.challenge, and will not activate a subscription unless the body is exactly the challenge as text/plain. A wrong or missing token is a bare 403 — never a 200 with a helpful hint about what was expected.
Getting data out, without an API.
Files, not endpoints. It is less convenient than a read API and we are not going to pretend otherwise — but it is what exists, so plan around it rather than around a promise.
Fifteen reports
Funnel, lead source, site visits, disputes and duplicates, commission aging, collections, payment ledger, bookings and cancellations, RERA compliance, KYC and document readiness, approval turnaround, offboarding, channel-partner performance, inventory absorption, user productivity. Each exports as CSV, Excel or PDF.
The audit log
Exports in the same three formats, capped at 10,000 rows per export and filtered the way the screen is. The export itself writes an audit row — the export of evidence is evidence.
Bulk import, precisely
Bulk CSV import today is inventory units — not leads, not contacts. It runs as a dry run first: every row is validated and the errors are shown before anything is committed, and the commit is a single audited transaction.
Export is a permission action in its own right, so who can take data out of your workspace is a role decision like any other, and every export is attributable.
What you cannot build against, stated once.
So that nobody discovers it in week three of an integration project.
- No public or tenant REST API. No endpoint you can call with a key to read or write leads, deals, inventory, bookings or commission.
- No API keys. There is no screen that issues one and no tenant key store. The only API keys in the system belong to our own internal operations console and are not reachable from a workspace.
- No OpenAPI or Swagger document, at any URL, in any version. If you were handed one, it did not come from us.
- No outbound webhooks. Saudaflow does not call your server when something changes. The traffic is one-way, inbound.
- No Zapier, Make or iPaaS connector, and no marketplace listing.
- No GraphQL, no bulk data API, no direct database access.
- WhatsApp Business is built but not live — no live Meta credentials yet, so no workspace is sending or receiving on it today.
Tell us what you are trying to build and what it would unblock, at support@saudaflow.in. A read API is a reasonable thing to want and knowing which shape of it people actually need is how it gets prioritised honestly. What you will not get from us is a date invented on a help page, or a spec URL that answers with a 404 when you curl it. That is the mistake this page replaced.
Before you open a ticket.
Where is the OpenAPI spec?
There isn't one. There is no specification file, no generator that would produce one, and no route that serves one — at api.saudaflow.in/v1/openapi.yaml or anywhere else. An older page advertised that address and rendered an interactive explorer against it; the address never resolved, and the path was wrong twice over because every route in this product lives under /api/v1/.
The inbound endpoint on this page is the whole surface, and this page is its documentation.
How do I get an API key?
You don't, because there is nothing to authenticate against. The workspace app authenticates people — a fifteen-minute access token with a rotating per-device refresh token — not programs.
What an integration gets instead is a signing secret, minted per integration when you create it, which proves a delivery is yours. It is stored encrypted under a key held in the server environment rather than in the database, and it is shown to you once, so keep it where you keep your other secrets.
My signature keeps failing. What is wrong?
Nine times in ten it is the body. The HMAC has to be computed over the exact bytes you put on the wire, not over a re-serialised copy of your object — passing your JSON through a second JSON.stringify, a pretty-printer or an HTTP client that re-encodes it will reorder keys or change whitespace, and the digest changes with it. Build the string once, sign that string, send that string.
After that: check you are using the secret for the right integration, that the header name matches the one the screen printed, and that you are sending lowercase hex. The rejection is recorded on the delivery log with the reason, so look there rather than guessing.
What happens if I send the same lead twice?
Two different things, at two different layers, and both are safe. A redelivery of the same delivery — same idempotency key — is marked Duplicate and stops; you still get a 200. A genuinely new delivery about a buyer who is already in the workspace within the attribution window becomes a duplicate touch on the original owner's lead, not a second owned lead, and both people are told.
So send everything. Do not try to deduplicate before you POST: whether two enquiries are the same lead is a decision the workspace's own rules make, and the window that governs it is a setting the builder controls, defaulting to 45 days.
Can Saudaflow push updates to my system?
No. There are no outbound webhooks and no callbacks — Saudaflow never calls your server. If you need to know what happened to a lead you sent, that is an export today, not a subscription. It is a real limitation and it is better to hear it now than to architect around a listener that will never be called.
Can I import our old CRM's leads as a CSV?
Not as a self-service CSV upload — bulk import in the product today covers inventory units, with row-by-row validation and a dry run before anything is committed. Leads and contacts do not have that screen yet.
Two ways round it, both real. Point a script at the inbound endpoint and replay your history through it, which has the advantage of running every record through deduplication and attribution exactly as a live enquiry would. Or ask us at support@saudaflow.in — a migration is part of onboarding and we would rather do it with you than have you find this paragraph at 11pm.
Is the endpoint rate limited?
Yes, and normal portal traffic will not come near it. Requests to an endpoint slug that does not exist are charged against a much tighter budget than real deliveries, because scanning for live endpoints is exactly what that budget is for. If you are planning a bulk replay of historical leads, tell us first and pace it — the retry ladder is designed for a portal that sends a few enquiries a minute, not for a backfill.
Related
Building an integration and stuck on something this page does not answer? support@saudaflow.in — a person answers, 09:30–19:00 IST, every day.