Skip to content

Tags & attribution

Tags are key=value labels you attach to a call — which customer it served, which feature sent it, which environment. Belarel records them with the call, returns them on GET /v1/requests/{id}, and lets you aggregate usage by any of them. Tags are attribution only: they never change what a key is allowed to do.

Source Example
X-Belarel-Tags header X-Belarel-Tags: customer=acme,feature=support
metadata in the body (the OpenAI field, on Chat Completions and Responses) "metadata": { "customer": "acme" }
belarel_tags in the body "belarel_tags": { "customer": "acme" }

The header works with any client, including ones that cannot add body fields. metadata is the field the OpenAI SDKs already type, so it needs no cast.

Terminal window
curl https://api.belarel.com/v1/chat/completions \
-H "Authorization: Bearer $BELAREL_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Belarel-Tags: customer=acme,feature=support" \
-d '{"model": "<model-id>", "messages": [{"role": "user", "content": "Hello"}]}'
Rule Limit
Header format Comma-separated key=value pairs; spaces around each pair are trimmed.
Body format An object whose values are all strings.
Keys Lowercased, then 1–40 characters of a-z, 0-9, _, ., -, starting with a letter or digit.
Values Strings of at most 200 characters.
Count At most 16 distinct tags that you send per call — header, metadata and belarel_tags combined. The key’s default tags are added on top and do not count.

A malformed tag refuses the call before anything is spent:

Code HTTP Meaning What to do
invalid_tags 400 A pair without =, a body value that is not a string, an invalid key, a value over 200 characters, or more than 16 tags. The message names the problem. Fix the tag and retry.
missing_tags 400 The key requires a tag the model call did not carry. The message lists the missing names. Send the tag, or ask for a default on the key.

Both errors carry param: "belarel_tags", whichever source held the tag.

When the same tag name arrives from several places, the most specific one wins:

  1. belarel_tags in the body
  2. metadata in the body
  3. the X-Belarel-Tags header
  4. the application headers (X-Title, HTTP-Referer, below)
  5. the key’s default tags

Following the OpenRouter convention, a client application can name itself:

  • X-Title: Acme Support becomes the tag app=Acme Support (cut to 200 characters).
  • HTTP-Referer: https://support.acme.com/inbox becomes app_url=https://support.acme.com — the origin only.

These two never cause a refusal. They are added only if you did not set app or app_url yourself, and only if the call has room under the 16-tag limit.

A key can carry required tags — names every model call must include — and default tags, applied when the call does not set them. A default satisfies a requirement. Both are visible on GET /v1/key as required_tags and default_tags.

A sub-key always requires at least its parent’s required tags, and inherits the parent’s default tags under its own. A reseller whose key requires customer therefore gets that tag on every sub-key’s traffic.

Group your aggregates by any tag name with group_by=tag:<name>:

Terminal window
curl "https://api.belarel.com/v1/usage?group_by=tag:customer" \
-H "Authorization: Bearer $BELAREL_API_KEY"

Each row’s group is a tag value; calls without that tag are grouped under null. The aggregate covers the key and its sub-keys. See Cost & usage.