Webhook integration
The GEOHATS webhook for Zapier, Make, n8n and your own backend
X-Geohats-Signatureheader. Reply with a URL and GEOHATS records it as the page’s live address.When to use the webhook
Use it when your site runs on a platform without a direct GEOHATS integration, or when you want an automation in the middle: post to a CMS that Zapier, Make or n8n supports, send a Slack message for review, save the page to a Google Doc, or call your own API. If you are on WordPress, Webflow, Shopify, Framer, Ghost, HubSpot, Wix or GitHub, a direct integration is simpler.
Setup
- Create a catch hook: Webhooks by Zapier (Catch Hook) in Zapier, a Custom webhook in Make, a Webhook node in n8n, or an endpoint of your own.
- In GEOHATS, open the website, go to Integrations, choose Webhook and paste the URL. It must be https.
- GEOHATS sends a
pingevent. If your endpoint answers with a 2xx status, the connection is saved and your signing secret is shown. Copy it then. - Pick an auto-publish mode for the website: Off, Send as draft or Publish live (see auto-publish modes).
Events
| event | When it is sent | Main fields |
|---|---|---|
ping | When you connect, and when you click Test connection | event, sentAt |
page.draft | A new page passes the quality gate while the website is in Send as draft mode | event, sentAt, page |
page.publish | You click Publish, your agent calls publish_page, or a new page passes the gate in Publish live mode | event, sentAt, page |
seo.fix | You apply or undo a site optimizer fix | event, sentAt, fix |
Payload: pages
{
"event": "page.publish",
"sentAt": "2026-10-23T09:14:02.511Z",
"page": {
"title": "Best invoicing software for freelancers",
"slug": "best-invoicing-software-for-freelancers",
"excerpt": "A short summary taken from the page...",
"bodyHtml": "<article>...</article>",
"fullHtml": "<!doctype html><html>...</html>",
"jsonLd": ["{\"@context\":\"https://schema.org\", ...}"],
"status": "publish"
}
}| Field | What it contains |
|---|---|
| title | The page title, cleaned of HTML entities |
| slug | The URL slug. The same page always has the same slug, so use it as the key when updating. |
| excerpt | A short summary taken from the page |
| bodyHtml | The article body: no document head, executable scripts, style blocks, comments or placeholder markers. JSON-LD blocks inside the body are kept. |
| fullHtml | The complete standalone HTML document, with internal links made absolute on your domain |
| jsonLd | An array of strings, one per JSON-LD block in the page, as raw JSON text |
| status | "publish" or "draft" |
Only pages that passed the GEOHATS quality gate are sent. The values above are illustrative; the structure is exact.
Payload: SEO fixes
{
"event": "seo.fix",
"sentAt": "2026-10-23T09:20:41.007Z",
"fix": {
"type": "meta_description",
"pageUrl": "https://yoursite.com/pricing",
"target": "",
"value": "Plans for teams of every size...",
"anchor": "",
"revert": false
}
}typeis one oftitle,meta_description,canonical,faq_schema,article_schema,org_schema,image_altorinternal_link.targetis the image src forimage_altor the link destination forinternal_link;anchoris the phrase that becomes the link.valueis the text, canonical URL, JSON-LD block or alt text to write.- An undo arrives with
revert: true. GEOHATS cannot read the previous value through a webhook, so the value it sends on undo is empty; your automation decides how to restore.
The fixes come from the site optimizer.
Verify the signature
When you connect, GEOHATS generates a random signing secret. Every request carries the header X-Geohats-Signature: sha256=<hex>, where the hex is the HMAC-SHA256 of the raw request body keyed with that secret. Recompute it and compare with a constant-time check. Verify the raw bytes before parsing: parsing and re-serializing the JSON can change them.
import crypto from "node:crypto";
// rawBody must be the exact bytes GEOHATS sent: verify BEFORE parsing JSON.
export function verifyGeohats(rawBody, signatureHeader, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const got = Buffer.from(signatureHeader ?? "");
const want = Buffer.from(expected);
return got.length === want.length && crypto.timingSafeEqual(got, want);
}// app/api/geohats/route.js (Next.js route handler)
import { verifyGeohats } from "./verify";
export async function POST(req) {
const raw = await req.text();
const ok = verifyGeohats(
raw,
req.headers.get("x-geohats-signature"),
process.env.GEOHATS_WEBHOOK_SECRET,
);
if (!ok) return new Response("Bad signature", { status: 401 });
const evt = JSON.parse(raw);
if (evt.event === "page.publish") {
// Create or update your post keyed by evt.page.slug, then return its URL.
return Response.json({ url: `https://yoursite.com/blog/${evt.page.slug}` });
}
return Response.json({ ok: true }); // ping, page.draft, seo.fix
}In Zapier, Make or n8n, verifying is optional. If your automation can run a code or crypto step, recompute the HMAC there the same way. If it cannot, keep the webhook URL private, because anyone who has it can send requests to it.
Your response
- Any 2xx status means success. Anything else is reported as a failed publish, and the page stays in GEOHATS to retry.
- Return the live URL on
page.publishwith a JSON body like{"url": "https://yoursite.com/blog/slug"}. GEOHATS records it if it starts with http(s) and is on a different host than the webhook URL (so echo services that return the request URL are ignored). That URL is what GEOHATS watches for citations in later scans. - Answer within 20 seconds. GEOHATS stops waiting after that. If your automation takes longer, reply first and do the slow work afterwards.
Security notes
The webhook URL must be https, and GEOHATS refuses URLs that resolve to private or cloud-metadata network addresses. The connection settings, including the secret, are stored encrypted.
Related
- All publishing integrations
- WordPress publishing and the Connector plugin
- The GEOHATS MCP server, to publish from Cursor or Claude Code
- The AI content writer that produces the pages
- MCP servers for AI visibility compared
FAQ
How do I connect GEOHATS to Zapier?
Create a Zap with a Webhooks by Zapier Catch Hook trigger and copy its URL. In GEOHATS, open the website, go to Integrations, choose Webhook and paste the URL. GEOHATS sends a test ping, so the trigger has sample data, and shows your signing secret.
How do I verify that a request came from GEOHATS?
Compute HMAC-SHA256 of the raw request body with your signing secret, hex-encode it, prefix it with sha256= and compare it to the X-Geohats-Signature header using a constant-time comparison. Verify before parsing the JSON, since re-serializing changes the bytes.
How does GEOHATS learn the live URL of the post?
Reply to a page.publish request with JSON like {"url": "https://yoursite.com/blog/slug"}. GEOHATS records it as the page's live URL if it starts with http or https and is on a different host than the webhook itself.
What happens when I republish a page?
GEOHATS sends the page again with the same slug. Use the slug as the key in your automation and update the existing post instead of creating a new one.
Can the webhook receive SEO fixes too?
Yes. Site optimizer fixes arrive as seo.fix events for all eight fix types: title, meta description, canonical, FAQ schema, Article schema, Organization schema, image alt text and internal link. An undo arrives as a seo.fix event with revert set to true.
What if I lose the signing secret?
It is shown when the connection is saved. If you lose it, connect the webhook again: GEOHATS generates a new secret, and the old one stops being used.
Route your GEOHATS pages anywhere
The webhook is included in every plan, alongside the direct CMS integrations and the MCP server. You pay once and use your own AI keys.
See pricingLast updated