> For the complete documentation index, see [llms.txt](https://upgrade-platform.gitbook.io/upgrade-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://upgrade-platform.gitbook.io/upgrade-documentation/developer-guide/reference/api-documentation.md).

# API

Swagger API details

The operations below cover the v6 client API. You can [download the full specification](https://github.com/CarnegieLearningWeb/UpGrade/tree/gitbook_wip/6.7/gitbook/.gitbook/assets/upgrade-api-6.7-spec.json) as a snapshot of the swagger.json file at the time of this release (backend `6.7.0`).\
\
You can also view the 'live' swagger API documentation details for your hosted backend environment by adding `/swagger` to your backend hostUrl link.

## POST /v6/init

> Create/Update Experiment User

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/init":{"post":{"description":"Create/Update Experiment User","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Set Group Membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/v6initResponse"}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"schoolId":{"type":"array","items":{"type":"string"}},"classId":{"type":"array","items":{"type":"string"}},"instructorId":{"type":"array","items":{"type":"string"}}}},"workingGroup":{"type":"object","properties":{"schoolId":{"type":"string"},"classId":{"type":"string"},"instructorId":{"type":"string"}}}}}}},"required":true,"description":"ExperimentUser"}}}},"components":{"schemas":{"v6initResponse":{"type":"object","properties":{"id":{"type":"string","minLength":1},"group":{"type":"object","properties":{"class":{"type":"array","items":{"type":"string"}}},"required":["class"]},"workingGroup":{"type":"object","properties":{"school":{"type":"string","minLength":1},"class":{"type":"string","minLength":1},"instructor":{"type":"string","minLength":1}},"required":["school","class","instructor"]}},"required":["id","group","workingGroup"]}}}}
```

## PATCH /v6/groupmembership

> Set group membership for a user

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/groupmembership":{"patch":{"description":"Set group membership for a user","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Set Group Membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/v6initResponse"}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"schoolId":{"type":"array","items":{"type":"string"}},"classId":{"type":"array","items":{"type":"string"}},"instructorId":{"type":"array","items":{"type":"string"}}}}}}}},"required":true,"description":"ExperimentUser"}}}},"components":{"schemas":{"v6initResponse":{"type":"object","properties":{"id":{"type":"string","minLength":1},"group":{"type":"object","properties":{"class":{"type":"array","items":{"type":"string"}}},"required":["class"]},"workingGroup":{"type":"object","properties":{"school":{"type":"string","minLength":1},"class":{"type":"string","minLength":1},"instructor":{"type":"string","minLength":1}},"required":["school","class","instructor"]}},"required":["id","group","workingGroup"]}}}}
```

## PATCH /v6/workinggroup

> Set working group for a user

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/workinggroup":{"patch":{"description":"Set working group for a user","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Set Group Membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/v6initResponse"}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"workingGroup":{"type":"object","properties":{"schoolId":{"type":"string"},"classId":{"type":"string"},"instructorId":{"type":"string"}}}}}}},"required":true,"description":"ExperimentUser"}}}},"components":{"schemas":{"v6initResponse":{"type":"object","properties":{"id":{"type":"string","minLength":1},"group":{"type":"object","properties":{"class":{"type":"array","items":{"type":"string"}}},"required":["class"]},"workingGroup":{"type":"object","properties":{"school":{"type":"string","minLength":1},"class":{"type":"string","minLength":1},"instructor":{"type":"string","minLength":1}},"required":["school","class","instructor"]}},"required":["id","group","workingGroup"]}}}}
```

## POST /v6/mark

> Mark a Experiment Point

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/mark":{"post":{"description":"Mark a Experiment Point","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Experiment Point is Marked","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1},"userId":{"type":"string","minLength":1},"experimentId":{"type":"string","minLength":1},"site":{"type":"string","minLength":1},"target":{"type":"string","minLength":1},"condition":{"type":"string","minLength":1}},"required":["id","experimentId","enrollmentCode","userId","condition"]}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"site":{"type":"string"},"target":{"type":["string","null"]},"assignedCondition":{"type":"object","properties":{"conditionCode":{"type":"string"}}},"experimentId":{"type":"string"}}},"status":{"type":"string"},"context":{"type":"string"}}}}},"required":true,"description":"ExperimentUser"}}}}}
```

## POST /v6/assign

> Assign a Experiment Point

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/assign":{"post":{"description":"Assign a Experiment Point","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Experiment Point is Assigned","content":{"application/json":{"schema":{"type":"array","description":"","minItems":1,"uniqueItems":true,"items":{"type":"object","required":["site","target","condition"],"properties":{"site":{"type":"string","minLength":1},"target":{"type":"string","minLength":1},"experimentType":{"type":"string","enum":["Simple","Factorial"]},"assignedCondition":{"type":"array","items":{"type":"object","properties":{"conditionCode":{"type":"string","minLength":1},"payload":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}}},"id":{"type":"string"},"experimentId":{"type":"string"}}}}}}}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"context":{"type":"string"}}}}},"required":true,"description":"User Document"}}}}}
```

## POST /v6/log

> Post log data

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/log":{"post":{"description":"Post log data","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Log data"},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"array","required":true,"items":{"type":"object","properties":{"timestamp":{"type":"string"},"metrics":{"type":"object","properties":{"attributes":{"type":"object","properties":{"continuousMetricName":{"type":"integer"},"categoricalMetricName":{"type":"string"}}},"groupedMetrics":{"type":"array","items":{"type":"object","properties":{"groupClass":{"type":"string","required":true},"groupKey":{"type":"string","required":true},"groupUniquifier":{"type":"string","required":true},"attributes":{"type":"object","properties":{"continuousMetricName":{"type":"integer"},"categoricalMetricName":{"type":"string"}}}}}}}}}}}}}}},"required":true,"description":"User Document"}}}}}
```

## POST /v6/featureflag

> Get feature flags that have been assigned to the user.\
> \
> This endpoint supports three different modes of operation based on the optional parameters:\
> \
> \*\*Stored-user Mode\*\* (Standard stored user lookup):\
> \- Omit both \`groupsForSession\` and \`includeStoredUserGroups\` parameters\
> \- Uses only stored user groups from the database\
> \- User must already have been initialized, will 404 if user does not exist\
> \
> \*\*Ephemeral Mode\*\* (Session-only groups):\
> \- Set \`includeStoredUserGroups\` to \`false\` and provide \`groupsForSession\`\
> \- Uses only the groups provided in the session, ignoring any stored user groups.\
> \- Does not require the user to be initialized (it will bypass stored user lookup)\
> \- Useful when complete group information is always provided at runtime.\
> \
> \*\*Merged Mode\*\* (Stored + Session groups):\
> \- Set \`includeStoredUserGroups\` to \`true\` and provide \`groupsForSession\`\
> \- User must already have been initialized, will 404 if user does not exist.\
> \- Session groups are merged with stored groups if they don't already exist for stored user.\
> \- Session groups are never persisted.\
> \- Useful for adding context-specific ephemeral groups to an existing user.<br>

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/featureflag":{"post":{"description":"Get feature flags that have been assigned to the user.\n\nThis endpoint supports three different modes of operation based on the optional parameters:\n\n**Stored-user Mode** (Standard stored user lookup):\n- Omit both `groupsForSession` and `includeStoredUserGroups` parameters\n- Uses only stored user groups from the database\n- User must already have been initialized, will 404 if user does not exist\n\n**Ephemeral Mode** (Session-only groups):\n- Set `includeStoredUserGroups` to `false` and provide `groupsForSession`\n- Uses only the groups provided in the session, ignoring any stored user groups.\n- Does not require the user to be initialized (it will bypass stored user lookup)\n- Useful when complete group information is always provided at runtime.\n\n**Merged Mode** (Stored + Session groups):\n- Set `includeStoredUserGroups` to `true` and provide `groupsForSession`\n- User must already have been initialized, will 404 if user does not exist.\n- Session groups are merged with stored groups if they don't already exist for stored user.\n- Session groups are never persisted.\n- Useful for adding context-specific ephemeral groups to an existing user.\n","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Feature flags list","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["context"],"properties":{"context":{"type":"string","description":"The context for feature flag evaluation"},"groupsForSession":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"Optional groups to provide for the session (not persisted)"},"includeStoredUserGroups":{"type":"boolean","description":"Whether to include stored user groups in evaluation"}},"description":"Feature flag request parameters"}}},"required":true}}}}}
```

## PATCH /v6/useraliases

> Set aliases for current user

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/useraliases":{"patch":{"description":"Set aliases for current user","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"Experiment User aliases added","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","minLength":1},"aliases":{"type":"array","items":{"type":"string"}}},"required":["userId","userAliases"]}}}},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"404":{"description":"Experiment User not defined"},"500":{"description":"Internal Server Error"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["aliases"],"properties":{"aliases":{"type":"array","items":{"type":"string"}}}}}},"required":true,"description":"Set user aliases"}}}}}
```

## POST /v6/reward

> Send a reward signal for an adaptive (Thompson Sampling) experiment.\
> \
> This endpoint allows sending binary reward feedback (SUCCESS or FAILURE) for adaptive experiments.\
> The reward is used by the adaptive algorithm to update its learning model and improve future assignments.\
> \
> \*\*Lookup Methods:\*\*\
> \
> The endpoint supports two methods to identify the experiment:\
> \
> 1\. \*\*Direct Lookup\*\* - Provide the \`experimentId\` directly\
> 2\. \*\*Decision Point Lookup\*\* - Provide \`context\` and \`decisionPoint\` (site and target) to look up the experiment\
> \
> At least one of these methods must be provided.\
> \
> \*\*Response mode:\*\* By default this endpoint acknowledges receipt immediately and records the reward\
> (config/enrollment lookup, posterior update) in the background — the response does not wait on it.\
> A problem with the reward itself (unknown experiment, no matching enrollment, experiment no longer\
> enrolling, etc.) is therefore not returned to the caller; it is only visible in server-side logs.\
> Pass \`awaitResult=true\` to instead wait for that work and receive the real outcome, including a\
> 409 if the reward could not be recorded.<br>

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/v6/reward":{"post":{"description":"Send a reward signal for an adaptive (Thompson Sampling) experiment.\n\nThis endpoint allows sending binary reward feedback (SUCCESS or FAILURE) for adaptive experiments.\nThe reward is used by the adaptive algorithm to update its learning model and improve future assignments.\n\n**Lookup Methods:**\n\nThe endpoint supports two methods to identify the experiment:\n\n1. **Direct Lookup** - Provide the `experimentId` directly\n2. **Decision Point Lookup** - Provide `context` and `decisionPoint` (site and target) to look up the experiment\n\nAt least one of these methods must be provided.\n\n**Response mode:** By default this endpoint acknowledges receipt immediately and records the reward\n(config/enrollment lookup, posterior update) in the background — the response does not wait on it.\nA problem with the reward itself (unknown experiment, no matching enrollment, experiment no longer\nenrolling, etc.) is therefore not returned to the caller; it is only visible in server-side logs.\nPass `awaitResult=true` to instead wait for that work and receive the real outcome, including a\n409 if the reward could not be recorded.\n","parameters":[{"schema":{"type":"string"},"in":"header","name":"User-Id","required":true,"description":"The unique identifier for the user"},{"schema":{"type":"boolean","default":false},"in":"query","name":"awaitResult","required":false,"description":"When true, waits for the reward to be recorded and returns its real outcome (200 on success,\n409 if it could not be recorded) instead of an immediate receipt.\n"}],"tags":["Client Side SDK"],"responses":{"200":{"description":"With awaitResult unset/false: a receipt confirming the reward was queued (`mode: \"accepted\"`).\nThis does not guarantee the reward was recorded - see \"Response mode\" above.\n\nWith awaitResult=true: confirmation that the reward was actually recorded (`mode: \"processed\"`).\n","content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["accepted","processed"],"description":"accepted = queued for background processing; processed = recorded synchronously"},"success":{"type":"boolean","description":"Only present when mode is \"processed\""},"message":{"type":"string"},"request":{"type":"object","description":"Echo of the original request data","properties":{"rewardValue":{"type":"string","enum":["SUCCESS","FAILURE"]},"experimentId":{"type":"string"},"context":{"type":"string"},"decisionPoint":{"type":"object","properties":{"site":{"type":"string"},"target":{"type":"string"}}}}}}}}}},"400":{"description":"BadRequestError - Invalid parameters (e.g., missing required fields, invalid rewardValue)"},"401":{"description":"AuthorizationRequiredError"},"409":{"description":"Only possible with awaitResult=true. The reward could not be recorded (unknown experiment,\nno matching enrollment, experiment no longer enrolling, etc.).\n"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["rewardValue"],"properties":{"rewardValue":{"type":"string","enum":["SUCCESS","FAILURE"],"description":"The binary reward value indicating success or failure of the assigned condition"},"experimentId":{"type":"string","description":"The ID of the adaptive experiment. Required if context and decisionPoint are not provided."},"context":{"type":"string","description":"The context for the decision point. Required with decisionPoint if experimentId is not provided."},"decisionPoint":{"type":"object","properties":{"site":{"type":"string","description":"The site of the decision point"},"target":{"type":"string","description":"The target of the decision point"}},"description":"The decision point information. Required with context if experimentId is not provided."}}}}},"required":true,"description":"Reward data for the adaptive experiment"}}}}}
```

## DELETE /clearDB

> Only available in DEMO mode. Removes everything except UpGrade users, metric metadata, UpGrade settings, and migrations

```json
{"openapi":"3.1.1","info":{"title":"A/B Testing Backend","version":"6.7.0"},"tags":[{"name":"Client Side SDK","description":"CRUD operations related to experiments points"}],"servers":[{"url":"/api"}],"paths":{"/clearDB":{"delete":{"description":"Only available in DEMO mode. Removes everything except UpGrade users, metric metadata, UpGrade settings, and migrations","tags":["Client Side SDK"],"responses":{"200":{"description":"Database cleared"},"400":{"description":"BadRequestError - InvalidParameterValue"},"401":{"description":"AuthorizationRequiredError"},"500":{"description":"DEMO mode is disabled"}}}}}}
```
