> ## Documentation Index
> Fetch the complete documentation index at: https://docs.riseworks.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Quick Start

> Set up your first Rise webhook in under 10 minutes

This guide will walk you through setting up your first Rise webhook in under 10 minutes. By the end, you'll have a working webhook endpoint that receives real-time notifications from Rise.

## Prerequisites

Before you start, make sure you have:

<CardGroup cols={2}>
  <Card title="Rise Account" icon="user-check">
    A Rise account with a RiseID (get one at <a href="https://app.rise.works" target="_blank">app.rise.works</a>)
  </Card>

  <Card title="Server Setup" icon="server">
    A server that can receive HTTP POST requests
  </Card>

  <Card title="HTTPS Recommended" icon="shield">
    HTTPS recommended for production (HTTP works for development/testing)
  </Card>

  <Card title="API Credentials" icon="key">
    Your Rise API credentials from the dashboard
  </Card>
</CardGroup>

## Step 1: Create your webhook endpoint

First, create an endpoint on your server to receive webhook events. Your endpoint should:

* Accept POST requests
* Return a `200` status code quickly
* Handle the JSON payload Rise sends

<CodeGroup>
  ```javascript Express.js theme={null}
  const express = require('express');
  const app = express();

  app.use(express.json());

  app.post('/rise-webhooks', (req, res) => {
    const event = req.body;

    // Handle the event
    console.log('Received event:', event.event_type);
    console.log('Event ID:', event.id);
    console.log('Event data:', event);

    // Respond quickly with 200
    res.status(200).json({ received: true });
  });

  app.listen(3000, () => {
    console.log('Webhook server running on port 3000');
  });
  ```

  ```python Flask theme={null}
  from flask import Flask, request, jsonify

  app = Flask(__name__)

  @app.route('/rise-webhooks', methods=['POST'])
  def handle_webhook():
      event = request.get_json()

      // Handle the event
      print(f"Received event: {event['event_type']}")
      print(f"Event ID: {event['id']}")
      print(f"Event data: {event}")

      // Respond quickly with 200
      return jsonify({'received': True}), 200

  if __name__ == '__main__':
      app.run(port=3000)
  ```
</CodeGroup>

## Step 2: Register your webhook in the Rise app

Now you'll register your webhook endpoint using the Rise app's user interface:

<Steps>
  <Step title="Navigate to webhooks">
    Open the Rise app and navigate to the Developer section, then click on **"Webhooks"** in the sidebar menu.
  </Step>

  <Step title="Create new webhook">
    Click **"Create Webhook"** to add a new webhook endpoint.
  </Step>

  <Step title="Configure webhook details">
    Fill in the webhook details:

    * **Endpoint URL**: `https://your-server.com/rise-webhooks`
    * **Secret**: Enter a secure secret string for signature verification (e.g., `whsec_your_secure_random_string`)
    * **Description**: Optional description for your webhook
    * **Event Types**: Select the events you want to receive
    * **Team**: Choose company-level or specific team (optional)
  </Step>

  <Step title="Select event types">
    For this tutorial, select:

    * `payment.sent` - When a payment is successfully sent
    * `payment.group.created` - When a new payment group is created
  </Step>

  <Step title="Save configuration">
    Click **"Create Webhook"** to save your configuration. Remember the secret you entered - you'll need it for signature verification.
  </Step>
</Steps>

<Warning>
  Keep your webhook secret secure! You'll need it for signature verification, and you can view it later in the webhook details page.
</Warning>

## Step 3: Test your webhook

Rise provides a built-in testing feature directly in the app:

<Steps>
  <Step title="Access test feature">
    Go to your webhook details page in the Rise app and click the **"Test Webhook"** button.
  </Step>

  <Step title="Select event type">
    Choose an event type to test (e.g., `payment.sent`).
  </Step>

  <Step title="Send test event">
    Click **"Send Test Event"** to deliver a test payload to your endpoint.
  </Step>

  <Step title="Verify receipt">
    Check your server logs to confirm you received the test event.
  </Step>
