Integrations: webhooks and Google Sheets
On this page
Integrations in your site's menu sends what happens on your site to other tools the moment it happens:
- Webhooks send new inquiries, new subscribers and paid orders to any app that accepts them: Zapier, Make, n8n, Pabbly, your CRM or your own server.
- Google Sheets adds every new inquiry as a row in a spreadsheet, without connecting a Google account to Closer.
Each site has its own integrations. You can add up to 5 addresses per site (webhooks and spreadsheets together).
Webhooks
A webhook is an address (URL) that Closer calls with the details of an event. Most automation tools give you one: in Zapier it is the trigger Webhooks by Zapier → Catch Hook, in Make the module Webhooks → Custom webhook, in n8n the Webhook node.
Add a webhook
- Open Integrations.
- Paste the address under Webhook address. It must start with
https://. - Check the events you want:
- New inquiry: a visitor sent a contact form (or a quiz with a form).
- New subscriber: someone signed up through a newsletter form.
- New order: an order in your store was paid.
- Click Add webhook.
- Closer shows the signing secret (
whsec_…) once. Copy it and keep it with the receiving app if you want to check that requests really come from Closer. If you lose it, click New secret; the old one stops working right away. - Click Send test. The receiving app gets a sample event (with
"test": true), and the result appears in the Delivery log.
You can turn events on and off later by clicking them, pause a webhook with its switch, or Remove it.
For security, Closer only calls public internet addresses. Addresses on private or internal networks (like
localhost,10.x.x.x,192.168.x.x) are refused.
What Closer sends
Every request is a POST with a JSON body:
{
"id": "1f6c2a9e-…",
"event": "inquiry.created",
"created_at": "2026-10-22T09:30:00+00:00",
"test": false,
"site": { "id": 12, "name": "My site", "url": "https://mysite.closer.rs" },
"data": { … }
}
The event is inquiry.created, subscriber.created or order.paid. What is in data:
| Event | Fields in data |
|---|---|
inquiry.created |
id, created_at, language, page (where the form was sent, e.g. /contact), name, email, fields (a list of key, label, value for every form field), quiz (answers and result, or null) |
subscriber.created |
id, email, name, language, source (the form), created_at |
order.paid |
id, code, email, name, language, items (id, name, price), subtotal, discount, coupon_code, total, currency, mode (test, live or free), paid_at |
Amounts are in cents (the smallest unit of the currency): 4500 with "currency": "EUR" means €45.00.
Headers sent with every request:
X-Closer-Event: the event name.X-Closer-Delivery: a unique id of the event. Retries of the same event keep the same id, so you can ignore duplicates.X-Closer-Signature:t=1761125400,v1=5d41…, wheretis the time of sending (Unix seconds) andv1is the HMAC-SHA256 oft + "." + raw body, made with your signing secret.
To check a request, compute the same HMAC with your secret and compare it with v1, and reject requests whose t is older than a few minutes. In PHP:
[$t, $v1] = sscanf($_SERVER['HTTP_X_CLOSER_SIGNATURE'], 't=%d,v1=%s');
$body = file_get_contents('php://input');
$valid = hash_equals(hash_hmac('sha256', $t.'.'.$body, $secret), $v1) && abs(time() - $t) < 300;
Retries and the delivery log
The receiving address must answer with a 2xx status within 15 seconds. If it does not (an error, a timeout, a 500), Closer tries again twice more: after about 30 seconds and after about 5 minutes. Redirects are not followed, so use the final address.
The Delivery log shows the last 50 requests of the site: time, event, address, which try it was (1/3, 2/3, 3/3), the status code the address answered and the error, if any.
Google Sheets
Every new inquiry becomes a row in a Google Sheet: Date, Page, Name, Email, then a column for every other form field (by its label). New fields get a new column automatically. It works through a small Google Apps Script that you add to your own spreadsheet, so Closer never needs access to your Google account.
Set it up (about 3 minutes)
- Create a new spreadsheet in Google Sheets (the quickest way is to open
sheets.new). Inquiries go to its first tab. - In the spreadsheet, open Extensions → Apps Script.
- Delete the sample code in the editor. In Closer, under Google Sheets, click Copy script and paste it into the Apps Script editor. Click Save (the disk icon).
- Click Deploy → New deployment. Click the gear next to Select type and choose Web app. Set:
- Execute as: Me
- Who has access: Anyone
- Click Deploy. Google asks you to authorize the script: choose your account. If you see "Google hasn't verified this app", click Advanced → Go to (your project) and Allow. This is your own script, running in your own account.
- Copy the Web app URL. It looks like
https://script.google.com/macros/s/…/exec. - In Closer, paste it under Web app URL and click Connect. Closer sends a test row right away; check that it appears in the sheet (you can delete it).
Use Send test row at any time to check the connection. The switch pauses the spreadsheet, and the trash button disconnects it.
Keep the Web app URL private: anyone who has it can add rows to that sheet. If it leaks, create a new deployment in Apps Script and connect the new URL.
Good to know
- Only inquiries go to the spreadsheet. For subscribers and orders, use a webhook (for example to Zapier or Make, which can also write to Google Sheets).
- If you change the script later, use Deploy → Manage deployments → Edit → New version, so the URL stays the same.
- Text that starts like a formula (
=,+,-,@) is written as plain text, so a form entry cannot run a formula in your sheet. - Each row is added the moment the inquiry arrives. Failed attempts are retried like webhooks and shown in the Delivery log.
- Inquiries over your plan's monthly limit are saved in the Inbox but not sent to integrations.
Can’t find an answer?
Write to us and we reply within one business day.