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

# Credits and Budget Alerts

> Track credit usage in-app, estimate costs before calling, set budget alerts, and gracefully degrade when credits run low

## When to Use This

You're building a production integration and need to:

* **Track credits** across requests in your application (not just rely on dashboard)
* **Estimate cost** before making expensive calls (Mave, Focus Groups, Video Analysis)
* **Alert** when usage approaches 50%, 75%, 90% of monthly allocation
* **Handle 402** gracefully — show users a clear message instead of a raw error
* **Pre-flight check** to avoid starting expensive jobs when credits are insufficient

***

## Credit Costs at a Glance

| Endpoint               | Typical Cost    | Notes                   |
| ---------------------- | --------------- | ----------------------- |
| `/responses`           | 1–5 credits     | Depends on tokens       |
| `/mave/chat`           | 10–50 credits   | Research complexity     |
| `/personas` (GET)      | 0               | Free                    |
| `/focus-groups`        | 50–200 credits  | Sample size + questions |
| `/video-analyses`      | 100–500 credits | Video length            |
| `/personas` (POST)     | 300 credits     | Custom persona creation |
| `/brand-voices` (POST) | 50 credits      | Brand voice creation    |

***

## Pattern 1: In-App Usage Tracking

Accumulate `credits_used` from each response and persist it (e.g. Redis, DB) so you have real-time usage without polling the dashboard.

<CodeGroup>
  ```python Python theme={"dark"}
  import os
  import redis
  from contextlib import contextmanager

  redis_client = redis.Redis.from_url(os.environ.get("REDIS_URL", "redis://localhost:6379"))
  USAGE_KEY = "mavera:credits_used:current_period"

  def track_credits(credits: int, workspace_id: str = "default"):
      """Increment in-app usage counter."""
      key = f"{USAGE_KEY}:{workspace_id}"
      redis_client.incrby(key, credits)
      redis_client.expire(key, 86400 * 35)  # Expire after ~billing cycle

  def get_tracked_usage(workspace_id: str = "default") -> int:
      """Get accumulated credits used this period."""
      key = f"{USAGE_KEY}:{workspace_id}"
      return int(redis_client.get(key) or 0)

  def chat_with_tracking(client, messages, persona_id, workspace_id="default"):
      response = client.responses.create(
          model="mavera-1",
          input=messages,
          extra_body={"persona_id": persona_id},
      )
      credits = response.usage.credits_used
      track_credits(credits, workspace_id)
      return response
  ```

  ```javascript JavaScript theme={"dark"}
  // Using a simple in-memory store; replace with Redis/DB for production
  const usageStore = new Map();

  function trackCredits(credits, workspaceId = "default") {
    const key = `mavera:credits:${workspaceId}`;
    usageStore.set(key, (usageStore.get(key) || 0) + credits);
  }

  function getTrackedUsage(workspaceId = "default") {
    return usageStore.get(`mavera:credits:${workspaceId}`) || 0;
  }

  async function chatWithTracking(client, messages, personaId, workspaceId = "default") {
    const response = await client.responses.create({
      model: "mavera-1",
      input: messages,
      persona_id: personaId,
    });
    trackCredits(response.usage.credits_used, workspaceId);
    return response;
  }
  ```
</CodeGroup>

