Add usage limits for your users with almost no extra code — a drop-in
OpenAI client metered against Devic's tenant/subtenant limits engine,
tokens and cost, tranche by tranche.
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const chat = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(chat.choices[0].message.content);
Every tenantId you send is auto-provisioned on first use — no separate
"create tenant" call. Subtenants nest underneath for per-end-user limits without
building a new hierarchy.
tenantMetadata, filled in once and never overwritten.RuleUsage shape throughout.Assign a tier and move on, or layer a temporary rule on top without touching it — a Black Friday token bump, a one-off cost cap raise, scoped to a tenant or narrowed to a single subtenant.
checkLimits call, no separate code path.Mix as many rules as you need, freely combining metric and window — a fast 5-hour token burst limit alongside a monthly € budget, per tenant or per subtenant. The first rule a request would break wins; every other rule keeps counting in the background.
| Metric | Window | Usage | Origin | Resets |
|---|---|---|---|---|
tokens |
1× hour (rolling 5h) | 82,340 / 100,000 | tier | 38m |
tokens |
1× month | 612,000 / 1,000,000 | tier | 12d |
cost |
1× day | €3.20 / €5.00 | tier | 9h |
cost |
1× month | €61.40 / €150.00 | ad-hoc | 12d |
const chat = await client.chat.completions.create({ ... });
chat.devic?.usage;
// [{ metric: 'tokens', windowUnit: 'hour', windowEvery: 5,
// limit: 100000, current: 82340, percent: 82.3, resetsAt }, …]
const res = await fetch(
'https://api.devic.ai/api/v1/tenant-usage/acme-corp',
{ headers: { Authorization: `Bearer ${DEVIC_API_KEY}` } },
);
const { data } = await res.json();
data.usage; // same RuleUsage[] as chat.devic.usage
OpenAIClient extends the official OpenAI class from the openai
package. It only overrides baseURL and defaultHeaders — every resource stays
the untouched implementation from the SDK you already know.
Your OpenAI key is still yours. It's forwarded to OpenAI on every request exactly as-is — the gateway never stores it, only meters the traffic passing through.
tenantId/subtenantId plug into the same limits and cost-tracking engine that powers Devic's own assistants and agents in production — not a toy limiter.
OpenAIClient is the first client this package exports. Anthropic, Gemini and others will share the same metering and error contract as they're added.
Every method call is identical to the official SDK. What changes is what you get back and how limits surface — as a normal, catchable error.
openaichat.completions, embeddings, responses… all untouched.// exactly the same call as the official SDK
const chat = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages,
});
console.log(chat.choices[0].message.content);
new OpenAIClient({
apiKey, devicApiKey,
tenantId: 'acme-corp',
tenantMetadata: {
name: 'Acme Corp',
email: 'ops@acme.com',
},
});
APIError, narrowed for you.try {
await client.chat.completions.create({ ... });
} catch (err) {
if (isTenantLimitExceeded(err)) {
const { current, limit, resetsAt } = err.error.details;
// "you're at 1,000,000/1,000,000 tokens, resets in 4h"
}
throw err;
}
tenantId out — still a valid BYOK passthrough, nothing metered.new OpenAIClient({
apiKey: process.env.OPENAI_API_KEY,
devicApiKey: process.env.DEVIC_API_KEY,
// no tenantId → pure passthrough,
// no limits checked, nothing recorded
});