Yelp Lead Outcomes Webhook: Partner Integration Guide
This guide explains how to send lead status updates from your system to Yelp. When a Yelp lead progresses in your platform (for example, a job is scheduled, started, completed or closed), you send Yelp a signed HTTPS request. Yelp then keeps the lead's status in sync for the business owner.
1. Overview
| Method | POST |
| URL | https://api.yelp.com/v3/integrations/{partner}/lead_updates |
| Content type | application/json |
| Authentication | Request signature (see Section 3) |
| Success response | 204 No Content |
{partner} is the partner identifier Yelp assigns you during onboarding.
2. Onboarding
Before you send events, Yelp will provide you with:
- Your partner identifier. This is the
{partner}value in the URL. - A signing secret. Yelp generates it and shares it with you over a secure channel (a one-time secret link, never email or chat). If your platform generates its own webhook signing secrets, you can share yours with Yelp the same way instead. It must be at least 32 random bytes and unique to this integration. Store it securely and never send it in a request.
- Your signing configuration. This covers the header names and encoding, and defaults to the settings in Section 3.
Please share with Yelp:
- The event types you plan to send, and a sample payload for each.
- How your lead/job statuses correspond to the Yelp statuses listed in Section 4.
- A technical contact for the integration.
If your platform already sends webhooks in a fixed format that you can't change, let us know. Yelp can often accept your existing format and map it on our side.
3. Authentication: Request Signing
Every request must be signed with HMAC-SHA256 using the secret Yelp provides. Yelp rejects any request with a missing or invalid signature.
Required headers
| Header | Value |
|---|---|
X-Timestamp | Current Unix time in seconds, e.g. 1760875200 |
X-Signature | Hex-encoded HMAC-SHA256 signature (lowercase) |
Content-Type | application/json |
How to compute the signature
- Take the exact raw request body you will send (the bytes, not a re-serialized copy).
- Build the string to sign:
{X-Timestamp}.{raw body}. That is the timestamp, a literal., then the body. - Compute
HMAC-SHA256(secret, string_to_sign). - Hex-encode the result and send it in
X-Signature.
Replay protection
Yelp only accepts a request if X-Timestamp is within 5 minutes of the current time. Make sure your servers' clocks are synchronized (NTP), and generate a fresh timestamp and signature for every attempt, including retries.
Secret rotation
To rotate your signing secret, contact Yelp. During the changeover Yelp accepts signatures made with either the old or the new secret, so you can switch over without failed requests. Tell Yelp when you have finished, and the old secret will be revoked. If you think your secret has been exposed, contact Yelp right away.
Example (Python)
import hashlib
import hmac
import json
import time
import requests
SECRET = b"<your signing secret>"
URL = "https://api.yelp.com/v3/integrations/<partner>/lead_updates"
body = json.dumps({
"type": "lead.status_updated",
"timestamp": "2026-10-19T12:00:00Z",
"data": {
"yelp_lead_id": "<yelp lead id>",
"lead_status": "done",
"external_id": "job-42",
"event_timestamp": "2026-10-19T12:00:00Z",
},
})
ts = str(int(time.time()))
signature = hmac.new(SECRET, f"{ts}.{body}".encode("utf-8"), hashlib.sha256).hexdigest()
resp = requests.post(
URL,
data=body.encode("utf-8"), # send exactly the bytes you signed
headers={
"Content-Type": "application/json",
"X-Timestamp": ts,
"X-Signature": signature,
},
timeout=10,
)
resp.raise_for_status() # expect 204
Example (shell)
BODY='{"data":{"yelp_lead_id":"<yelp lead id>","lead_status":"scheduled"}}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -X POST "https://api.yelp.com/v3/integrations/<partner>/lead_updates" \
-H "Content-Type: application/json" \
-H "X-Timestamp: $TS" \
-H "X-Signature: $SIG" \
--data "$BODY"
If your platform signs with a different scheme (for example Ed25519, base64 encoding, or a prefix such as
sha256=), tell Yelp during onboarding. Yelp can usually match it.
4. Payload Format
The request body must be a JSON object. Put the lead update fields inside a data object:
{
"type": "lead.status_updated",
"timestamp": "2026-10-19T12:00:00Z",
"data": {
"yelp_lead_id": "<yelp lead id>",
"lead_status": "done",
"external_id": "job-42",
"total_amount": 450.00,
"paid_amount": 450.00,
"event_timestamp": "2026-10-19T12:00:00Z"
}
}
Fields in data
data| Field | Type | Required | Description |
|---|---|---|---|
yelp_lead_id | string | Yes | The Yelp lead ID you received when Yelp sent you the lead. Send it back exactly as you received it. |
lead_status | string | Yes | The lead's new status. Must be one of the values listed under "Lead statuses" below. |
external_id | string | Recommended | Your own ID for the lead, job or work order. |
event_timestamp | string | Recommended | When the change happened in your system, as ISO-8601 UTC (e.g. 2026-10-19T12:00:00Z). |
total_amount | number | Optional | Total value of the job. |
paid_amount | number | Optional | Amount paid so far. |
type and timestamp at the top level are optional and are used for traceability.
Lead statuses
lead_status | Meaning |
|---|---|
active | The business is engaging with the customer (contacted, quoting, etc.) |
scheduled | An appointment or job has been scheduled |
in_progress | Work has started, or the lead has been converted into a job |
done | The job is completed |
archived | The lead is closed without a job (lost, cancelled, declined, spam) |
Yelp accepts and records events with any other status value, but doesn't use them to update the lead.
5. Responses
| Status | Meaning | What to do |
|---|---|---|
204 No Content | Event accepted. | Nothing; don't resend. |
400 Bad Request | The body isn't a valid JSON object. | Fix the payload; don't retry it unchanged. |
401 Unauthorized | The signature is missing or invalid, or the timestamp is outside the 5-minute window. | Check the secret, the string to sign, and your clock. Retry with a new timestamp and signature. |
404 Not Found | Unknown {partner} identifier. | Check the URL. |
429 Too Many Requests | Rate limit exceeded. | Retry with exponential backoff. |
5xx | Temporary Yelp-side error. | Retry with exponential backoff. |
Error responses include a JSON body like this:
{
"error": {
"code": "INVALID_WEBHOOK_SIGNATURE",
"description": "Webhook signature verification failed."
}
}
Asynchronous processing
A 204 means Yelp authenticated and accepted the event. Yelp applies it to the lead shortly afterwards, so the response doesn't confirm that the lead was updated. You don't need to poll or confirm anything.
6. Delivery Guidelines
- Retries: Retry on
429,5xxand network timeouts, using exponential backoff (for example 1s, 2s, 4s… up to a few minutes). Re-sign every retry with a fresh timestamp. - Duplicates: Sending the same update more than once is safe.
- Ordering: Send events in the order they happened where possible, and always include
event_timestamp. - Rate limits: Keep traffic to a steady flow of real-time events. If you need to backfill historical data, contact Yelp first.
- Timeouts: Use a client timeout of about 10 seconds. The endpoint responds quickly because processing is asynchronous.
- Personal data: Only send the fields described above. Don't include customer names, phone numbers, addresses or other personal data unless Yelp has agreed to it.
Updated 5 days ago
