Skip to content

Get started

Most read

Connect your host

Trigger a full check within the second following every release, and what that run produces in addition.

Updated on 25 September 2026

On this page

Without a connection, PostShip checks your site on its cycle — every 5 to 30 minutes depending on the plan. With one, the check runs as soon as your host announces the release, while you're still looking at the deploy: it's the moment a regression costs the least. This page is the overview; each host has its own.

The host signals the release; the check runs within the second.

The 5 ways to learn about a deploy

PathWhat it bringsPage
VercelThe commit, the production / preview distinction, 2 deduplicated eventsConnect Vercel
NetlifyThe commit, the context (production, deploy-preview, branch-deploy)Connect Netlify
Cloudflare PagesThe commit when Cloudflare gives it, the preview recognised by its subdomainConnect Cloudflare
Generic webhookAn address and a secret for Coolify, Railway, Render, a VPS or a CI jobGeneric webhook
Build fingerprintNothing to connect: PostShip reads the home page's compiled filesDetection without webhook

The first 4 are webhooks: the host tells you, and brings the commit. The fingerprint is the fallback when no webhook has delivered yet; it steps aside as soon as one of them works.

What a production deploy triggers

On receiving an authenticated production webhook, PostShip chains, in this order:

  1. All the project's URLs, in one pass, with the hint “Vercel deploy 14:32” (or “deploy 14:32” for the generic webhook) that appears in the alerts as “Since the last deploy: …”.
  2. The DNS drift: did the domain change host during the ship (Domain & DNS).
  3. The Ship Score, grade out of 100 of this deploy.
  4. The record in Deployments, with the state of each page (title, H1, description, og:title, og:image, HTTP status, third-party domains) that will serve the deploy diff.
  5. The T+2 and T+8 re-checks.
  6. The social card archive, the PageSpeed measurement and the visual comparison.
  7. The home page's links, as one line on the deploy page.
  8. The GitHub Check on the commit, for Vercel and Netlify.

A deploy during a freeze is marked as such, announced on your channels, and turns the Check red.

From PostShip, or by hand

On Project → Settings → Integrations, Incoming section, each host has its card. For Vercel, the Connect button: you authorise PostShip at Vercel, it finds the site whose domain matches the project, and it's connected — the webhook is created for you, the secret too. The same button becomes Disconnect and removes the webhook on both sides. For Netlify and Cloudflare Pages, the button is shelved since 25 Sept. 2026: they are connected by hand.

If you prefer not to authorise anything, each card shows The webhook URL to copy and a secret field. The exact steps on the provider's side are on each “Connect” page.

Check that it works

Each card carries a status line: “No webhook received yet”, then “Last webhook received … ago” as soon as a correctly authenticated request arrives. The timestamp is written as soon as the signature is verified, before any decision about the event: an ignored deployment.created still proves the chain is right, secret included. As long as the line doesn't move after a deploy, it's the URL or the secret.

Duplicates

Vercel sends 2 events per release, and every host replays a webhook that was answered with an error. PostShip reserves each deploy by its id for 600 seconds: the first to arrive triggers, the second is ignored (skipped: "duplicate_event"). Past the window, a late promotion is a real change of what visitors see, and it's checked.

Previews

Check previews, in Project → Settings → Monitoring, extends the check to preview deploys: each preview is checked on its own address, never against your production. Alert on failing previews is a second decision, disabled by default. Everything is detailed on Previews.

Limits and plans

DiscoverProPro Plus
Vercel, Netlify, Cloudflare, generic webhooksYesYesYes
Build fingerprint detectionYesYesYes
Deploy quality alerts (indexability, DNS drift)NoYesYes
Minimum score on the GitHub CheckNoNoYes

Webhooks are included in every plan, Discover too; without them, fingerprint detection fills Deployments.

Troubleshooting

Why does the card say “Last webhook received” but nothing appears in Deployments?

The signature was right, but the event wasn't a release: a deployment.created at Vercel, a notification of another type at Cloudflare, a duplicate in the 600-second window. The webhook's JSON response, visible in your host's logs, carries the reason (skipped).

