Vibeview
Pricing

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>, and vibeview 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:

HeaderValue
Content-Typeapplication/json
X-VibeView-Eventbuild.completed or ping
X-VibeView-DeliveryThe delivery id (whd_...) — use it to deduplicate
X-VibeView-Signaturesha256=<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 one vibeview builds list and the dashboard show.
  • platformios, android, tvos, or androidtv.
  • productiontrue for --production builds, false for simulator/emulator builds.
  • app — the app the build belongs to, or null if it isn’t linked to one.
  • error_summary — a short failure description; null when the build succeeded.
  • created_at / started_at / finished_at — UTC timestamps; started_at is null if the build never started.
  • duration_seconds — whole seconds from start to finish; null if the build never started.
  • artifact — the produced file’s name and size in bytes, or null when 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-Delivery id to deduplicate.
  • The Webhooks tab and vibeview webhooks list show each endpoint’s last delivery status and response code. vibeview webhooks test <id> sends a ping and 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.