Developer docs

Receive Postbing articles with a webhook

Connect any website, headless CMS or automation: when an article publishes, Postbing sends it to your endpoint as signed JSON. This page has everything a developer needs, including copy-paste signature checks and recipes for Wix, Framer, Next.js and Zapier.

Quick start

  1. Create an HTTPS endpoint that accepts POST requests with a JSON body.
  2. In Postbing, open Integrations, choose Webhook and paste the endpoint address. Postbing sends a signed ping to check it answers.
  3. Copy the signing secret shown once after connecting. Store it on your server as POSTBING_WEBHOOK_SECRET.
  4. Verify every request, then save the article. Answer with any 2xx status within 30 seconds.

Endpoints must be public http(s) addresses. Redirects are not followed, so give the final address. Private and internal network addresses are refused.

Events

EventWhen
article.publishA new article goes live.
article.updateAn article that is already live was edited and pushed again. article.id is the id you returned the first time.
page.publish, page.updateThe same for standalone pages (local landing pages), which you may want to route differently.
article.unpublishThe article was taken down in Postbing. Remove or hide it.
pingConnection checks and the Test connection button. Answer 2xx; test: true marks a manual test.

Headers

HeaderValue
X-Postbing-EventThe event name, as above.
X-Postbing-TimestampUnix seconds when the request was signed.
X-Postbing-Signaturev1=<hex>: HMAC-SHA256 of <timestamp>.<raw body> with your secret. Right after you make a new secret it carries two values, comma separated, for 24 hours; accept the request if either matches.
Idempotency-KeyThe same on every retry of the same delivery. Use it to ignore repeats.
X-Postbing-DeliveryA unique id for this attempt (useful in your logs).
X-Postbing-Article-IdThe Postbing article id (article events).

Until February 11, 2027 we also send the two legacy event and signature headers from before our rename (sha256= HMAC of the body only) so existing receivers keep working. Move to X-Postbing-Signature before then: it includes the timestamp, which stops replayed requests.

Payload

Version 2. Fields may be added over time; ignore the ones you don't use.

{
  "event": "article.publish",
  "version": 2,
  "id": "4b0c2f0e-6c1d-4a59-9f0b-1f8b6a2b9d11",
  "created_at": "2026-11-13T09:00:00Z",
  "idempotency_key": "pb-1842-publish-9c1f2a7d5e3b4c60",
  "mode": "publish",
  "article": {
    "id": null,
    "postbing_id": 1842,
    "type": "article",
    "title": "How to choose a coffee grinder",
    "slug": "how-to-choose-a-coffee-grinder",
    "html": "<p>...</p><script type=\"application/ld+json\">...</script>",
    "body_html": "<p>...</p>",
    "markdown": "...",
    "excerpt": "Burr or blade, flat or conical ...",
    "meta_title": "How to choose a coffee grinder (2026 guide)",
    "meta_description": "Burr or blade, flat or conical ...",
    "faqs": [{"question": "...", "answer": "..."}],
    "schema_jsonld": {"@context": "https://schema.org", "@graph": []},
    "tags": ["grinders"],
    "categories": ["Brewing guides"],
    "language": null,
    "scheduled_date": "2026-11-13",
    "featured_image": {"url": "https://...", "alt": "..."},
    "images": [{"url": "https://...", "alt": "...", "kind": "featured"}]
  }
}

Verify the signature

Compute the HMAC over the raw request body, before any JSON parsing, and reject requests whose timestamp is more than 5 minutes from your clock.

const crypto = require("crypto");

// Express: use the raw body, e.g.
// app.post("/postbing-webhook", express.raw({ type: "*/*" }), handler)
function verifyPostbing(req, secret = process.env.POSTBING_WEBHOOK_SECRET) {
  const ts = req.get("X-Postbing-Timestamp") || "";
  if (!/^\d+$/.test(ts)) return false;
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;  // replay window
  const expected = crypto.createHmac("sha256", secret)
    .update(Buffer.concat([Buffer.from(ts + "."), req.body]))
    .digest("hex");
  return (req.get("X-Postbing-Signature") || "").split(",").some((part) => {
    const sig = part.trim().replace(/^v1=/, "");
    return sig.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  });
}

Retries and duplicates

Recipes

These platforms have no native Postbing app yet. Each recipe needs a developer for about an hour.

Wix Wix Blog API

  1. Create a site API key in your Wix account (API Keys) with the Wix Blog permissions, and note your site id.
  2. Deploy a small endpoint (a Wix HTTP function, a Cloudflare Worker or any server) that verifies the signature as above.
  3. Convert article.body_html to Wix rich content with Wix's Ricos conversion endpoint, then create a draft post with the title, slug and a site member as the author, and publish it.
  4. Answer with {"id": "<wix post id>", "url": "<post url>"}; use the id to update the same post on article.update.

Framer CMS collection

  1. In Framer, create a CMS collection with Title, Slug, Content (formatted text), Excerpt and Image fields, and create a Server API key for the project (Site settings, General).
  2. Run a small Node service with Framer's framer-api package that verifies the signature, upserts the item by postbing_id (store it in a plain text field) and publishes the site.
  3. Framer's Server API is in open beta; check Framer's changelog before relying on it in production.

Next.js and custom sites Any stack

  1. Add a route handler, for example app/api/postbing/route.ts. Read the body with await req.text() so you can verify the raw bytes, then JSON.parse it.
  2. Upsert into your database or markdown folder by postbing_id; use slug for the URL and meta_title / meta_description for your page metadata.
  3. Revalidate the page (revalidatePath) so the article is live at once, and answer with its URL.

Zapier or Make No code

  1. Create a Zap with Webhooks by Zapier, Catch Raw Hook (or a Make custom webhook) and paste its address into Postbing.
  2. Check the signature in a Code step (the Python version above runs in Zapier's Code by Zapier), then map article.title, article.body_html and article.slug into the app you publish with.
  3. Catch hooks can't answer with your post id, so updates arrive as article.update with postbing_id: look the post up by that id in your Zap.

Testing

Use Test connection on the site in Integrations to send a signed ping with "test": true and see your endpoint's answer. Lost the secret? Make a new one from the site's menu; the old one keeps working for 24 hours.