{"openapi":"3.1.0","info":{"title":"Obelism Improve API","version":"1.0.0","summary":"Fetch A/B test and feature flag configuration, post analytics.","description":"The public API behind the Obelism Improve SDKs (`@obelism/improve-sdk`, `-react`, `-next`).\nUse it directly when no SDK fits your language: fetch the datafile, evaluate tests and flags locally, and post events and exposures to the analytics endpoint.\n\n**Authentication.** Browser requests are authorized by their `Origin`, which must be listed under Allowed origins for the environment. Server requests send the environment API token in a `token` header.\n\n**Versioning.** This is version 1 of the API; every response says so in an `Api-Version` header. Paths are stable: a breaking change ships under a new `/v2/` path prefix next to the current paths, never in place. An endpoint being retired sends `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers, and the operation is marked `deprecated` here, at least 6 months before it stops working. Additive changes (new endpoints, optional fields, response fields, error codes) can ship at any time, so ignore unknown fields. Policy: https://improve.obelism.studio/docs/api/versioning.\n\n**Rate limits** follow the IETF RateLimit header fields draft. The datafile's browser path sends `RateLimit-Policy`; a `429` adds `RateLimit` and `Retry-After`. Server (`token`) requests are not rate limited.\n\n**Errors** are RFC 9457 problem details (`application/problem+json`) with a stable `code` and a `hint`. Every code is documented at https://improve.obelism.studio/docs/api/errors.","contact":{"name":"Obelism Improve","email":"improve@obelism.studio","url":"https://improve.obelism.studio"}},"externalDocs":{"description":"API documentation","url":"https://improve.obelism.studio/docs/api"},"servers":[{"url":"https://improve.obelism.studio"}],"tags":[{"name":"Datafile","description":"Test, flag and audience configuration.","externalDocs":{"url":"https://improve.obelism.studio/docs/api/datafile"}},{"name":"Analytics","description":"Events and exposures.","externalDocs":{"url":"https://improve.obelism.studio/docs/api/analytics"}}],"paths":{"/config/{organizationId}/{environment}":{"get":{"operationId":"getDatafile","tags":["Datafile"],"summary":"Get the active datafile","description":"Returns every active test and flag in the environment, plus the audiences they target. Equivalent to the `/{status}` variant with `status=active`. Evaluate tests and flags locally against this; it is cached at the edge for 30 seconds.","parameters":[{"name":"organizationId","in":"path","required":true,"description":"Organization id, shown on the Implementation tab of the dashboard.","schema":{"type":"string","pattern":"^org_","examples":["org_01H8XYZ"]}},{"name":"environment","in":"path","required":true,"description":"Which environment of the organization to load.","schema":{"type":"string","enum":["develop","staging","production"]}}],"security":[{"token":[]},{"origin":[]}],"responses":{"200":{"description":"The datafile. An empty object `{}` is returned when the path parameters are invalid or the organization has nothing configured, so SDKs fall back to their defaults.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Cache-Control":{"description":"`public, s-maxage=30, stale-while-revalidate=60`","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Configuration"},{"type":"object","maxProperties":0}]}}}},"403":{"description":"The Origin is not allow-listed, or the token is invalid.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Browser (Origin) requests are rate limited to 600 per 60 seconds per IP. Token requests are exempt.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/config/{organizationId}/{environment}/{status}":{"get":{"operationId":"getDatafileByStatus","tags":["Datafile"],"summary":"Get the datafile for one test state","description":"Like getDatafile, but returns tests in the given state. Use `draft` to preview a test before it goes live; production traffic should use `active`.","parameters":[{"name":"organizationId","in":"path","required":true,"description":"Organization id, shown on the Implementation tab of the dashboard.","schema":{"type":"string","pattern":"^org_","examples":["org_01H8XYZ"]}},{"name":"environment","in":"path","required":true,"description":"Which environment of the organization to load.","schema":{"type":"string","enum":["develop","staging","production"]}},{"name":"status","in":"path","required":true,"description":"Which test state to return.","schema":{"type":"string","enum":["draft","active","finished","archived"]}}],"security":[{"token":[]},{"origin":[]}],"responses":{"200":{"description":"The datafile. An empty object `{}` is returned when the path parameters are invalid or the organization has nothing configured, so SDKs fall back to their defaults.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Cache-Control":{"description":"`public, s-maxage=30, stale-while-revalidate=60`","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Configuration"},{"type":"object","maxProperties":0}]}}}},"403":{"description":"The Origin is not allow-listed, or the token is invalid.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Browser (Origin) requests are rate limited to 600 per 60 seconds per IP. Token requests are exempt.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/log":{"post":{"operationId":"postAnalytic","tags":["Analytics"],"summary":"Record an event or an exposure","description":"Posts one analytic. An `event` is a tracked action (page view, click, purchase). An `exposure` records which variant of a test or flag a visitor was assigned; events are attributed to tests by joining on exposures. Bodies are limited to 8 KB.","security":[{"token":[]},{"origin":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/AnalyticEvent"},{"$ref":"#/components/schemas/AnalyticExposure"}],"discriminator":{"propertyName":"type"}}}}},"responses":{"200":{"description":"Accepted and queued.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"}},"content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"const":true}}}}}},"400":{"description":"Invalid body or reserved event name.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The Origin is not allow-listed, or the token is invalid.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unknown organization.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"Body larger than 8 KB.","headers":{"Api-Version":{"$ref":"#/components/headers/Api-Version"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"securitySchemes":{"token":{"type":"apiKey","in":"header","name":"token","description":"Environment API token from the dashboard. Server-side use only."},"origin":{"type":"apiKey","in":"header","name":"Origin","description":"Sent by browsers automatically. Must be listed under Allowed origins for the environment."}},"headers":{"Api-Version":{"description":"Major version of the public API this response was served under. Breaking changes ship under a new path prefix, so this only changes alongside the path.","schema":{"type":"string","const":"1"}},"RateLimit-Policy":{"description":"IETF draft-ietf-httpapi-ratelimit-headers quota policy: `q` requests per `w` seconds, per client IP. Sent on browser (Origin) requests only.","schema":{"type":"string","examples":["\"browser\";q=600;w=60"]}},"RateLimit":{"description":"IETF draft-ietf-httpapi-ratelimit-headers current state: `r` requests remaining, resetting in `t` seconds.","schema":{"type":"string","examples":["\"browser\";r=0;t=60"]}},"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Deprecation":{"description":"RFC 9745. Present only on deprecated endpoints: when the endpoint was deprecated, as `@<unix seconds>`.","schema":{"type":"string","examples":["@1767225600"]}},"Sunset":{"description":"RFC 8594. Present only on deprecated endpoints: the HTTP date after which the endpoint stops working.","schema":{"type":"string","examples":["Thu, 01 Jul 2027 00:00:00 GMT"]}}},"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem details.","required":["type","title","status","detail","code"],"properties":{"type":{"type":"string","format":"uri","description":"Link to the documentation for this error code."},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string","description":"What went wrong with this request."},"code":{"type":"string","enum":["invalid_json","payload_too_large","invalid_request","reserved_event_name","unknown_organization","forbidden","rate_limited","ingest_failed","not_found","method_not_allowed","internal_error"]},"hint":{"type":"string","description":"What to change before retrying."}}},"Configuration":{"type":"object","description":"The datafile the SDKs evaluate locally.","required":["name","version","flags","tests","audience"],"properties":{"name":{"type":"string","description":"Organization slug."},"version":{"type":"integer","const":1},"flags":{"type":"object","description":"Flags keyed by slug.","additionalProperties":{"$ref":"#/components/schemas/Flag"}},"tests":{"type":"object","description":"Tests keyed by slug.","additionalProperties":{"$ref":"#/components/schemas/Test"}},"audience":{"type":"object","description":"Audiences used by the tests and flags, keyed by slug.","additionalProperties":{"$ref":"#/components/schemas/Audience"}}}},"Flag":{"type":"object","required":["id","name","audience","options"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"audience":{"type":"string","description":"Audience slug."},"options":{"type":"array","items":{"type":"object","required":["name","slug","split"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"value":{"type":"string"},"split":{"type":"number","description":"Relative weight of this option."}}}}}},"Test":{"type":"object","required":["id","name","defaultValue","audience","allocation","options","events"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"defaultValue":{"type":"string"},"audience":{"type":"string","description":"Audience slug."},"allocation":{"type":"number","minimum":0,"maximum":100,"description":"Percentage of matching visitors entered."},"options":{"type":"array","items":{"type":"object","required":["name","slug","split"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"value":{"type":"string"},"split":{"type":"number","description":"Relative weight of this option."}}}},"events":{"type":"object","required":["start","metrics","conversion"],"properties":{"start":{"type":"object","required":["event","scope"],"properties":{"event":{"type":"string","examples":["button_click"]},"scope":{"type":"string","examples":["homepage_hero_login"]}}},"metrics":{"type":"array","items":{"type":"object","required":["event","scope"],"properties":{"event":{"type":"string","examples":["button_click"]},"scope":{"type":"string","examples":["homepage_hero_login"]}}}},"conversion":{"type":"object","required":["event","scope"],"properties":{"event":{"type":"string","examples":["button_click"]},"scope":{"type":"string","examples":["homepage_hero_login"]}}}}}}},"Audience":{"type":"object","description":"Targeting rules; a visitor matches when every listed parameter matches.","properties":{"country":{"type":"string","description":"ISO 3166-1 alpha-2 code."},"pointer":{"type":"string"},"device":{"type":"string"},"browser":{"type":"string"},"os":{"type":"string"}}},"AnalyticEvent":{"type":"object","required":["organizationId","environment","visitorId","pointer","device","screen","browser","os","visitor","type","event","scope"],"properties":{"type":{"const":"event"},"organizationId":{"type":"string","pattern":"^org_","maxLength":256},"environment":{"type":"string","enum":["develop","staging","production"]},"visitorId":{"type":"string","maxLength":256,"description":"Anonymous visitor id, `visi_` + 26 characters."},"pointer":{"type":"string","maxLength":256,"examples":["fine"]},"device":{"type":"string","maxLength":256,"examples":["desktop"]},"screen":{"type":"string","maxLength":256,"examples":["large"]},"browser":{"type":"string","maxLength":256,"examples":["chrome"]},"os":{"type":"string","maxLength":256,"examples":["mac os"]},"visitor":{"type":"string","maxLength":256,"examples":["new"]},"event":{"type":"string","maxLength":256,"description":"snake_case event name. Names starting with `gtm.` are rejected.","examples":["page_view"]},"scope":{"type":"string","minLength":1,"maxLength":256,"description":"Second half of the event identity, e.g. a path.","examples":["/pricing"]},"value":{"type":"number","description":"e.g. an order total."},"currency":{"type":"string","maxLength":256,"examples":["EUR"]},"params":{"type":"object","additionalProperties":true}}},"AnalyticExposure":{"type":"object","required":["organizationId","environment","visitorId","pointer","device","screen","browser","os","visitor","type","subjectKind","subjectId","variant"],"properties":{"type":{"const":"exposure"},"organizationId":{"type":"string","pattern":"^org_","maxLength":256},"environment":{"type":"string","enum":["develop","staging","production"]},"visitorId":{"type":"string","maxLength":256,"description":"Anonymous visitor id, `visi_` + 26 characters."},"pointer":{"type":"string","maxLength":256,"examples":["fine"]},"device":{"type":"string","maxLength":256,"examples":["desktop"]},"screen":{"type":"string","maxLength":256,"examples":["large"]},"browser":{"type":"string","maxLength":256,"examples":["chrome"]},"os":{"type":"string","maxLength":256,"examples":["mac os"]},"visitor":{"type":"string","maxLength":256,"examples":["new"]},"subjectKind":{"type":"string","enum":["test","flag"]},"subjectId":{"type":"string","maxLength":256},"variant":{"type":"string","maxLength":256}}}}}}