LeveredDocs
API Reference

Optimizations

Endpoints for creating, managing, and serving optimizations.

All endpoints are under /api/v2/optimizations. Unless noted otherwise, requests require an Authorization: Bearer <token> header.

List optimizations

GET /api/v2/optimizations

Auth: Required

Query parameters:

ParameterTypeDefaultDescription
limitinteger10Maximum number of results (1--100)
order_bystringupdated_atSort field: updated_at or created_at
include_archivedbooleanfalseInclude archived optimizations

Response:

{
  "optimizations": [
    {
      "id": "699ede80-c94b-4f98-9c79-dd3e07748703",
      "name": "Landing Page Hero",
      "status": "live",
      "model_status": "trained",
      "model_id": "4754cae0-9a5d-4691-91af-6d15ccc1f82e",
      "created_at": "2026-01-15T10:30:00Z",
      "updated_at": "2026-03-28T14:22:00Z",
      "model_source": "store",
      "summary": {
        "reference": "baseline",
        "variants_count": 6,
        "exposures": 1250
      }
    }
  ],
  "meta": {
    "request_id": "req_abc123",
    "organization_id": "org_xyz"
  }
}

The summary object is null for optimizations that have not been trained yet. reference is what lifts are measured against: baseline, or equal_split (an even split of traffic) for an optimization without a baseline variant.

model_source says where the optimization's model figures (lift, confidence, probability of best, importance) come from:

  • store: the posterior. No optimization response carries them; query them there. Every new optimization is store.
  • bandit: optimizations created before the posterior store keep their figures where they always were, on the optimization's results endpoints and in its summary (estimated_improvement_percent, estimated_improvement_confidence, …), so their numbers don't change mid-run. Their design-factor importance is a share of the reward's variance rather than a lift.

Get optimization

GET /api/v2/optimizations/:id

Auth: Required

Path parameters:

ParameterTypeDescription
idstring (UUID)Optimization ID

Response: Returns the full optimization object including training_data_config, linked metrics, current status, and model_source (see List optimizations).

Create optimization

POST /api/v2/optimizations

Auth: Required

Request body:

{
  "name": "Landing Page Hero",
  "model_type": "cmab",
  "training_data_config": {
    "reward": {
      "name": "signup",
      "type": "bool"
    },
    "design_factors": [
      {
        "name": "headline",
        "dtype": "str",
        "levels": ["Ship faster", "Save time", "Build better"]
      },
      {
        "name": "cta_text",
        "dtype": "str",
        "levels": ["Start free", "Try it now"]
      }
    ],
    "context_factors": [
      {
        "name": "device",
        "dtype": "str",
        "levels": ["mobile", "desktop"]
      }
    ],
    "attribution": {
      "type": "last_touch",
      "conversion_window_hours": 24
    }
  },
  "reward_metric_id": "metric-uuid",
  "holdout_percentage": 10
}
FieldTypeRequiredDescription
namestringYesHuman-readable name
descriptionstringNoOptional description (max 1000 characters)
model_typestringNomab or cmab (default: cmab)
training_data_config.rewardobjectNoReward definition with name and type (bool or numeric). Defaults to {"name": "reward", "type": "bool"}
training_data_config.design_factorsarrayYesVariables you control. Each has name, dtype, and levels
training_data_config.context_factorsarrayNoContextual variables for personalization. Same shape as design factors
training_data_config.attributionobjectNoAttribution model. type is last_touch, conversion_window_hours sets the lookback (default: 24)
reward_metric_idstring (UUID)YesLink a reward metric at creation time
assignment_source_idstring (UUID)NoLink an assignment source. If omitted, Levered auto-creates one pointing to an exposures table in your warehouse
holdout_percentageintegerNoPercentage of traffic to hold out for control (0--50, default: 10)
control_typestringNoWhat users outside the optimization see: variant (default; one of the variants is the baseline and the holdout is served it), external (the holdout keeps a version that is none of the variants; needs holdout_percentage above 0; holdout users are left out of training) or none (every variant is new; holdout_percentage must be 0, no guardrails). See Holdout
baseline_variantobjectNoWith control_type variant: the baseline as a map of factor name to level value, one entry for every design factor. Default: every factor's first level
guardrailsarrayNoGuardrail definitions to attach at creation time

Response:

{
  "id": "699ede80-c94b-4f98-9c79-dd3e07748703",
  "model_id": "4754cae0-9a5d-4691-91af-6d15ccc1f82e",
  "meta": {
    "request_id": "req_abc123",
    "organization_id": "org_xyz"
  }
}

Update optimization

PATCH /api/v2/optimizations/:id

Auth: Required

Request body:

{
  "name": "Updated Name",
  "status": "completed"
}

Allowed status transitions:

