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

# Fund Usage After a Purchase

> Discover how to grant meter credits to users after a purchase using the Meter Credits benefit or Webhooks.

export const RubaSocials = () => {
  if (typeof document === "undefined") {
    return null;
  }
  const inject = () => {
    const footer = document.getElementById("footer");
    if (!footer || document.getElementById("ruba-socials")) {
      return false;
    }
    const isDark = document.querySelector("html.dark") || document.querySelector('html[class*="dark"]');
    const lineColor = isDark ? "#383838" : "#e8e8ed";
    const iconColor = isDark ? "#8e8e93" : "#86868b";
    const markColor = isDark ? "#ffffff" : "#08080c";
    const hoverColor = "#0071e3";
    const div = document.createElement("div");
    div.id = "ruba-socials";
    div.style.cssText = `display:flex;align-items:center;gap:20px;padding-top:20px;margin-top:20px;border-top:1px solid ${lineColor};width:100%`;
    const logo = `<a href="https://getruba.com" style="display:inline-flex;height:24px;align-items:center;gap:8px;text-decoration:none;color:${markColor};line-height:1;"><svg width="24" height="24" viewBox="0 0 120 120" aria-hidden="true" style="display:block;flex:none;"><g transform="translate(2.5 0)"><path d="M19 25h59L65.43 47H19a4 4 0 0 1-4-4V29a4 4 0 0 1 4-4Z" fill="${markColor}"/><path d="M19 73h31.57L38 95H19a4 4 0 0 1-4-4V77a4 4 0 0 1 4-4Z" fill="${markColor}"/><path d="M85 25h25L70 95H45Z" fill="#007AFF"/></g></svg><span style="display:inline-flex;height:24px;align-items:center;font-weight:600;font-size:15px;color:${markColor};letter-spacing:-0.01em;line-height:1;">Ruba</span></a>`;
    const spacer = `<div style="flex:1"></div>`;
    const ig = `<a href="https://instagram.com/getruba" target="_blank" rel="noopener" aria-label="Instagram" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"/></svg></a>`;
    const li = `<a href="https://linkedin.com/company/getruba" target="_blank" rel="noopener" aria-label="LinkedIn" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/></svg></a>`;
    const x = `<a href="https://x.com/getruba" target="_blank" rel="noopener" aria-label="X" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg></a>`;
    const github = `<a href="https://github.com/rubadot" target="_blank" rel="noopener" aria-label="GitHub" style="display:flex;align-items:center;color:${iconColor};transition:color 0.15s ease;" onmouseover="this.style.color='${hoverColor}'" onmouseout="this.style.color='${iconColor}'"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .7a11.5 11.5 0 0 0-3.64 22.4c.58.1.79-.25.79-.56v-2.24c-3.22.7-3.9-1.37-3.9-1.37-.53-1.34-1.29-1.7-1.29-1.7-1.05-.72.08-.71.08-.71 1.16.08 1.78 1.2 1.78 1.2 1.04 1.77 2.72 1.26 3.38.96.1-.75.4-1.26.74-1.55-2.57-.3-5.27-1.29-5.27-5.69 0-1.26.45-2.29 1.19-3.09-.12-.29-.52-1.47.11-3.05 0 0 .97-.31 3.16 1.18A10.96 10.96 0 0 1 12 6.09c.98 0 1.94.13 2.86.39 2.2-1.49 3.16-1.18 3.16-1.18.63 1.58.23 2.76.11 3.05.74.8 1.19 1.83 1.19 3.09 0 4.42-2.7 5.39-5.28 5.68.42.36.79 1.06.79 2.14v3.28c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .7Z"/></svg></a>`;
    div.innerHTML = logo + spacer + ig + li + x + github;
    footer.appendChild(div);
    return true;
  };
  if (!document.documentElement.dataset.rubaSocialsObserver) {
    document.documentElement.dataset.rubaSocialsObserver = "true";
    const observer = new MutationObserver(() => {
      if (!document.getElementById("ruba-socials")) {
        inject();
      }
    });
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }
  setTimeout(() => {
    if (!inject()) {
      const interval = setInterval(() => {
        if (inject()) {
          clearInterval(interval);
        }
      }, 500);
      setTimeout(() => clearInterval(interval), 10000);
    }
  }, 300);
  return null;
};

<link rel="stylesheet" href="/style.css" />

<RubaSocials />

## The operating model

When building usage-based billing systems, you may want to give new customers an initial credit balance (e.g., 10 free units) the moment they purchase a product. This comes in handy for:

