For the complete Netlify documentation index, see llms.txt. Markdown versions of any documentation page are available by appending .md to its URL. Organization webhooks send an HTTP POST request to a URL of your choice whenever a project is created or published in any team that belongs to your organization. You subscribe once for the whole organization, and every team in it is covered, including teams you add later.
Use cases
Section titled “Use cases”Use organization webhooks to automate security, compliance, and monitoring for every new project across all teams in your organization, and to act on every new production deploy. For example, you can:
- Turn on Password Protection for every new project that is public.
- Keep an inventory of every project in your organization, with its team, owner, and repository.
- Get notified when an AI agent or app builder creates a project.
- Flag projects whose repository is outside your approved Git organizations.
- Send project creation and production releases to an audit log, a SIEM, or a change-management system.
- Run accessibility or security scans, or register uptime monitoring, whenever a project goes live.
Events
Section titled “Events”Organization webhooks support the following events:
- Project created (
site_created): event emitted when a project is created in any team in the organization, whether from the Netlify UI, the Netlify CLI, the Netlify API, or an AI agent. The request body is the project, in the same format the Netlify API returns fromGET /api/v1/sites/{site_id}. Because the project has not been deployed yet,published_deployisnull. - Project published (
site_published): event emitted when a production deploy becomes the published version of a project in any team in the organization. The request body is the deploy, in the same format the Netlify API returns fromGET /api/v1/deploys/{deploy_id}.
Projects in teams that are not linked to your organization don't trigger organization webhooks.
Request format
Section titled “Request format”Each webhook is a POST request with a JSON body and these headers:
Content-Type:application/jsonX-Netlify-Event: the name of the event, such assite_createdorsite_publishedX-Webhook-Signature: a JSON Web Signature (JWS), sent only if you set a signature secretsecretSensitive credentials, like passwords and API keys, that must be kept private. for the webhook
The signature works the same way as for deploy notification webhooks: it's signed with HS256 using your secret, and its data section includes iss with the value netlify and sha256 with the hexadecimal SHA256 hash of the request body. Verify the signature against the raw request body before you parse it.
Your endpoint must respond with a 2xx status code. Netlify retries a delivery that fails because of a network error or timeout. If deliveries keep failing, Netlify disables the webhook. To learn how to turn it back on, check out Enable a disabled webhook.
Create an organization webhook
Section titled “Create an organization webhook”Organization Owners can create organization webhooks with the Netlify API. You need a personal access tokenpersonal access tokenA credential tied to your personal account that lets tools and scripts act on your behalf without your password. that belongs to an Organization Owner.
-
Find your organization ID. This request lists the organizations you have access to:
Terminal window curl -H "Authorization: Bearer $NETLIFY_TOKEN" \https://api.netlify.com/api/v1/organizations -
Create a webhook for each event you want to receive. Replace
ORGANIZATION_IDwith your organization ID, and seturlto the endpoint that should receive the event:Terminal window curl -X POST "https://api.netlify.com/api/v1/organizations/ORGANIZATION_ID/hooks" \-H "Authorization: Bearer $NETLIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"event": "site_created","data": {"url": "https://example.com/netlify-hooks","signature_secret": "your signature secret"}}'To also receive the Project published event, send the same request with
"event": "site_published". Each URL can be registered only once for each event.signature_secretis optional, but we recommend setting it so you can confirm that requests come from Netlify.
The response includes the webhook's id, which you need to update or delete the webhook later.
Manage organization webhooks
Section titled “Manage organization webhooks”Use these Netlify API endpoints to manage your organization's webhooks. Like creating a webhook, these requests require a personal access token that belongs to an Organization Owner.
| Action | Request |
|---|---|
| List webhooks | GET /api/v1/organizations/{organization_id}/hooks. Add ?event=site_created to filter by event. |
| Get a webhook | GET /api/v1/organizations/hooks/{hook_id} |
| Update a webhook | PUT /api/v1/organizations/hooks/{hook_id} with a new event or data |
| Delete a webhook | DELETE /api/v1/organizations/hooks/{hook_id} |
| Enable a disabled webhook | POST /api/v1/organizations/hooks/{hook_id}/enable |
Enable a disabled webhook
Section titled “Enable a disabled webhook”If deliveries to your endpoint keep failing, Netlify disables the webhook so it stops sending requests. After you fix your endpoint, turn the webhook back on:
curl -X POST "https://api.netlify.com/api/v1/organizations/hooks/HOOK_ID/enable" \ -H "Authorization: Bearer $NETLIFY_TOKEN"Example: require team login for new projects
Section titled “Example: require team login for new projects”This Netlify Function receives the Project created event, verifies the signature, and turns on team login protection for any new project that isn't already protected. It expects two environment variablesenvironment variablesNamed values stored outside your code that your site can read at build time or runtime.: HOOK_SECRET, the signature secret you set for the webhook, and NETLIFY_TOKEN, a personal access token with access to every team in your organization.
// netlify/functions/organization-hooks.mjsimport { createHash, createHmac, timingSafeEqual } from "node:crypto";
function verify(token, body, secret) { const [header, claims, signature] = token.split("."); if (!signature) return false;
const expected = createHmac("sha256", secret) .update(`${header}.${claims}`) .digest("base64url"); if ( signature.length !== expected.length || !timingSafeEqual(Buffer.from(signature), Buffer.from(expected)) ) { return false; }
const { iss, sha256 } = JSON.parse(Buffer.from(claims, "base64url")); return iss === "netlify" && sha256 === createHash("sha256").update(body).digest("hex");}
export default async (req) => { const body = await req.text(); const signature = req.headers.get("x-webhook-signature") ?? ""; if (!verify(signature, body, Netlify.env.get("HOOK_SECRET"))) { return new Response("Invalid signature", { status: 401 }); }
const site = JSON.parse(body); if (req.headers.get("x-netlify-event") === "site_created" && !site.sso_login && !site.has_password) { const res = await fetch(`https://api.netlify.com/api/v1/sites/${site.id}`, { method: "PATCH", headers: { Authorization: `Bearer ${Netlify.env.get("NETLIFY_TOKEN")}`, "Content-Type": "application/json", }, body: JSON.stringify({ sso_login: true, sso_login_context: "all" }), }); if (!res.ok) { console.error(`Couldn't protect ${site.name}: ${res.status} ${await res.text()}`); } }
return new Response("OK");};
export const config = { path: "/api/organization-hooks" };Password Protection offers basic password protection and team login protection. This example turns on team login protection for the whole project.
When you turn on protection from a webhook, always confirm that the PATCH request succeeded. If it fails, the project stays unprotected. The example logs any error, so check your function logs after projects are created instead of assuming every project is protected. If a request returns an error, see Troubleshooting.
Troubleshooting
Section titled “Troubleshooting”Turning on protection returns a 422 error
Section titled “Turning on protection returns a 422 error”If the PATCH request returns 422 with a message such as Login With Netlify protection is not available on this plan, the project's team can't turn on that protection yet. The project stays unprotected. Contact Netlify Support or your Netlify account team, and we'll help you set up protection for that team.
Did you find this doc useful?
Your feedback helps us improve our docs.