Send events to a URL, a Slack channel or a GitHub repository (repository_dispatch) as they happen (paid plans).
kind:
webhook(default): POST of the event JSON, signed withappgoat-signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>. Thesecretis returned once, here (rotate with PATCH rotate_secret). Answer 2xx within 10 s.slack:urlis a Slack incoming webhook (https://hooks.slack.com/services/…).github:urlisowner/repo,github_tokena fine-grained token with Contents: read and write on it. Sends{ event_type: "appgoat.<type>", client_payload: <event> }to start a workflowon: repository_dispatch. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h; an endpoint failing 50 times in a row is disabled (PATCH active=true re-enables it). Free plan: 402 plan_limit (use GET /v1/events).
Request
curl -X POST "https://api.appgoat.ai/v1/webhooks" \
-H "x-api-key: $APPGOAT_API_KEY" \
-H "content-type: application/json" \
-d '{"url":"string","kind":"webhook","event_types":["new_version"],"description":"string","github_token":"string"}'const res = await fetch("https://api.appgoat.ai/v1/webhooks", {
method: 'POST',
headers: {
"x-api-key": process.env.APPGOAT_API_KEY,
"content-type": "application/json",
},
body: JSON.stringify({"url":"string","kind":"webhook","event_types":["new_version"],"description":"string","github_token":"string"}),
});
const { data, meta } = await res.json();import os, requests
res = requests.post(
"https://api.appgoat.ai/v1/webhooks",
headers={"x-api-key": os.environ["APPGOAT_API_KEY"]},
json={"url":"string","kind":"webhook","event_types":["new_version"],"description":"string","github_token":"string"}
)
body = res.json()Request body
JSON, required.
urlstringhttps URL (webhook), Slack incoming webhook URL (slack) or owner/repo (github).
kind"webhook" | "slack" | "github"event_typesEventType[] | nullnull = every type.
descriptionstring | nullgithub_tokenstringkind github only; never returned.
Response
201 with data: WebhookEndpoint. See the envelope for meta.
idintegerkind"webhook" | "slack" | "github"urlstringgithub: owner/repo; slack: shortened (the URL is a credential).
descriptionstring | nullevent_typesEventType[] | nullactivebooleandisabled_reasonstring | nullfailure_countintegerConsecutive failed attempts.
last_success_atstring | nulllast_failure_atstring | nullsecretstringkind webhook: the signing secret, only in the create / rotate response.
secret_hintstringcreated_atstringupdated_atstringExample
{
"data": {
"id": 1,
"kind": "webhook",
"url": "string",
"description": "string",
"event_types": [
"new_version"
],
"active": false,
"disabled_reason": "string",
"failure_count": 1,
"last_success_at": "2026-10-22T10:00:00Z",
"last_failure_at": "2026-10-22T10:00:00Z",
"secret": "string",
"secret_hint": "string",
"created_at": "2026-10-22T10:00:00Z",
"updated_at": "2026-10-22T10:00:00Z"
},
"meta": {
"credits_used": 0,
"credits_remaining": 9981,
"request_id": "req_123"
}
}Status codes
201
The endpoint, with its signing
secret (only now).A parameter is missing or invalid (
error.param names it).Missing or invalid API key.
insufficient_credits (the call costs more than you have left; nothing was done) or plan_limit (a feature of a higher plan).429
rate_limitedToo many requests per second for your plan.