* Offering free trials with credits
* Onboarding bonuses
* Promotional credits for new signups or purchases

There are two ways to award initial meter credits in Ruba:

1. **Meter Credits Benefit (Recommended)** - Automatically grant credits when a customer purchases a product
2. **Webhook + Events API** - Programmatically grant credits for advanced use cases

### Which Method Should I Use?

| Feature                | Meter Credits Benefit         | Webhook + Events API                      |
| ---------------------- | ----------------------------- | ----------------------------------------- |
| **Setup Complexity**   | ✅ Simple - No code required   | ⚙️ Advanced - Requires coding             |
| **Automatic Credits**  | ✅ Yes                         | ❌ No - Manual implementation              |
| **Custom Logic**       | ❌ No                          | ✅ Yes - Full control                      |
| **One-time Products**  | ✅ Credits granted at purchase | ✅ Credits granted at purchase             |
| **Recurring Products** | ❌ No                          | ✅ Can be credited every cycle (with code) |
| **Best For**           | Most use cases                | Complex crediting rules                   |

<Tip>
  **Start with Method 1** (Meter Credits Benefit) unless you need custom logic or complex crediting rules. It's simpler and requires no code.
</Tip>

***

## Method 1: Using Meter Credits Benefit (Recommended)

The simplest route to award initial credits is to lean on the built-in **Meter Credits** benefit. It grants credits (once) to customers automatically when they purchase a product.

### Step 1: Create a Meter with Sum Aggregation

First, [create a meter](/features/usage-based-billing/meters) to track your customers' usage.

### Step 2: Create a Meter Credits Benefit

Now [create a Meter Credits benefit](/features/benefits/credits) that grants the initial credits.

### Step 3: Create a Product with the Benefit

Create a product and attach the Meter Credits benefit to it.