</Steps>

You should see output similar to this in your server logs:

```bash theme={null}
Received event: payment.sent
Event ID: we-IQGikif4ja4A
Event data: { id: 'we-IQGikif4ja4A', object: 'event', created: 1751590453, ... }
```

## Step 4: Add signature verification with Rise SDK

For security, verify that webhook requests actually come from Rise using the official Rise SDK:

<CodeGroup>
  ```javascript Node.js with Rise SDK theme={null}
  const express = require('express');
  const { WebhookValidator } = require('@riseworks/riseworks-sdk');

  const app = express();

  // Use raw body for signature verification
  app.use('/rise-webhooks', express.raw({ type: 'application/json' }));

  app.post('/rise-webhooks', (req, res) => {
    const signature = req.headers['x-rise-signature'];
    const secret = process.env.RISE_WEBHOOK_SECRET;

    try {
      // Validate webhook using Rise SDK WebhookValidator
      const riseValidator = new WebhookValidator({
        secret: secret,
        tolerance: 720 // 12 minutes tolerance
      });
      
      // Option 1: validateEvent (throws on failure)
      let event = riseValidator.validateEvent(req.body, signature);
      
      // Option 2: validateEventSafe (returns result object)
      const result = riseValidator.validateEventSafe(req.body, signature);
      if (!result.isValid) {
        console.error('Validation failed:', result.error);
        return res.status(400).json({ error: result.error });
      }
      event = result.event;

      // Signature is valid, process the event
      handleEvent(event);

      res.status(200).json({ 
        received: true,
        timestamp: new Date().toISOString(),
        event_type: event.event_type,
        event_version: event.event_version,
        idempotency_key: event.idempotency_key
      });
    } catch (error) {
      console.error('Webhook verification failed:', error.message);
      res.status(400).json({ 
        error: 'Webhook processing failed',
        message: error.message,
        timestamp: new Date().toISOString()
      });
    }
  });
  ```

  ```typescript TypeScript with Rise SDK theme={null}
  import express from 'express';
  import { WebhookValidator, type RiseWebhookEvent } from '@riseworks/riseworks-sdk';

  const app = express();

  // Use raw body for signature verification
  app.use('/rise-webhooks', express.raw({ type: 'application/json' }));

  app.post('/rise-webhooks', (req, res) => {
    const signature = req.headers['x-rise-signature'] as string;
    const secret = process.env.RISE_WEBHOOK_SECRET!;

    try {
      // Validate webhook using Rise SDK WebhookValidator with full type safety
      const riseValidator = new WebhookValidator({
        secret: secret,
        tolerance: 720 // 12 minutes tolerance
      });
      
      // Option 1: validateEvent (throws on failure)
      let event = riseValidator.validateEvent(req.body, signature);
      
      // Option 2: validateEventSafe (returns result object)
      const result = riseValidator.validateEventSafe(req.body, signature);
      if (!result.isValid) {
        console.error('Validation failed:', result.error);
        return res.status(400).json({ error: result.error });
      }
      event = result.event;

      // Signature is valid, process the event with full TypeScript support
      handleEvent(event);

      res.status(200).json({ 
        received: true,
        timestamp: new Date().toISOString(),
        event_type: event.event_type,
        event_version: event.event_version,
        idempotency_key: event.idempotency_key
      });
    } catch (error) {
      console.error('Webhook verification failed:', error instanceof Error ? error.message : String(error));
      res.status(400).json({ 
        error: 'Webhook processing failed',
        message: error instanceof Error ? error.message : String(error),
        timestamp: new Date().toISOString()
      });
    }
  });
  ```

  ```python Python (Manual implementation) theme={null}
  import hmac
  import hashlib
  import time
  import json

  def verify_rise_signature(payload, signature, secret):
      # Parse the signature header
      elements = signature.split(',')
      timestamp = int(elements[0].split('=')[1])
      received_hash = elements[1].split('=')[1]

      # Create the signed payload
      signed_payload = f"{timestamp}.{json.dumps(payload, separators=(',', ':'))}"

      # Generate expected signature
      expected_hash = hmac.new(
          secret.encode('utf-8'),
          signed_payload.encode('utf-8'),
          hashlib.sha256
      ).hexdigest()

      # Verify timestamp (prevent replay attacks)
      current_time = int(time.time())
      tolerance = 600  # 10 minutes

      if current_time - timestamp > tolerance:
          raise ValueError('Timestamp too old')

      # Verify signature
      if not hmac.compare_digest(received_hash, expected_hash):
          raise ValueError('Invalid signature')

      return True

  @app.route('/rise-webhooks', methods=['POST'])
  def handle_webhook():
      signature = request.headers.get('X-Rise-Signature')
      secret = os.environ.get('RISE_WEBHOOK_SECRET')

      try:
          verify_rise_signature(request.get_json(), signature, secret)

          # Signature is valid, process the event
          event = request.get_json()
          handle_webhook_event(event)

          return jsonify({'received': True}), 200
      except ValueError as e:
          print(f'Webhook verification failed: {e}')
          return 'Invalid signature', 400
  ```
