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

# How to Grant Meter Credits After Purchase

> Drop a starting credit balance into a customer's meter the moment they buy — with a no-code benefit, or a webhook for full control.

A prepaid balance is one of the friendliest ways to onboard a usage-based product: the customer pays (or signs up for free), and a pool of credits is waiting for them before they make their first API call. Macropay gives you two ways to seed that balance — pick based on how much control you need over the crediting logic.

<Info>
  Macropay is the **merchant of record** on every one of these purchases. We collect and remit sales tax and VAT worldwide, appear as the seller on the customer's statement, and absorb PCI and chargeback handling — so the only thing you wire up here is the credit grant itself.
</Info>

## Two ways to grant credits

|                              | Meter Credits benefit             | Webhook + Events API             |
| ---------------------------- | --------------------------------- | -------------------------------- |
| **Effort**                   | No code — toggle in the dashboard | Write and host a webhook handler |
| **When it fires**            | Automatically at purchase         | Whenever your code decides       |
| **Custom amounts / rules**   | Fixed per product                 | Anything you can compute         |
| **One-time products**        | Granted at purchase               | Granted at purchase              |
| **Recurring products**       | One grant on first purchase       | Re-grant every cycle if you want |
| **Reach existing customers** | Purchase only                     | Backfill any customer, any time  |

<Tip>
  Reach for the **Meter Credits benefit** first. Only step up to the webhook approach when you need amounts that vary per customer, grants triggered by something other than a purchase, or per-cycle top-ups.
</Tip>

These are billing primitives, not just sign-up perks. If you're metering an **AI product or agent**, the same prepaid balance covers token usage billed through the [AI proxy](/guides/ai-billing) — drop in a block of credits at purchase, then let real consumption draw it down.

***

## Option A — the Meter Credits benefit (no code)

A [Meter Credits benefit](/features/benefits/credits) attaches to a product and grants a fixed number of credits once, automatically, the first time someone buys. No handler, no signature verification, no idempotency bookkeeping.

<Steps>
  <Step title="Create a meter">
    Set up a [meter](/features/usage-based-billing/meters) with **Sum** aggregation to track the units your product consumes (requests, tokens, GB-hours — whatever you bill on).
  </Step>

  <Step title="Create the Meter Credits benefit">
    Add a [Meter Credits benefit](/features/benefits/credits) and set the number of credits to hand out — say, 500 units for a launch promo.
  </Step>

  <Step title="Build the product and attach it">
    Under **Products → Catalogue → New Product**, give it a name, choose **Recurring** (or one-time), and set the price — `$0` works for a free starter tier, or any amount you like. Click **Add Additional Price** to attach your meter and set the per-unit overage cost. Then scroll to **Automated Benefits** and toggle on your Meter Credits benefit.
  </Step>

  <Step title="Save">
    Click **Create Product**. Every new purchase now lands with its credit balance pre-loaded.
  </Step>
</Steps>

***

## Option B — webhooks + the Events API (full control)

When the grant logic lives in your code — variable amounts, referral bonuses, monthly top-ups, or backfilling customers who already signed up — listen for the purchase event and write the credit yourself.

### How crediting works under the hood

A meter on **Sum** aggregation simply adds up the values of the events you send it. Usage events carry positive values; a **credit is just a negative event**. Ingest `-500` and the customer's balance drops to `-500`, which means "500 units in the bank."

```text theme={null}
grant 500 credits   →  balance = -500   (500 available)
customer uses 120   →  balance = -380   (380 available)
customer uses 380   →  balance =    0   (credits exhausted)
customer uses 40    →  balance =   40   (40 units of metered overage)
```

<Info>
  Because the balance is a running sum, a negative number is good — it's stored credit. The balance crosses zero into positive territory only once consumption outruns the granted credits, at which point your metered price bills the overage.
</Info>

### What you'll need

* A meter on **Sum** aggregation, with a product attached
* A [webhook endpoint](/integrate/webhooks/endpoints) subscribed to `order.paid`
* An organization access token for the Events API