FromTo
draftlive
livepaused, shipped, completed, archived
pausedlive, shipped, completed, archived
shippedlive, completed
completedarchived

control_type and baseline_variant can also be changed with PATCH, while the optimization is a draft. Send "baseline_variant": null to go back to every factor's first level. The holdout percentage of an external optimization can only be changed while it's a draft.

Setting status to shipped requires a trained model. It freezes the leading variant for each context into a policy, which GET /optimizations/:id returns as shipped_policy. holdout_percentage cannot be changed while an optimization is shipped.

Delete (archive) optimization

DELETE /api/v2/optimizations/:id

Auth: Required

Archives the optimization. Data is retained but the optimization is hidden from default list views. Equivalent to setting status to archived.

Response:

{
  "id": "699ede80-c94b-4f98-9c79-dd3e07748703",
  "status": "archived",
  "meta": {
    "request_id": "req_abc123",
    "organization_id": "org_xyz"
  }
}

Upload variant screenshot

POST /api/v2/optimizations/:id/variants/by-number/:n/screenshot

Auth: Required

Multipart upload of a single PNG showing variant :n rendered in your app. Validated by magic bytes (PNG only), 10 MB limit. Re-uploading overwrites the previous screenshot. Variant numbers are assigned when results are first fetched. If the variant number is unknown, fetch GET /api/v2/optimizations/:id/results/state once and retry.

This is normally called by levered optimizations screenshot, which captures and uploads all variants in one step.

Request: multipart/form-data with a single file field.

Response:

{
  "ok": true,
  "variant_number": 3,
  "meta": {
    "request_id": "req_abc123",
    "organization_id": "org_xyz"
  }
}

Get variant screenshot

GET /api/v2/optimizations/:id/variants/by-number/:n/screenshot

Auth: Required

Returns the stored PNG (Content-Type: image/png), or 404 if no screenshot has been uploaded for that variant. Screenshots are private to the owning organization and are only served through this endpoint. The variants in GET /api/v2/optimizations/:id/results/state carry a screenshot_uploaded_at timestamp (null when absent) so clients can tell which variants have a preview.

Serve variant

POST /api/v2/optimizations/:id/serve

Auth: Not required (public endpoint). Rate limited to 300 requests/min per IP.

Returns one variant selected for the user by the optimization policy. This is the primary endpoint for real-time variant delivery.

Request body:

{
  "anonymous_id": "user-abc",
  "context": {
    "device": "mobile"
  }
}
FieldTypeRequiredDescription
anonymous_idstringYesStable user identifier, required so the exposure can be joined with rewards in your warehouse
contextobjectNoContext factor values for personalization

Each successful request returns one selection in the variants array. Omit the legacy n_variants field; an explicit 1 is also accepted. Counts greater than 1 are rejected with HTTP 400.

Response:

{
  "variants": [
    {
      "props": { "headline": "Ship faster", "cta_text": "Start free" },
      "expected_reward": 0.31
    }
  ],
  "optimization_id": "699ede80-c94b-4f98-9c79-dd3e07748703",
  "is_holdout": false,
  "trained": true,
  "meta": {
    "request_id": "req_abc123",
    "organization_id": "org_xyz"
  }
}
FieldTypeDescription
variantsarrayOne selected variant with its design factor values and expected reward
optimization_idstringThe optimization that served this variant
is_holdoutbooleanWhether the user was assigned the baseline through holdout routing
trainedbooleantrue if the model has been trained, false for an untrained policy

Best variant by design factors

POST /api/v2/optimizations/best-variant

Auth: Not required (public endpoint). Rate limited to 300 requests/min per IP.

Matches design factor names to a live optimization and returns one variant selected for the user by its policy. The JavaScript SDK uses /optimizations/:id/serve with an explicit optimization ID.

Request body:

{
  "anonymous_id": "user-abc",
  "props": ["headline", "cta_text"],
  "context": {
    "device": "mobile"
  }
}
FieldTypeRequiredDescription
anonymous_idstringYesStable user identifier
propsstring[]YesArray of design factor names. Levered finds the live optimization whose design factors match this set.
contextobjectNoContext factor values for personalization

Each successful request returns one selection in the variants array. Omit the legacy control.n_variants field; an explicit 1 is also accepted. Counts greater than 1 are rejected with HTTP 400.

Response:

Same shape as the /serve endpoint:

{
  "variants": [
    {
      "props": { "headline": "Build better", "cta_text": "Try it now" },
      "expected_reward": 0.29
    }
  ],
  "optimization_id": "699ede80-c94b-4f98-9c79-dd3e07748703",
  "is_holdout": false,
  "trained": true,
  "meta": {
    "request_id": "req_abc123",
    "organization_id": "org_xyz"
  }
}