Webhooks (Beta)¶
Beta
Webhooks are currently in beta. The API and event types described on this page may change. A management UI is coming in a future release.
Webhooks let the Manifest Platform push real-time event notifications to your application. When an event occurs (e.g., a workflow completes, a deployment succeeds, an agent execution finishes), the platform sends an HTTP POST request to your configured URL with a JSON payload describing the event.
How Webhooks Work¶
sequenceDiagram
participant Platform as Manifest Platform
participant Queue as Event Queue
participant Target as Your Endpoint
Platform->>Queue: Event occurs (workflow.completed)
Queue->>Target: POST /your-webhook-url
Target-->>Queue: 200 OK
Note over Queue: Event delivered successfully
Queue->>Target: POST /your-webhook-url
Target-->>Queue: 500 Error
Note over Queue: Retry with backoff
Queue->>Target: POST /your-webhook-url (retry 1)
Target-->>Queue: 200 OK
Managing Webhooks¶
Creating a Webhook¶
Webhooks are managed via the API using your organization API key.
curl -X POST \
-H "X-API-Key: fl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/flow-events",
"secret": "whsec_your_signing_secret",
"events": [
"workflow.completed",
"workflow.failed",
"deployment.succeeded",
"deployment.failed"
],
"tenant_id": null
}' \
https://api.flow.marut.cloud/api/v1/webhooks/subscriptions
Listing Webhooks¶
Updating a Webhook¶
To change events or URL, delete and recreate the subscription.
Deleting a Webhook¶
curl -X DELETE \
-H "X-API-Key: fl_your_api_key" \
https://api.flow.marut.cloud/api/v1/webhooks/subscriptions/{subscription_id}
Event Types¶
Subscribe to the events relevant to your use case. Each event type follows the pattern resource.action.
Workflow Events¶
| Event | Triggered When |
|---|---|
workflow.completed |
A workflow execution finishes successfully |
workflow.failed |
A workflow execution fails |
workflow.started |
A workflow execution begins |
workflow.step.completed |
An individual workflow step completes |
workflow.step.failed |
An individual workflow step fails |
Agent Events¶
| Event | Triggered When |
|---|---|
agent.execution.completed |
An agent execution finishes |
agent.execution.failed |
An agent execution fails |
agent.execution.started |
An agent execution begins |
Deployment Events¶
| Event | Triggered When |
|---|---|
deployment.succeeded |
A deployment completes successfully |
deployment.failed |
A deployment fails |
deployment.promoted |
A deployment is promoted to a new ring |
deployment.rolled_back |
A deployment is rolled back |
Hosted Service Events¶
| Event | Triggered When |
|---|---|
service.published |
A hosted service version is published |
service.deployed |
A persistent service is deployed |
service.job.completed |
An async job completes |
service.job.failed |
An async job fails |
service.job.failed |
An async job exhausts all retry attempts |
Organization Events¶
| Event | Triggered When |
|---|---|
user.invited |
A new user is invited to the organization |
user.removed |
A user is removed from the organization |
api_key.created |
A new API key is created |
api_key.revoked |
An API key is revoked |
Use wildcards
Subscribe to workflow.* to receive all workflow events, or * to receive every event type. Wildcards apply only at the resource level.
Payload Format¶
Every webhook delivery includes a JSON payload with a consistent structure.
Common Envelope¶
{
"id": "evt_550e8400-e29b-41d4-a716-446655440000",
"type": "workflow.completed",
"timestamp": "2026-03-21T14:30:00.000Z",
"organization_id": "org-uuid",
"workspace_id": "ws-uuid",
"data": {
// Event-specific payload (see below)
}
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique event ID (for deduplication) |
type |
string | Event type (e.g., workflow.completed) |
timestamp |
string | ISO 8601 timestamp of when the event occurred |
organization_id |
string | Organization that owns the resource |
workspace_id |
string | Workspace context |
data |
object | Event-specific details |
Example: workflow.completed¶
{
"id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "workflow.completed",
"timestamp": "2026-03-21T14:30:00.000Z",
"organization_id": "org-uuid",
"workspace_id": "ws-uuid",
"data": {
"workflow_id": "wf-uuid",
"workflow_name": "Document Ingestion Pipeline",
"execution_id": "exec-uuid",
"duration_ms": 12450,
"steps_completed": 5,
"output": {
"documents_processed": 42,
"errors": 0
}
}
}
Example: deployment.failed¶
{
"id": "evt_f1e2d3c4-b5a6-7890-fedc-ba0987654321",
"type": "deployment.failed",
"timestamp": "2026-03-21T15:00:00.000Z",
"organization_id": "org-uuid",
"workspace_id": "ws-uuid",
"data": {
"deployment_id": "deploy-uuid",
"component_id": "comp-uuid",
"component_name": "Invoice Classifier Agent",
"ring": "staging",
"error": "Health check failed after 3 attempts",
"revision": "v2.1.0"
}
}
Webhook Configuration¶
Signing Secret¶
Every webhook delivery includes a signature in the X-Flow-Signature-256 header. Use this to verify that the payload was sent by Manifest Platform and was not tampered with.
The signature is an HMAC-SHA256 hex digest of the raw request body, using your webhook secret as the key.
Verifying Signatures¶
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
"""Verify a Manifest Platform webhook signature."""
expected = hmac.new(
secret.encode("utf-8"),
payload,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
# In your webhook handler:
# payload = request.body (raw bytes)
# signature = request.headers["X-Flow-Signature-256"]
# secret = "whsec_your_signing_secret"
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func verifyWebhook(payload []byte, signature string, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
Always verify signatures
Without signature verification, an attacker could send forged events to your endpoint. Always verify the X-Flow-Signature-256 header before processing a webhook payload.
Retry Logic¶
If your endpoint returns a non-2xx status code or times out, the platform retries the delivery with exponential backoff.
| Attempt | Delay After Failure |
|---|---|
| 1st retry | 10 seconds |
| 2nd retry | 1 minute |
| 3rd retry | 10 minutes |
| 4th retry | 1 hour |
| 5th retry | 6 hours |
After 5 failed retries, the event is marked as failed and no further attempts are made.
Delivery Requirements¶
Your endpoint must meet these requirements for reliable delivery:
- Return 2xx within 10 seconds. If processing takes longer, accept the webhook, queue the work, and return
200 OKimmediately. - Be idempotent. Use the
idfield to deduplicate events. The platform may deliver the same event more than once due to retries. - Accept POST requests with
Content-Type: application/json.
Debugging Webhooks¶
Delivery Logs¶
View recent webhook deliveries via the API:
curl -H "X-API-Key: fl_your_api_key" \
"https://api.flow.marut.cloud/api/v1/webhooks/deliveries?subscription_id={subscription_id}"
Each delivery record includes the event type, response status code, duration, and timestamp.
Testing with a Ping¶
When a ping is sent, your endpoint receives a ping event with a minimal payload:
{
"id": "evt_ping_abc123",
"type": "ping",
"timestamp": "2026-03-21T14:30:00.000Z",
"organization_id": "org-uuid",
"workspace_id": "ws-uuid",
"data": {}
}
Troubleshooting¶
Webhooks are not being delivered
- Check that the URL is reachable from the internet (HTTPS required).
- Review the delivery logs for error details.
- Send a test event to verify connectivity.
Receiving duplicate events
- This is expected behavior during retries. Use the
idfield on each event to deduplicate in your handler. - If duplicates appear outside of retry windows, check that you do not have multiple webhooks configured for the same events.
Signature verification is failing
- Verify you are using the raw request body bytes (not a parsed/re-serialized JSON string).
- Confirm the secret matches exactly what was configured when the webhook was created.
- Check for any middleware that modifies the request body before your verification logic runs.
Next Steps¶
- MCP Servers -- Register external tool servers
- Hosted Services -- Build custom APIs on the platform
- Security -- Audit logs and access control