Why was the deploy checked twice?

2 paths connected at the same time: 2 webhooks for the same site, or a Netlify notification created twice. The build fingerprint, for its part, turns off as soon as a webhook has delivered.

Why is the deploy line missing on a big project?

The webhook runs all URLs synchronously, in a function limited to 60 seconds. Beyond that, the invocation is killed: results are written, not the deploy line. If it repeats, temporarily disable the slowest URLs (payment journey) to find the one that overflows.

Connect Vercel

Check the site as soon as Vercel has finished deploying, from PostShip or with a hand-pasted webhook.

Without a webhook, PostShip checks your site on its usual cycle. With one, the check runs within the second following the release, with the deployed commit and the distinction between production and preview. 2 paths lead to the same processing: the integration from PostShip, and the team webhook you create yourself.

  1. Open Project → Settings → Integrations, Incoming section, Vercel card.
  2. Click Connect Vercel. Vercel asks you to authorise PostShip on your account or your team, with the sole permissions Deployments: Read and Projects: Read.
  3. On return, PostShip looks for the Vercel project whose domain matches the project's production URL and connects it by itself. If none matches, the card shows “Authorisation accepted” and the question “Which Vercel project matches this one?”: choose it and click Connect.
  4. The card now shows Disconnect Vercel; there is nothing left to copy.

It's the only path that works on a Vercel Hobby account: team webhooks, below, require a Vercel Pro or Enterprise plan. Disconnect detaches the project and forgets the authorisation on PostShip's side if no project uses it anymore; the integration itself is removed from your Vercel dashboard (Integrations).

From the Vercel listing

You can also start from Vercel: vercel.com/integrations/postship → Add Integration, then the team and the projects to authorise. Vercel brings you to postship.fr/vercel — log in or create your account —, where one question is left: which PostShip project listens to which Vercel project. Connect Vercel stores the authorisation and takes you back to Vercel. The installation waits for you for 30 minutes; after that, start it again from the listing.

The fallback: create the webhook by hand

The webhook is created at the Vercel team level, not the project's.

  1. On vercel.com, open your team's Settings → Webhooks → Create Webhook. The Open Vercel webhooks link on the card takes you there.
  2. Tick Deployment Ready and Deployment Promoted, then limit the webhook to the project concerned.
  3. Paste The webhook URL shown on the card as endpoint. It has the form https://postship.fr/api/vercel/deploy/<project id>.
  4. Create it: Vercel shows a secret only once. Copy it into the card's Secret field and click Save.

What PostShip does with the event

  • The x-vercel-signature signature (HMAC-SHA1 of the body) is checked first; a wrong signature answers 401 Invalid signature, and nothing is recorded.
  • payload.target decides: "production" triggers the full check described on Connect your host. Any other value (staging, empty) is treated as a preview — checking a preview by mistake costs a few requests, the reverse would rewrite the project's state.
  • The commit comes from meta.githubCommitSha, present only on a deploy from Vercel's Git integration. Without it, no GitHub Check, and everything else runs.
  • The other types (deployment.created, error, canceled) answer skipped: true.

Previews

With Check previews (Project → Settings → Monitoring), every deploy whose host is in *.vercel.app is checked on its own address; the alerts, if you enable them, are prefixed “Preview — ”. A preview also gets a GitHub Check, without a Ship Score, and a visual comparison with the last production capture. See Previews.

Check that it works

The card shows “Last webhook received … ago” as soon as a correctly signed request arrives, whatever the event. As long as it says “No webhook received yet” after a deploy, it's the URL or the secret. Deploy once to settle it.

Limits and plans

DiscoverProPro Plus
One-click integration and manual webhookYesYesYes
Previews checkedYesYesYes
Minimum score on the GitHub CheckNoNoYes

The webhook and the integration are included in every plan, Discover too; without them, the fingerprint detection takes over.

Troubleshooting

Why “No webhook received” when Vercel shows a successful delivery?

