Metadata

How to fetch token metadata and user-wide token counts.

Metadata returns a snapshot for one API token and a summary of the owning user's token counts. The response combines token-level details, such as usage and timestamps, with user-wide totals for valid, invalid, and total tokens.

The IAM service builds this response in two steps. It first verifies the target token without consuming usage counters, then it queries the database again to count every token owned by the same user.

The metadata helper reads the existing token state. It does not rotate, revoke, rename, or update the token.

You can fetch metadata in 3 ways:

  • Directly calling getUserApiKeysMetaData.
  • Using the privateActionManager to validate ownership before fetching.
  • Calling the POST /api/manage/metadata endpoint from an authenticated client.
Follow the Creating Tokens docs to create a token first before attempting to fetch its metadata.

Fetching Metadata

You can fetch metadata either directly in the library or through the manager-backed route. The direct helper resolves one token and the owning user's counts, while the route adds ownership validation and authenticated request protections.

Using the library

To fetch metadata directly, you call getUserApiKeysMetaData. This function verifies the token, skips IP checks, skips usage counter updates, and then fetches the user's token counts.

example.ts
import { getUserApiKeysMetaData } from '@riavzon/auth'

const results = await getUserApiKeysMetaData(token, 'restricted')

if (results.ok) {
    console.log(results.data.tokenMeta.name)
    console.log(results.data.counts.totalValidTokens)
}

On success you get back an object with:

{
    ok: true,
    date: new Date().toISOString(),
    data: {
        tokenMeta: {
            name: 'server token',
            tokenId: 12,
            userId: 42,
            createdAt: '2026-05-01T10:00:00.000Z',
            expiresAt: '2026-05-01T11:00:00.000Z',
            lastUsed: '2026-05-01T10:30:00.000Z',
            usageCount: 19,
            providedPrivilege: 'restricted'
        },
        counts: {
            totalInvalidTokens: 2,
            totalValidTokens: 4,
            total: 6
        }
    }
}

The counts object is user-wide. It does not only describe the selected token. It includes every token row owned by the same user.

The helper forwards the stored token metadata from verifyApiKey with skipCountUpdates = true. Because of that, usageCount and lastUsed are read-only snapshots and are not incremented by the metadata request.

On error, getUserApiKeysMetaData returns the branch that failed.

If verifyApiKey cannot validate the token, the helper forwards that reason:

{
    ok: false,
    date: new Date().toISOString(),
    reason: 'Invalid key'
}

This happens when the token is missing, revoked, malformed, or does not match the provided privilege. Because the helper calls verifyApiKey with byPassIpCheck = true, it does not return Invalid Host.

If the verification step finds an expired token, it invalidates it and returns:

{
    ok: false,
    date: new Date().toISOString(),
    reason: 'Token expired'
}

If the verification query fails, the helper can return:

{
    ok: false,
    date: new Date().toISOString(),
    reason: 'Server error validating token.'
}

If the metadata helper fails after verification, such as when the count query throws, the catch block returns:

{
    ok: false,
    date: new Date().toISOString(),
    reason: 'Error getting metadata'
}

If verifyApiKey fails without a reason, the helper falls back to Cant get metadata about invalid token.

Signature

The direct helper exposes the following signature:

export async function getUserApiKeysMetaData(
        rawApiKey: string,
        providedPrivilege: 'demo' | 'restricted' | 'protected' | 'full' | 'custom',
): Promise<Results<SingleTokenMeta>>

Parameters

The metadata helper accepts the following parameters:

FieldTypeDescription
rawApiKeystringThe token to inspect. It can be hashed or Raw.
providedPrivilege'demo' | 'restricted' | 'protected' | 'full' | 'custom'The privilege that must match the stored token privilege.

Return type

The metadata helper returns Results<SingleTokenMeta>.

SingleTokenMeta contains the following fields:

FieldTypeDescription
tokenMetaTokenMetaMetadata for the selected token.
counts{ totalInvalidTokens: number; totalValidTokens: number; total: number }User-wide token counts for the token owner.

TokenMeta contains the following fields on successful responses:

FieldTypeDescription
namestringThe token name.
tokenIdnumberThe internal database id of the token.
userIdnumberThe owner of the token.
createdAtstringThe stored creation timestamp.
expiresAtstring | nullThe stored expiration timestamp, or null when the token does not expire.
lastUsedstring | nullThe last stored successful usage timestamp.
usageCountnumberThe stored usage count.
providedPrivilege'demo' | 'restricted' | 'protected' | 'full' | 'custom'The privilege used for metadata verification.
This action should be performed by a fully authenticated client.
This function bypasses IP restriction checks and does not increment usage counters while it verifies the token. Only call it directly in trusted server-side code.

