BuildBaseBuildBase

Overview

Receive real-time event notifications when users sign up, subscribe, pay, or trigger actions.

BuildBase fires webhook events for every significant action — user signups, subscription changes, payments, credit usage, workspace updates, and more. Verify the signature and handle the event in your backend.

import { parseWebhookEvent } from '@buildbase/sdk';

export async function POST(req) {
  const body = await req.text();

  const event = parseWebhookEvent({
    body,
    signature: req.headers.get('x-buildbase-signature'),
    timestamp: req.headers.get('x-buildbase-timestamp'),
    secret: process.env.WEBHOOK_SECRET,
  });

  if (!event) {
    return Response.json({ error: 'Invalid signature' }, { status: 401 });
  }

  switch (event.event) {
    case 'subscription.upgraded':
      await enablePremiumFeatures(event.data.workspaceId);
      break;
    case 'credit.low_balance':
      await notifyTeam(event.data.workspaceId);
      break;
    case 'workspace.member_added':
      await syncToExternalCRM(event.data);
      break;
  }

  return Response.json({ received: true });
}

Before you start

Create a webhook endpoint in the BuildBase dashboard and copy the signing secret.

Two functions

FunctionWhat it does
verifyWebhookSignature({ body, signature, timestamp, secret })Returns true if the signature is valid
parseWebhookEvent({ body, signature, timestamp, secret })Verifies and parses — returns the event object or null

Use parseWebhookEvent for most cases. Use verifyWebhookSignature if you want to parse the body yourself.

Express / Hono example

import { parseWebhookEvent } from '@buildbase/sdk';

app.post('/webhooks', (req, res) => {
  const event = parseWebhookEvent({
    body: req.body,
    signature: req.headers['x-buildbase-signature'],
    timestamp: req.headers['x-buildbase-timestamp'],
    secret: process.env.WEBHOOK_SECRET,
  });

  if (!event) return res.status(401).json({ error: 'Invalid' });

  // Handle event.event and event.data
  res.json({ received: true });
});

Event categories

CategoryEvents
Useruser.registered, user.logged_in, user.new_device_login, user.email_verified, user.password_changed, user.password_reset_requested, user.profile_updated, user.tag_added, user.tag_removed, user.blocked, user.unblocked
Organizationorganization.member_invited, organization.member_accepted, organization.member_removed
Workspaceworkspace.created, workspace.updated, workspace.deleted, workspace.settings_updated, workspace.features_updated, workspace.member_added, workspace.member_removed, workspace.member_role_changed
Subscriptionsubscription.created, subscription.updated, subscription.upgraded, subscription.canceled, subscription.cancel_scheduled, subscription.resumed, subscription.suspended, subscription.trial_started, subscription.trial_will_end, subscription.trial_expired, subscription.downgraded
Paymentpayment.succeeded, payment.failed, payment.action_required
Quotaquota.limit_exceeded
Creditcredit.purchased, credit.consumed, credit.expired, credit.granted, credit.revoked, credit.low_balance
Workflowworkflow.created, workflow.deleted, workflow.published, workflow.paused, workflow.resumed, workflow.instance_completed, workflow.instance_failed
Audienceaudience.member_created, audience.member_updated, audience.added_to_list, audience.removed_from_list, audience.attribute_changed, audience.tag_added, audience.tag_removed, audience.unsubscribed, audience.resubscribed, audience.email_invalidated, audience.blocked, audience.unblocked, audience.import_completed, audience.list_created, audience.list_updated, audience.list_deleted
Emailemail.domain_added, email.domain_verified, email.sent, email.opened, email.clicked, email.bounced, email.unsubscribed, email.campaign_sent
Pushpush.campaign_sent
Authauth.method_deleted, auth.domain_added, auth.domain_verified, auth.domain_deleted, auth.client_self_registered
Tokentoken.created, token.revoked
Group_versiongroup_version.published
Formform.submitted

84+ event types total. See the SDK README for the full list.

Event payload structure

Every webhook POST body has this shape — three fields, nothing else:

{
  "event": "subscription.upgraded",
  "timestamp": 1719000000,
  "data": {
    "workspaceId": "ws_xyz789",
    "userId": "usr_def456",
    "planId": "plan_starter",
    "planVersionId": "pv_v2",
    "billingInterval": "monthly",
    "previousPlanId": "plan_free"
  }
}

timestamp is Unix seconds, set when the event fires. Each delivery also carries three headers:

HeaderContents
x-buildbase-signaturesha256=<hex> — HMAC-SHA256 of {timestamp}.{body} with your signing secret
x-buildbase-timestampSame Unix timestamp as the body
x-buildbase-eventThe event name (e.g. subscription.upgraded)

The data object varies by event type. Some common patterns:

EventKey data fields
user.registereduserId, email, name
subscription.createdworkspaceId, planId, billingInterval, trialEnd
payment.failedworkspaceId, amount, currency, failureReason
credit.consumedworkspaceId, amount, description, remaining
workspace.member_addedworkspaceId, userId, role

Delivery guarantees

  • At-least-once delivery — events may be delivered more than once. Deliveries carry no unique event ID; retries repeat the same event and timestamp, so deduplicate on a hash of the raw request body.
  • No ordering guarantee — events may arrive out of order. Use event.timestamp if ordering matters.
  • Automatic retries — failed deliveries (non-2xx response) are retried with backoff.
  • Signature expiry — signatures are valid for 5 minutes by default. Reject older payloads to prevent replay attacks.

Next Steps

  • Server SDK — Backend setup and all server methods.
  • Billing — Subscription events and payment handling.