> ## 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 Before Purchase

> Pre-load usage credits onto a customer before they ever pay — for trials, promos, demos, and AI-agent starter balances.

## Why pre-fund a balance?

Sometimes the meter needs to start in the green before a single dollar changes hands. A free trial that hands out 1,000 tokens, a launch promo that drops bonus API calls into early accounts, or an AI agent you want to let run a few tasks before billing kicks in — all of these are the same primitive: **a credit balance that exists ahead of any purchase.**

Macropay models this with metered usage. You seed a customer's meter with credits, then let normal usage draw it down. Because Macropay is the **merchant of record**, the moment that customer does convert to a paid order, sales tax and VAT are calculated and remitted for you — the pre-funded balance and the eventual invoice live in the same system.

Common scenarios:

| Use case              | Example                                                 |
| --------------------- | ------------------------------------------------------- |
| Trial allowance       | New signup gets 500 free API calls                      |
| Promotional grant     | "Launch week" accounts get 2,000 bonus units            |
| Sales demo / POC      | Prospect's sandbox seeded with credits                  |
| Agent starter balance | An AI agent gets a budget before its first billable run |

## The mechanic: a negative event on a Sum meter

A meter with **Sum** aggregation simply adds up the values of every event it matches. Usage events carry a positive value and push the total up; a credit grant carries a **negative value** and pushes it down.

That sign convention is the whole trick: the running sum represents *consumption*, so a negative running sum means the customer has **unused credit in reserve**. Each real usage event nudges it back toward zero, and once it crosses zero, billable usage begins.

<Info>
  **Reading the balance**

  Ingest `-1000` on a Sum meter and the meter total becomes `-1000`. As the customer consumes, the total climbs: `-1000` → `-700` → `0`. While the total is negative, its absolute value is the credit still available.
</Info>

## What you'll need

* A meter using **Sum** aggregation (created below)
* A customer record — or your own `externalId` to reference one
* A Macropay access token (`MACROPAY_ACCESS_TOKEN`)

## Step 1 — Create a Sum meter

The meter defines which events count and how they're aggregated.

<Steps>
  <Step title="Open the Meters list">
    In the dashboard sidebar, go to **Products** → **Meters**.
  </Step>

  <Step title="Configure the meter">
    Click **Create Meter** and set:

    * **Name** — something descriptive, e.g. `API Calls` or `Render Minutes`
    * **Filter** — match your usage events, e.g. event name equals `api_usage`
    * **Aggregation** — choose **Sum**, then enter the property to total (e.g. `units`)

    <Warning>
      Aggregation **must** be **Sum** for the credit mechanic to work. Count, max, or unique aggregations don't net positive and negative events against each other.
    </Warning>
  </Step>

  <Step title="Save it">
    Save the meter and keep the event name handy — your ingested events must match the filter.
  </Step>
</Steps>

See [how meters aggregate events](/features/usage-based-billing/meters) for filter and aggregation details.

## Step 2 — Make sure the customer exists

You can grant credits to a Macropay customer ID or to your own `externalId`. Create the customer first if they aren't in Macropay yet.

### From the dashboard

<Steps>
  <Step title="Open Customers">
    Click **Customers** in the sidebar.
  </Step>

  <Step title="Add the customer">
    Click **Add Customer** and provide:

    * **Email** (required)
    * **Name** (optional)
    * **External ID** — your own user ID, e.g. `user_8842` (optional but recommended)
  </Step>

  <Step title="Save">
    Save and copy the Customer ID.
  </Step>
</Steps>

### From the API

```typescript icon="square-js" title="create-customer.ts" theme={null}
import { Macropay } from "@macropayments/sdk";

const macropay = new Macropay({
  accessToken: process.env.MACROPAY_ACCESS_TOKEN,
});

await macropay.customers.create({
  email: "ada@hooli.dev",
  name: "Ada Pinsky",
  externalId: "user_8842", // your internal user ID (optional)
});
```

<Tip>
  Set `externalId` so you can address customers by *your* identifier everywhere — no need to store Macropay's IDs in your database. Pass it as `externalCustomerId` when ingesting events.
</Tip>

Read more on [customer management](/features/customer-management).

## Step 3 — Grant the credits

Ingest a single event with a **negative value**. This is the same `events.ingest` call you'd use for real usage — only the sign differs.

### By Macropay customer ID

```typescript icon="square-js" title="grant-credits.ts" theme={null}
import { Macropay } from "@macropayments/sdk";

const macropay = new Macropay({
  accessToken: process.env.MACROPAY_ACCESS_TOKEN,
});

async function grantCredits(customerId: string, credits: number) {
  await macropay.events.ingest({
    events: [
      {
        customerId,
        name: "api_usage", // must match the meter's filter
        metadata: {
          units: -credits, // negative value = credit grant
        },
      },
    ],
  });

  console.log(`Granted ${credits} credits to ${customerId}`);
}

// Seed a new trial with 1,000 credits
await grantCredits("cus_abc123", 1000);
```

### By your own external ID

```typescript icon="square-js" title="grant-credits-external.ts" theme={null}
import { Macropay } from "@macropayments/sdk";

const macropay = new Macropay({
  accessToken: process.env.MACROPAY_ACCESS_TOKEN,
});

async function grantCreditsToExternalUser(externalUserId: string, credits: number) {
  await macropay.events.ingest({
    events: [
      {
        name: "api_usage", // must match the meter's filter
        externalCustomerId: externalUserId, // your internal ID
        metadata: {
          units: -credits, // negative value = credit grant
        },
      },
    ],
  });

  console.log(`Granted ${credits} credits to ${externalUserId}`);
}

// Promo: hand "launch week" signups a 2,000-unit bonus
await grantCreditsToExternalUser("user_8842", 2000);
```

## Step 4 — Confirm the balance

Read the customer's meters back. A negative `balance` means credits are still in reserve, so the credits remaining is `Math.abs(balance)`.

```typescript title="check-balance.ts" theme={null}
import { Macropay } from "@macropayments/sdk";

const macropay = new Macropay({
  accessToken: process.env.MACROPAY_ACCESS_TOKEN,
});

const meters = await macropay.customerMeters.list({
  customerId: "cus_abc123",
});

meters.items
  .filter((meter) => meter.balance < 0) // negative balance = unused credit
  .forEach((meter) => {
    console.log(`${Math.abs(meter.balance)} credits available on ${meter.meter_id}`);
  });
```

## How the balance moves

Once seeded, the meter behaves like any other — positive events are real usage that draws the reserve down:

```typescript title="lifecycle.ts" theme={null}
// Start: customer created, meter total = 0

// Grant 1,000 credits → total = -1000 (1,000 available)
await macropay.events.ingest({
  events: [{ name: "api_usage", customerId: "cus_abc123", metadata: { units: -1000 } }],
});

// Customer makes 300 API calls → total = -700 (700 available)
await macropay.events.ingest({
  events: [{ name: "api_usage", customerId: "cus_abc123", metadata: { units: 300 } }],
});

// Customer makes 750 more → total = 50 (credits exhausted, 50 units now billable)
await macropay.events.ingest({
  events: [{ name: "api_usage", customerId: "cus_abc123", metadata: { units: 750 } }],
});
```

<Tip>
  **Billing AI agents this way.** The same negative-event grant gives an agent a starter budget before its first paid task. Because activity, usage, and outcome all flow through [metered billing](/features/usage-based-billing/meters), the credits a free agent burns sit in the same ledger as the revenue it later earns — so agentic margin (billed revenue vs. AI cost) stays accurate from the very first run.
</Tip>
