Testing with Postman
This guide explains how to configure Postman to authenticate against the bloqr-backend API using either a Clerk JWT or an API key. It covers environment...
Testing with Postman
This guide explains how to configure Postman to authenticate against the bloqr-backend API using either a Clerk JWT or an API key. It covers environment setup, obtaining tokens, pre-request scripts for automatic token refresh, and ready-to-use request examples for every major endpoint.
Table of Contents
Prerequisites
- Postman desktop app (v10+) or Postman Web
- An account registered at the bloqr-backend web UI (
https://api.bloqr.dev/)
or a locally running worker (http://localhost:8787) - (Optional) Clerk account access for your bloqr-backend application — see Clerk Dashboard Setup
Postman Environment Setup
Create a dedicated Postman environment to avoid hard-coding values into requests.
Step 1: Create the Environment
- Open Postman → Environments (left sidebar) → +
- Name it
bloqr-backend(orbloqr-backend-localfor local dev) - Add the following variables:
| Variable | Type | Initial / Current Value | Notes |
|---|---|---|---|
baseUrl | default | https://api.bloqr.dev | Change to http://localhost:8787 for local dev |
apiKey | secret | (your abc_... key) | Obtained from Settings → API Keys in the web UI |
clerkJwt | secret | (leave blank — populated automatically) | Set by pre-request script or manually |
clerkPublishableKey | default | pk_test_... or pk_live_... | From Clerk Dashboard → API Keys |
adminKey | secret | (your admin key) | Only needed for /admin/* endpoints — see Admin Access |
- Click Save
- Select
bloqr-backendas the Active Environment (top-right dropdown)
Step 2: Activate the Environment
Ensure the environment is selected in the top-right Environment dropdown before sending any requests.
Authentication Methods
The bloqr-backend supports three authentication methods. For Postman testing, API Key is the simplest option. Use Clerk JWT when you need to test tier-specific behaviour or user-scoped endpoints.
Method 1: API Key (Recommended for Testing)
API keys are the easiest way to authenticate in Postman because they never expire (unless you set an expiration) and don't require a browser sign-in flow.
Step 1: Create an API Key
-
Sign in to the web UI
-
Go to Settings → API Keys
-
Click "Create API Key"
-
Fill in:
- Name:
Postman Testing - Scopes: Select all that apply —
compile,rules,admin - Expiration: Optional
- Name:
-
Copy the key (starts with
abc_) — it's only shown once
Step 2: Add the Key to Postman
Paste the key into the apiKey environment variable created above.
Step 3: Configure Authorization
In any request (or at the collection level — see Collection Setup):
- Open the request → Authorization tab
- Select Type:
Bearer Token - Set Token:
{{apiKey}}
All requests using {{apiKey}} will automatically send:
Authorization: Bearer abc_Xk9mP2...
Method 2: Clerk JWT (Manual Token)
Use this method when you need a real Clerk JWT (e.g., to test tier enforcement or user-specific features) but don't want to set up a script.
Step 1: Sign in to the Web UI and Copy the JWT
-
Open the web UI in your browser and sign in
-
Open DevTools → Application tab → Session Storage or Local Storage
— look for keys prefixed with__clerk_db_jwtor__session
or use DevTools Network tab:- Filter requests for
Authorization: Bearer - Copy the token value (a long JWT string starting with
eyJ...)
Tip: The easiest method is to use the browser DevTools console:
// Paste this in the browser console while on the web UI const session = window.Clerk?.session; session?.getToken().then(t => console.log(t));Copy the printed token.
- Filter requests for
-
Paste the JWT into the
clerkJwtenvironment variable in Postman
Step 2: Configure Authorization
In your request → Authorization tab:
- Type:
Bearer Token - Token:
{{clerkJwt}}
Note: Clerk JWTs are short-lived (typically 60 seconds). You will need to refresh this token frequently. Consider using the pre-request script method instead.
Method 3: Clerk JWT (Automatic via Pre-Request Script)
Status: Clerk's hosted sign-in flow requires browser interaction, so fully headless JWT retrieval is not natively supported in Postman without the Clerk Frontend API. This script uses the Clerk Frontend API to obtain a session token from existing credentials.
Best for: Teams with a dedicated test account where the session can be refreshed programmatically.
See Pre-Request Script for Automatic JWT Refresh for the full script.
Collection Setup
Creating the Collection
- In Postman, click Collections (left sidebar) → + → Blank Collection
- Name it
bloqr-backend - Add a description:
Testing collection for the bloqr-backend API
Collection-Level Authorization
Set authorization once at the collection level so all requests inherit it automatically.
-
Click the collection name → Authorization tab
-
Configure:
- Type:
Bearer Token - Token:
{{apiKey}}
- Type:
-
Click Save
All requests in the collection will default to Inherit auth from parent, which picks up {{apiKey}} automatically. Override on individual requests as needed.
Pre-Request Script for Automatic JWT Refresh
Add this script to the collection's Pre-request Script tab to automatically fetch and cache a fresh Clerk JWT before each request. This uses the Clerk Frontend API with a long-lived session token.
Setup required: This script requires a
clerkSessionTokenenvironment variable containing a long-lived Clerk session token. Obtain it by signing in to the Clerk-powered web UI and copying the__clientcookie value, or by calling Clerk's/v1/clientendpoint from a signed-in browser session.
// Collection Pre-request Script — Automatic Clerk JWT Refresh
// Refreshes the JWT if it is missing or expires within the next 30 seconds.
const jwt = pm.environment.get('clerkJwt');
const jwtExpiry = pm.environment.get('clerkJwtExpiry');
const now = Math.floor(Date.now() / 1000);
// Skip refresh if the cached JWT is still valid for > 30 seconds
if (jwt && jwtExpiry && parseInt(jwtExpiry) > now + 30) {
console.log('[Clerk] Using cached JWT, expires in', parseInt(jwtExpiry) - now, 's');
return;
}
// Derive the Clerk Frontend API URL from the publishable key
// pk_test_<base64(domain)> → decode to get the Clerk Frontend API host
const publishableKey = pm.environment.get('clerkPublishableKey');
if (!publishableKey) {
console.warn('[Clerk] clerkPublishableKey not set — skipping JWT refresh');
return;
}
const sessionToken = pm.environment.get('clerkSessionToken');
if (!sessionToken) {
console.warn('[Clerk] clerkSessionToken not set — cannot refresh JWT');
console.warn('[Clerk] Set clerkSessionToken to your __client cookie value from a signed-in browser session');
return;
}
// Decode the Clerk Frontend API host from the publishable key
const keyPart = publishableKey.replace(/^pk_(test|live)_/, '');
let clerkFapiHost;
try {
clerkFapiHost = atob(keyPart).replace(/\$$/, '');
} catch (e) {
console.error('[Clerk] Failed to decode publishable key:', e.message);
return;
}
const fapiUrl = `https://${clerkFapiHost}/v1/client/sessions/{{clerkSessionId}}/tokens`;
pm.sendRequest(
{
url: fapiUrl,
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': `__client=${sessionToken}`,
},
},
(err, res) => {
if (err) {
console.error('[Clerk] JWT refresh failed:', err);
return;
}
if (res.code !== 200) {
console.error('[Clerk] JWT refresh returned', res.code, res.text());
return;
}
const body = res.json();
const newJwt = body?.jwt;
if (!newJwt) {
console.error('[Clerk] No JWT in response:', body);
return;
}
// Decode expiry from JWT payload (middle segment)
try {
const payload = JSON.parse(atob(newJwt.split('.')[1]));
pm.environment.set('clerkJwtExpiry', String(payload.exp));
} catch (_) {
// If decode fails, assume 60s expiry
pm.environment.set('clerkJwtExpiry', String(now + 60));
}
pm.environment.set('clerkJwt', newJwt);
console.log('[Clerk] JWT refreshed successfully');
}
);
Simpler alternative: For most Postman testing, use the
{{apiKey}}variable instead. API keys are long-lived and require no scripting.
Request Examples
Import the examples below by creating new requests in the bloqr-backend collection. All examples assume the bloqr-backend environment is active.
Health & Version (Public — No Auth)
These endpoints require no authentication and are useful to verify connectivity.
GET /health
| Field | Value |
|---|---|
| Method | GET |
| URL | {{baseUrl}}/health |
| Auth | No auth |
Expected Response (200 OK):
{
"status": "ok",
"timestamp": "2026-03-13T00:00:00.000Z"
}
GET /api/version
| Field | Value |
|---|---|
| Method | GET |
| URL | {{baseUrl}}/api/version |
| Auth | No auth |
Compile Filter List
POST /compile — Compile with API Key
| Field | Value |
|---|---|
| Method | POST |
| URL | {{baseUrl}}/compile |
| Auth | Bearer Token → {{apiKey}} |
| Content-Type | application/json |
Body (raw JSON):
{
"sources": [
"https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_2_Base/filter.txt"
],
"transformations": ["validate", "deduplicate"]
}
Expected Response (200 OK):
{
"success": true,
"rules": ["...", "..."],
"stats": {
"total": 1234,
"deduplicated": 56
}
}
POST /compile — Unauthenticated (Anonymous)
| Field | Value |
|---|---|
| Method | POST |
| URL | {{baseUrl}}/compile |
| Auth | No auth |
| Content-Type | application/json |
Note: Anonymous access is rate-limited to 10 req/min and will be removed in a future release. Expect a
429 Too Many Requestsif you exceed the limit. See Removing Anonymous Access.
Expected Response Headers:
X-Auth-Warning: Anonymous access will be removed. Please register for continued access.
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
POST /compile/batch — Batch Compile
| Field | Value |
|---|---|
| Method | POST |
| URL | {{baseUrl}}/compile/batch |
| Auth | Bearer Token → {{apiKey}} (requires compile scope) |
| Content-Type | application/json |
Body:
{
"jobs": [
{
"id": "job-1",
"sources": ["https://example.com/list1.txt"],
"transformations": ["validate"]
},
{
"id": "job-2",
"sources": ["https://example.com/list2.txt"],
"transformations": ["deduplicate", "compress"]
}
]
}
API Key Management
These endpoints require a valid Clerk JWT (not an API key — you can't manage API keys using an API key).
GET /api/keys — List Your API Keys
| Field | Value |
|---|---|
| Method | GET |
| URL | {{baseUrl}}/api/keys |
| Auth | Bearer Token → {{clerkJwt}} |
Expected Response (200 OK):
{
"success": true,
"keys": [
{
"id": "key_abc123",
"name": "Postman Testing",
"scopes": ["compile", "rules"],
"createdAt": "2026-01-01T00:00:00.000Z",
"expiresAt": null,
"lastUsedAt": "2026-03-13T00:00:00.000Z"
}
]
}
POST /api/keys — Create an API Key
| Field | Value |
|---|---|
| Method | POST |
| URL | {{baseUrl}}/api/keys |
| Auth | Bearer Token → {{clerkJwt}} |
| Content-Type | application/json |
Body:
{
"name": "My New Key",
"scopes": ["compile"],
"expiresInDays": 90
}
Expected Response (201 Created):
{
"success": true,
"key": {
"id": "key_xyz789",
"name": "My New Key",
"token": "abc_Xk9mP2...",
"scopes": ["compile"],
"expiresAt": "2026-06-11T00:00:00.000Z"
}
}
Important: Copy the
tokenvalue immediately — it is only returned once.
PATCH /api/keys/:id — Update an API Key
| Field | Value |
|---|---|
| Method | PATCH |
| URL | {{baseUrl}}/api/keys/key_xyz789 |
| Auth | Bearer Token → {{clerkJwt}} |
| Content-Type | application/json |
Body:
{
"name": "Renamed Key",
"scopes": ["compile", "rules"]
}
DELETE /api/keys/:id — Revoke an API Key
| Field | Value |
|---|---|
| Method | DELETE |
| URL | {{baseUrl}}/api/keys/key_xyz789 |
| Auth | Bearer Token → {{clerkJwt}} |
Expected Response (200 OK):
{
"success": true,
"message": "API key revoked"
}
Admin Endpoints
Admin endpoints require both the X-Admin-Key header and (if Cloudflare Access is configured) a valid CF-Access-JWT-Assertion header. See Admin Access for full details.
GET /admin/storage/stats
| Field | Value |
|---|---|
| Method | GET |
| URL | {{baseUrl}}/admin/storage/stats |
| Auth | No auth (uses custom header instead) |
| Custom Header | X-Admin-Key: {{adminKey}} |
To add the custom header:
-
Open the request → Headers tab
-
Add a new header:
- Key:
X-Admin-Key - Value:
{{adminKey}}
- Key:
Expected Response (200 OK):
{
"success": true,
"stats": {
"totalUsers": 42,
"totalApiKeys": 108
}
}
Unauthorized Response (401 Unauthorized):
{
"success": false,
"error": "Unauthorized"
}
Testing All Tiers
To verify tier-based rate limiting and access control, test with credentials for each tier:
| Tier | How to Get Credentials | Rate Limit | Test Strategy |
|---|---|---|---|
| Anonymous | No auth | 10 req/min | Send 11+ requests in a minute — expect 429 on the 11th |
| Free | Sign up → {{clerkJwt}} or API key with compile scope | 60 req/min | Same as above with higher limit |
| Pro | Set tier: pro in Clerk public metadata | 300 req/min | Same |
| Admin | Set tier: admin, role: admin in Clerk metadata | Unlimited | Test /admin/* endpoints |
Setting Up a Test User's Tier (via Clerk Dashboard)
-
Go to dashboard.clerk.com → your application → Users
-
Find the test user → click their name
-
Scroll to Public metadata → click Edit
-
Set:
{ "tier": "pro" } -
Click Save
-
The webhook fires a
user.updatedevent, syncing the tier to PostgreSQL -
Obtain a fresh JWT (the old JWT may still carry the old tier until it expires)
Common Errors & Troubleshooting
| HTTP Status | Error | Likely Cause | Fix |
|---|---|---|---|
401 Unauthorized | "Authentication required" | Missing or malformed Authorization header | Ensure Bearer {{apiKey}} or Bearer {{clerkJwt}} is set correctly |
401 Unauthorized | "Invalid token" | Expired or invalid JWT | Refresh {{clerkJwt}} — Clerk JWTs are short-lived (~60s) |
403 Forbidden | "Insufficient tier" | Your account tier is too low for the endpoint | Upgrade tier in Clerk metadata, or use an API key with the required scope |
403 Forbidden | "Missing required scope" | API key lacks a required scope (e.g., admin) | Create a new API key with the correct scopes |
429 Too Many Requests | "Rate limit exceeded" | Exceeded your tier's request-per-minute limit | Wait 60 seconds, or use a higher-tier account |
401 Unauthorized (admin) | "Unauthorized" | Missing or incorrect X-Admin-Key header | Check {{adminKey}} environment variable |
400 Bad Request | "Invalid request body" | Malformed JSON or missing required fields | Validate your JSON body — check sources is an array |
Verifying the Authorization Header is Set
In Postman, after sending a request:
- Click Console (bottom bar) → find the request
- Expand Request Headers
- Confirm
Authorization: Bearer abc_...(oreyJ...for JWT) is present
JWT Claims Inspection
To inspect a Clerk JWT's claims (tier, expiry, user ID):
-
Copy the value of
{{clerkJwt}}from the environment -
Paste it into jwt.io
-
The Payload section shows:
sub— Clerk user IDexp— expiry timestamp (Unix)metadata.tier— user's tier (if JWT template is configured — see Clerk Setup)
Further Reading
- API Authentication Guide — Full auth method reference
- Clerk Dashboard Setup — Creating and configuring the Clerk application
- Configuration Guide — All environment variables
- Admin Access Guide — Admin endpoint authentication
- Removing Anonymous Access — Migration timeline