Skip to main content
When an agent closes a ticket in Zoho Desk, send the customer a WhatsApp confirmation. Zoho pushes to us — you don’t poll. This works without writing any code, provided the contact’s number is stored in a form we can use. Read the phone number section before you start; it’s where these integrations actually fail.

What you need first

1

An API key scoped to sending

Settings → API Keys, scoped to conversations:send. Nothing more — this key will live in your Zoho configuration, where any Zoho admin can read it. See Authentication.
2

Your channel id

From GET /v1/connect/channels. You’ll paste this into the Zoho webhook as a literal value.
3

An approved template

From GET /v1/templates. Note its exact name, its language, and how many {{n}} placeholders its body has.

Configure the Zoho webhook

In Zoho Desk: Setup → Automation → Webhooks → Create Webhook. Then add the body parameters:
These are flat fields, not the nested template object shown elsewhere in these docs. Zoho’s webhook builder can only emit flat key/value pairs, so /v1/conversations/send accepts templateName, templateLanguage and templateParams at the top level as an equivalent form. See the send endpoint for both.templateParams also accepts a JSON array string — ["Ahmed","4821"]. Use that form when a value can contain a comma, such as a ticket subject or an address.

Trigger it on close

Setup → Automation → Workflows → Create Rule
  • ModuleTickets
  • Execute onEdit, restricted to the Status field
  • CriteriaStatus is Closed (add Resolved too if your team uses both)
  • Action — the webhook you just created
Workflow rules are per-department. If the team runs several departments, the rule has to exist in each one, or tickets closed elsewhere silently send nothing.

The phone number is the hard part

to must be E.164 without a +201001234567. Zoho stores whatever the agent typed, which in practice means all of these for the same person:
Only the third is close to usable. A local-format number is not rejected — the send is accepted and simply never arrives, which is the worst possible failure mode. Three ways to handle it, best first:
Replace the plain webhook with a custom function on the same workflow rule, and clean the number before sending:
Hard-coding 20 assumes every customer is in one country. If that isn’t true, this approach will mangle the exceptions.
Send ${Contact.Mobile} and fall back to ${Contact.Phone} or ${Ticket.Phone} when it’s empty. This only helps with missing numbers, not badly formatted ones — the formatting problem remains.

Two things to watch in production

Duplicate sends. A ticket can be closed, reopened, and closed again — each close fires the rule. Zoho also retries on a non-2xx response. If double-messaging matters, add a custom checkbox field like Confirmation Sent, include Confirmation Sent is false in the rule criteria, and set it as a second workflow action. Silent failures. Zoho records webhook failures in its own log, which nobody reads. Our API returns 202 as soon as the message is queued, so a 202 does not mean delivered — check status on the message if delivery confirmation matters. See step 4 of the quickstart.

Testing it

Close a real ticket on a contact whose number is your own. If nothing arrives, check in this order:
  1. Zoho’s webhook log — did the rule fire at all? If not, the criteria or the department is wrong.
  2. The response code401 means the key or headers are wrong, 403 means the key lacks conversations:send, 400 means the payload is malformed and the message field says how.
  3. The message’s status — a 202 followed by failed means we accepted it and the platform rejected it. error_reason will say why; the usual causes are a badly-formatted number or a template whose parameter count doesn’t match.