Skip to content

Custom integration (any language) ​

The FastAPI SDK is a convenience layer over three HTTP endpoints — the SDK API. If you run Go, Node, Rails, Laravel or anything else, you can implement the same pattern yourself. This guide documents the endpoints and the caching/reporting pattern that makes the integration fast and robust.

Authentication ​

All SDK API calls authenticate with your tenant secret key (mas_..., from the tenant's Overview tab):

Authorization: Bearer mas_...

Base URL: https://api.microauth.com. A suspended tenant receives 403 from snapshot and key verification. Usage authentication remains available so durable items from requests admitted before suspension can finish reporting; items at or after the suspension time are rejected.

Keys are matched by hash

Your customers send you their API key (map_...). You never forward the raw key to MicroAuth — you always work with its SHA-256 hex digest (sha256("map_...") → 64 lowercase hex chars). The snapshot contains key hashes, and the verify endpoint takes a key_hash query parameter.

The pattern in one diagram ​

your API process                          MicroAuth
┌──────────────────────────┐
│  in-memory cache         │  every ~30s   GET /sdk/v1/snapshot
│  keys + customers  ◄─────┼───────────────────────────────────►
│                          │  cache miss   GET /sdk/v1/keys/verify
│  per-request:            │───────────────────────────────────►
│   hash key → lookup →    │
│   enforce → count        │  every ~15s   POST /sdk/v1/usage
│  usage counters   ───────┼───────────────────────────────────►
└──────────────────────────┘

The normal known-key path does not call MicroAuth. Serve it from the cache. Key verification is the deliberate exception when a newly created key has not reached the latest snapshot.

1. GET /sdk/v1/snapshot ​

Returns everything you need to authenticate and enforce limits locally: all active customers with their effective limits, and all active key hashes.

bash
curl https://api.microauth.com/sdk/v1/snapshot \
  -H "Authorization: Bearer mas_..."
json
{
  "tenant_id": "0d2a95a4-...",
  "generated_at": "2026-08-03T18:00:00Z",
  "billable_status_codes": [200],
  "platform_monthly_limit": 10000000,
  "platform_monthly_used": 48210,
  "platform_monthly_remaining": 9951790,
  "platform_period_end": "2026-09-01T00:00:00Z",
  "platform_hard_cap": true,
  "customers": [
    {
      "id": "c6f7e9a2-...",
      "status": "active",
      "credit_balance_micro": 12500000,
      "month_requests": 48210,
      "usage_policy_id": "4c758db0-...",
      "policy_valid_until": "2026-08-04T18:00:00Z",
      "effective": {
        "rps": 10,
        "price_per_request_micro": 500,
        "monthly_quota": 1000000,
        "source": "plan",
        "billing_model": "subscription"
      }
    }
  ],
  "keys": [
    {
      "id": "9b1d3f60-...",
      "key_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "customer_id": "c6f7e9a2-..."
    }
  ]
}

Notes:

  • effective.monthly_quota is omitted when the customer has no request cap.
  • effective.source is custom, plan or payg — which layer of the limit resolution won.
  • Keep each customer's usage_policy_id with the cached limits and copy it into every usage item admitted under that snapshot. This freezes pricing, quota, billable statuses and the platform allowance for delayed reports. policy_valid_until is the latest traffic time authorized by that policy; it is not a deadline for delivering already-recorded items.
  • platform_monthly_* is the API owner's MicroAuth plan allowance, not one customer's quota. platform_hard_cap is always true for this contract. Reserve locally before the handler; use Redis to coordinate conforming processes. This is not a gateway guarantee against code the API owner can bypass.
  • Refresh every ~30 seconds. The endpoint is rate limited to 60 requests/minute per tenant; polling faster returns 429 and buys you nothing.
  • Build two lookup maps from the response: key_hash → key and customer_id → customer.

2. GET /sdk/v1/keys/verify ​

For keys that aren't in your cached snapshot (e.g. created seconds ago). Resolves one key hash to its customer and limits:

bash
curl "https://api.microauth.com/sdk/v1/keys/verify?key_hash=$(printf '%s' "map_..." | shasum -a 256 | cut -d' ' -f1)" \
  -H "Authorization: Bearer mas_..."

Valid key:

json
{
  "valid": true,
  "key_id": "9b1d3f60-...",
  "billable_status_codes": [200],
  "customer": { "id": "c6f7e9a2-...", "status": "active", "credit_balance_micro": 12500000, "month_requests": 48210, "usage_policy_id": "4c758db0-...", "policy_valid_until": "2026-08-04T18:00:00Z", "effective": { "rps": 10, "price_per_request_micro": 500, "source": "plan", "billing_model": "subscription" } }
}

Unknown or revoked key: { "valid": false } (HTTP 200 — the call succeeded, the key didn't).

Guidelines:

  • Only call this on a cache miss, and de-duplicate concurrent misses for the same hash (single-flight), so a burst can't stampede MicroAuth.
  • Negatively cache invalid hashes for ~30 seconds; a flood of garbage keys should be absorbed by your cache, not forwarded.
  • Rate limit: 600 requests/minute per tenant.

3. POST /sdk/v1/usage ​

Report every authenticated response in batches. For each accepted item, MicroAuth records usage, checks the customer and platform monthly limits, and charges the customer's prepaid balance only when status_code is billable. Each item is processed in its own transaction.

bash
curl -X POST https://api.microauth.com/sdk/v1/usage \
  -H "Authorization: Bearer mas_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "idempotency_key": "f3cf32d1-e2b8-4d33-a37e-60f5f70b92e8",
        "api_key_id": "9b1d3f60-...",
        "usage_policy_id": "4c758db0-...",
        "status_code": 200,
        "count": 42,
        "period_start": "2026-08-03T18:00:00+00:00"
      }
    ]
  }'