Vercel shows the delivery's HTTP status: a 401 appears there as a delivery. Open the delivery in Vercel and read the response. Invalid signature: the pasted secret isn't this webhook's. Not configured: no secret saved on the card.

Why was production checked when I only deployed another Vercel project?

A team webhook that isn't limited to a project receives the deploys of all the team's projects: a neighbouring site's release then triggers this one's check. Edit the webhook on Vercel's side and restrict it to the right project. A branch preview, for its part, is treated as a preview: its target isn't production.

Why doesn't the GitHub Check show on the commit?

The deploy must come from Vercel's Git integration to carry githubCommitSha. A vercel deploy --prod from a terminal has none. And GitHub must be connected on the project.

The Connect button doesn't appear

The integration is only offered if PostShip has its Vercel keys in production. The manual webhook remains available in all cases.

Connect Netlify

Check the site as soon as Netlify has finished deploying, with a signed webhook created by hand.

Same principle as Vercel: the check runs at the end of the deploy instead of waiting for the next cycle, with the commit and the deploy context. One practical difference on the manual path — the secret is generated by PostShip, because Netlify asks you to invent one.

By hand: the notification

This path keeps the body signature, the best proof.

  1. On the Netlify card, click Generate a secret. It's shown only once: copy it before leaving the page. You can always click Generate a new secret, but you'll then have to update it on Netlify's side.
  2. On app.netlify.com, open the site concerned, then Project configuration → Notifications → Deploy notifications → Add notification → Outgoing webhook.
  3. Event: Deploy succeeded.
  4. URL: The webhook URL copied from the card, of the form https://postship.fr/api/netlify/deploy/<project id>.
  5. JWS secret token: the secret generated by PostShip.
  6. Save.

From PostShip (shelved)

  1. Open Project → Settings → Integrations, Incoming section, Netlify card.
  2. Click Connect Netlify and accept the authorisation at Netlify.
  3. On return, PostShip finds by itself the site whose domain matches the project's production URL. If there are several or none, it asks “Which Netlify site matches this one?”: choose, then Connect.
  4. The card shows Disconnect Netlify. Nothing to generate, nothing to paste.

