Discovered vs managed
A discovered resource is one your code reported but no one has configured yet. A managed resource is one someone has explicitly chosen to control. The platform tracks both so you can see what your code is actually doing without needing to pre-register everything — and only the things you choose to manage count against your subscription quota.
A concrete example
Your payments service has 200 logger categories from various libraries — sqlalchemy.engine.Engine, urllib3.connectionpool, boto3.resources, and so on. The smplkit logging SDK auto-discovers all of them on startup. They all show up in the developer console, dimmed, with their currently-resolved log levels visible — but none of them are "managed."
You only care about three: acme.payments, acme.payments.stripe_client, and urllib3.connectionpool. You click each one in the console and set a level. That click promotes those three to managed. They count against your logging.managed_loggers quota; the other 197 don't.
Two months later you stop debugging urllib3.connectionpool. You release it back to discovered — its level reverts to whatever the framework was producing originally, and your quota gets a slot back.
Why discovery exists
Two reasons:
- You can see what's running without registering it. The platform shows the actual loggers in your code, the actual flags your code declared, the services that have ever booted. No drift between "what's documented" and "what's deployed."
- You don't pay for things you don't configure. The 197 loggers you're not actively managing don't pull from your subscription quota. You only pay for what you actually control.
The model lets you start free, scale gradually, and avoid pre-registration ceremony.
Where discovery happens
| Product | Auto-discovery? | Default state | What's reported |
|---|---|---|---|
| Logging | Yes | managed = false | Loggers your framework knows about |
| Flags | Yes | managed = false for bulk-discovered, managed = true for explicit creates | Flags your code declared with booleanFlag(...) etc. |
| Config | No | n/a — all configs are explicit | n/a |
| Environments | Yes | managed = false for SDK-discovered (classification = AD_HOC), managed = true for console / API creates (classification = STANDARD) | New environment names the SDK reports via POST /api/v1/contexts/bulk |
Logging, Flags, and Environments all auto-discover. Config does not — see the Config note.
Auto-discovery in flags and logging happens via POST /api/v1/flags/bulk and POST /api/v1/loggers/bulk. The SDK buffers declarations and flushes them to the bulk endpoint at startup, periodically (every ~60s), and at shutdown.
What promoting to managed does
The mechanics differ by product, but the user-visible effect is the same: the resource becomes configurable, counts against quota, and respects platform-controlled state instead of being read-only.
Logging
Two click paths in the console promote a logger:
- Click the Default column cell and pick a level. The logger is set
managed = true,level = <chosen>,environments = {}(apply everywhere). - Click an environment-specific cell and pick a level. The logger is set
managed = true,level = "DEBUG"as the base, andenvironments[env].level = <chosen>.
The first promotion also runs a one-time descendant cascade: every dot-notation descendant that's still unmanaged (e.g. acme.payments.stripe_client under acme.payments) is marked with level = NULL so the resolution chain handles inheritance from the newly-managed ancestor.
The promotion is gated by your logging.managed_loggers quota. If you're at the limit, the API returns 402 Payment Required.
Flags
Promotion is implicit. There's no dedicated promote endpoint — when an admin edits a discovered flag in the console (saves any change), the PUT request includes managed: true and the platform records the new state. Once a flag is managed, you can configure environment-specific defaults, rules, and per-environment kill switches.
Note: unlike logging, all flags count against flags.items quota — both managed and discovered. The "discovered" state on a flag is a UI distinction (it's not yet configured), not a quota carve-out. See Flags concepts.
Config
Config doesn't yet have a discovered/managed split — see below.
Environments
Promotion is an explicit action — the standard PUT /api/v1/environments/{id} with managed: true in the body. The console exposes a Promote button per environment row. Unlike flags / loggers, promotion isn't implicit on edit; the environment lives in the app service while per-environment values live in the product services, and the cross-service slot consumption deserves a conscious customer act.
The promotion is gated by your platform.managed_environments quota. If you're at the limit, the API returns 402 Payment Required.
Until promoted, any product write that tries to set a per-environment value on the unmanaged environment is rejected with 400 environment_unmanaged.
production is special: it's always managed and cannot be demoted, so every account is guaranteed at least one writable environment.
What demoting to discovered does
Logging
Setting managed = false on a logger releases all platform control:
levelclearedenvironmentsclearedgroup_idcleared (if assigned)
The logger reverts to discovered state and stops counting against quota. The SDK stops applying server-resolved levels to it; the framework's own level wins again.
Flags
There's no demote operation for flags. Once a flag is managed, it stays managed until you delete it. Deleting a flag removes it from your account; the next time the SDK reports it via auto-discovery, it'll come back as discovered.
Config
n/a — there's no managed/discovered concept.
Environments
Demoting an environment (managed = true → false) is allowed for any non-production environment. Demotion is passively non-destructive: per-environment values across all product services (flag rules, config overrides, logger levels) are retained but frozen — runtime evaluations still see them, but new writes targeting the demoted environment get rejected with environment_unmanaged. Re-promoting later restores write access without data loss.
This makes demotion safe as a slot-management tool: a customer can park an environment to free a quota slot without losing the configuration that targets it.
Entitlement implications
Each product counts toward quota differently:
platform.managed_environments— counts only environments withmanaged = true. Unmanaged (AD_HOC/SDK-discovered) environments are free. Resolves at the highest plan held across any paid product.logging.managed_loggers— counts only loggers withmanaged = true. Discovered loggers are free.logging.groups— counts log groups (always created managed; no discovery for groups).flags.items— counts every flag in the account, managed or not. The check is wired onPOST /api/v1/flags, but ships-1(unlimited) on every plan today, so flag count is effectively uncapped. Auto-discovery (POST /api/v1/flags/bulk) is exempt from governance entirely.config.items— counts every config in the account.
When you hit a limit on a write that would push past it, the API returns:
HTTP/1.1 402 Payment Required
Content-Type: application/vnd.api+json
{
"errors": [{
"status": "402",
"code": "entitlement_limit_reached",
"title": "Subscription limit reached",
"detail": "Your free plan allows a maximum of 15 managed loggers. Upgrade your subscription to increase this limit.",
"meta": {
"limit_key": "logging.managed_loggers",
"current": 15,
"maximum": 15,
"plan": "free"
}
}]
}See Subscriptions and entitlements.
Config doesn't have this yet
Config has no managed column, no POST /api/v1/configs/bulk endpoint, and no auto-discovery from the SDK. Every config is explicitly created (in the console or via the management API) and every config counts toward config.items quota.
This is a known parity gap with Flags and Logging. If and when discovery comes to Config, this page will be updated to describe it.
The closest thing to auto-creation in Config is the common config — a per-account default-parent config that's lazily created the first time you list configs through the API. See Config concepts.

