Bridge by Wekoya / Docs / Your account

Your users' budgets

Updated

If your app has its own users, give each one an AI budget instead of letting them all draw from one bucket. Your backend keeps one key and names the user on each request. Bridge tracks what each person spends, refuses them once their budget is used up, and resets it each period. Nobody has to step in.

  1. Create a key under API keys with the End users permission. Only an owner or admin can. Keep it on your server.
  2. Create each user when they sign up (below), or turn on auto-create on the End users page.
  3. Send X-Bridge-End-User: <their id> with every chat request made for them.
import os, requests

BRIDGE = "https://wekoya-api.wekoya.tech/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['WEKOYA_API_KEY']}"}

def on_signup(user):
    # Safe to retry: the same external_id returns the same user, 200 not 201.
    r = requests.post(f"{BRIDGE}/end-users", headers=HEADERS, json={
        "external_id": user.id,      # your own id for them
        "allowance": 5000,           # ₦5,000 of AI per period
        "period": "monthly",
    }, timeout=10)
    r.raise_for_status()
from openai import OpenAI, APIStatusError

client = OpenAI(base_url="https://wekoya-api.wekoya.tech/v1",
                api_key=os.environ["WEKOYA_API_KEY"])

def ask(user_id, prompt):
    try:
        reply = client.chat.completions.create(
            model="claude-sonnet-5",
            max_tokens=800,
            messages=[{"role": "user", "content": prompt}],
            extra_headers={"X-Bridge-End-User": user_id},
        )
    except APIStatusError as e:
        if e.status_code == 402 and e.body["error"]["type"] == "end_user_budget_exhausted":
            return "You've used this month's AI credit. Top up to keep going."
        raise
    return reply.choices[0].message.content

The reply says what that user has left, so you can show it in your app without a second call. cost is what this request charged, in naira; remaining_minor is in kobo, as a string, and is null for a user with no limit.

{
  "choices": [{ "message": { "role": "assistant", "content": "Correct: x = 3, y = 2 ..." } }],
  "usage": { "prompt_tokens": 212, "completion_tokens": 318 },
  "wekoya": {
    "cost": 18.4,
    "currency": "NGN",
    "end_user": {
      "id": "e3a1c0de-...",
      "external_id": "user_8412",
      "remaining_minor": "481160",
      "period_end": "2026-11-01T09:12:00.000Z"
    }
  }
}

A reply is also shortened to fit what the user has left, so a user with ₦40 remaining gets a shorter answer rather than a refusal sized for a long one. Only when they cannot afford a useful reply is the request refused.

# When the user pays you. Idempotency-Key makes a retried call add the credit once:
# use your payment's own reference.
curl https://wekoya-api.wekoya.tech/v1/end-users/user_8412/top-up \
  -H "Authorization: Bearer $WEKOYA_API_KEY" \
  -H "Idempotency-Key: flw_tx_7781230" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 2000 }'

# How much have they used this period, by model and by day?
curl https://wekoya-api.wekoya.tech/v1/end-users/user_8412/usage \
  -H "Authorization: Bearer $WEKOYA_API_KEY"

# Change the budget, suspend, resume, start a new period, or delete.
curl -X PATCH https://wekoya-api.wekoya.tech/v1/end-users/user_8412 \
  -H "Authorization: Bearer $WEKOYA_API_KEY" -H "Content-Type: application/json" \
  -d '{ "allowance": 10000, "allowed_models": ["claude-*"] }'
curl -X POST   .../v1/end-users/user_8412/suspend
curl -X POST   .../v1/end-users/user_8412/resume
curl -X POST   .../v1/end-users/user_8412/reset
curl -X DELETE .../v1/end-users/user_8412
FieldWhat it does
external_idRequired on create. Your id for the user: 1 to 128 printable characters, no spaces. Used in paths and the header.
allowance / allowance_minorBudget per period, in naira (5000) or in kobo (500000). Send one, not both. null means no limit, but usage is still tracked.
period"none" (one budget that never resets), "monthly", or "days" with period_days. Periods count from when the user was created.
rolloverWhat survives a reset. "top_ups" (default): unused top-ups carry, the allowance does not. "none" or "all".
allowed_modelsOptional list: an exact id, a prefix such as "claude-*", or "vendor:anthropic". Applies on top of the key's own list.
rpm_limitOptional requests-per-minute cap for this user.
max_output_tokensOptional cap on the length of any reply for this user.
name, metadataOptional, for you. metadata is any JSON object and comes back in webhooks.
status"active" or "suspended".

Never call Bridge from the mobile or browser app itself. The key would be visible to anyone who unpacks it, and it can spend your whole balance. Call from your server, which already knows who the user is.