Docs
Everything your app needs to read flags and config from Konfigo.
Quickstart
- Create a project and add a flag or config value in the dashboard.
- Open API keys and create a key for the environment you want to read. Copy it right away, it is only shown once.
- Install the SDK, or call the REST API directly with the key as a bearer token:
curl https://konfigo.dev/api/v1/flags \
-H "Authorization: Bearer $KONFIGO_KEY"{
"env": "production",
"flags": { "new-checkout": true, "dark-mode": false }
}JavaScript SDK
The SDK is a tiny, dependency-free TypeScript package. It works in Node 18+, Bun, Deno, edge runtimes and browsers, anything with fetch. It caches values, refreshes them in the background and hands you typed answers, so you don't have to call the REST API yourself.
npm install konfigo-sdkimport { Konfigo } from "konfigo-sdk";
const konfigo = new Konfigo({
apiKey: process.env.KONFIGO_API_KEY!, // kfg_... (one project + environment)
baseUrl: "https://konfigo.dev",
});
await konfigo.start(); // initial load, then refresh every 30s
if (konfigo.isEnabled("new-checkout")) {
showNewCheckout();
}
const timeout = konfigo.get("api_timeout_ms", 3000);| new Konfigo({ apiKey, baseUrl, timeoutMs?, flagsOnly?, fetch? }) | Create a client. |
| start() / stop() | Load, then poll in the background (default every 30 seconds). |
| refresh() | Load flags and config into the cache on demand. |
| isEnabled(key, fallback = false) | Flag state from the cache. |
| get(key, fallback?) | Config value from the cache. Numbers stay numbers, JSON values come back as objects or arrays. |
| onChange(fn) | Subscribe to refreshes. Returns an unsubscribe function. |
| fetchFlags() / fetchConfig() | Uncached reads straight from the API. |
JSON values arrive already parsed. Pass a type to get to tell TypeScript what shape to expect. Konfigo checks that the value is valid JSON, not that it matches your type, so keep a sensible fallback.
type Incident = {
slug: string;
name: string;
steps: string[];
links: { label: string; url: string }[];
};
const incident = konfigo.get<Incident | null>("major_vulnerability", null);
if (incident) showBanner(incident.name, incident.links);- Failed background refreshes keep the last good values. Errors are KonfigoError with a status and, on 429, retryAfter in seconds.
- Default polling uses 4 requests per minute, well under the 120 per minute key limit.
- In the browser, use a flags-only key and pass flagsOnly: true so the SDK skips the config endpoint. baseUrl must be https:// (plain http only for localhost).
Authentication
Every request needs an Authorization: Bearer kfg_... header. A key belongs to one project and one environment (development, staging or production), and the response always contains that environment's values.
- Scope: all keys read flags and config. flags keys can only read flags, which makes them safer to ship in client code.
- Expiry: keys can expire on a date you choose. Expired and revoked keys return 401.
- Keys are stored hashed. If you lose one, create a new key and revoke the old one.
Flags (REST)
GET /api/v1/flags returns every flag in the project as a boolean. A flag with no value in the environment is reported as false.
{
"env": "production",
"flags": {
"new-checkout": true,
"dark-mode": false
}
}const res = await fetch("https://konfigo.dev/api/v1/flags", {
headers: { Authorization: `Bearer ${process.env.KONFIGO_KEY}` },
});
const { flags } = await res.json();
if (flags["new-checkout"]) showNewCheckout();Config (REST)
GET /api/v1/config returns every config value in the project. Requires a key with all scope, otherwise you get 403. Each value comes back according to its type:
- number: a JSON number.
- text: a string, up to 2,000 characters.
- json: the parsed object or array, not a string, so there is nothing to JSON.parse on your side. The top level must be an object or an array, and a value can be up to 32 KB (UTF-8, measured with whitespace removed).
curl https://konfigo.dev/api/v1/config \
-H "Authorization: Bearer $KONFIGO_KEY"{
"env": "production",
"config": {
"api_timeout_ms": 3000,
"welcome_message": "Hi there",
"major_vulnerability": {
"slug": "cve-2026-1234",
"name": "Remote code execution in uploads",
"steps": ["Update to 2.4.1", "Rotate API keys"],
"links": [{ "label": "Advisory", "url": "https://example.com/advisory" }]
}
}
}Errors
Errors are JSON with an error message.
| Status | Meaning |
|---|---|
| 401 | Missing, invalid, expired or revoked key. |
| 403 | The key is flags-only and tried to read config. |
| 429 | Rate limit or monthly quota reached. Check the Retry-After header. |
Limits
- Rate limit: 120 requests per key per minute.
- Monthly requests (UTC calendar month): Free includes 100,000, then requests are blocked with 429 until next month. Pro includes 2,000,000 and keeps serving beyond that.
- Caching: responses are sent with Cache-Control: no-store. Cache values in your app for a few seconds to stay well within limits.
Browser use
The API allows cross-origin GET requests from any origin, so you can call it straight from a web page. Because the key is visible to anyone who opens dev tools, use a flags-only key for this and never a key that can read config.