Buyer postback API
The postback API lets a buyer’s CRM, dialer, or internal automation report outcomes on delivered leads — exactly the same actions the buyer portal UI offers under the Leads page.
Two actions, one auth header, one optional idempotency key. Each action can name the lead by our lead ID, or by the customer’s phone or email when your CRM never stored our ID (see Match by phone or email).
Authentication
Section titled “Authentication”Send your buyer-scoped API key as a Bearer token:
Authorization: Bearer pk_live_8f3c…If you don’t have a key yet, see Authentication for how an admin generates one.
POST /api/v1/postback/leads/{leadId}/disposition
Section titled “POST /api/v1/postback/leads/{leadId}/disposition”Sets the pipeline stage on a delivered lead. Use this for sale, won/lost, qualification updates — anything the portal’s “Stage” dropdown sets.
{ "stage": "closed_won", "notes": "closed at $1200"}| Field | Type | Required | Notes |
|---|---|---|---|
stage |
string | yes | A stage in your disposition pipeline, by slug (e.g. quoted, closed_won) or by its label in any case (e.g. Closed Won). |
value |
number | no | Deal or quote value, e.g. 1200. A numeric string ("1200", "$1,200") is read the same way. Send it on a quoted stage (the quote amount) or closed_won (the sale value) so it flows through to reporting. |
currency |
string | no | ISO 4217 code for value, e.g. AUD. Defaults to the buyer’s billing currency. |
notes |
string | no | Free-text, stored in the audit row. |
Example
Section titled “Example”curl -X POST https://api.sent247.com/api/v1/postback/leads/$LEAD_ID/disposition \ -H "Authorization: Bearer $POSTBACK_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"stage":"closed_won","notes":"closed at $1200"}'import { randomUUID } from 'node:crypto';
const res = await fetch( `https://api.sent247.com/api/v1/postback/leads/${leadId}/disposition`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.POSTBACK_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': randomUUID(), }, body: JSON.stringify({ stage: 'closed_won', notes: 'closed at $1200' }), },);import os, uuid, requests
requests.post( f"https://api.sent247.com/api/v1/postback/leads/{lead_id}/disposition", headers={ "Authorization": f"Bearer {os.environ['POSTBACK_KEY']}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), }, json={"stage": "closed_won", "notes": "closed at $1200"},)Response
Section titled “Response”HTTP/1.1 200 OKContent-Type: application/json
{ "id": "9c45…", "status": "delivered", "dispositionStage": "closed_won", "firstName": "Jane", ...}POST /api/v1/postback/leads/{leadId}/reject
Section titled “POST /api/v1/postback/leads/{leadId}/reject”Marks a delivered lead as rejected, with a reason and optional notes. Same semantics as the Reject button in the portal.
{ "reason": "wrong_number", "notes": "phone disconnected"}| Field | Type | Required | Notes |
|---|---|---|---|
reason |
string | yes | Must match one of the reasons allowed by your rejection policy. |
notes |
string | no | Free-text, stored in the audit row. |
Policy enforcement
Section titled “Policy enforcement”Rejections that don’t match your account’s policy are refused with
HTTP 400 and a message saying which rule failed:
- Reason not in the allowlist — Your buyer or tenant has a fixed list of
acceptable reasons. The portal’s Postback API card and
/portal/apiexpose your current allowlist; the doc you generate from the dashboard embeds them inline. - Window expired — If your account has a rejection window (e.g. 72 h after delivery), late rejections are refused.
- Lead not in a rejectable state — Only
deliveredorconvertedleads can be rejected via this endpoint.
Example
Section titled “Example”curl -X POST https://api.sent247.com/api/v1/postback/leads/$LEAD_ID/reject \ -H "Authorization: Bearer $POSTBACK_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"reason":"wrong_number","notes":"phone disconnected"}'Match by phone or email
Section titled “Match by phone or email”Most CRMs never store our lead ID. These two endpoints take the customer’s phone or email instead and find the lead for you, so a “deal won” automation in Zapier or Make needs nothing added to your CRM. There is a step-by-step recipe in Report sales from Zapier or Make.
POST /api/v1/postback/disposition: same body as disposition, plus the fields below.POST /api/v1/postback/reject: same body as reject, plus the fields below.
| Field | Type | Required | Notes |
|---|---|---|---|
phone |
string | phone or email |
Any format: 0412 345 678, +61412345678 and 61412345678 all match. |
email |
string | phone or email |
Case and surrounding spaces are ignored. |
ignore_unmatched |
boolean | no | true (or "true") answers 200 with "matched": false when no lead matches, instead of 404. Set it in automations that run for every deal you close. |
Only leads delivered to your buyer account are searched. If the same person was delivered to you more than once, a lead matching both phone and email wins, then the most recently delivered. We don’t store the phone or email you send: the attempt log keeps the call with those fields blanked.
curl -X POST https://api.sent247.com/api/v1/postback/disposition \ -H "Authorization: Bearer $POSTBACK_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $DEAL_ID" \ -d '{"phone":"0412 345 678","email":"[email protected]","stage":"closed_won","value":12500,"ignore_unmatched":true}'The response wraps the lead so an automation can branch on one field:
{ "matched": true, "lead": { "id": "9c45…", "dispositionStage": "closed_won", "...": "..." } }or, with ignore_unmatched, when nothing matched:
{ "matched": false }Retries and idempotency
Section titled “Retries and idempotency”Send a unique Idempotency-Key header (a UUID is ideal) with every
request. If the same key is replayed within 24 hours, the response is
served from cache:
HTTP/1.1 200 OKIdempotent-Replay: trueContent-Type: application/jsonThe body is byte-identical to the first response, so consumers don’t need special handling — the header is informational.
Error codes
Section titled “Error codes”| HTTP | When | Recoverable? |
|---|---|---|
| 400 | Invalid JSON; missing reason, stage, or both phone and email; reason not allowed, window expired, or stage not in your pipeline |
Fix and retry |
| 401 | Missing or revoked Authorization | Get a new key from your admin |
| 403 | Key isn’t buyer-scoped (e.g. a seller sk_live_…) |
Use a pk_live_… key |
| 404 | Lead not found, or not delivered to your buyer account. On the phone/email endpoints, no match (unless ignore_unmatched) |
Check the ID, phone or email |
| 429 | Rate limit exceeded | Back off; retry with same Idempotency-Key |
| 5xx | Platform error | Safe to retry with same Idempotency-Key |