How it works / Usage widget
Usage widget
The usage widget shows one customer their usage inside your own dashboard: how much of this period's plan they've used, up to 30 days of history (7 days on the Free plan), and which endpoints they called. You put it in an iframe. Your server signs a short-lived token for that customer, and the widget shows nothing without it.
Set it up
- In the console, open your app's Portal page and find Usage widget. Create an embed key. It's shown once: put it in your server's environment as
USAGERAIL_EMBED_KEY. - When a signed-in customer opens the page with the widget, sign a token for them on your server.
- Put the widget in an iframe, with the token after the
#.
import { createHmac, randomUUID } from "node:crypto";
// One customer's usage widget link, valid for an hour. Sign it on your server only.
export function usageWidgetToken(consumerId) {
const now = Math.floor(Date.now() / 1000);
const part = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
const claims = { app_id: "app_...", consumer_id: consumerId, iat: now, exp: now + 3600, jti: randomUUID() };
const unsigned = `${part({ alg: "HS256", typ: "JWT" })}.${part(claims)}`;
const signature = createHmac("sha256", process.env.USAGERAIL_EMBED_KEY).update(unsigned).digest("base64url");
return `${unsigned}.${signature}`;
}import base64, hashlib, hmac, json, os, time, uuid
# One customer's usage widget link, valid for an hour. Sign it on your server only.
def usage_widget_token(consumer_id: str) -> str:
def part(data: bytes) -> str:
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
now = int(time.time())
claims = {"app_id": "app_...", "consumer_id": consumer_id, "iat": now, "exp": now + 3600, "jti": str(uuid.uuid4())}
unsigned = part(json.dumps({"alg": "HS256", "typ": "JWT"}).encode()) + "." + part(json.dumps(claims).encode())
signature = hmac.new(os.environ["USAGERAIL_EMBED_KEY"].encode(), unsigned.encode(), hashlib.sha256).digest()
return unsigned + "." + part(signature)<iframe src="https://your-app.usagerail.app/embed#token=TOKEN" title="Usage" width="100%" height="560" style="border: 0"></iframe>The console's copy of this code has your app's ID and portal address filled in. Any JWT library that signs HS256 works too.
The token
| Claim | What it is |
|---|---|
app_id | Your app's ID. It has to match the portal the widget is loaded from. |
consumer_id | The customer's ID in UsageRail, such as cons_.... Your backend gets it from the console, or from the server API: POST /v1/consumers with your own user ID returns that customer. |
iat | When you signed it, in Unix seconds. |
exp | When it stops working, at most 24 hours after iat. |
jti | Any unique string, such as a UUID. |
The header must say HS256; tokens signed with any other algorithm are refused. A token that's expired, signed with the wrong key, missing a claim, or made for another app or for a customer being deleted gets the same answer: the widget says the link has expired or isn't valid. A suspended customer can still see their usage.
Who can see it
Anyone who has the link can see that customer's usage until the token expires. Forwarding the link shares it, so keep tokens short-lived: an hour is plenty for a dashboard page, and you sign a fresh one each time the page loads.
The token sits after the #. Browsers don't send that part of a URL to servers, so it doesn't end up in access logs. The widget's own script reads it and sends it with each request for data. The widget uses no cookies, so it works in browsers that block third-party cookies.
There's no way yet to cancel a single link before it expires. Rotating the embed key cancels all of them at once.
Rotating the embed key
Rotate the key from the same place in the console. The old key stops working at once, and so does every token signed with it, including widgets that are already open: their next request for data is refused. Put the new key on your servers straight away.
Options
Add these before the #:
| Option | What it does |
|---|---|
?theme=light or ?theme=dark | Fixes the theme. Without it, the widget follows the visitor's system setting. |
?days=7 | How many days of history to show, from 1 to 30. The default is 30, and your UsageRail plan's analytics retention caps it: 7 days on Free. |
For example: https://your-app.usagerail.app/embed?theme=dark&days=14#token=TOKEN.
What it shows
The top of the widget answers how much of this period the customer has used. The wording follows their plan:
- Stops at the limit: usage against the limit and the date it resets, with a bar. A mark on the bar shows how far through the period today is, so a customer can see whether they're on pace to run out. Once the limit is used up, the bar turns red and says requests are refused until the reset.
- Billed for overage (not live yet): usage this period and how much the plan includes.
- Prepaid credits (not live yet): the credits left.
- No cap: usage this period.
Below that, a daily chart splits each day into requests that counted toward the plan and requests that didn't, such as refused ones, and a list shows the units each endpoint used. Dates follow your app's time zone.
The widget uses your portal's name, color and footer setting. "Powered by UsageRail" follows the same rule as in the portal: it can't be hidden on the Free plan.