# LaunchDarkly

URL: https://docs.hanzo.ai/docs/guides/migrate/launchdarkly

> LaunchDarkly decides which users see which behaviour. Here that is /v1/flag — eight operations, one deterministic decision, and no SDK that has to be kept warm.

LaunchDarkly holds flag definitions and evaluates them per user. `/v1/flag`
(8 operations) does the same: `PUT /v1/flag/defs/{key}` stores a definition,
`POST /v1/flag/decide` answers for a subject.

The difference is where evaluation happens. LaunchDarkly's SDKs stream rules into
your process and decide locally. Here the decision is a call, and it is
**deterministic**: the same subject and key always land in the same bucket,
because the bucket is a hash of them rather than a roll.

## Start here

Three calls: mint a key, store one definition, ask what it says for a subject.

```bash
# 1. mint a key — sk- belongs on a server, pk- is safe in a browser
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
  -H "Authorization: Bearer $HANZO_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{"type":"secret"}'

# 2. store one definition — the key in the URL wins over any "key" in the body
curl -sS -X PUT https://api.hanzo.ai/v1/flag/defs/new-editor \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"active": true, "filters": {"groups": [{"rollout_percentage": 25}]}}'

# 3. ask what it says for one identity
curl -sS -X POST https://api.hanzo.ai/v1/flag/decide \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"distinct_id": "u1", "person_properties": {"plan": "pro"}}'
```

Step 3 answers `{"featureFlags": {"new-editor": true}, "featureFlagPayloads": {}, "errorsWhileComputingFlags": false}` — every flag for that identity in one
call, not one flag per call. Run it twice and `u1` gets the same answer, which
is the property a streaming SDK was buying you.

### Core capabilities

| Capability       | What it does                                                                                                          | Operations |
| ---------------- | --------------------------------------------------------------------------------------------------------------------- | ---------- |
| `/v1/flag`       | Stores definitions and evaluates them for one identity, in-process over your own org and project store                | 8          |
| `/v1/experiment` | Registers an experiment with its assignment flag, buckets subjects, and reports lift, z and p against the control arm | 7          |
| `/v1/event`      | Takes the per-subject outcome rows that `POST /v1/experiment/{id}/analyze` folds into per-variant samples             | 12         |

## Nouns

| LaunchDarkly                           | Hanzo                                                                                                     |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Project                                | Your org and project, from the validated key                                                              |
| Environment                            | No such field. A definition is scoped to one org and project, so a second environment is a second project |
| Feature flag                           | Definition — `/v1/flag/defs/{key}`                                                                        |
| Variation                              | A variant on the definition, with a weight                                                                |
| Targeting rule                         | Rules on the definition                                                                                   |
| Percentage rollout                     | Weights. The subject hash decides, not chance                                                             |
| `client.variation(key, user, default)` | `POST /v1/flag/decide`                                                                                    |
| User / context                         | `distinct_id` plus `person_properties` on the decide call                                                 |
| Audit log                              | `GET /v1/flag/activity`                                                                                   |
| Experiment                             | `/v1/experiment`, which composes flags for assignment                                                     |

## The call

LaunchDarkly, in-process:

```javascript
const show = await client.variation('new-checkout', { key: 'user-42' }, false)
```

Hanzo — one call, every flag for that identity, and you read out the one you
came for:

```bash
curl -sS -X POST https://api.hanzo.ai/v1/flag/decide \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "distinct_id": "user-42",
    "person_properties": {"plan": "pro"}
  }'
```

Storing the definition:

```bash
curl -sS -X PUT https://api.hanzo.ai/v1/flag/defs/new-checkout \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "active": true,
    "filters": {"groups": [{"rollout_percentage": 10}]}
  }'
```

The body is the definition document itself, kept byte-for-byte, so edit one by
reading it back with `GET /v1/flag/defs/{key}` and writing the whole thing —
not by rebuilding it from the parts you recognise.

Because the assignment is a hash of `(key, subject)`, a subject keeps its variant
across processes, restarts and languages without any shared state — and an
experiment reading the same flag sees the same split.

## What does not carry

**No streaming SDK, and no local evaluation.** LaunchDarkly's whole latency story
is that your process already holds the rules. `decide` is a network call. Cache
it yourself if the hot path cannot afford one; the decision is deterministic, so
a cache cannot disagree with the server about a subject it has already seen.

**No flag prerequisites.** LaunchDarkly lets one flag gate another. Definitions
here are flat. Compose them in your own code, where the reader can see it.

**No approval queue inside the flag service.** A definition is written by whoever
holds the key; change control is your deploy pipeline and `GET /v1/flag/activity`.
A change that should happen later is a job — `/v1/tasks` is a durable engine and
outlives any one deploy — rather than a scheduled-change feature bolted onto the
flag.

**No `/sdk/goals` or client-side experimentation bundle.** Measurement is
`/v1/experiment`, which reads outcomes from analytics rather than from a
browser SDK.

**Removing a flag is a delete.** `DELETE /v1/flag/defs/{key}` removes the
definition. There is no archive state that keeps serving a last-known value, so
retire the call site first and the definition second.
