I needed to point a cache revalidation webhook at a new domain for a Next.js site, and I realised I had never written down how to manage Experience Edge webhooks without clicking through anything. There is no UI for this. It's the Edge Admin API, and on Windows the quoting will fight you if you let it.
Here is the whole flow: create, list, update, delete, verify. Everything runs in PowerShell.
Prerequisites
- Access to the Deploy app in Sitecore Cloud Portal
- Windows PowerShell with
curl.exe
Note the .exe. In Windows PowerShell, curl is an alias for Invoke-WebRequest and it breaks the quoting in every command below. Always type curl.exe.
Step 1: Create an Edge administration client
- Open Sitecore Cloud Portal and go to the Deploy app.
- Click Credentials in the left navigation.
- Open the Environment tab.
- Click Create credentials > Edge administration.
- Enter a Label, for example
Edge Admin - Production. - Select the Project and the Environment the webhook belongs to.
- Click Create.
- Copy the client ID and client secret now.
A few things worth knowing:
- The client secret is shown only once. Lose it and you create a new client.
- Credentials are per environment. Create a separate client for development and production, and use the matching one.
- The label cannot contain these characters:
# @ " + & > - Treat the client ID and secret like a username and password. Don't commit them, and don't paste them into tickets or chats.
Step 2: Get an access token
$token = (curl.exe --request POST --url "https://auth.sitecorecloud.io/oauth/token" `
--header "content-type: application/x-www-form-urlencoded" `
--data "audience=https://api.sitecorecloud.io" `
--data "grant_type=client_credentials" `
--data "client_id=<CLIENT_ID>" `
--data "client_secret=<CLIENT_SECRET>" | ConvertFrom-Json).access_token
The token is valid for 24 hours. If you get access_denied / Unauthorized, the client ID or secret is wrong, or you're using credentials from a different environment.
Keep the token in $token rather than pasting the full JWT into every command. Commands stay short and you avoid the truncated-paste problem that produces confusing errors later.
Step 3: List existing webhooks
curl.exe -X GET "https://edge.sitecorecloud.io/api/admin/v1/webhooks" `
--header "Authorization: Bearer $token"
Note the id of the webhook you want to change. You need it for update and delete.
Step 4: Create a webhook
Save the definition as webhook.json:
{
"label": "Cache Revalidate onUpdate Webhook",
"uri": "https://<YOUR-DOMAIN>/api/admin/webhook/onUpdate",
"method": "POST",
"headers": {
"x-webhook-secret": "<WEBHOOK_SECRET>"
},
"createdBy": "<YOUR NAME>",
"executionMode": "OnUpdate"
}
Send it:
curl.exe -X POST "https://edge.sitecorecloud.io/api/admin/v1/webhooks" `
--header "Authorization: Bearer $token" `
--header "Content-Type: application/json" `
--data "@webhook.json"
Two lessons here. A missing Content-Type gives you 415 Unsupported Media Type. And passing the body as a file with --data "@webhook.json" saves you from escaping JSON quotes in PowerShell, which is not a fun way to spend an afternoon.
Step 5: Update a webhook
My case was a new domain. Update in place. Don't delete and recreate, because that gives you a new ID and wipes lastRuns, which is the history you'll want when checking that things still work.
Edit webhook.json with the new uri, then:
curl.exe -X PUT "https://edge.sitecorecloud.io/api/admin/v1/webhooks/<WEBHOOK_ID>" `
--header "Authorization: Bearer $token" `
--header "Content-Type: application/json" `
--data "@webhook.json"
Send the full definition (label, uri, method, headers, executionMode), not just the field you changed. It's a PUT, not a patch.
Step 6: Delete a webhook
curl.exe -X DELETE "https://edge.sitecorecloud.io/api/admin/v1/webhooks/<WEBHOOK_ID>" `
--header "Authorization: Bearer $token"
No response body means it worked.
Step 7: Verify
List the webhooks again:
curl.exe -X GET "https://edge.sitecorecloud.io/api/admin/v1/webhooks" `
--header "Authorization: Bearer $token"
Check that the uri is correct. Then publish something and look at lastRuns for "success": true.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
access_denied / Unauthorized | Wrong client ID or secret | Check the credentials and the environment |
415 Unsupported Media Type | Missing Content-Type header | Add Content-Type: application/json |
JWT Verification failed | Expired, truncated, or mixed-up token | Request a new token and pass it with $token |
Webhook created but lastRuns is empty | Nothing has been published yet | Publish content and check again |
Keep the secrets out of sight
Never share tokens, client secrets, or webhook secrets in blog posts, tickets, or chats. If any of them leak, revoke the client in Deploy > Credentials and create a new one.
On the receiving side, validate the x-webhook-secret header in your endpoint. That's what makes sure only Experience Edge can trigger revalidation on your Next.js site.



