Datafile format
The datafile is the JSON snapshot of one environment, served to SDKs by feat. Every flag evaluation in every SDK runs against an in-memory copy of this file. The format is stable; we version it and add fields backward-compatibly.
Most teams do not need to read this page. Reach for it when you are:
- Writing a custom SDK for a runtime feat does not ship for.
- Building a CI tool that validates flag definitions offline.
- Debugging an SDK and want to see exactly what it was evaluating.
The canonical TypeScript types live in @feathq/datafile-schema.
Top-level shape
Section titled “Top-level shape”{ "version": 1, "etag": "abc123…", "environmentKey": "production", "flags": [ /* FlagSpec[] */ ], "segments": [ /* SegmentSpec[] */ ], "contextKinds": [ /* ContextKindSpec[] */ ]}versionis the schema version. Currently1.etagis the cache key. SDKs send it back withIf-None-Match; an unchanged datafile returns304 Not Modified.flags,segments,contextKindsare the evaluatable definitions.
FlagSpec
Section titled “FlagSpec”{ "key": "checkout-v2", "valueType": "boolean", "salt": "f3a9…", "archived": false, "variations": [ { "id": "v_true", "name": "true", "value": true }, { "id": "v_false", "name": "false", "value": false } ], "environments": { "production": { "isEnabled": true, "offVariationId": "v_false", "defaultVariationId": "v_true", "defaultRollout": null, "defaultBucketingContextKindKey": null, "targets": [ /* TargetSpec[] */ ], "rules": [ /* RuleSpec[] */ ] } }}The environments map carries per-environment state. The wire format scopes a flag’s state to one environment because each datafile is for one environment, so in practice the map has exactly one entry whose key matches environmentKey at the top level. The shape allows future multi-env datafiles without breaking.
Exactly one of defaultVariationId and defaultRollout is set. If defaultRollout is set, defaultBucketingContextKindKey is required.
RuleSpec
Section titled “RuleSpec”{ "id": "r_42", "position": 1, "conditionGroups": [ /* ConditionGroupSpec[] */ ], "variationId": "v_true", "rollout": null, "bucketingContextKindKey": null}Rules are ordered by position ascending. Exactly one of variationId and rollout is set; if rollout, bucketingContextKindKey is required.
ConditionGroupSpec
Section titled “ConditionGroupSpec”{ "id": "g_7", "position": 1, "conditions": [ /* ConditionSpec[] */ ]}Conditions within a group are AND’d. Groups within a rule are OR’d.
ConditionSpec
Section titled “ConditionSpec”{ "id": "c_99", "attributePath": "user.plan", "operator": "is_one_of", "values": ["pro", "enterprise"]}attributePath is "kind.attribute". operator is one of the operators in Operators. values is an array of strings (the engine coerces to numbers, dates, semver, or booleans based on the operator).
For segment operators, values is a list of segment keys.
Rollout
Section titled “Rollout”{ "variations": [ { "variationId": "v_true", "weight": 10000 }, { "variationId": "v_false", "weight": 90000 } ]}Weights are integers summing to exactly 100,000. The engine resolution is 0.001%; the dashboard surfaces percentages.
TargetSpec
Section titled “TargetSpec”{ "id": "t_1", "contextKindKey": "user", "contextKey": "user-123", "variationId": "v_true"}Targets are exact (kind, key) → variation overrides. See Individual targeting.
SegmentSpec
Section titled “SegmentSpec”{ "key": "beta-customers", "rules": [ { "id": "sr_1", "conditionGroups": [ { "id": "sg_1", "conditions": [ { "id": "sc_1", "attributePath": "user.plan", "operator": "is_one_of", "values": ["pro"] } ] } ] } ]}Segment rules and conditions mirror flag targeting rules. Rules within a segment are OR’d. See Segments.
ContextKindSpec
Section titled “ContextKindSpec”{ "key": "user", "name": "User" }The list of declared context kinds for the project. Used by the dashboard for autocomplete and by some SDKs to validate context shapes; the evaluation engine treats kinds as opaque strings.
Serving
Section titled “Serving”SDKs fetch the datafile from feat’s edge over authenticated HTTPS using their API key. Server keys receive the full datafile; client-side-ID keys receive a filtered datafile that excludes flags and segments not marked as client-safe.
Datafile responses are application/json with an ETag header. SDKs send back If-None-Match: <etag> on subsequent polls so unchanged datafiles return 304 Not Modified, and the response is served from a point of presence close to the SDK.
You should not implement this fetch yourself. The SDKs handle authentication, ETag caching, retry, and parsing.
Reference
Section titled “Reference”@feathq/datafile-schemaexportsDatafile,FlagSpec,RuleSpec,ConditionGroupSpec,ConditionSpec,Rollout,TargetSpec,SegmentSpec,ContextKindSpec, and literal-union enums forOperator,ValueType,ApiKeyType.@feathq/feat-evalis the reference engine. Hand it aDatafileand a context and it will evaluate.