JSON Flags
Structured remote configuration for multi-field settings, nested options, and typed objects without redeploys.
Introduction
JSON flags store a structured value — a JSON object {} or array
[]. Use them when configuration spans multiple related fields
that should change together as a single unit.
Applications read JSON flags locally from an in-memory snapshot. Evaluation is synchronous — no network request occurs on the read path. SDKs deserialize the stored JSON into typed objects or return a deep copy of dict/list values.
Common use cases:
- Feature module config — enable flags, limits, and UI options in one object
- Checkout and payment settings — providers, timeouts, and display rules grouped together
- Rollout configuration — percentage, allowlists, and variant metadata
- Integration settings — third-party endpoints, retry policies, and feature toggles per integration
- UI component config — labels, layout options, and visibility rules for a widget
- Operational tuning — related numeric and Boolean fields that operators adjust as a set
When to use JSON flags
Use a JSON flag when configuration requires multiple related fields, nested structure, or a typed object — not a single scalar value.
| Scenario | Example key | Example value | Typical default |
|---|---|---|---|
| Checkout config | checkout-config |
{ "enableUpsells": true, "maxItems": 10 } |
Safe object with conservative fields |
| Rollout rules | feature-rollout |
{ "percentage": 25, "enabled": true } |
{ "percentage": 0, "enabled": false } |
| Retry policy | retry-policy |
{ "maxAttempts": 3, "backoffMs": 500 } |
{ "maxAttempts": 1, "backoffMs": 1000 } |
| Banner config | promo-banner |
{ "visible": true, "message": "Sale ends Sunday" } |
{ "visible": false, "message": "" } |
Checkout config — group related checkout behavior in one flag. Change all fields atomically from the dashboard.
Rollout rules — combine enablement with percentage or segment metadata. Default to disabled with zero rollout.
Retry policy — keep retry count and backoff together instead of separate Number flags that can drift out of sync.
Banner config — visibility and copy in one object. Default to hidden with empty message.
For a single on/off gate, use Boolean flags. For one text variant, use String flags. For one numeric limit, use Number flags.
Create a JSON flag
Create flags in the SOASAP dashboard. If you have not set up a project yet, start with Create Your First Flag.
-
Open your project
Sign in to the dashboard, select your project, and open the environment where you want to configure the initial value — typically Development for local work.
-
Create the flag
Add a new flag with the following properties:
Key checkout-configType JSON Initial value { "enableUpsells": false, "maxItems": 10 } -
Save and verify
Save the flag. Connected SDKs receive the update over SSE. Read the flag in your application to confirm the deserialized object matches the dashboard for that environment.
Example configuration for checkout-config:
| Environment | Value |
|---|---|
| Development | { "enableUpsells": true, "maxItems": 50 } |
| Staging | { "enableUpsells": true, "maxItems": 20 } |
| Production | { "enableUpsells": false, "maxItems": 10 } |
JSON property names in the dashboard are typically camelCase. SDKs deserialize into typed models according to platform conventions.
Evaluate a JSON flag
SDKs evaluate JSON flags locally from an in-memory snapshot. The getter deserializes or deep-copies the stored value synchronously — no HTTP request, no disk I/O, and no dependency on SOASAP Cloud availability at read time.
If the key is missing, the value is not a JSON object or array, or deserialization fails, the SDK returns your explicit default. Getters never throw.
Method names vary by SDK; the semantics are identical.
.NET
public sealed class CheckoutConfig
{
public bool EnableUpsells { get; set; }
public int MaxItems { get; set; }
}
var config = client.GetJson(
"checkout-config",
new CheckoutConfig { EnableUpsells = false, MaxItems = 10 });
JSON property names are matched case-insensitively — camelCase dashboard payloads deserialize into PascalCase C# properties without extra attributes.
Node.js
interface CheckoutConfig {
enableUpsells: boolean;
maxItems: number;
}
const config = flags.getJson<CheckoutConfig>("checkout-config", {
enableUpsells: false,
maxItems: 10,
});
Python
from typing import TypedDict
class CheckoutConfig(TypedDict):
enableUpsells: bool
maxItems: int
config = flags.get_json(
"checkout-config",
{"enableUpsells": False, "maxItems": 10},
)
Kotlin
import kotlinx.serialization.Serializable
@Serializable
data class CheckoutConfig(
val enableUpsells: Boolean = false,
val maxItems: Int = 10,
)
val config = client.getJson<CheckoutConfig>("checkout-config")
?: CheckoutConfig()
React / React Native
interface CheckoutConfig {
enableUpsells: boolean;
maxItems: number;
}
const config = useSoasapJson<CheckoutConfig>("checkout-config", {
enableUpsells: false,
maxItems: 10,
});
Angular
readonly config = this.soasap.json<CheckoutConfig>("checkout-config", {
enableUpsells: false,
maxItems: 10,
});
Flag evaluation never performs network requests. Values are read from the local SDK snapshot. Background synchronization (SSE) updates the snapshot; subsequent reads return the new value without a redeploy. See Local Evaluation.
Explicit defaults
Always provide a complete default object on every JSON read:
client.GetJson(
"checkout-config",
new CheckoutConfig { EnableUpsells = false, MaxItems = 10 });
The default is returned when:
- The flag key does not exist in the synchronized snapshot
- The flag was deleted or disabled in the dashboard for this environment
- Synchronization has not completed and no cached snapshot is available
- The restored disk or browser cache is empty or invalid
- The stored value is not a JSON object or array (type mismatch)
- Deserialization fails — unknown shape, invalid types, or schema mismatch
Explicit defaults are your production safety net. Define every field with a safe fallback value so missing or partial configuration never enables risky behavior.
Details: Default Values.
Common usage patterns
Feature module configuration
Load a typed config object and pass it to the module initializer.
var config = client.GetJson(
"checkout-config",
new CheckoutConfig { EnableUpsells = false, MaxItems = 10 });
if (config.EnableUpsells)
{
ShowUpsells(config.MaxItems);
}
Grouped operational settings
Change retry behavior as one unit from the dashboard.
var policy = client.GetJson(
"retry-policy",
new RetryPolicy { MaxAttempts = 1, BackoffMs = 1000 });
httpClient.ConfigureRetries(policy.MaxAttempts, policy.BackoffMs);
UI remote config
Drive component visibility and copy from a single JSON flag.
const banner = flags.getJson<PromoBanner>("promo-banner", {
visible: false,
message: "",
});
if (banner.visible) {
renderBanner(banner.message);
}
Environment-specific tuning
Ship the same code everywhere; tune the JSON object per environment in the dashboard without redeploying.
val config = client.getJson<CheckoutConfig>("checkout-config")
?: CheckoutConfig()
applyCheckoutLimits(config.maxItems)
Runtime behavior
- An operator changes a JSON flag value in the dashboard for a specific environment
- SOASAP Cloud pushes the update to connected SDKs over Server-Sent Events (SSE)
- The SDK validates the payload and atomically replaces its in-memory snapshot
- Subsequent
getJson/GetJsoncalls deserialize or copy the new value
No redeploy is required. No network requests occur during evaluation — only during background synchronization. JavaScript and Python SDKs return a deep copy on each read; treat returned objects as snapshots, not mutable shared state.
See Real-Time Synchronization and Offline Operation.
Production recommendations
- Use explicit defaults — pass a complete default object with safe values for every field.
- Prefer strong typing — classes, interfaces,
TypedDict, or@Serializabledata classes instead of untyped dictionaries. - Keep payloads focused — one JSON flag per logical config domain; avoid monolithic objects that mix unrelated settings.
- Do not store secrets — API keys, tokens, and credentials belong in secret managers, not in JSON flag values (especially client-side SDKs).
- Use optional fields carefully — add new dashboard fields with safe defaults in code; unknown keys are typically ignored by deserializers.
- Prefer JSON over String for structured data — do not serialize objects into String flags and parse manually on the hot path.
Common mistakes
Untyped dictionaries everywhere
Avoid:
var config = client.GetJson<Dictionary<string, object>>(
"checkout-config",
new Dictionary<string, object>());
var maxItems = (int)config["maxItems"]; // fragile
Prefer:
var config = client.GetJson(
"checkout-config",
new CheckoutConfig { EnableUpsells = false, MaxItems = 10 });
var maxItems = config.MaxItems;
Strong types catch schema drift at compile time and make code review and refactoring safer.
Incomplete default objects
A default like {} or null forces null-checks
throughout the codebase and hides missing fields. Provide a fully populated default with conservative values
for every property your application reads.
Storing secrets in JSON flags
JSON flag values sync to client-side SDKs and appear in browser bundles or mobile app storage. Never embed API keys, signing secrets, or private credentials in JSON flags. Use server-side SDKs with secret managers for sensitive configuration.
Splitting one object across many flags
Related fields such as checkout-max-items (Number) and
checkout-enable-upsells (Boolean) can drift independently during dashboard edits.
Group them in one checkout-config JSON flag when they change together.
Using JSON for a single scalar value
A JSON object with one field — { "enabled": true } — adds parsing overhead
without benefit. Use a Boolean,
Number, or
String flag for single values.
Related documentation
- Boolean Flags — on/off toggles and kill switches
- String Flags — text values for modes, themes, and labels
- Number Flags — limits, rates, and thresholds
- Feature Flag Best Practices — inventory hygiene and rollout patterns
- Naming Conventions — key format and consistency
- Architecture — local evaluation, SSE, cache, and offline operation
- SDK Documentation — install and configure your platform SDK