WarmReply

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.

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

Status codes used across the whole surface:

StatusMeaning
200Success.
201Created — returned only by hook subscribe.
400The request body is malformed or missing something required. The message says which. Retrying the identical request will fail again.
401The account token is missing, malformed or unknown. Reconnect with a fresh token.
403The 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.
404No such path. Check the URL.
410The 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.
500Something 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"
}
FieldTypeDescription
idstringWarmReply's internal account identifier (UUID).
labelstringHuman-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_idstringThe HighLevel location (sub-account) this token belongs to.
planstringThe 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:

FieldTypeRequiredDescription
event_typestringYes 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 stringYes 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 valueRead from the first of these keys that has a value
Lead id requiredyelp_lead_id, lead_id
Customer namelead_user_name, user_display_name, consumer_name, display_name
Yelp business idyelp_business_id, business_id
Event timelead_time_created, time_created, event_time, created_at
Event idevent_id, message_id
Phone numberphone_number, temporary_phone_number, consumer_phone
Email addresstemporary_email, consumer_email
Message textmessage_text, text, message
Job detailsevery key beginning project_, rendered as a list

The same values may instead be sent as top-level fields alongside event_type and raw_eventlead_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:

StatusBodyWhat 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:

FieldTypeRequiredDescription
hookUrlstringYes 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_idstringNo 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:

StatusBodyWhat 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:

FieldTypeRequiredDescription
hookUrlstringYes 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?"
}
FieldTypeDescription
reply_idstringIdentifier for this delivery attempt's reply record.
yelp_lead_idstringThe 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 nullThe 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.)
messagestringThe 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" }
]
FieldTypeDescription
idstringThe 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.
namestringDisplay 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:

StatuserrorEndpointHow 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 keyAllThe 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.AllThe 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 pathsCheck 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.AllReturned 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 errorAllOur 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.