Send answers to your own endpoint with webhooks
Post every answer a campaign receives to an HTTPS address of yours, signed, or send any payload from a workflow step. How the Webhooks connector differs from Settings → API → Webhooks.
On this page
The Webhooks connector sends every answer a campaign receives to an address on your own server, as a signed POST, a few moments after it is given. A workflow can use it too, to send whatever the run knows. At the end of this guide you will have an endpoint receiving answers, and the means to check that each request really comes from Polzy.
The Webhooks connector is on Business. Only Admins can connect it and choose where a campaign's answers go.
Which webhook do you need?
Polzy can call your server in three ways:
| Webhooks connector (this article) | Settings → API → Webhooks | Outbound webhook step | |
|---|---|---|---|
| What is sent | Each answer to one campaign, or a workflow step's payload | Workspace events: a response created, a campaign finished, an action created or completed | Whatever the workflow step says |
| Where it is set | Integrations, then a campaign's Integrations tab or a workflow step | Settings → API → Webhooks | A workflow |
| Plan | Business | Team | Team |
| Signed | X-Polzy-Signature, over the body | X-Polzy-Signature, over the timestamp and the body | No |
| When delivery fails | Tried three times in all | Tried six times over about half an hour, with a log of every attempt | The step's error rule decides |
For events across the whole workspace, see Send and receive webhooks.
Before you start
You need:
- A workspace on Business, and the Admin role in it.
- Integrations switched on for the workspace, under Settings → General → What this workspace does.
- An endpoint on the internet, over HTTPS, that accepts a
POSTwith a JSON body and answers with a 2xx status within 10 seconds. Polzy refuseshttp://addresses and addresses inside a private network.
Connect the Webhooks connector
Connecting holds what every request shares: the signing secret, and any headers.
- In the sidebar, under Automate, open Integrations and choose the Available tab.
- Find Webhooks and choose Connect.
- In Signing Secret, enter a long random value and keep a copy for your server.
- Under HTTP Headers, add any headers you want sent, such as
Authorization. Their values are stored encrypted. See Good to know for where they go. - Choose Connect. The test answers Connected. Each survey and step names the endpoint it posts to.
Left empty, the signing secret is made for you — but it is never shown, so you could not check signatures with it. Enter your own. To change it later, choose Configure and type the new one over the stored value.
Give a campaign its endpoint
- Open Campaigns, then the campaign, and its Integrations tab.
- Under Add a connection, find Webhooks and choose Connect. If the button reads Set up, the connector is not connected to the workspace yet.
- In Endpoint, enter the address this campaign's answers are posted to.
- Choose Save changes.
Webhooks then appears under Connected to this campaign, with Sends to and the address, its switch, Edit configuration, Disconnect, and how many answers were sent and failed. Each campaign can post to its own endpoint.
What your endpoint receives
Each answer is one POST with these headers:
| Header | What it holds |
|---|---|
Content-Type | application/json |
X-Polzy-Event | response.submitted for a campaign's answer; the step's Event name from a workflow. |
X-Polzy-Signature | The HMAC-SHA256 of the raw request body, made with the signing secret, in hexadecimal. |
X-Polzy-Timestamp | The timestamp from the body. |
Idempotency-Key | From a workflow step only: the same on a repeat of the same step, so you can ignore it. |
And this JSON body:
| Field | What it holds |
|---|---|
event | response.submitted. |
timestamp | When the request was sent. For an anonymous answer, the day it was given. |
survey.id, survey.title | The survey's ID and title. |
response.id | The answer's ID in Polzy. Empty for an anonymous answer. |
response.submittedAt | When the answer was finished. For an anonymous answer, the day alone. |
response.answers | One item per question answered, each with questionId, questionText and value. |
response.score | The overall score, if it was worked out by the time the request was sent. |
An anonymous answer carries what it said, its score and its day, and nothing that could name or time its author. Polzy waits for each answer's safety reading, up to 10 minutes, before it sends it; an answer withheld for safety review arrives as its placeholder. The privacy floor does not hold single answers back.
Check the signature
- Read the request body exactly as it arrived, before parsing it.
- Compute its HMAC-SHA256 with your signing secret, written as hexadecimal.
- Compare it with
X-Polzy-Signature, with a constant-time comparison. If they differ, refuse the request.
The timestamp is part of the body, so a changed timestamp breaks the signature too.
Send a webhook from a workflow
- Open Workflows and the workflow, or start a new one.
- Under Connectors in Installed steps, drag Send a webhook · Webhooks onto the canvas.
- In Endpoint, enter where this step posts.
- Under Event name, keep
workflow.stepor name the event your server expects. It is sent asX-Polzy-Event. - Leave Payload empty to send everything the run knows, or write the JSON you want, with
{{to insert values from the run. - Choose Send a test to post one request filled from a sample response.
The step is signed with the connection's secret, and leaves your server's answer as {{connector.statusCode}} and {{connector.response}}.
Check that it works
On the campaign's Integrations tab, the activity line counts the answers sent and those that failed. The connector's own page lists each delivery under Activity, with Worked or Failed and the reason.
Troubleshooting
Under Activity, a failed delivery reads Webhook failed: and the reason; a workflow's run shows the same reason without it.
- Webhook failed: only https endpoints are allowed. The endpoint must start with
https://. - Webhook failed: private addresses are not allowed. The address points inside a private network, such as
localhostor10.0.0.5. Use an address reachable from the internet. - Webhook failed: that host could not be found. The name in the address does not exist. Check its spelling.
- Webhook failed: and a status, such as
500, or timeout of 10000ms exceeded. Your endpoint answered with an error, or took longer than 10 seconds. Anything but a 2xx answer counts as failed, and the delivery is tried three times in all. - No endpoint for this survey. Set one on its Integrations tab. The campaign has no endpoint. Add one with Edit configuration on the campaign's Integrations tab.
- The signature does not match. Compute it over the raw body, before any parsing, with the secret you entered. If the secret was made for you, enter one of your own.
- Your header does not arrive. See below: headers go only to a host the connection itself posts to.
Answers given before the endpoint was saved, or while the switch was off, are not sent later. Export them from the survey's Responses instead.
Good to know
- Where do the HTTP headers go? Only to a host the connection itself posts to. An endpoint a campaign or a step chooses on another host is called without them, so a stored
Authorizationvalue never reaches an address it was not meant for. Rely on the signature to tell Polzy's requests from anyone else's. - Are unfinished answers sent? No. Only finished answers are, once each.
- Can my endpoint answer with data? For a workflow step, yes: the answer is kept as
{{connector.response}}for later steps. For a campaign's answers, the answer is ignored beyond its status.
Related articles
Signed-in members can ask Polzy or message the team from Support. How fast we reply depends on your plan.