PostShip then creates 2 outgoing notifications on the site: deploy_created (what Netlify's interface calls “Deploy succeeded”) and deploy_failed. Disconnect deletes them at Netlify along with the authorisation — an orphan notification would keep calling an address nobody listens to anymore.

What PostShip does with the event

  • The proof is checked first: either the X-Webhook-Signature signature (an HS256 JWS whose issuer netlify and SHA-256 digest of the body PostShip verifies), or the URL's token, compared in constant time. Without one of the two: 401 Invalid signature.
  • The body's context field decides: deploy-preview and branch-deploy are previews; everything else, including an empty or unreadable body, is a production. Doubt leans towards production because a production mistaken for a preview would silence an expected alert.
  • For a preview, the checked address is deploy_ssl_url — the HTTPS address specific to this deploy, in *.netlify.app. ssl_url designates the site and would point to production.
  • The commit comes from commit_ref and feeds the GitHub Check.
  • A production triggers everything described in Connect your host: full check, DNS drift, Ship Score, T+2 / T+8, archive, PageSpeed, GitHub Check.

Check that it works

The card switches to “Last webhook received … ago” from the first accepted request. If it stays on “No webhook received yet” after a deploy, it's the secret or the URL. Netlify keeps the delivery history of each notification, with the response status.

Limits and plans

DiscoverProPro Plus
One-click integration and manual notificationYesYesYes
Previews (deploy-preview, branch-deploy) checkedYesYesYes

The webhook is included in every plan, Discover too. The plans detail the rest.

Troubleshooting

Why was a preview checked against my production?

It's no longer possible: the body's context is read, and a deploy-preview without a usable *.netlify.app address is simply not checked (skipped: "invalid_preview_url") rather than folded onto production. If you see a “production” line for a branch deploy, check that the manual notification wasn't created on an event other than “Deploy succeeded”.

Why “Invalid signature” after regenerating the secret?

A new secret invalidates the old one immediately. Copy it into the JWS secret token field of the Netlify notification. The one-click path isn't concerned: it doesn't use that secret.

Why 2 checks per deploy?

The one-click connection and a manual notification coexist: each one calls PostShip. Delete the manual notification on Netlify's side, or click Disconnect Netlify.

The Connect button doesn't appear

The integration is only offered if PostShip has its Netlify keys in production. The manual path remains available in all cases.

Connect Cloudflare Pages

Check the site as soon as Cloudflare Pages has finished deploying, with the destination and the notification created by hand.

Cloudflare separates the webhook (the destination) from the notification (what triggers it): by hand, you have to create both, in that order, at account level. This integration is only for sites deployed on Cloudflare Pages.

By hand: create the destination

In the Cloudflare dashboard, at account level. The Open Cloudflare notifications link on the card leads there.

  1. Open Notifications → Destinations → Webhooks → Create.
  2. Paste The webhook URL shown on the card, of the form https://postship.fr/api/cloudflare/deploy/<project id>.
  3. Cloudflare generates a secret at creation: copy it into the card's Secret field and click Save.

By hand: create the notification

The webhook alone receives nothing as long as no notification points to it.

  1. Open Notifications → Add.
  2. Choose Pages, then the Pages Deployment Success event.
  3. As destination, select the webhook created just before.
  4. Save.

From PostShip (shelved)

  1. Open Project → Settings → Integrations, Incoming section, Cloudflare Pages card.
  2. Click Connect Cloudflare. Cloudflare shows the permissions requested: reading the account settings, reading Pages, reading and writing notifications, and offline_access to be able to disconnect months later.
  3. On return, PostShip looks for the Pages project whose domain matches the project's production URL. If a choice is needed: “Which Pages project matches this one?”, then Connect.
  4. The card shows Disconnect Cloudflare.

PostShip has then created in your account a webhook destination (with a secret it keeps) and a pages_event_alert rule filtered on the chosen Pages project. Without that filter, an account hosting 4 sites would have this one checked at every deploy of the other 3. Disconnect deletes the rule and the destination.

What PostShip does with the event

  • Cloudflare doesn't sign the body: the destination's secret is sent back as is in the cf-webhook-auth header on every delivery. PostShip compares it in constant time; otherwise 401 Invalid signature.
  • The alert_type field is read. A notification that is clearly not a Pages deploy (a health alert, a DNS change you'd have pointed to the same destination) is ignored and answers triggered: false. What isn't recognised goes through: erring in that direction costs one check too many, not a silent outage.
  • Doubt means production. PostShip only switches to preview on an explicit signal: environment equal to preview, or a deploy address in <label>.<project>.pages.dev — one label more than production, which answers on <project>.pages.dev.
  • The commit comes from deployment_trigger.metadata.commit_hash when Cloudflare gives it. The Cloudflare webhook doesn't publish a GitHub Check.

A production triggers everything described in Connect your host.

Check that it works

The card switches to “Last webhook received … ago” from the first accepted request. If nothing arrives after a successful deploy, it's almost always the notification that's missing: the webhook can very well exist without anything triggering it.

Limits and plans

DiscoverProPro Plus
One-click integration, manual destination and notificationYesYesYes
Cloudflare Pages previews checkedYesYesYes

The webhook is included in every plan, Discover too. See the plans.

Troubleshooting

Why does a check run when another of my Pages sites deploys?

The notification created by hand isn't filtered by project. Edit it in Cloudflare and limit it to the Pages project concerned, or go through Connect Cloudflare, which sets that filter.

Why did “Disconnect” fail months later?

The Cloudflare token expires after about an hour and is renewed thanks to offline_access. If the authorisation was revoked on Cloudflare's side in the meantime, PostShip can no longer remove the rule: delete it yourself in Notifications, then disconnect.

Why does the line in Deployments have no commit?

Cloudflare doesn't document the shape of data for Pages deploys; the commit is only read if it's present under deployment_trigger.metadata.commit_hash. Without it, the line remains right — it simply carries no sha.

Generic webhook

Tell PostShip from any host or CI pipeline with an address, a secret and one line of curl.

Vercel, Netlify and Cloudflare each have their card because each signs and speaks its own way. Everything else — Coolify, Railway, Render, a VPS deployed by rsync, a CI job — only needs an address to call after the release, with a secret to prove it's really them. It's the Other host card on the Integrations page.

Set it up

  1. Open Project → Settings → Integrations, Other host card.
  2. Click Generate a secret. It's shown only once: store it in an environment variable of your deployment, for instance POSTSHIP_HOOK_SECRET. It won't be shown again; Generate a new secret makes another one and invalidates the old one.
  3. Copy The address — https://postship.fr/api/generic/deploy/<project id> — or directly The curl line.
  4. Call that address at the end of every release.

The card shows “Not connected yet”, then “Secret generated, no call received yet”, then “Connected — last call … ago” as soon as a correctly authenticated call arrives. Disable erases the secret; the following calls answer 404 Not configured.

The call

The secret travels in the X-PostShip-Secret header, and only there: an address ends up in access logs, yours as well as ours, and this secret doesn't change. A tool that can't set a header goes through a small script (curl, below). The secret is compared in constant time. The body is optional and read kindly: a sha (or commit, commit_sha, COMMIT_SHA, GIT_COMMIT) and a url (or deployment_url, deploy_url, DEPLOY_URL). Everything else is ignored, and an empty or unreadable body isn't an error — it's a deploy about which we know nothing more than “it happened”, which is enough.

At the end of your deploy script
curl -fsS -X POST https://postship.fr/api/generic/deploy/<project id> \
  -H "X-PostShip-Secret: $POSTSHIP_HOOK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"sha":"'$(git rev-parse HEAD)'"}'

The sha is only kept if it looks like a sha (hexadecimal, 7 to 64 characters): it's a value shown in Deployments. The URL is only kept as http or https.

What PostShip does with the call

Production only: an unknown host has no notion of preview that PostShip could read. What arrives here is a release, and triggers everything a release triggers (overview): all the project's URLs with the hint “deploy 14:32”, the DNS drift, the Ship Score, the line in Deployments (provider “Generic webhook”), the T+2 / T+8 re-checks, the archive, the PageSpeed measurement and the visual diff, the home page's links.

The response summarises what happened:

{ "triggered": true, "deployEventId": "…", "checked": 8, "failed": 0, "score": 100 }

The generic webhook doesn't publish a GitHub Check, even when a sha is provided. A call that arrives during a deploy freeze is announced on your channels like any other.

What triggers an alert

The webhook itself doesn't alert. It's the checks it launches that alert, under the same conditions as on the cycle (rules), with the mention “Since the last deploy” in the message. What changed only rings on those runs.

Limits and plans

DiscoverProPro Plus
Generic webhookYesYesYes
Calls per month—No dedicated capNo dedicated cap

There is no quota specific to the webhook: each call runs the project's checks, that is, exactly what a cycle does. A pipeline that called the address in a loop would produce one Deployments line per call — it's the webhook that deduplicates by id at Vercel, not here.

Troubleshooting

Why 401 Invalid secret?

The secret sent isn't the card's, or it was regenerated. Check your pipeline's environment variable and the exact header: X-PostShip-Secret, or token in the URL.

Why 404 Not configured?

No secret exists for this project: it was never generated, or Disable was clicked. Generate a new one.

Why does the call take up to a minute?

It waits for the checks to finish before answering — that's what makes it possible to return checked, failed and score. Don't place it in a step with a shorter timeout, and accept its failure without failing the deploy: curl -fsS returns an error on a 5xx, add || true if your pipeline must continue no matter what.

Why did fingerprint detection stop?

Deliberate: as soon as a call has been received, the detection without webhook steps aside so as not to record 2 deploys per release.

Detection without webhook

How PostShip recognises a deploy by reading your home page's build fingerprint, without connecting anything.

PostShip already downloads every project's home page on every cycle. A deploy leaves a trace there: the name of the compiled files changes. By reading it, PostShip knows a build went through without a webhook having been configured — and the next run becomes that deploy's T+0 check. It's the fallback when nothing is connected; a webhook remains better, because it knows the commit.

What PostShip reads

On every cycle run, one more request on the project's production URL, of which we only keep what changes only at build, in this order of preference:

SourceWhat is keptFrameworks
nextNext.js's buildId, read in __NEXT_DATA__ or in the /_next/static/<buildId>/ pathNext.js
nuxtNuxt's buildIdNuxt
actifsThe sorted set of JS and CSS files carrying a fingerprint: index-D3f8Kw2a.js, main-1a2b3c.css, chunk-A1B2C3D4.jsVite, Astro, SvelteKit, Remix, Gatsby, and most build chains

What's refused, and why: x-vercel-id and x-nf-request-id are request identifiers, they change on every call and would announce a deploy every 5 minutes; ETag and Last-Modified vary on a page rendered on demand. A file fingerprint must contain digits and letters, 8 to 64 characters: /assets/main.js isn't one.

When no signal is present — a hand-written HTML site — the fingerprint is null and no deploy is ever announced. The feature must be invisible when it doesn't know.

When a deploy is concluded

2 consecutive readings are compared. 4 ways to answer “no”, each closes a false positive:

  • First reading: we set a baseline, we observe nothing. Without this rule, going live would have announced a deploy on every project at once.
  • Identical: the common case.
  • Doubtful read: the fingerprint rested on 18 files and now counts half or fewer. A deploy changes the names, it doesn't make a site's JavaScript disappear: it's an error page, a host's holding page, a truncated response. Only 2xx HTML responses are compared anyway.
  • Addition only: files appeared, none disappeared. A banner, an A/B test, a conditional carousel make files appear without a deploy; a build, for its part, replaces. An addition alone isn't a deploy.

Remains the case where at least one recorded file disappeared: that's a deploy. A 10-minute debounce then prevents a single git push from producing 5 while a CDN alternately serves the old and the new version.

What it triggers

The deploy is recorded in Deployments with the provider “Detected without webhook”, without a commit or a deploy URL — it's the only admission of weakness of this path. The ongoing cycle run becomes its T+0 check: alerts carry the hint “deploy detected”, and the word matters — we know neither the host nor the release time, only the moment we noticed. Then, as for a webhook: DNS drift, Ship Score, state of the pages for the deploy diff, T+2 and T+8 re-checks.

No GitHub Check: there is no commit to annotate. No social card archive or PageSpeed measurement on this path.

When it turns off

Detection only runs on projects where no webhook — Vercel, Netlify, Cloudflare or generic — has ever delivered anything, and which aren't paused. We look at what was received, not what was configured: a secret pasted in a field and never connected on the host's side is exactly the case where you believe you're covered without being so. As soon as a webhook has delivered, the host knows better than PostShip what it deployed, and 2 sources for the same event would write 2 lines in the history.

Limits and plans

Fingerprint detection runs on all plans, Discover included: it's what fills Deployments without asking for anything. It costs one HTTP request per project and per run, within the limit of 512 KB read per page. The Pro Plus 30-second probe doesn't read fingerprints: it watches for outages, not deploys.

DiscoverProPro Plus
Fingerprint detectionYesYesYes
Reading frequencyEvery 5 minEvery 2 minEvery minute
Webhooks, which take overYesYesYes

Troubleshooting

Why do my deploys never appear?

Your home page carries no kept signal (HTML without fingerprinted files), or it doesn't answer 2xx with an HTML Content-Type, or your deploys only added files. Connect a webhook: that's the normal path, this one is only a fallback.

Why was a deploy detected when I deployed nothing?

A recorded file disappeared from the home page between 2 runs: a script removed by an experiment, a bundle renamed by an automatic rebuild at the host. The verdict says what changed; it's your page that changed build.

Why did detection stop after connecting Vercel?

That's intended: from the webhook's first delivery, detection steps aside, and it doesn't resume after a disconnection.