<Steps>
  <Step title="Create the meter">
    In the dashboard go to **Products → Meters → Create Meter**. Name it (e.g. `image_generations`), add a filter that matches your usage events (such as name equals `image_generation`), and choose **Sum** over the property you bill on (e.g. `units`). Note the meter name — you'll reference it when ingesting.

    <Warning>
      The meter must use **Sum** aggregation. Count or other aggregations can't represent a drawdown balance.
    </Warning>
  </Step>

  <Step title="Create the product">
    Build the product as in Option A, but skip the Meter Credits benefit — your handler does the granting instead.
  </Step>

  <Step title="Wire up the webhook">
    Follow the [webhook endpoints guide](/integrate/webhooks/endpoints), subscribe to `order.paid` (fired on every completed purchase), and store the secrets:

    ```bash Terminal theme={null}
    MACROPAY_ACCESS_TOKEN="macropay_pat_..."
    MACROPAY_WEBHOOK_SECRET="whsec_..."
    PRODUCT_ID="prod_..."   # product that should trigger a grant
    ```
  </Step>
</Steps>

### The handler

Verify the signature, confirm it's the right product, then ingest one negative event. Macropay signs webhooks with the Standard Webhooks scheme, so any compliant library validates them.

```typescript icon="square-js" title="app/api/webhook/macropay/route.ts" theme={null}
import { NextRequest, NextResponse } from "next/server";
import { Macropay } from "@macropayments/sdk";
import { Webhook } from "standardwebhooks";

const macropay = new Macropay({ accessToken: process.env.MACROPAY_ACCESS_TOKEN! });
const wh = new Webhook(process.env.MACROPAY_WEBHOOK_SECRET!);

export async function POST(req: NextRequest) {
  const body = await req.text();
  const headers = {
    "webhook-id": req.headers.get("webhook-id")!,
    "webhook-timestamp": req.headers.get("webhook-timestamp")!,
    "webhook-signature": req.headers.get("webhook-signature")!,
  };

  let event: any;
  try {
    event = wh.verify(body, headers);
  } catch {
    return NextResponse.json({ error: "invalid signature" }, { status: 401 });
  }

  if (event.type === "order.paid") {
    const order = event.data;
    if (order.product_id === process.env.PRODUCT_ID) {
      // A negative value on a Sum meter = stored credit.
      await macropay.events.ingest({
        events: [
          {
            name: "image_generations",
            customerId: order.customer_id,
            metadata: {
              units: -500, // grant 500 credits
              reason: "launch_promo",
            },
          },
        ],
      });
      console.log(`Granted 500 credits to ${order.customer_id}`);
    }
  }

  return NextResponse.json({ received: true });
}
```

### Don't double-grant

`order.paid` can be redelivered, and webhook handlers should be idempotent anyway. Key off the order ID and record each grant before you return a success response.

```typescript theme={null}
const alreadyGranted = await checkIfAlreadyGranted(order.id);

if (!alreadyGranted) {
  await macropay.events.ingest({
    events: [{
      name: "image_generations",
      customerId: order.customer_id,
      metadata: { units: -500, reason: "launch_promo" },
    }],
  });
  await recordCreditGrant(order.id);
}
```

### Confirm it worked

<Steps>
  <Step title="Run it in the sandbox">
    Exercise the whole flow in the [sandbox](/integrate/sandbox) so no real money or production data is touched.
  </Step>

  <Step title="Make a test purchase">
    Open a checkout session and complete it as a customer would.
  </Step>

  <Step title="Confirm delivery">
    Check your logs for the `order.paid` event reaching your handler.
  </Step>

  <Step title="Read the balance back">
    Pull the customer's meter with the [Customer Meters API](/api-reference/customer-meters/list):

    ```typescript theme={null}
    const meters = await macropay.customerMeters.list({
      customerId: "cus_...",
    });

    console.log(meters.items[0].balance); // -500 right after the grant
    ```
  </Step>
</Steps>

<Tip>
  Re-granting on a recurring product? Subscribe to the renewal event for that subscription and ingest the same negative event each cycle, keying idempotency off the billing period instead of the order ID.
</Tip>
