Build Webhooks
Get an HTTPS POST from VibeView every time a cloud build finishes — wire your CI, a Slack bridge, or any internal tooling to react the moment a build succeeds or fails. Build webhooks are the cloud-build counterpart of EAS webhooks.
Build webhooks are separate from test & platform event webhooks. Those cover test runs, sessions, and visual regressions, and use a different signature scheme. Everything on this page applies only to build webhooks.
What fires
One build.completed event per finished cloud build, delivered to every
active webhook endpoint in your organization. “Finished” means any final
status: succeeded, failed, canceled, timed_out, or infra_failed
(the build could not run for reasons on VibeView’s side — you are not
charged for those). Builds that finished before an endpoint was added never
fire.
There is also a ping event, sent when you test an endpoint from the
dashboard or CLI.
Managing endpoints
- Dashboard: the Webhooks tab on the Builds page — add, test, enable/disable, and delete endpoints. Anyone in your organization can view the list; adding, testing, enabling/disabling, and deleting require the Admin role.
- CLI:
vibeview webhooks add --url <https-url> [--secret <s>],vibeview webhooks list,vibeview webhooks test <id>, andvibeview webhooks delete <id>.
Endpoint URLs must be public HTTPS endpoints — URLs that point or resolve to private or internal addresses are rejected. An organization can have up to 10 endpoints. Endpoints are organization-wide: every endpoint receives every finished build.
Each endpoint has a signing secret. Supply your own when you create the endpoint (16–64 characters) or let VibeView generate one. Either way the secret is shown exactly once, at creation — it can never be viewed again, so store it then. If you lose it, delete the endpoint and create a new one.
The request
Each delivery is a POST with these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-VibeView-Event | build.completed or ping |
X-VibeView-Delivery | The delivery id (whd_...) — use it to deduplicate |
X-VibeView-Signature | sha256=<hex HMAC-SHA256 of the raw body> |
build.completed payload
{
"event": "build.completed",
"delivery_id": "whd_k3n9x2m4ab",
"build": {
"id": "bld1x2y3z4",
"platform": "ios",
"production": true,
"status": "succeeded",
"app": { "id": "a1b2c3d4e5", "name": "My App" },
"error_summary": null,
"created_at": "2026-08-27T12:00:00",
"started_at": "2026-08-27T12:00:41",
"finished_at": "2026-08-27T12:14:34",
"duration_seconds": 833,
"artifact": { "filename": "MyApp.ipa", "size_bytes": 81788000 }
}
}
Field notes:
build.id— the build’s public id, the same onevibeview builds listand the dashboard show.platform—ios,android,tvos, orandroidtv.production—truefor--productionbuilds,falsefor simulator/emulator builds.app— the app the build belongs to, ornullif it isn’t linked to one.error_summary— a short failure description;nullwhen the build succeeded.created_at/started_at/finished_at— UTC timestamps;started_atisnullif the build never started.duration_seconds— whole seconds from start to finish;nullif the build never started.artifact— the produced file’s name and size in bytes, ornullwhen there is no artifact (the build failed before producing one, or the artifact was deleted before the webhook was delivered).
The payload deliberately contains no download link — artifact downloads
require authentication. Use the build id with
vibeview builds download <id> or download from the dashboard.
ping payload
{
"event": "ping",
"delivery_id": "whd_p8q7r6s5tu",
"webhook": { "id": "wh_a1b2c3d4e5", "url": "https://example.com/hooks/vibeview" }
}
Verifying the signature
X-VibeView-Signature is sha256= followed by the hex HMAC-SHA256 of the
raw request body bytes, keyed with your endpoint’s secret. Always verify
against the raw bytes, before any JSON parsing or re-serialization.
Node:
const crypto = require("crypto");
function verify(secret, rawBody, signatureHeader) {
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const received = signatureHeader || "";
return (
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
);
}
Python:
import hashlib
import hmac
def verify(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header or "")
Reject anything that doesn’t verify — the signature is what proves the request came from VibeView.
Delivery and retries
- Respond with any 2xx status to acknowledge a delivery. Respond quickly (VibeView times out after 10 seconds) — do slow work after acknowledging.
- Anything else — a non-2xx status, a timeout, a connection failure — is retried with increasing backoff: up to 4 attempts over roughly 35 minutes. After that the delivery is marked failed and won’t be retried.
- Delivery is at-least-once: your endpoint may occasionally receive the
same event twice (for example around a retry). Use the
X-VibeView-Deliveryid to deduplicate. - The Webhooks tab and
vibeview webhooks listshow each endpoint’s last delivery status and response code.vibeview webhooks test <id>sends apingand reports the result. - Disabling an endpoint pauses its deliveries. Anything already queued is held and delivered when you re-enable the endpoint within 24 hours; after that, held deliveries are marked failed.