Skip to content

Webhooks

Barakah Tasks webhooks deliver published CloudEvents to an HTTPS receiver even when no MCP client or model session is connected. MCP remains the authenticated control plane for creating, inspecting, pausing, resuming, rotating, replaying, and deleting subscriptions.

Use events:read plus the underlying read scopes for the selected event types. Webhook reads require webhooks:read; controls require webhooks:write.

Creating a subscription queues one durable verification attempt. Repeated verification commands coalesce while that attempt is live, so they reuse the same challenge. The challenge expires after ten minutes.

Generation 6 subscriptions use the selected structured or binary representation and the same v2 signature profile as production. The CloudEvent data object is closed:

{"version":"2","kind":"barakah.webhook.verification","subscription_id":"<subscription-id>","probe_id":"<probe-id>","challenge":"<challenge>"}

Locate that five-key object from the received Content-Type:

  • For binary v2 application/json, the entire parsed request body is the five-key object. Echo the entire parsed body exactly; never use body.data.
  • For structured v2 application/cloudevents+json, the parsed request body is a CloudEvents envelope. Echo only the five-key object at body.data, never the envelope.

This rule is vendor-neutral: select the body location from Content-Type, not from whether the receiver is n8n, Activepieces, Zapier, Make, or custom code. The platform-specific field name may differ, but it must resolve to the same five-key object. A host that exposes neither a customizable synchronous response nor an outbound step for asynchronous confirmation needs a small compatible HTTP ingress in front of it; it is not a reason for a vendor-specific Barakah wire format.

Return exactly those five keys as UTF-8 JSON with base media type application/json and no more than 1024 bytes. Key order and insignificant JSON whitespace are ignored. Missing or unknown keys, duplicate JSON keys, malformed UTF-8, wrong values, the wrong media type, and oversized output are rejected. Compare the challenge through a constant-time digest comparison. The challenge expires after ten minutes and can activate the subscription once only.

Legacy subscriptions retain their version 1 verification representation. A host that cannot customize its callback response can submit the same version-specific object asynchronously. For v2, post that same five-key object directly, not a CloudEvents envelope, to:

POST https://mcp.barakahtasks.com/webhooks/verification/confirm
Content-Type: application/json

Every syntactically valid asynchronous submission receives HTTP 202, including unknown, stale, expired, already used, or incorrect challenges. This public route does not reveal whether a subscription or attempt exists. Read the authenticated subscription to see the truthful verification status, expiry, failure code, and recovery action. A valid confirmation activates promptly and both confirmation paths use the same atomic activation operation.

Replacing an active endpoint immediately stops new delivery to the old receiver. Matching events advance the subscription cursor and form an endpoint_reverification gap until the replacement proves possession. They do not flow automatically after activation; use explicit replay if they are still required.

Every new endpoint or delivery-mode revision uses barakah.http-message-signatures.v2: RFC 9421 HTTP Message Signatures with Ed25519 and an RFC 9530 Content-Digest. Preserve the untouched body bytes. Pin both the exact subscription ID and the authenticated MCP-returned whpk_ public key for that subscription. Reject an unpinned key, a mismatched barakah-subscription-id, or a keyid that does not match the pinned public key. A valid signature delivered to the wrong receiver must fail those checks.

Signature-Input and Signature contain barakah-current first. During an eligible same-endpoint rotation overlap they contain barakah-previous second. Parameters are exactly ordered created, expires, keyid, alg, tag, with alg="ed25519" and tag="barakah.http-message-signatures.v2". keyid is the unpadded base64url SHA-256 digest of the raw 32-byte public key.

The digest is exactly sha-256=:<standard-padded-Base64-of-the-raw-body-SHA-256>:. expires must equal created + 300; webhook-timestamp is the same unsigned decimal as created. Accept only from 60 seconds before created through 60 seconds after expires. All profile headers are emitted once with lowercase names. Reject duplicates, obsolete folding, malformed structured fields, missing components, and non-canonical decimal values.

Structured production, in order:

