Webhooks
Subscribe to real-time events delivered to your HTTPS endpoint. Webhooks notify your application whenever something happens in a project — tasks created, statuses changed, comments posted, and more.
Management Endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /webhooks | webhooks.manage | List endpoints |
POST | /webhooks | webhooks.manage | Subscribe |
GET | /webhooks/:id | webhooks.manage | Get detail + recent deliveries |
PATCH | /webhooks/:id | webhooks.manage | Update |
DELETE | /webhooks/:id | webhooks.manage | Delete |
POST | /webhooks/:id/ping | webhooks.manage | Send test event |
Subscribing
curl -X POST https://projects.cyrus365.com/api/v1/webhooks \
-H "Authorization: Bearer cyp_pub_xxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/projects",
"events": ["task.created", "task.updated", "task.status_changed"]
}'
The response includes a secret field (shown only once) — save it for signature verification.
Event Types
| Event | Fired When |
|---|---|
task.created | New task created |
task.updated | Task fields updated |
task.deleted | Task deleted |
task.status_changed | Task status changed |
task.assigned | Assignees changed |
task.moved | Task moved between groups/sprints |
comment.created | New comment on a task |
sprint.created | Sprint created |
sprint.updated | Sprint updated |
sprint.status_changed | Sprint status changed |
group.created | Group created |
group.updated | Group updated |
project.updated | Project details changed |
project.deleted | Project deleted |
* | Subscribe to all events |
Webhook Payload
{
"id": "evt_abc123",
"event": "task.created",
"timestamp": "2025-01-15T10:30:00.000Z",
"projectId": "clxyz...",
"data": {
"task": { "id": "...", "title": "...", "status": "TODO" }
}
}Signature Verification
All deliveries are signed with HMAC-SHA256. Verify the X-Webhook-Signature header against the raw JSON body using your endpoint's secret.
JavaScript
const crypto = require("crypto");
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
const received = signature.replace("sha256=", "");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex")
);
}
// Usage:
const isValid = verifyWebhook(rawBody, req.headers["x-webhook-signature"], secret);Python
import hmac, hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
received = signature.replace("sha256=", "")
return hmac.compare_digest(expected, received)Delivery Headers
| Header | Description |
|---|---|
X-Webhook-Signature | sha256=<hex> HMAC-SHA256 of the JSON body |
X-Webhook-Event | Event type string |
X-Webhook-ID | Unique event ID (for idempotency) |
X-Webhook-Timestamp | ISO 8601 event timestamp |
User-Agent | Cyrus365-Projects/1.0 |
Delivery Behavior
| Behavior | Detail |
|---|---|
| Timeout | 10 seconds per attempt |
| Retries | 3 attempts — 1 s, 10 s, 60 s backoff |
| Auto-disable | After 10 consecutive failures |
| Re-enable | PATCH /webhooks/:id with { "isActive": true } |
| Max endpoints | 10 per API key |
Related
- •API Overview — Base URL, response format, and error codes
- •Endpoints — All available API endpoints