With the privateActionManager

The safest internal method is using the privateActionManager. This function validates the publicIdentifier, token id, token name, user id, and current valid status before it dispatches the metadata lookup.

example.ts
const metaDataRes = await privateActionManager(
    userId,
    tokenId,
    publicIdentifier,
    tokenName,
    { action: 'metadata' }
)

The privateActionManager returns the response of getUserApiKeysMetaData directly. Learn more at the introduction page.

The manager only selects rows where valid = 1. Because of that, manager-backed metadata cannot target an already revoked token, and the manager returns Bad Request before it reaches getUserApiKeysMetaData.

Using the route

Let's say you want to fetch metadata for a token from your authenticated client or BFF.

POST /api/manage/metadata body:

{
    "tokenId": 12,
    "publicIdentifier": "public_identifier",
    "name": "the token name"
}

On success you will get the following response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
    "ok": true,
    "date": "current date",
    "data": {
        "tokenMeta": {
            "name": "server token",
            "tokenId": 12,
            "userId": 42,
            "createdAt": "2026-05-01T10:00:00.000Z",
            "expiresAt": "2026-05-01T11:00:00.000Z",
            "lastUsed": "2026-05-01T10:30:00.000Z",
            "usageCount": 19,
            "providedPrivilege": "restricted"
        },
        "counts": {
            "totalInvalidTokens": 2,
            "totalValidTokens": 4,
            "total": 6
        }
    }
}

Standard authentication, body validation, and other shared management-route errors are documented on the introduction page.

The route-specific failures come from the manager and the metadata helper. Unlike rotation and IP updates, the metadata controller returns these failures as 401 Unauthorized.

If the publicIdentifier checksum is invalid, the route returns Invalid identity.

If the manager cannot match a currently valid token for the given tokenId, publicIdentifier, name, and userId, it returns Bad Request. That means the token is already revoked, belongs to a different user, or the request body does not point to an active row.

If the manager finds the token but getUserApiKeysMetaData cannot verify it, the route forwards the helper reason. That can be Invalid key, Token expired, Server error validating token., Cant get metadata about invalid token, or Error getting metadata.

If privateActionManager itself fails unexpectedly, the route can also forward Server Error.

All of these route-specific failures are returned as 401 Unauthorized by the controller:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8

{
    "ok": false,
    "date": "current date",
    "reason": "reason from privateActionManager or getUserApiKeysMetaData"
}

Rate Limits

The endpoint also enforces rate limits controlled under the following configuration options:

  • rate_limiters.apiTokensLimiters.operationRateLimits.getMetadataTokenLimiter
    • The main limiter for the metadata endpoint. The default allows 20 requests in a window of 2 seconds and will trigger a block for 30 minutes if this limit is met. Consecutive triggers in this period will block the client permanently.
  • rate_limiters.apiTokensLimiters.generalUnionLimiter - A burstLimiter and a slowLimiterunion limiter. It enforces no more than one request per second, and only 50 per minute. No consecutive triggers in this limiter are provided, and triggering it again will result in a permanent ban. The limiter is restarted on successful metadata requests.
    • burstLimiter - Will block the client for 15 minutes.
    • slowLimiter - Will block for 1 hour.

Metadata Process

Metadata collection reads the current token state without consuming the token.

Validate and resolve the target token

When you use the manager or route, the system validates the publicIdentifier checksum and confirms that the token belongs to the current user and is still valid. This check happens before getUserApiKeysMetaData runs.

Verify without consuming usage

The helper calls verifyApiKey with skipCountUpdates = true, byPassIpCheck = true, and isInternalHash = true. That means it can inspect the stored token without incrementing usageCount, without refreshing lastUsed, and without enforcing the token's IP whitelist.

Count the owner's tokens

After verification succeeds, the helper calls totalUserTokensCount for the resolved userId. This returns the number of valid tokens, invalid tokens, and the total token count for that user.

Return the combined snapshot

The final payload returns tokenMeta for the selected token and counts for the owner. This lets you inspect one token while also understanding the user's overall token inventory.

Configuration Reference

These configuration keys control metadata throttling in the service.

Rate limiters

The metadata flow relies on the following limiter configuration:

LimiterDescription
rate_limiters.apiTokensLimiters.operationRateLimits.getMetadataTokenLimiterThe main rate limiter for the metadata endpoint
rate_limiters.apiTokensLimiters.generalUnionLimiterGeneral burst limiter
Logo