@method content-digest content-type webhook-id webhook-timestamp
barakah-subscription-id barakah-endpoint-generation barakah-message-kind
barakah-event-id barakah-delivery-id barakah-attempt-id
barakah-replay-generation barakah-key-id

Structured verification, in order:

@method content-digest content-type webhook-id webhook-timestamp
barakah-subscription-id barakah-endpoint-generation barakah-message-kind
barakah-probe-id barakah-key-id

Binary production has exactly two valid covered-component sequences, with or without ce-barakahresourceat at position 12; the verifier rejects every other shape:

@method content-digest content-type ce-specversion ce-id ce-source ce-type
ce-subject ce-time ce-barakahpublishedat ce-barakahsubscriptionsequence
[ce-barakahresourceat] webhook-id webhook-timestamp barakah-subscription-id
barakah-endpoint-generation barakah-message-kind barakah-event-id
barakah-delivery-id barakah-attempt-id barakah-replay-generation barakah-key-id

Binary verification, in order:

@method content-digest content-type ce-specversion ce-id ce-source ce-type
ce-subject ce-time webhook-id webhook-timestamp barakah-subscription-id
barakah-endpoint-generation barakah-message-kind barakah-probe-id barakah-key-id

@method is POST. Structured content type is exactly application/cloudevents+json; binary is exactly application/json, without parameters. Binary CloudEvents use specversion 1.0, source https://barakahtasks.com/events, the registry type and subject, and the exact received RFC 3339 ce-time text. Production always signs authoritative ce-barakahpublishedat and ce-barakahsubscriptionsequence. ce-barakahresourceat is informational but must be covered when present and must never drive authorization or routing. Future extensions are non-authoritative unless a new profile version adds them to an ordered list.

Endpoint or mode changes create a fresh subscription key without overlap. Explicit rotation on the same endpoint revision retains the previous key for 96 hours; both signatures have the same receiver audience. During that overlap, the verifier accepts the matching signed member for either authenticated pinned key, so a receiver can move from the previous pin to the current pin safely.

Download the copy-paste Node verifier and the fixed positive and negative vectors. The verifier accepts raw body bytes, request headers, the expected subscription ID, and the pinned whpk_ public key. It validates the profile, clock window, digest, exact component order, signature, key pin, subscription audience, and message-kind invariants. The vectors cover structured and binary production, both binary production shapes, structured and binary verification, dual-key overlap, body/header tampering, component changes, duplicates, time failures, wrong keys/subscriptions, and unsigned authoritative extensions.

Callbacks use the Standard Webhooks 1.0.0 Ed25519 profile. Preserve the raw request body bytes. Do not parse and reserialize JSON before verification.

The signed bytes are:

webhook-id + "." + webhook-timestamp + "." + raw-body

Required headers are:

  • webhook-id: the immutable CloudEvent ID for event delivery, or the durable verification-attempt ID for verification.
  • webhook-timestamp: Unix seconds for this HTTP request.
  • webhook-signature: one or more space-delimited v1a,<standard-base64-signature> values.

The authenticated subscription read returns Ed25519 public keys serialized as whpk_ followed by standard Base64 of the raw 32-byte public key. Trust that control-plane value or a separately pinned configuration. Never trust a key that appears only in the callback.

During rotation, callbacks carry current and previous signatures for 96 hours. Verify each member against its corresponding current or previous key. The overlap is longer than the 72-hour automatic retry horizon.

Download the fixed byte-exact vector and the independent Node verifier. The vector contains synthetic keys only.

The CloudEvent id and webhook-id identify the underlying business event. They remain stable across automatic retry and explicit replay, but they are not the receiver’s delivery-deduplication key.

  • Automatic HTTP retry is the same logical delivery: barakah-delivery-id and barakah-replay-generation stay stable while barakah-attempt-id, webhook-timestamp, and the signature change. Deduplicate repeated attempts by the stable delivery identity.
  • Explicit Barakah replay preserves the event ID but creates a new deliberate barakah-delivery-id and replay generation. It is allowed to execute again. Never deduplicate explicit replay by CloudEvent ID alone.
  • barakah-attempt-id, webhook-timestamp, and the signature change for every HTTP request.
  • The CloudEvent extension barakahsubscriptionsequence is monotonic only within one subscription. It is stable on replay and does not reveal Barakah Tasks’ private global event position.

