Create a webhook
create_site_webhook
Creates a webhook on a site, a URL Modulify calls when something happens to that site.
The only events today are publish.succeeded, publish.failed and publish.cancelled. There is no all events default, so at least one is required, and a call without one is refused with Select at least one event to send! An event outside that list is refused as well.
type shapes the payload: raw sends Modulify's own JSON, slack and discord send a message those services render, and ping sends an empty body. A site holds at most 10 webhooks, and the call is refused once that is reached.
The signing secret is generated for you and is not returned here. Read it with get_site_webhook_secret, which needs the separate credentials:reveal scope. The new webhook comes back with its destination as Host only, because the full URL is never read back. See Webhooks.
Request
Call it with a POST to https://api.modulify.ai/v1/create_site_webhook, sending the inputs below as a JSON object. The token needs the config:write scope.
It makes changes, so send an Idempotency-Key header whenever you might retry it. A retry with the same key gets the first answer back instead of running again.
curl -X POST https://api.modulify.ai/v1/create_site_webhook \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"PROJECT_ID","url":"URL","events":[]}'Over MCP, the same method is the create_site_webhook tool.
Inputs
| Input | Type | Required | Description |
|---|---|---|---|
projectId |
string | Yes | The site id. |
url |
string | Yes | The absolute URL to call. It must use https and be at most 2,048 characters, and private and loopback addresses are refused. It is treated as a credential and never read back in full. |
name |
string | No | A label for the webhook, cut to 64 characters. Defaults to the destination host. |
type |
string | No | The payload shape, raw, slack, discord or ping. Defaults to raw, and any other value is refused. |
events |
array of strings | Yes | Which events to send, from publish.succeeded, publish.failed and publish.cancelled. At least one is required, and an event outside that list is refused. |
Response
Every call answers with the JSON envelope of success, message, data, code and version. data holds the result described above, and on a method that returns a total, count carries it. The response headers carry the call's X-Request-Id and what is left of your per-minute budget in X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Errors explains every status code a call can answer with.