Webhook
Pro · Destination step
Sends each record to a URL of your choice, such as your own API, Make, n8n, Slack or a CRM other than Zoho. Webhook feeds use the same queue, retries and log as Zoho feeds.
Set it up
- Switch to WebhookOn the Destination step, switch from Zoho app to Webhook. A webhook feed needs no Mapping step, so it disappears from the step list.
- Choose a method and URLPick
POST,PUT,PATCH,GETorDELETE, then paste the URL. - Build the requestWrite the body and add any headers, query parameters and auth. Use
{{field_id}}wherever you want a form answer. - Send a testPress Send test or Ctrl + Enter. Check what came back under Response.
- Save the feedThe button at the bottom of this step saves the feed (Create feed or Save feed).
Body & placeholders
Choose a body format at the top of the Body tab:
| Format | Use it for | Content-Type |
|---|---|---|
| JSON | Most APIs and automation tools. You write the JSON yourself. | application/json |
| Form data | Forms with file uploads. In each row, choose Text or File; for File, pick the upload field. | multipart/form-data |
| URL encoded | Endpoints that expect a classic form post: key=value&key2=value2. | application/x-www-form-urlencoded |
The Content-Type header is added automatically from the format you choose.
Placeholders
Write {{field_id}} anywhere in the body, a header or a query value. When the form is submitted, each placeholder is replaced with the visitor’s answer. A few more are filled in automatically:
| Placeholder | Becomes |
|---|---|
{{field_id}} | The answer to that form field |
{{all_fields}} | The whole submission, as one object |
{{date}} | The time it was sent, in ISO 8601 (UTC), e.g. 2026-10-03T09:15:00+00:00 |
{{timestamp}} | The same time as a Unix timestamp, e.g. 1790932500 |
{{uuid}} | A new random ID for every request |
{
"name": "{{your-name}}",
"email": "{{your-email}}",
"interests": "{{interests}}",
"note": "From {{your-name}}",
"submitted_at": "{{date}}"
}{
"name": "Rahim Uddin",
"email": "rahim@example.com",
"interests": ["CRM", "Books"],
"note": "From Rahim Uddin",
"submitted_at": "2026-10-03T09:15:00+00:00"
}- Keep placeholders inside quotes, like
"age": "{{age}}". That keeps the JSON valid while you edit it, and the editor tells you straight away if it isn’t. - A value that is only a placeholder keeps its type. A checkbox arrives as a list, an upload as its files, and
"{{all_fields}}"as the whole submission. A placeholder mixed with other text, like"From {{your-name}}", always becomes text. - Leave the body empty to send every field as it is, under the form’s own field names.
- Add to body inserts a form field, or All fields, for you. Format tidies the JSON. Sample fills in a starter body with every form field plus
submitted_at. GETandDELETEsend no body. Whatever you set up in the Body tab is sent in the query string instead.
File uploads
An upload field arrives as a list of files. Each file has name, type, size and url. When the file has no public URL (Contact Form 7 keeps uploads in a private folder), the file itself is included as base64 content, up to 5 MB per file.
"resume": [
{ "name": "cv.pdf", "type": "application/pdf", "size": 182044,
"url": "https://example.com/wp-content/uploads/2026/10/cv.pdf" }
]Headers, query & auth
| Tab | What it does |
|---|---|
| Form fields | Lists every placeholder used in the request. Type test values here to try a request now. They are only for testing and are not saved. Use Fill sample values to fill them all at once. |
| Headers | One row per header. Quick-add buttons: Webhook secret, Accept JSON, Source and Request ID ({{uuid}}). Use Bulk edit to paste many at once. Headers added automatically from the Auth tab and the body format are listed below the rows. |
| Query | Parameters added to the end of the URL. Names and values are encoded for you. |
| Auth | No auth, Bearer token, Basic (username + password) or API key. An API key can go in a header or in the URL. |
Testing
Send test sends a real request with your test values. The output panel has four tabs:
| Tab | Shows |
|---|---|
| Response | The status code, response time, and the body and headers that came back |
| Payload | Exactly what was sent, with the test values filled in. Has a Copy button. |
| Code | The same request as cURL, fetch, PHP (WordPress) or Python code, ready to copy |
| History | Your recent tests. Stored only in this browser. |
To capture a request without building an endpoint, paste a URL from a request inspector such as webhook.site, then press Send test.
Delivery & retries
- Any 2xx status is a success. The log says, for example, Sent to My CRM, which answered 201.
- 4xx errors are not retried. A 400 or 422 will fail the same way every time, so it is logged straight away with the status and the start of the endpoint’s reply.
- 5xx, 429 and network errors are retried in the background, up to 4 attempts in total. Uploaded files are kept so a retry sends them too.
- Each request waits up to 15 seconds for an answer.
Retries are not logged one by one. The log shows the final result once, with the number of attempts it took. From the Logs screen you can resend a failed record.
Troubleshooting
| What you see | What to do |
|---|---|
| The JSON body is not valid when saving | Open the Body tab, press Format, and put quotes around every placeholder. |
| The destination answered 401 / 403 | Check the Auth tab and your headers. Look under Headers → Added automatically to see what is actually sent. |
| This feed points at a destination that no longer exists | Open the feed, go to the Destination step and set up the webhook again. |