API reference
The endpoints behind the WarmReply integration for Zapier. Everything the integration does — testing a connection, delivering a Yelp event, subscribing and unsubscribing a reply hook, and listing a customer's Yelp pages — is one of the six calls documented here. If something is wrong or missing, tell us at [email protected].
What this API is
WarmReply puts Yelp Request-a-Quote conversations inside HighLevel Conversations, in both directions. Yelp's own Zapier integration is the transport: a Zap carries each Yelp event into this API, and a second Zap carries each agent reply back out to Yelp. This API is the middle of that road.
- Base URL —
https://api.warmreply.io. HTTPS only; plain HTTP is not served. - Surface — every endpoint below is under
/zapier. Nothing else on this host is part of the public integration. - Audience — the WarmReply Zapier app. The same calls work from any HTTP client with a valid account token, which is how you would test them.
A customer's traffic flows through it like this. Inbound: a Yelp trigger fires in
Zapier → the Send Yelp Event action calls
POST /zapier/inbound → the lead appears in
HighLevel. Outbound: an agent replies in HighLevel → WarmReply POSTs the reply to
the hook URL registered by POST /zapier/hooks
→ the Reply Ready trigger fires → Yelp's Create Message action
delivers it into the same Yelp thread.
Authentication
Every endpoint requires an account token — one opaque secret per HighLevel sub-account, issued when WarmReply is installed and shown on the WarmReply setup screen inside the CRM. It is a 48-character hexadecimal string.
Send it as the X-Api-Key request header:
X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123 Authorization: Bearer <token> is accepted as an equivalent
alternative, for clients that cannot set a custom header. If both are present,
X-Api-Key wins.
The token identifies the account; there is no separate account id to send. A token that is missing, malformed or unknown is rejected with 401 and this body:
{
"error": "invalid API key"
} A valid token whose account WarmReply has been uninstalled from is a different failure, and gets its own status: 410. Copying the token again cannot fix it — the install that issued it is gone, and so is the setup screen it came from. The install has to be restored first:
{
"error": "WarmReply is no longer installed on this CRM sub-account. Reinstall WarmReply from the marketplace, then reconnect this Zap."
} A valid token whose account is in any other non-active state — suspended after a billing or authorisation failure, for instance — is rejected with 403. The token is fine and does not need re-copying; the account needs attention inside the CRM, and the call will start working again once it is active:
{
"error": "This WarmReply account is not active right now. Open WarmReply in your CRM to see what needs attention, then run this Zap again."
} Rotation. An account owner can rotate the token from the WarmReply settings screen; the previous token stops working immediately and connections using it must be re-authenticated with the new one. Treat the token as a password: it grants write access to that sub-account's conversations. Never put it in a URL, a log or a support ticket.
Requests and responses
- Bodies are JSON. Send
Content-Type: application/jsononPOSTandDELETE. - Responses are JSON — an object, except the two list endpoints, which return a JSON array.
- Errors are always an object with a single
errorkey holding a human-readable sentence. There are no error codes to parse; the HTTP status carries the machine meaning. - Character encoding is UTF-8 throughout.
- Idempotency is handled for you on
/zapier/inbound: re-sending the same event is safe and never produces a duplicate message. See that section. - Rate limits. There is no published request limit. Traffic is shaped by real lead volume; if an account's usage looks like a runaway loop we contact the owner rather than throttling silently.
Status codes used across the whole surface:
| Status | Meaning |
|---|---|
200 | Success. |
201 | Created — returned only by hook subscribe. |
400 | The request body is malformed or missing something required. The message says which. Retrying the identical request will fail again. |
401 | The account token is missing, malformed or unknown. Reconnect with a fresh token. |
403 | The token is valid but the account is not active right now — suspended after a billing or authorisation failure, for example. The token is not the problem; the account needs attention in the CRM. |
404 | No such path. Check the URL. |
410 | The token is valid but WarmReply has been uninstalled from that sub-account. Reinstall it from the marketplace, then reconnect. A fresh token cannot be obtained until then. |
500 | Something failed on our side. The body is { "error": "internal error" }. Safe to retry — see the idempotency note. |
GET /zapier/me
GET https://api.warmreply.io/zapier/me
Connection test. Confirms the account token is valid and returns a label used to
name the connection, so an operator with several sub-accounts can tell them apart.
It has no side effects and can be called as often as you like.
Request: no parameters and no body — only the X-Api-Key header.
curl https://api.warmreply.io/zapier/me \
-H "X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123"
Response 200:
{
"id": "11111111-2222-3333-4444-555555555555",
"label": "WarmReply — Example Home Services",
"location_id": "ExampleLocationId00001",
"plan": "trial"
}
Field Type Description idstring WarmReply's internal account identifier (UUID). labelstring Human-readable connection name: WarmReply — followed by the CRM location's name. If the location name cannot be read at that moment, the location id is used instead. location_idstring The HighLevel location (sub-account) this token belongs to. planstring The account's current plan, e.g. trial, pro, growth, scale.
Errors: 401 if the token is missing or unknown;
410 if WarmReply has been uninstalled from the sub-account;
403 if the account is otherwise not active.
POST /zapier/inbound
POST https://api.warmreply.io/zapier/inbound
Delivers one Yelp event — a new lead, a customer's follow-up message, or a shared
phone number — into the account's CRM. This is what the Send Yelp Event
action calls. The event becomes a contact and a conversation in HighLevel, or is
appended to the existing conversation for that lead.
Request fields:
Field Type Required Description event_typestring Yes Which kind of Yelp event this is. One of new_lead, consumer_message, phone_available. It must match the Yelp trigger the Zap uses: new_lead renders the full job details into the first message, phone_available attaches the number to the contact and notes it in the thread. raw_eventobject, or a JSON string Yes The Yelp trigger's entire output, passed through unchanged. WarmReply reads everything it needs from it — see below. A JSON-encoded string is accepted because Zapier renders mapped step output as text.
Why only two fields. Every other value is derived server-side from
raw_event, so a customer maps one thing in the Zap editor instead of a
dozen, and so Yelp changing a field name is a fix we ship rather than a Zap every
customer has to repair. Keys are matched case-insensitively and one level of
nesting is flattened (project.job_type is read as
project_job_type):
Derived value Read from the first of these keys that has a value Lead id required yelp_lead_id, lead_id Customer name lead_user_name, user_display_name, consumer_name, display_name Yelp business id yelp_business_id, business_id Event time lead_time_created, time_created, event_time, created_at Event id event_id, message_id Phone number phone_number, temporary_phone_number, consumer_phone Email address temporary_email, consumer_email Message text message_text, text, message Job details every key beginning project_, rendered as a list
The same values may instead be sent as top-level fields alongside
event_type and raw_event — lead_id,
event_id, event_time, consumer_name,
business_id, message, phone,
email and project are all accepted and take precedence
over the derived value. The Zapier integration does not use them; they exist for
direct API callers who already have the fields separated.
Example request:
curl -X POST https://api.warmreply.io/zapier/inbound \
-H "X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123" \
-H "Content-Type: application/json" \
-d '{
"event_type": "new_lead",
"raw_event": {
"yelp_lead_id": "EXAMPLE-LEAD-0001",
"yelp_business_id": "EXAMPLE-BUSINESS-0001",
"lead_user_name": "Jamie Example",
"lead_time_created": "2026-01-15T18:04:11Z",
"temporary_email": "[email protected]",
"message_text": "Do you have availability on the 21st?",
"project": {
"job_type": "Local moving",
"zip_code": "00000",
"requested_dates": "January 21"
}
}
}'
Response 200:
{
"ok": true
}
Duplicates. Every event is de-duplicated per account, keyed on the event id
when Yelp supplies one and otherwise on a fingerprint of the lead id, event type,
timestamp, phone and message text. A repeat of an event we have already delivered
returns 200 with:
{
"ok": true,
"duplicate": true
}
This is a success, not a warning: it means the customer's conversation already has
the message. It is why retrying a timed-out or 500ed request is always
safe.
Errors:
Status Body What it means 400invalid JSON bodyThe request body was not parseable JSON. 400lead_id required (missing from raw_event too)No lead id could be found. Almost always a Zap mapping problem: the trigger's step output was not mapped into raw_event, or only a single field was mapped instead of the whole output. 401invalid API keyThe token is missing, malformed or unknown. 403This WarmReply account is not active right now. Open WarmReply in your CRM to see what needs attention, then run this Zap again.The token is valid; the account is not active — suspended after a billing or authorisation failure, for example. Nothing to change in the Zap. 410WarmReply is no longer installed on this CRM sub-account. Reinstall WarmReply from the marketplace, then reconnect this Zap.The token is valid, but WarmReply has been uninstalled from that sub-account. Reinstall from the marketplace, then reconnect the connection. 500internal errorDelivery to the CRM failed unexpectedly. Retry; de-duplication prevents a double message.
{
"error": "lead_id required (missing from raw_event too)"
}
Size limits. Message text is truncated at 8,000 characters and job details at
4,000, rather than being rejected — a long customer message still arrives.
POST /zapier/hooks
POST https://api.warmreply.io/zapier/hooks
Subscribes a REST hook for the Reply Ready trigger. Called when a customer
turns that Zap on. From then until it is unsubscribed, every reply an agent writes
in the CRM is POSTed to the registered URL.
Request fields:
Field Type Required Description hookUrlstring Yes The target URL to deliver replies to. Must be an https URL on hooks.zapier.com or another zapier.com host. Anything else is refused — we POST customer conversation content to this URL, so an arbitrary host would be a data-exfiltration route. yelp_business_idstring No Restricts this subscription to one Yelp business page — use the id of an entry from GET /zapier/businesses. Omit it (or send an empty string, which is treated as omitted) to make this the account's default hook, receiving replies for every page that has no hook of its own. Accounts with a single Yelp page should leave it empty.
Example request:
curl -X POST https://api.warmreply.io/zapier/hooks \
-H "X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123" \
-H "Content-Type: application/json" \
-d '{
"hookUrl": "https://hooks.zapier.com/hooks/standard/0000000/0000000000000000/",
"yelp_business_id": "EXAMPLE-BUSINESS-0001"
}'
Response 201:
{
"id": "99999999-8888-7777-6666-555555555555"
}
id is the subscription's identifier. Subscribing the same
hookUrl twice updates the existing subscription rather than creating a
second one, so a retry after a network failure is harmless.
Errors:
Status Body What it means 400hookUrl requiredThe field was absent or empty. 400hookUrl must be an https Zapier hook URLThe URL was not parseable, was not https, or was not a Zapier host. 401invalid API keyThe token is missing, malformed or unknown. 403This WarmReply account is not active right now. Open WarmReply in your CRM to see what needs attention, then run this Zap again.The token is valid; the account is not active — suspended after a billing or authorisation failure, for example. Nothing to change in the Zap. 410WarmReply is no longer installed on this CRM sub-account. Reinstall WarmReply from the marketplace, then reconnect this Zap.The token is valid, but WarmReply has been uninstalled from that sub-account. Reinstall from the marketplace, then reconnect the connection.
{
"error": "hookUrl must be an https Zapier hook URL"
}
DELETE /zapier/hooks
DELETE https://api.warmreply.io/zapier/hooks
Removes a subscription. Called when the customer turns the Reply Ready Zap
off. Replies stop being delivered to that URL immediately; if the account has no
other matching hook, agent replies are marked failed in the CRM rather than
vanishing.
Request fields:
Field Type Required Description hookUrlstring Yes The exact URL that was subscribed. Only subscriptions belonging to the authenticated account can be removed.
Example request:
curl -X DELETE https://api.warmreply.io/zapier/hooks \
-H "X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123" \
-H "Content-Type: application/json" \
-d '{
"hookUrl": "https://hooks.zapier.com/hooks/standard/0000000/0000000000000000/"
}'
Response 200:
{
"ok": true
}
Unsubscribing a URL that is not subscribed also returns 200 — the
operation is idempotent, so an unsubscribe retry cannot get stuck.
Errors: 400 hookUrl required if the field is
absent or the body is not JSON; 401 for a missing or unknown token;
410 if WarmReply has been uninstalled from the sub-account;
403 if the account is otherwise not active.
What we POST to your hook
This is not an endpoint you call — it is the request WarmReply makes to a
subscribed hookUrl, and the payload the Reply Ready trigger
hands to the next Zap step. It is documented here so the contract is complete.
POST https://hooks.zapier.com/hooks/standard/0000000/0000000000000000/
Content-Type: application/json
{
"reply_id": "99999999-8888-7777-6666-555555555555",
"yelp_lead_id": "EXAMPLE-LEAD-0001",
"business_id": "EXAMPLE-BUSINESS-0001",
"message": "Yes — the 21st is open. Shall we hold it for you?"
}
Field Type Description reply_idstring Identifier for this delivery attempt's reply record. yelp_lead_idstring The Yelp lead this reply belongs to — pass it to Yelp's Create Message action so the reply lands in the right thread. business_idstring or null The Yelp business page the conversation came from. In a live delivery it is null when the lead carried no business id. (The sample endpoint below is the exception: it always carries a placeholder id, never null, so the field can be mapped in the Zap editor.) messagestring The agent's reply text, as written. Replies over 3,000 characters are refused earlier and never dispatched.
Delivery behaviour. Any 2xx counts as accepted. A failure is
retried three times with increasing backoff, and unresolved replies are retried
again later by a scheduled sweep; a reply that still cannot be delivered is marked
failed in the CRM with its reason, visible to the agent who wrote it. A
410 Gone is honoured as the REST hook contract requires: the
subscription is deleted and no further replies are sent to it.
GET /zapier/hooks/sample
GET https://api.warmreply.io/zapier/hooks/sample
Returns one fabricated example of the payload described above, so the Reply
Ready trigger can be tested and its fields mapped in the Zap editor before any
real reply exists.
Request: no parameters and no body.
curl https://api.warmreply.io/zapier/hooks/sample \
-H "X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123"
Response 200: a JSON array containing exactly one item.
[
{
"reply_id": "sample-not-real",
"yelp_lead_id": "SAMPLE-DO-NOT-SEND",
"business_id": "sample-business-not-real",
"message": "Sample reply from WarmReply (editor test — not a real conversation)."
}
]
The sample is always fake, by design. This endpoint never returns a real
lead. Testing a Zap runs the next action for real, so returning a genuine lead id
here would send a test message to an actual customer on Yelp. The placeholder ids
are deliberately unusable.
Errors: 401 for a missing or unknown token, 410
if WarmReply has been uninstalled from the sub-account, 403 if the
account is otherwise not active. Nothing else can fail.
GET /zapier/businesses
GET https://api.warmreply.io/zapier/businesses
Lists the Yelp business pages connected to the account. It exists to populate the
Yelp Business dropdown on the Reply Ready trigger, so a customer with
several locations picks a page from a list instead of pasting an id.
Request: no parameters and no body.
curl https://api.warmreply.io/zapier/businesses \
-H "X-Api-Key: abc123abc123abc123abc123abc123abc123abc123abc123"
Response 200: a JSON array, which may be empty.
[
{ "id": "EXAMPLE-BUSINESS-0001", "name": "Example Home Services" },
{ "id": "example-home-services-northside", "name": "Example Home Services Northside" },
{ "id": "EXAMPLE-BUSINESS-0002", "name": "Yelp business EXAMPLE-BUSINESS-0002" }
]
Field Type Description idstring The value to send as yelp_business_id when subscribing a hook. Normally the Yelp business id; for a page registered during setup that has not yet received its first lead, it is the page's Yelp URL slug instead. Both forms route replies correctly. namestring Display label. The page's name where we know it, otherwise Yelp business followed by the id.
The list combines pages the owner registered during setup with any page WarmReply
has seen leads from, so a page appears whether it was configured first or simply
started sending leads.
Errors: 401 for a missing or unknown token, 410
if WarmReply has been uninstalled from the sub-account, 403 if the
account is otherwise not active.
Error reference
Every error response is a JSON object with one error key. The complete
set across this surface:
Status errorEndpoint How to resolve it 400invalid JSON body/zapier/inboundSend a well-formed JSON body. 400lead_id required (missing from raw_event too)/zapier/inboundMap the Yelp trigger's whole step output into raw_event. 400hookUrl required/zapier/hooks (both methods)Include hookUrl in the body. 400hookUrl must be an https Zapier hook URLPOST /zapier/hooksSubscribe an https Zapier hook URL. Other hosts are refused on purpose. 401invalid API keyAll The token is missing, malformed or unknown. Copy the account token again from the WarmReply setup screen in the CRM and reconnect. 403This WarmReply account is not active right now. Open WarmReply in your CRM to see what needs attention, then run this Zap again.All The token is valid, so re-copying it changes nothing. Open WarmReply in the CRM, clear whatever is flagged — a billing or authorisation problem, usually — and run the call again. 404not foundUnknown paths Check the method and path against this page. 410WarmReply is no longer installed on this CRM sub-account. Reinstall WarmReply from the marketplace, then reconnect this Zap.All Returned when WarmReply has been uninstalled from that sub-account — reinstall, then reconnect. Re-copying the token cannot help: the setup screen it came from went away with the install. 500internal errorAll Our fault. Retry; the write endpoints are safe to repeat. If it persists, email us with the approximate time.
Support and changes
There is no version number in the URL. The endpoints above are the stable contract
for the Zapier integration: fields may be added to responses, but existing fields
will not be removed or repurposed without notice to connected accounts, because
doing so would break live customer Zaps.
Questions, bug reports and access issues:
[email protected]. Include the CRM location
name and roughly when the failing call happened and we can trace the exact request.
Never include an account token in an email — tell us the location and we will look
it up.
Product documentation for customers lives on the
documentation page, and the step-by-step Zap build is on the
setup walkthrough.