Skip to content

Webhooks (Inbound)

Pulsabase exposes a public inbound webhook endpoint for each edge function. Use it to receive events from external services — Stripe payment confirmations, GitHub push events, Twilio SMS replies, and so on.

POST https://api.yourproject.pulsabase.io/webhooks/{project_id}/{function-slug}
  • No X-Api-Key required — this endpoint is intentionally public so external services can call it
  • The request body (the external event payload) is passed as the function’s input
  • The full HTTP request headers are also available via {{input.headers}}

Create an edge function that handles the incoming payload. Name it something descriptive (e.g., stripe-payment-handler).

Function: stripe-payment-handler
Step 1: verify_signature → [Transform]
// Validate Stripe webhook signature from headers (see below)
{{input.headers['stripe-signature']}} !== null
Step 2: get_order → [Database]
SELECT * FROM orders WHERE stripe_payment_id = {{input.body.data.object.id}}
Step 3: update_order → [Database]
UPDATE orders SET status = 'paid' WHERE id = {{steps.get_order.body.id}}
Step 4: notify_user → [HTTP]
POST https://push.example.com/notify
Body: { "userId": "{{steps.get_order.body.user_id}}", "message": "Payment confirmed!" }

Copy the webhook URL from the Dashboard → Edge Functions → Webhooks page and paste it into your external service’s webhook configuration (e.g., Stripe Dashboard → Webhooks → Add Endpoint).

Step 3 — Verify the Signature (Security)

Section titled “Step 3 — Verify the Signature (Security)”

Always verify the webhook signature from the external service. Use a before hook or the first step of your function:

Step 1: verify → [Transform]
Compute HMAC-SHA256 of request body using your Stripe signing secret.
Compare with {{input.headers['stripe-signature']}}.
If mismatch → return { "error": "INVALID_SIGNATURE", "status": 401 }

Without signature verification, anyone can call your webhook URL with a fake payload. Always verify the signature before processing the event.

Inside a webhook-triggered function, the following variables are available:

VariableDescription
{{input.body}}The full JSON body of the incoming request
{{input.body.<field>}}A specific field from the body
{{input.headers}}All HTTP request headers
{{input.headers['x-signature']}}A specific header (case-insensitive)
{{input.method}}The HTTP method (POST, GET, etc.)
{{input.query.<param>}}Query string parameters
ServiceEventUse Case
Stripepayment_intent.succeededMark order as paid
Stripecustomer.subscription.deletedDowngrade user plan
GitHubpushTrigger a deployment
TwilioIncomingMessageProcess SMS reply
SendGridbouncedMark email as undeliverable
Lemon Squeezyorder_createdProvision user account

Your function’s final step output is returned as the HTTP response to the external service. Most services (Stripe, GitHub) expect a 200 OK response quickly. If your function takes more than a few seconds, consider running heavy processing asynchronously:

// Acknowledge the webhook immediately — process async
return { "received": true }; // 200 OK to external service

Then dispatch the actual work via an after-hook or a separate async invocation.