Docs/Deploy Hooks

Deploy Hooks

Rebuild and redeploy your live site from WordPress, Sanity, a CI step, a cron job or any service that can call a URL.

Deploy Hooks

A deploy hook is a private URL that rebuilds and redeploys your live site. Point your CMS at it, and publishing a post updates the site without anyone opening the editor.

What a deploy hook deploys

A deploy hook builds the site from your GitHub repository's default branch, the same code your live site already runs. While building, the site fetches its data again from WordPress, Sanity or your other sources.

Goes live with a deploy hook
New or updated content in WordPress, Sanity and other sourcesYes
Code already pushed to GitHub (from PhantomStudio, a teammate or CI)Yes
Edits in the editor you haven't publishedNo: use Publish
Drafts on other branchesNo

The hook never commits or pushes anything. That's deliberate: the hook means "the content changed, rebuild the site", and Publish means "my changes are ready, ship them". A post published in WordPress can't take half-finished design work live with it.

💡

Pages set to Dynamic read your CMS on every request, so they show new content without a deploy hook. You need a hook for static pages, and for Dynamic, cached pages when you don't want to wait for the cache to expire.

Before you start

  • Deploy the site once to Vercel or Cloudflare Workers. A deploy hook redeploys an existing deployment; until there is one, calling it answers "No deployment configured for this repository".
  • The project owner's GitHub account must be linked. Hook deploys run as the project owner, so they work even when nobody is signed in.

Create a deploy hook

  1. In the editor, open Publish ▾ › Deploy hook. (WordPress projects also have it under Site › WordPress › Deployment.)
  2. Click Create deploy hook.
  3. Copy the Deploy hook URL.

Each project has one deploy hook. You don't need to connect WordPress or any other source to create it.

Trigger a deploy

With the URL

Send a request to the deploy hook URL. GET and POST both work, and no headers or body are needed. Whatever body the sender includes is ignored, so a CMS can send its usual payload.

The examples on this page use https://phantomwp.com, which keeps answering for PhantomStudio. Copy your own URL from the deploy hook panel rather than typing it: it has your secret in it, and the panel shows the address to use.

curl -X POST 'https://phantomwp.com/api/webhook/deploy/<secret>'

Opening the URL in a browser deploys too.

With the secret in a header

URLs end up in server logs, browser history and CMS delivery logs; headers usually don't. If the sender can set headers, send the secret in X-Webhook-Secret to the base URL instead. You'll find this form under Keep the secret out of the URL in the deploy hook panel.

curl -X POST 'https://phantomwp.com/api/webhook/deploy' \
  -H 'X-Webhook-Secret: <secret>'

Setups from before the deploy hook URL existed send {"repo": "<project name>"} as the JSON body with the header. They keep working.

The response

A successful call answers 200 and starts the build:

{
  "success": true,
  "message": "Deployment triggered successfully",
  "deployment": { "status": "building", "provider": "vercel" }
}

Every call starts a new deploy. Bulk-editing 20 posts in WordPress can start 20 builds; most hosts cancel or queue the older ones, but it uses build minutes.

Rebuild when WordPress content changes

Site › WordPress › Deployment shows a ready-made snippet with your URL filled in. Add it to your theme's functions.php or a small plugin:

add_action('publish_post', 'trigger_phantomwp_deploy');
add_action('publish_page', 'trigger_phantomwp_deploy');

function trigger_phantomwp_deploy($post_id) {
    // Skip autosaves and revisions
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    if (wp_is_post_revision($post_id)) return;

    wp_remote_post('https://phantomwp.com/api/webhook/deploy/<secret>');
}

It deploys whenever a post or page is published or updated. For a custom post type, add a line with its name, for example add_action('publish_event', 'trigger_phantomwp_deploy');. To keep the secret out of URLs, send it as a header instead:

wp_remote_post('https://phantomwp.com/api/webhook/deploy', array(
    'headers' => array('X-Webhook-Secret' => '<secret>'),
));

Rebuild when Sanity content changes

The Sanity connection dialog links to the deploy hook. To set up the webhook in Sanity:

  1. Open sanity.io/manage, pick your project, then API › Webhooks › Create webhook.
  2. Paste the deploy hook URL as the URL.
  3. Pick the Dataset your site reads (usually production).
  4. Trigger on Create, Update and Delete.
  5. Optionally add a Filter so only content your site shows deploys, for example _type in ["post", "page"].
  6. Leave drafts out, so only published changes deploy, and save.

Other services

Anything that can call a URL can deploy your site:

  • Other CMSs (Contentful, Strapi, Storyblok, Payload): add the deploy hook URL as a webhook on publish.
  • Scheduled rebuilds: call the URL from a cron job, for example every night with 0 3 * * * curl -X POST '<deploy hook URL>', or from a service like cron-job.org.
  • CI and automation: a GitHub Actions step, Zapier or Make.

Keep the URL private

The deploy hook URL contains the secret: anyone who has it can deploy your site. They can't change your code or content, but they can start builds.

  • Don't paste the URL into chats or emails. Slack, Teams, Discord and email security scanners open links to preview or check them, and each visit starts a deploy. Send the header form to services that support it.
  • Regenerate in the deploy hook panel replaces the secret. The old URL stops working at once, so update every service that uses it.
  • Disable turns the deploy hook off until you create a new one.

Troubleshooting

AnswerWhat it meansFix
403 Invalid deploy hook secretThe secret is wrong, was regenerated, or the hook was disabledCopy the current URL from Publish ▾ › Deploy hook
400 No deployment configured for this repositoryThe site was never deployedDeploy once from Publish ▾, then try again
409 The repository owner has no GitHub account linkedHook deploys run as the owner, who has no GitHub linkThe owner links GitHub in the editor
401 Missing X-Webhook-Secret headerA header-form request without the headerAdd the header, or use the deploy hook URL
The site deploys when nobody published anythingThe URL was opened by a link preview, or leakedRegenerate, and send the header form where possible
The deploy ran but new content is missingThe page is Dynamic, cached, or the CMS cached its API responseWait for the cache to expire, or check your CMS's API caching

For build failures, see Deployment Troubleshooting.