<Steps>
  <Step title="Create a product">
    Open [**Products → New Product**](https://getruba.com/to/dashboard/products/new) in the dashboard.
  </Step>

  <Step title="Configure the product">
    * Set a name and description
    * Choose your product type (Recurring)
    * Set the price (can be \$0 for free signup, or any amount)
    * Click "Add Additional Price"
      * Attach your meter to the product and set the per unit cost
  </Step>

  <Step title="Add the Meter Credits benefit">
    Scroll to the **Automated Benefits** section and toggle ON the Meter Credits benefit you created.
  </Step>

  <Step title="Save the product">
    Click **Create Product** to save.
  </Step>
</Steps>

That's it! Now when a customer purchases this product, they automatically get the specified amount of meter credits.

***

## Method 2: Using Webhooks + Events API (Advanced)

For trickier scenarios where you need custom logic or want to grant credits outside the purchase flow, lean on webhooks and the Events API.

### When to Reach for This Method

* You need custom logic to determine credit amounts
* You want to grant credits based on external events
* You need to grant credits to existing customers programmatically
* You want to implement complex crediting rules

### System flow

This approach boils down to:

1. Creating a product with a meter attached (using sum aggregation)
2. Setting up webhooks to listen for purchases
3. When a purchase is made, ingesting a negative event value to grant credits

<Info>
  **Why negative values?**

  When you ingest an event with a negative value (e.g., `-10`) into a meter using **Sum** aggregation, it effectively grants the customer 10 units of credit, lowering their usage meter balance.
</Info>

### Before you build

* A Ruba account with an organization
* A meter created with **Sum** aggregation
* A product with the meter attached
* Webhooks enabled
* A Ruba access token for API calls

### Step 1: Create a Meter

First, create a meter to track your customers' usage.

<Steps>
  <Step title="Navigate to Meters">
    Open [**Products → Meters**](https://getruba.com/to/dashboard/products/meters) in the dashboard.
  </Step>

  <Step title="Create a new meter">
    Click **Create Meter** and configure:

    * **Name**: Give your meter a descriptive name (e.g., "API Calls" or "Storage Usage")
    * **Filter**: Add filters to match your usage events (e.g., name equals "api\_usage")
    * **Aggregation**: Select **Sum** and enter the property to sum (e.g., `units`)

    <Warning>
      The meter **must use Sum aggregation** for this approach to work.
    </Warning>
  </Step>

  <Step title="Save the meter">
    Save your meter and note the meter name, you'll need this when ingesting events.
  </Step>
</Steps>

Learn more about [creating meters](/features/usage-based-billing/meters).

### Step 2. Create a Product

Follow the same steps as Method 1 to create your product (you don't need to create or attach the Meter Credits benefit for this approach).

### Step 3: Set Up Webhooks

Configure webhooks to get notified when users make purchases.

<Steps>
  <Step title="Add webhook endpoint">
    Follow our [Configure Webhooks](/integrate/webhooks/endpoints) guide to create a new webhook endpoint.
  </Step>

  <Step title="Subscribe to order.paid event">
    When configuring your webhook, make sure to subscribe to the `order.paid` event. This event fires when a customer successfully completes a purchase.
  </Step>

  <Step title="Save webhook secret">
    Store your webhook secret securely in your environment variables.

    ```bash Terminal theme={"system"}
    RUBA_ACCESS_TOKEN="ruba_pat_..."
    RUBA_WEBHOOK_SECRET="whsec_..."
    PRODUCT_ID="prod_..." # The product ID to grant credits for
    ```
  </Step>
</Steps>

### Step 4: Implement the Webhook Handler

Build a webhook handler that listens for `order.paid` events and grants initial credits by ingesting negative event values.

#### Next.js Example

```typescript icon="square-js" title="app/api/webhook/ruba/route.ts" theme={"system"}
import { Ruba } from '@getruba/sdk'
import { validateEvent } from '@getruba/sdk/webhooks'

const ruba = new Ruba({
  accessToken: process.env.RUBA_ACCESS_TOKEN,
  server: 'sandbox',
})

export async function POST(request: Request) {
  const body = await request.text()
  const event = validateEvent(
    body,
    Object.fromEntries(request.headers),
    process.env.RUBA_WEBHOOK_SECRET!,
  )

  if (event.type !== 'order.paid') return new Response(null, { status: 204 })

  const order = event.data as { product_id: string; customer_id: string }
  if (order.product_id !== process.env.PRODUCT_ID) {
    return new Response(null, { status: 204 })
  }

  const { error } = await ruba.POST('/v1/events/ingest', {
    body: {
      events: [{
        name: 'meter-name',
        customer_id: order.customer_id,
        metadata: { units: -10, reason: 'initial_signup_bonus' },
      }],
    },
  })

  if (error) return new Response('Unable to grant credits', { status: 500 })
  return new Response(null, { status: 204 })
}
```

### Step 5: Test Your Integration

Verify that credits are properly granted when a customer makes a purchase.

<Steps>
  <Step title="Use the sandbox environment">
    Test your integration in Ruba's [sandbox environment](/integrate/sandbox) to avoid disturbing production data.
  </Step>

  <Step title="Make a test purchase">
    Create a checkout session and complete a test purchase.
  </Step>

  <Step title="Verify webhook receipt">
    Check your server logs to confirm the `order.paid` webhook was received.
  </Step>

  <Step title="Check customer balance">
    Use the [Customer Meters API](/api-reference/customer-meters/list) to confirm the credits were applied:

    ```typescript theme={"system"}
    const { data } = await ruba.GET('/v1/customer-meters/', {
      params: { query: { customer_id: 'cus_...' } },
    })

    console.log(data?.items[0]?.balance)
    ```
  </Step>
</Steps>

### Important Considerations for the Webhook Method

#### Negative Balance vs. Positive Usage

When using negative events to grant credits:

* A **negative balance** (e.g., `-10`) means the customer has 10 credits available
* As the customer uses your service, positive events reduce this negative balance
* When the balance reaches `0`, the customer has used all their credits
* Positive balances indicate usage beyond the granted credits

#### Example Flow

```typescript theme={"system"}
// Initial state: Customer has 0 balance
// You grant 10 credits: balance = -10

// Customer uses 3 units: balance = -7 (7 credits remaining)
// Customer uses 5 more units: balance = -2 (2 credits remaining)
// Customer uses 3 more units: balance = 1 (1 unit of overage, if metered pricing is enabled)
```

#### Preventing Double Credits

To avoid granting credits multiple times, consider:

1. **Check order status** - Only grant credits for new orders
2. **Use idempotency** - Track which orders you've already processed
3. **Database records** - Store a record of credit grants

```typescript theme={"system"}
// Example with idempotency check
const hasGrantedCredits = await checkIfAlreadyGranted(order.id);

if (!hasGrantedCredits) {
  await ruba.POST('/v1/events/ingest', {
    body: {
      events: [{
        name: 'meter-name',
        customer_id: order.data.customer_id,
        metadata: { units: -10 },
      }],
    },
  })

  await recordCreditGrant(order.id)
}
```
