To process billing and access control, every billable request must include a user identity. This guide shows exactly how to pass it from popular clients and runtimes.
Avoid PII in user IDs. Prefer stable, pseudonymous strings (e.g., “user_123”).
Avoid spaces and special characters.
Supported methods
- Body field
user
— recommended (works with OpenAI‑compatible SDKs)
- Header
X-Paywall-User
— easy to inject via middleware/proxy
- URL prefix
/{user}/…
— fallback when body/headers cannot be modified
Base URL and auth for all examples:
- Base URL:
https://api.paywalls.ai/v1
- Header:
Authorization: Bearer $PAYWALLS_API_KEY
Option 1 — Body user
(recommended)
POST /v1/chat/completions
Authorization: Bearer sk-paywalls-...
Content-Type: application/json
{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Write a haiku about paywalls."}],
"user": "user_12345"
}
POST /v1/chat/completions
Authorization: Bearer sk-paywalls-...
X-Paywall-User: user_12345
Content-Type: application/json
Option 3 — URL prefix (fallback)
POST https://api.paywalls.ai/v1/user_12345/chat/completions
Authorization: Bearer sk-paywalls-...
Content-Type: application/json
Use the URL prefix only if you cannot modify body/headers.
Node (OpenAI SDK)
Body user
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.PAYWALLS_API_KEY,
baseURL: "https://api.paywalls.ai/v1",
});
const resp = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
user: "user_123",
messages: [{ role: "user", content: "Write a haiku about paywalls." }],
});
Header X-Paywall-User
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.PAYWALLS_API_KEY,
baseURL: "https://api.paywalls.ai/v1",
defaultHeaders: { "X-Paywall-User": "user_123" },
});
const resp = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Hi!" }],
});
Streaming
const stream = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
user: "user_123",
stream: true,
messages: [
{ role: "user", content: "Explain usage-based pricing in 2 lines." },
],
});
for await (const chunk of stream) {
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
Python (OpenAI SDK)
from openai import OpenAI
import os
client = OpenAI(api_key=os.environ["PAYWALLS_API_KEY"], base_url="https://api.paywalls.ai/v1")
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
user="user_123",
messages=[{"role": "user", "content": "Summarize usage-based billing."}],
)
print(resp.choices[0].message.content)
Header variant (if your SDK layer supports default headers):
client = OpenAI(
api_key=os.environ["PAYWALLS_API_KEY"],
base_url="https://api.paywalls.ai/v1",
default_headers={"X-Paywall-User": "user_123"}
)
Vercel AI SDK (Edge)
Body user
import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
export const runtime = "edge";
export async function POST(req: Request) {
const { messages } = await req.json();
const openai = new OpenAI({
apiKey: process.env.PAYWALLS_API_KEY!,
baseURL: "https://api.paywalls.ai/v1",
});
const response = await openai.chat.completions.create({
model: "openai/gpt-4o-mini",
user: "user_123",
stream: true,
messages,
});
return new StreamingTextResponse(OpenAIStream(response));
}
Header injection
const openai = new OpenAI({
apiKey: process.env.PAYWALLS_API_KEY!,
baseURL: "https://api.paywalls.ai/v1",
defaultHeaders: { "X-Paywall-User": "user_123" },
});
Fetch / cURL
fetch (header)
await fetch("https://api.paywalls.ai/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAYWALLS_API_KEY}`,
"Content-Type": "application/json",
"X-Paywall-User": "user_123",
},
body: JSON.stringify({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Hello!" }],
}),
});
cURL (body user
)
curl https://api.paywalls.ai/v1/chat/completions \
-H "Authorization: Bearer $PAYWALLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model":"openai/gpt-4o-mini",
"user":"user_123",
"messages":[{"role":"user","content":"Hello!"}]
}'
cURL (header)
curl https://api.paywalls.ai/v1/chat/completions \
-H "Authorization: Bearer $PAYWALLS_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Paywall-User: user_123" \
-d '{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"Hello!"}]}'
Behavior summary
On each request Paywalls extracts the user id (body > header > URL), checks authorization and balance, and either:
- Returns an assistant message with an authorization/top‑up link (no charge), or
- Forwards the request, meters usage, deducts balance, and streams the model response.
Best practices
- Use body
user
when possible; header is a great middleware fallback.
- Keep IDs stable across sessions and retries. Avoid PII.
- Ensure your server/edge passes the user on every billable request.