Testing with Postman
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 Environment Setup
- Authentication Methods
- Collection Setup
- Request Examples
- Testing All Tiers
- Common Errors & Troubleshooting
Prerequisites
- Postman desktop app (v10+) or Postman Web
- An account registered at the bloqr-backend web UI (
https://bloqr-backend.jk-com.workers.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://bloqr-backend.jk-com.workers.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 UIconst 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 secondsif (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 hostconst 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 keyconst 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: 10X-RateLimit-Remaining: 9POST /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