Guides · Authentication

Authentication

Every API request authenticates with an API key. Cost-aware ingestion also signs the request with HMAC. This page covers all three: keys, the scopes that gate each endpoint, and how to sign an ingestion request.

API keys

Create a key in your dashboard (Settings → API Keys) and send it on every request in the Authorization header as a bearer token.

bash
curl https://api.bearlumen.com/v1/ping \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"

Keep your keys secret

A key grants access to your organization's data. Never commit one to version control or ship it in client-side code. Load it from an environment variable in production, and rotate any key that may have been exposed.

Scopes

Each key carries scopes that limit what it can do. An endpoint returns 403 if your key is missing the scope it requires, so grant a key only the scopes it needs.

ScopeGrantsUsed by
costs:readRead cost summaries, breakdowns, trends, attribution, and per-end-user costs.Attribution Data, Cost Data, Dimension Data, End User Costs, Insights, Rate Cards
dimensions:readRead custom dimension definitions.Dimension Data
revenue:readRead margin and profitability analysis.Insights, Margin Data
usage:readQuery recorded usage events and aggregations.Usage Tracking
usage:writeSend usage events (POST /usage-events and /usage-events/batch).Usage Events

Signing ingestion requests (HMAC)

The usage-ingestion endpoints (POST /usage-events and /usage-events/batch) require an HMAC-SHA256 signature in addition to your API key. Read endpoints do not.

Using an SDK? This is automatic

The Node.js and Python SDKs sign every ingestion request for you. You only need this recipe when you call the ingestion API directly over HTTP.

  1. Set X-Timestamp to the current Unix time in seconds.
  2. Build the payload string {timestamp}.{body}, where body is the raw JSON request body exactly as sent.
  3. Compute HMAC-SHA256 of that payload keyed with your API key, hex-encoded.
  4. Send X-Signature: v1={hex digest}.
bash
API_KEY="sk_test_YOUR_API_KEY"
TS=$(date +%s)
BODY='{"idempotency_key":"3f9a2b1c-8d7e-4f6a-9b2c-1a2b3c4d5e6f","model":"gpt-4o","input_tokens":1500,"output_tokens":420,"event_invoked_at":"2026-01-15T10:30:00.000Z","provider":"openai"}'

# HMAC-SHA256 over "{timestamp}.{raw body}", keyed with your API key, hex-encoded.
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$API_KEY" -r | cut -d' ' -f1)

curl -X POST https://api.bearlumen.com/v1/usage-events \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: v1=$SIG" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Two details that trip people up

Sign the raw body bytes exactly as sent. If you re-serialize the JSON after signing (different key order or whitespace), the signature will not match.

The X-Timestamp must be current: requests whose timestamp is more than 5 minutes from server time are rejected with a 401. This is replay protection, not a limit on how old your event_invoked_at data can be (backdated events are fine).

Request headers

HeaderValue
AuthorizationBearer YOUR_API_KEY
X-TimestampCurrent Unix time in seconds
X-Signaturev1={hex HMAC-SHA256 of "{timestamp}.{body}"}
Content-Typeapplication/json

Next steps