<Tip>
  Use the **dashboard** ([app.mavera.io/settings/usage](https://app.mavera.io/settings/usage)) as source of truth. In-app tracking is for real-time estimates; reconcile periodically with the API or dashboard data if available.
</Tip>

***

## Pattern 2: Estimate Cost Before Calling

Before starting an expensive operation (focus group, video analysis), estimate the cost and compare against a threshold or remaining credits.

<CodeGroup>
  ```python Python theme={"dark"}
  # Conservative estimates (upper bound)
  CREDIT_ESTIMATES = {
      "chat": (1, 5),
      "mave_simple": (10, 15),
      "mave_moderate": (20, 30),
      "mave_complex": (35, 50),
      "focus_group_small": (50, 75),
      "focus_group_medium": (100, 150),
      "focus_group_large": (150, 250),
      "video_short": (100, 150),
      "video_medium": (200, 350),
      "video_long": (400, 600),
  }

  def estimate_focus_group(sample_size: int, num_questions: int) -> int:
      """Return upper-bound credit estimate for a focus group."""
      if sample_size <= 25:
          base = 75
      elif sample_size <= 50:
          base = 125
      else:
          base = 200
      return base + (num_questions * 2)  # Rough per-question cost

  def can_afford(operation: str, estimated_cost: int, remaining_credits: int) -> bool:
      """Check if we have enough credits (with 20% buffer)."""
      buffer = int(estimated_cost * 0.2)
      return remaining_credits >= estimated_cost + buffer
  ```

  ```javascript JavaScript theme={"dark"}
  const CREDIT_ESTIMATES = {
    chat: [1, 5],
    mave_simple: [10, 15],
    mave_complex: [35, 50],
    focus_group_small: [50, 75],
    focus_group_large: [150, 250],
    video_short: [100, 150],
    video_long: [400, 600],
  };

  function estimateFocusGroup(sampleSize, numQuestions) {
    let base = sampleSize <= 25 ? 75 : sampleSize <= 50 ? 125 : 200;
    return base + numQuestions * 2;
  }

  function canAfford(estimatedCost, remainingCredits, bufferPercent = 0.2) {
    const buffer = Math.ceil(estimatedCost * bufferPercent);
    return remainingCredits >= estimatedCost + buffer;
  }
  ```
</CodeGroup>

***

## Pattern 3: Pre-Flight Check (Block Expensive Jobs)

Before kicking off a focus group or video analysis, check estimated cost vs. remaining credits. Fail fast with a clear message instead of failing mid-run with 402.

<CodeGroup>
  ```python Python theme={"dark"}
  class InsufficientCreditsError(Exception):
      pass


  def create_focus_group_with_preflight(payload: dict, remaining_credits_fn) -> dict:
      """Create focus group only if we estimate enough credits."""
      sample_size = payload.get("sample_size", 25)
      num_questions = len(payload.get("questions", []))
      estimated = estimate_focus_group(sample_size, num_questions)
      remaining = remaining_credits_fn()

      if not can_afford("focus_group", estimated, remaining):
          raise InsufficientCreditsError(
              f"Estimated {estimated} credits; only {remaining} remaining. "
              "Please add credits or reduce sample size / questions."
          )

      return requests.post(
          f"{BASE}/focus-groups",
          headers=HEADERS,
          json=payload,
      ).json()
  ```

  ```javascript JavaScript theme={"dark"}
  async function createFocusGroupWithPreflight(payload, getRemainingCredits) {
    const sampleSize = payload.sample_size ?? 25;
    const numQuestions = (payload.questions ?? []).length;
    const estimated = estimateFocusGroup(sampleSize, numQuestions);
    const remaining = await getRemainingCredits();

    if (!canAfford(estimated, remaining)) {
      throw new Error(
        `Estimated ${estimated} credits; only ${remaining} remaining. ` +
          "Please add credits or reduce sample size."
      );
    }

    return fetch(`${BASE}/focus-groups`, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify(payload),
    }).then((r) => r.json());
  }
  ```
</CodeGroup>

***

## Pattern 4: Dashboard Budget Alerts

Configure alerts in the Mavera dashboard so you're notified before you run out. No code required — just settings.

<Steps>
  <Step title="Go to Usage Settings">
    Navigate to [app.mavera.io/settings/usage](https://app.mavera.io/settings/usage)
  </Step>

  <Step title="Set Alert Thresholds">
    Enable alerts at 50%, 75%, and 90% of monthly credits
  </Step>

  <Step title="Add Notification Email">
    Ensure the billing/ops email receives alerts
  </Step>

  <Step title="Optional: Workspace Budgets">
    For team accounts, set per-workspace budgets at [Workspace Settings > Budget](https://app.mavera.io/settings/workspace)
  </Step>
</Steps>

***

## Pattern 5: Auto-Recharge (Dashboard)

When credits run low, **auto-recharge** can automatically purchase more so high-priority jobs don't fail.

<Steps>
  <Step title="Go to Billing">
    [app.mavera.io/settings/billing](https://app.mavera.io/settings/billing)
  </Step>

  <Step title="Enable Auto-Recharge">
    Toggle on "Auto-recharge credits"
  </Step>

  <Step title="Set Threshold">
    e.g. Trigger when credits fall below 100
  </Step>

  <Step title="Set Recharge Amount">
    e.g. Purchase 500 credits
  </Step>
</Steps>

<Tip>
  Use auto-recharge as a **safety net**, not as primary capacity planning. Monitor usage trends and upgrade your plan or adjust thresholds as needed.
</Tip>

***

## Pattern 6: Graceful Degradation on 402

When you get a 402, don't show a generic "API error." Return a clear message and optionally queue the request for retry after credits are added.

<CodeGroup>
  ```python Python theme={"dark"}
  from enum import Enum

  class CreditStatus(Enum):
      OK = "ok"
      LOW = "low"
      EXHAUSTED = "exhausted"

  def handle_402(error, request_context: dict):
      """Handle 402 and decide: retry later, queue, or fail user-facing."""
      # Log for ops
      logger.warning("Credits exhausted", extra={"context": request_context})

      # Option A: Return user-facing message
      return {
          "success": False,
          "error": "credits_exhausted",
          "message": "You've run out of credits. Please add more in Settings.",
          "action_url": "https://app.mavera.io/settings/billing",
      }

      # Option B: Queue for later (if you have a job queue)
      # queue.push("mavera_retry", request_context, delay_minutes=60)
  ```

  ```javascript JavaScript theme={"dark"}
  function handle402(error, requestContext) {
    console.warn("Credits exhausted", requestContext);

    return {
      success: false,
      error: "credits_exhausted",
      message: "You've run out of credits. Please add more in Settings.",
      actionUrl: "https://app.mavera.io/settings/billing",
    };
  }
  ```
</CodeGroup>

***

## Pattern 7: Per-Workspace Budgets (Teams)

For team accounts, set **workspace-level** and **project-level** budgets. When a budget is exceeded, requests for that workspace/project return 402 with `budget_exceeded`.

| Setting       | Description                                  |
| ------------- | -------------------------------------------- |
| Budget Alert  | Notification when spending reaches threshold |
| Usage Limit   | Hard cap; blocks requests when reached       |
| Billing Email | Separate email for budget notifications      |

Configure at [Workspace Settings > Budget](https://app.mavera.io/settings/workspace).

***

## Metrics to Monitor

| Metric                     | What to track                                                  |
| -------------------------- | -------------------------------------------------------------- |
| `credits_used_total`       | Sum of `usage.credits_used` from all responses                 |
| `credits_used_by_endpoint` | Breakdown by endpoint (chat, mave, focus-groups, etc.)         |
| `402_count`                | Number of credit exhaustion errors                             |
| `estimated_vs_actual`      | Compare pre-flight estimates to actual usage (for calibration) |

***

## See Also

<CardGroup cols={2}>
  <Card title="Credits Guide" icon="coins" href="/guides/credits">
    Allocation, costs by endpoint, auto-recharge
  </Card>

  <Card title="Error Handling Patterns" icon="exclamation-triangle" href="/cookbooks/error-handling-patterns">
    Handling 402 in your error flow
  </Card>

  <Card title="Billing & Usage" icon="credit-card" href="https://app.mavera.io/settings/usage">
    Dashboard usage and alerts
  </Card>

  <Card title="Pricing" icon="dollar-sign" href="https://mavera.io/pricing">
    Plans and credit packages
  </Card>
</CardGroup>
