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

# Report Billing Events

> POST /v1/events — Report custom billing events (PER_USE, OAuth only)

Report custom billing events for App per-use (PER\_USE) billing scenarios. Each event report deducts the corresponding credit amount from the authorized user's account based on the Meter configuration.

```
POST https://api.profy.cn/v1/events
```

## Authentication

<Warning>
  This endpoint **only supports OAuth Bearer Tokens**. API Key authentication will return a `401` error.

  Custom billing events deduct credits from the user's account and must go through OAuth to ensure the user's informed consent.
</Warning>

```
Authorization: Bearer <access_token>
```

## Request Body

<ParamField body="event" type="string" required>
  Event name, must exactly match a Meter event name configured in the App. Maximum 100 characters.
</ParamField>

<ParamField body="idempotency_key" type="string" required>
  Idempotency key to prevent duplicate charges. The same `idempotency_key` will not be charged twice. Maximum 128 characters.
</ParamField>

<ParamField body="metadata" type="object">
  Additional metadata as key-value pairs. Used to record extra business information without affecting billing.
</ParamField>

## Request Examples

<CodeGroup>
  ```python Python theme={null}
  from profy import Profy

  client = Profy(api_key="<oauth_access_token>")

  result = await client.events.report(
      "image_generation",
      idempotency_key="order_12345_gen_1",
      metadata={"style": "watercolor", "resolution": "1024x1024"},
  )
  print(f"Charged: {result['charged']} credits")
  print(f"Remaining: {result['balance_remaining']} credits")
  ```

  ```typescript TypeScript theme={null}
  import { Profy } from "@profy-ai/sdk";

  const client = new Profy({ apiKey: "<oauth_access_token>" });

  const result = await client.events.report("image_generation", {
    idempotencyKey: "order_12345_gen_1",
    metadata: { style: "watercolor", resolution: "1024x1024" },
  });

  console.log(`Charged: ${result.charged} credits`);
  console.log(`Remaining: ${result.balance_remaining} credits`);
  ```

  ```bash curl theme={null}
  curl -X POST https://api.profy.cn/v1/events \
    -H "Authorization: Bearer <oauth_access_token>" \
    -H "Content-Type: application/json" \
    -d '{
      "event": "image_generation",
      "idempotency_key": "order_12345_gen_1",
      "metadata": {
        "style": "watercolor",
        "resolution": "1024x1024"
      }
    }'
  ```
</CodeGroup>

## Response

### Normal Charge

```json theme={null}
{
  "charged": 50,
  "balance_remaining": 9450
}
```

| Field               | Type     | Description                       |
| ------------------- | -------- | --------------------------------- |
| `charged`           | `number` | Credits deducted for this event   |
| `balance_remaining` | `number` | Remaining credits after deduction |

### Idempotent Replay

When the same `idempotency_key` is submitted again, no duplicate charge occurs:

```json theme={null}
{
  "charged": 50,
  "balance_remaining": 9450,
  "idempotent_replay": true
}
```

`idempotent_replay: true` indicates this was an idempotent replay and credits were not deducted again.

## Idempotency Guarantees

<Tip>
  Always use meaningful idempotency keys. Recommended format: `{entity}_{id}_{operation}`, for example `order_12345_gen_1`.
</Tip>

* **Prevents duplicate charges from network retries** — when clients retry on timeout, the same idempotency key ensures only one charge
* **Prevents duplicate charges from business logic** — multiple calls for the same business operation will only charge once
* **Idempotency window** — the same `(app_uuid, idempotency_key)` combination is permanently valid

## How It Works

```
Your App → POST /v1/events → Find Meter → Deduct credits → Return result
                                  ↑
                            App-configured Meter
                          (event_name → price)
```

1. Your App obtains user authorization through OAuth
2. When the user uses a paid feature, call the Events API to report the event
3. Profy looks up the corresponding credit price from the App's Meter configuration
4. Deducts the corresponding amount from the authorized user's credits
5. Returns the charge result and remaining balance

## Daily Spending Cap

The platform sets a daily spending cap for each user per App. When the cap is reached, subsequent event reports will be rejected (HTTP 429). Refer to the real-time cap displayed in the product.

## Error Codes

| HTTP Status Code | Error Type           | Description                                     |
| ---------------- | -------------------- | ----------------------------------------------- |
| `400`            | `invalid_request`    | Missing required fields or field exceeds length |
| `400`            | `unknown_event`      | No matching Meter configuration found           |
| `400`            | `meter_disabled`     | The corresponding Meter has been disabled       |
| `401`            | `unauthorized`       | Requires OAuth Token, API Key not supported     |
| `429`            | `daily_cap_exceeded` | User's daily spending cap reached               |

## Next Steps

<CardGroup cols={2}>
  <Card title="Meter Configuration" icon="gauge" href="/en/developers/api/meters">
    Query App Meter configurations
  </Card>

  <Card title="Build a Profy App" icon="grid-2" href="/en/developers/app-development">
    Complete App development guide
  </Card>
</CardGroup>