Delivery is at least once and is not globally ordered. A slow earlier event does not hold later events behind its backoff. Use the subscription sequence to detect ordering or gaps when that matters to the automation.

Network failures, 408, 429, and 5xx responses retry with full-jitter exponential backoff for at least 72 hours. A bounded Retry-After is honored. Ordinary terminal failures degrade health, create a visible gap, and allow later events to progress. HTTP 410 pauses the subscription immediately and atomically creates a receiver_gone gap.

Automatic retries use the original delivery’s pinned endpoint generation, filter revision, delivery mode, and wire profile. After repairing or replacing a receiver, complete any required verification or resume transition, then explicitly replay the exact preserved gap. Explicit replay uses the endpoint revision active when replay starts, keeps the CloudEvent ID and subscription sequence, and creates a new delivery ID and replay generation.

Task-ref filters bind to the selected occurrence row only. There is no series-wide selector. When a recurring task is selected, the agent must tell the user that only that occurrence matches instead of silently narrowing “this task.”

These are operational limits, not a plan feature: the numbers may change as usage patterns become clearer, and a receiver should treat them as capacity boundaries to design around rather than as marketed capabilities.

Subscriptions. An account may hold at most 5 subscriptions on any single connection and 20 subscriptions total across every connection combined. Creating past either bound is rejected with a clear error; delete an unused subscription to free a slot.

Delivery allowance. Each account has a rolling 30-day allowance of logical deliveries. One delivery ID is one logical delivery, so:

  • Automatic HTTP retry attempts on the same delivery are free: they never count again against the allowance.
  • An explicit replay creates a new delivery ID and does count, even when it replays an event that was already delivered once.

If an account’s rolling usage is exhausted, the affected subscription stops receiving new deliveries and the gap tracking already described above records exactly what was skipped: nothing is silently dropped. The subscription resumes receiving new deliveries once the rolling window has room again; nothing needs to be done manually, and once resumed, the gapped window remains available for explicit replay as usual.

Hidden attempt ceiling. Each account also has a short rolling ceiling on outbound HTTP attempts. This is invisible in ordinary operation: when it is reached, the affected delivery’s next attempt is deferred rather than failed; no attempt is consumed, the 72-hour retry window is not shortened, and the receiver sees no behavior change beyond the delivery arriving a little later than it otherwise would have. At-least-once delivery and the retry contract above are preserved throughout.

For n8n, choose binary mode after the workflow’s Webhook node can retain the raw request bytes for signature verification. The parsed request body is already the event data JSON, while CloudEvents attributes and Barakah delivery identities are in headers. The intended exact-task flow can route that JSON directly into Gmail without extracting an uploaded file or filtering task IDs again in n8n; the subscription filter is authoritative. If the installed trigger cannot make the raw bytes available before parsing, use structured mode until it can. Do not re-serialize parsed JSON and treat it as the signed body.

Activepieces should receive the same generic binary or structured HTTP request through its normal /sync ingress. Diagnose raw-body access, header retention, and custom verification-response support at that ingress. Record any residual receiver limitation as receiver-class evidence; do not add an Activepieces-only wire branch.

A receiver 2xx acknowledges only that one HTTP attempt. It does not prove that a downstream Gmail send or other workflow side effect completed. Make the receiver durably accept its own work before acknowledging, and use the platform’s own execution records for downstream confirmation.

  1. Read and pin the current and previous public keys from the authenticated subscription.
  2. Pin the exact subscription ID and reject a callback for another audience.
  3. Read the raw body and required profile headers without changing the bytes.
  4. Apply the v2 60-second skew and five-minute expiry checks, then verify the exact covered-component sequence and current Ed25519 signature.
  5. Deduplicate automatic attempts by delivery ID; allow a new explicit replay generation to execute even when the event ID was seen before.
  6. Return a 2xx response only after the receiver has durably accepted the callback.
  7. Monitor subscription health and open gaps through MCP.