</CodeGroup>

<Note>
  Use the exact same secret string you entered when creating the webhook. You can view it in your webhook details page in the Rise app if needed.
</Note>

### Installing the Rise SDK

To use the Rise SDK for webhook validation, install it in your project:

```bash theme={null}
# Using npm
npm install @riseworks/riseworks-sdk

# Using yarn
yarn add @riseworks/riseworks-sdk

# Using pnpm
pnpm add @riseworks/riseworks-sdk
```

The SDK provides:

* **Class-based webhook validation** using `WebhookValidator`
* **Type-safe webhook validation** with full TypeScript support
* **Automatic signature verification** with configurable tolerance
* **Proper error handling** with detailed error messages
* **Support for multiple input types** (string, Buffer, object)

## Step 5: Handle events in your application

Now you can add business logic to handle specific events with full type safety:

```typescript theme={null}
import type { 
  RiseWebhookEvent, 
  PaymentSentV1, 
  PaymentGroupCreatedV1, 
  DepositReceivedV1 
} from '@riseworks/riseworks-sdk';

function handleEvent(event: RiseWebhookEvent) {
  switch (event.event_type) {
    case 'payment.sent':
      // Send confirmation email with full type safety
      sendPaymentConfirmation(event as PaymentSentV1);
      break;

    case 'payment.group.created':
      // Update order status with full type safety
      updateOrderStatus(event as PaymentGroupCreatedV1);
      break;

    case 'deposit.received':
      // Trigger fulfillment with full type safety
      triggerFulfillment(event as DepositReceivedV1);
      break;

    default:
      console.log('Unhandled event type:', event.event_type);
  }
}

async function sendPaymentConfirmation(event: PaymentSentV1) {
  // Your email logic here with full type safety
  console.log(`Sending confirmation for payment ${event.payment.nanoid}`);
  console.log(`Amount: ${event.payment.amount_cents} cents`);
  console.log(`Recipients: ${event.payment.recipients.length}`);
}

async function updateOrderStatus(event: PaymentGroupCreatedV1) {
  // Your database update logic here with full type safety
  console.log(`Updating payment group ${event.payment_group.nanoid} to processing`);
  console.log(`Payments count: ${event.payment_group.payments?.length || 0}`);
}

async function triggerFulfillment(event: DepositReceivedV1) {
  // Your fulfillment logic here with full type safety
  console.log(`Triggering fulfillment for deposit ${event.deposit.nanoid}`);
  console.log(`Amount: ${event.deposit.destination_amount_cents} cents`);
}
```