json
{
  "accepted": 1,
  "results": [
    {
      "idempotency_key": "f3cf32d1-e2b8-4d33-a37e-60f5f70b92e8",
      "status": "accepted"
    }
  ]
}

Rules and semantics:

  • Report every authenticated response, whether or not its HTTP status is in billable_status_codes. Preserve the actual status in status_code. Monthly request allowance and customer quota accounting use every count; only a billable status debits the customer balance.
  • period_start is an RFC 3339 timestamp with an explicit UTC offset. Both 2026-08-03T18:00:00Z and 2026-08-03T18:00:00+00:00 are valid. The server buckets it to the hour and rejects timestamps older than 45 days or more than 5 minutes in the future.
  • Create idempotency_key before the first send. Once created, the item is immutable. Keep the same key, API key ID, status, count and period on every timeout or server-error retry, including after a process restart.
  • accepted means the item transaction was applied now. duplicate means the same tenant and identical idempotency payload was applied earlier. Both are terminal acknowledgements and must remove that item from the pending queue.
  • rejected is an item-level, actionable domain or validation failure. Do not retry the unchanged item. retry is temporary and keeps the item pending. One response can therefore acknowledge some items and reject or defer others.
  • Reusing an idempotency key with a different API key, policy, status, count, or period is rejected instead of being treated as a duplicate.
  • Limits: 1–1000 items per call, 1–10,000,000 in count per item, 600 calls/minute per tenant.
  • If the result is ambiguous, retain the item and retry with backoff. Durable receipts make a retry return duplicate instead of charging twice.

Local enforcement checklist ​

On each incoming request to your API:

  1. Read the key from your header (e.g. X-API-Key); missing → 401.
  2. hash = sha256_hex(key); look it up in the snapshot; miss → verify endpoint (single-flight) → still invalid → 401.
  3. Customer status != "active" → 403.
  4. billing_model != "none" and credit_balance_micro <= 0 → 402 (account for usage you've counted locally since the last snapshot).
  5. monthly_quota set and month_requests + local count ≥ quota → 429.
  6. If platform_hard_cap is true, reserve one request against the current platform_monthly_remaining before the handler. Redis can coordinate this reservation across conforming workers, but it cannot prevent bypass by code that does not use the integration.
  7. Enforce effective.rps per customer. Use one atomic Redis operation keyed by customer or credential when more than one worker shares a cap → over → 429 with Retry-After.
  8. Serve the request. For every authenticated response, create or increment a pending aggregate for (api_key_id, usage_policy_id, status_code, current_hour). When a flush detaches that count, assign the immutable usage item its own idempotency key. Only billable statuses affect the local balance estimate.

Minimal durable reporting core (Python) ​

The important property is that pending rows survive process restarts and are deleted only after their own acknowledgement. This standard-library SQLite example intentionally records one item per authenticated response for clarity. A production implementation can aggregate first, then create an immutable item when it detaches an aggregate for sending.

python
import sqlite3
import uuid
from datetime import datetime, timezone

import requests

BASE = "https://api.microauth.com"
HEADERS = {"Authorization": "Bearer mas_..."}

db = sqlite3.connect("microauth-usage.sqlite3")
db.execute("PRAGMA journal_mode=WAL")
db.execute(
    """
    CREATE TABLE IF NOT EXISTS pending_usage (
        idempotency_key TEXT PRIMARY KEY,
        api_key_id TEXT NOT NULL,
        usage_policy_id TEXT NOT NULL,
        status_code INTEGER NOT NULL,
        count INTEGER NOT NULL,
        period_start TEXT NOT NULL
    )
    """
)

def hour_start() -> str:
    return (
        datetime.now(timezone.utc)
        .replace(minute=0, second=0, microsecond=0)
        .isoformat(timespec="seconds")
    )

def record(api_key_id: str, usage_policy_id: str, status_code: int) -> None:
    db.execute(
        "INSERT INTO pending_usage VALUES (?, ?, ?, ?, ?, ?)",
        (str(uuid.uuid4()), api_key_id, usage_policy_id, status_code, 1, hour_start()),
    )
    db.commit()

def flush() -> None:
    rows = db.execute(
        """
        SELECT idempotency_key, api_key_id, usage_policy_id,
               status_code, count, period_start
        FROM pending_usage
        ORDER BY rowid
        LIMIT 1000
        """
    ).fetchall()
    if not rows:
        return

    names = (
        "idempotency_key", "api_key_id", "usage_policy_id",
        "status_code", "count", "period_start",
    )
    items = [dict(zip(names, row)) for row in rows]

    try:
        response = requests.post(
            f"{BASE}/sdk/v1/usage",
            headers=HEADERS,
            json={"items": items},
            timeout=5,
        )
    except requests.RequestException:
        return  # Every row remains durable for the next retry.

    if response.status_code >= 500 or response.status_code == 429:
        return
    response.raise_for_status()  # Keep rows and surface an actionable 4xx.

    terminal = {
        result["idempotency_key"]
        for result in response.json().get("results", [])
        if result.get("status") in {"accepted", "duplicate", "rejected"}
    }
    db.executemany(
        "DELETE FROM pending_usage WHERE idempotency_key = ?",
        [(item_id,) for item_id in terminal],
    )
    db.commit()

Production code must also bound the queue, serialize concurrent flushes, validate the complete acknowledgement set, use backoff with jitter, expose queue age and depth, and fail shutdown clearly when work remains. It also needs the snapshot, verify fallback, negative caching, shared RPS limiting, and the allowance reservation described above.

See also ​

MicroAuth is a product of Zyref, LLC.