> ## 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.

# S3 Strategy

> Meter S3 storage and upload usage automatically — wrap your S3 client, bill the bytes, and let Macropay handle tax as Merchant of Record.

Charging customers for storage means knowing exactly how many bytes each one wrote — without bolting a counter onto every upload path. The S3 Strategy wraps the official AWS S3 client so that **every `PutObject` you send is metered for you**, then forwarded to Macropay as a usage event keyed to a customer.

From there, a [meter](/features/usage-based-billing/meters) aggregates those bytes and a metered [product price](/features/products) turns them into a bill. Because Macropay is your Merchant of Record, the invoice that lands also carries the right sales tax / VAT for the buyer's jurisdiction — collected and remitted on your behalf, off your liability.

<Info>
  Pairs naturally with the [Stream Strategy](/features/usage-based-billing/ingestion-strategies/stream-strategy) when you also serve downloads, and with the [LLM Strategy](/features/usage-based-billing/ingestion-strategies/llm-strategy) if storage sits behind an AI workload.
</Info>

## What you'll need

* A Macropay [organization access token](/integrate/authentication) (`MACROPAY_ACCESS_TOKEN`).
* An S3-compatible bucket and credentials (AWS S3, Minio, R2, or any S3-API store).
* A customer identifier to attribute each upload to.

## Install

```bash theme={null}
pnpm add @macropayments/ingestion @aws-sdk/client-s3
```

## Wrap the S3 client

You build the ingestion pipeline once — pass it your `S3Client`, choose the `S3Strategy`, and name the event (`s3-uploads` below). At request time, `.client({ customerId })` hands you a drop-in S3 client that behaves exactly like the original, except every write it sends is measured and reported.

```typescript theme={null}
import { Ingestion } from "@macropayments/ingestion";
import { S3Strategy } from "@macropayments/ingestion/strategies/S3";
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";

const s3Client = new S3Client({
  region: process.env.AWS_REGION,
  endpoint: process.env.AWS_ENDPOINT_URL,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
});

// Build the metered S3 pipeline once, reuse it per request.
const s3Ingestion = Ingestion({ accessToken: process.env.MACROPAY_ACCESS_TOKEN })
  .strategy(new S3Strategy(s3Client))
  .ingest("s3-uploads");

export async function POST(request: Request) {
  try {
    // Resolve a customer-scoped S3 client so every byte is attributed correctly.
    const s3 = s3Ingestion.client({
      customerId: request.headers.get("X-Macropay-Customer-Id") ?? "",
    });

    // Persisting an exported audit log — billed by size, no extra plumbing.
    await s3.send(
      new PutObjectCommand({
        Bucket: process.env.AWS_BUCKET_NAME,
        Key: `audit-logs/${crypto.randomUUID()}.json`,
        Body: JSON.stringify({
          event: "invoice.exported",
          invoiceId: "inv_8f21",
          rows: 4096,
        }),
        ContentType: "application/json",
      })
    );

    return Response.json({});
  } catch (error) {
    return Response.json({ error: error.message });
  }
}
```

<Tip>
  Set `customerId` from whatever you trust as the source of truth — a header, a verified session, or your auth context. Macropay matches it to the customer record that owns the meter, so attribution stays accurate even under concurrency.
</Tip>

## What gets sent

Each wrapped `PutObject` emits one usage event. The strategy fills in `metadata.bytes` automatically; the rest describes the object so you can audit, filter, or build a [meter](/features/usage-based-billing/meters) on any field.

```json theme={null}
{
  "customerId": "123",
  "name": "s3-uploads",
  "metadata": {
    "bytes": 100,
    "bucket": "my-bucket",
    "key": "my-key",
    "contentType": "application/text"
  }
}
```

| Field                  | Meaning                                              |
| ---------------------- | ---------------------------------------------------- |
| `customerId`           | Customer the upload is billed to                     |
| `name`                 | Event name you passed to `.ingest()`                 |
| `metadata.bytes`       | Size of the written object — the usual metered value |
| `metadata.bucket`      | Destination bucket                                   |
| `metadata.key`         | Object key                                           |
| `metadata.contentType` | MIME type of the object                              |

## Turn bytes into revenue

<Steps>
  <Step title="Create a meter">
    Define a [meter](/features/usage-based-billing/meters) that filters on `name = "s3-uploads"` and aggregates `metadata.bytes`. Switch the aggregation to a `COUNT` of events if you'd rather bill per upload than per byte.
  </Step>

  <Step title="Attach a metered price">
    Add a metered price to a [product](/features/products) — for example, a per-GB rate, or a tier that includes a free allowance before usage charges begin.
  </Step>

  <Step title="Let Macropay close the loop">
    Usage rolls up automatically. At billing time Macropay charges the customer, applies the correct sales tax / VAT for their location, and remits it as Merchant of Record — so storage revenue never expands your tax footprint.
  </Step>
</Steps>

## Beyond storage: metering agents

The same wrap-and-meter pattern powers Macropay's AI billing. When an autonomous agent stages artifacts in S3 — render outputs, scraped corpora, generated reports — those writes become billable usage with zero extra instrumentation, and you can bill by **usage, activity, or outcome** rather than flat seats. Pair S3 metering with the [event ingestion API](/features/usage-based-billing/event-ingestion) to attribute both the work an agent does and the value it produces.

<Note>
  Need a metering pattern we don't cover yet? Reach us at [support@macropay.ai](mailto:support@macropay.ai) and we'll help you wire it up.
</Note>