```javascript theme={null}
// JavaScript version (without TypeScript types)
function handleEvent(event) {
  switch (event.event_type) {
    case 'payment.sent':
      // Send confirmation email
      sendPaymentConfirmation(event);
      break;

    case 'payment.group.created':
      // Update order status
      updateOrderStatus(event);
      break;

    case 'deposit.received':
      // Trigger fulfillment
      triggerFulfillment(event);
      break;

    default:
      console.log('Unhandled event type:', event.event_type);
  }
}

async function sendPaymentConfirmation(event) {
  // Your email logic here
  console.log(`Sending confirmation for payment ${event.payment.nanoid}`);
}

async function updateOrderStatus(event) {
  // Your database update logic here
  console.log(`Updating payment group ${event.payment_group.nanoid} to processing`);
}
```

## Common event types to start with

Here are the most commonly used Rise webhook events:

<CardGroup cols={2}>
  <Card title="payment.sent" icon="paper-plane">
    A payment has been successfully sent to the recipient
  </Card>

  <Card title="payment.group.created" icon="layer-group">
    A new payment group has been created with multiple payments
  </Card>

  <Card title="deposit.received" icon="coins">
    A deposit has been received and confirmed in your account
  </Card>

  <Card title="invite.accepted" icon="user-plus">
    A team member has accepted an invitation to join
  </Card>

  <Card title="invite.rejected" icon="user-xmark">
    An invitee has declined an invitation
  </Card>

  <Card title="invite.expired" icon="clock">
    An invitation lapsed after 90 days without a response
  </Card>
</CardGroup>

## Testing checklist

Before going live, verify that your webhook:

<AccordionGroup>
  <Accordion title="Functionality checks" icon="circle-check">
    * Responds with a `200` status code within 10 seconds
    * Handles all the event types you've subscribed to
    * Processes events idempotently (handles duplicates gracefully)
    * Logs events for debugging
  </Accordion>

  <Accordion title="Security checks" icon="shield">
    * Verifies webhook signatures correctly (use Rise SDK for best results)
    * Rejects invalid signatures properly
    * Validates timestamps to prevent replay attacks
    * Uses HTTPS for production (recommended)
    * Handles Buffer payloads correctly for signature verification
  </Accordion>

  <Accordion title="Error handling" icon="triangle-exclamation">
    * Handles unknown event types gracefully
    * Doesn't crash on malformed payloads
    * Has proper error logging and monitoring
  </Accordion>
</AccordionGroup>

## Monitor your webhook deliveries

Once your webhook is active, you can track its performance in the Rise app:

1. **Navigate to your webhook details page** in the Rise app
2. **Click on "Delivery History"** tab to see all webhook deliveries
3. **Monitor delivery success rates** and response times
4. **Review failed deliveries** to identify and fix issues

The delivery history shows:

* **Successful deliveries** - Events delivered with 200 response
* **Failed deliveries** - Events that couldn't be delivered
* **Pending retries** - Events waiting to be retried
* **Response details** - HTTP status codes and response times

## What's next?

Congratulations! You now have a working webhook endpoint. Here's what to explore next:

<CardGroup cols={2}>
  <Card title="Explore Event Types" icon="list" href="/webhooks/implementation/event-types">
    See the complete list of available events and their payloads
  </Card>

  <Card title="Security Best Practices" icon="shield" href="/webhooks/getting-started/security">
    Learn about advanced security features and best practices
  </Card>

  <Card title="Webhook Management" icon="gear" href="/webhooks/implementation/management">
    Learn how to manage multiple webhooks and team configurations
  </Card>

  <Card title="Testing Tools" icon="flask" href="/webhooks/implementation/testing">
    Testing your webhooks locally and troubleshooting common issues
  </Card>
</CardGroup>

<Tip>
  **Pro tip**: Start with just one or two event types to keep things simple, then add more as your integration grows. It's easier to debug and maintain fewer, well-configured webhooks.
</Tip>

<Tip>
  **SDK recommendation**: Use the Rise SDK for webhook validation to get automatic type safety, proper error handling, and protection against common security issues. The SDK handles Buffer payloads, signature verification, and timestamp validation automatically.
</Tip>
