Skip to content

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).

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.
Terminal window
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"}'
HTTP/1.1 200 OK
Content-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.

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/api expose 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 delivered or converted leads can be rejected via this endpoint.
Terminal window
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"}'

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.

Terminal window
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 }

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 OK
Idempotent-Replay: true
Content-Type: application/json

The body is byte-identical to the first response, so consumers don’t need special handling — the header is informational.

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