Skip to main content

Migrate from LaunchDarkly

This guide walks you through migrating feature flags, environments, and targeting rules from LaunchDarkly to FeatureSignals using the built-in migration API.

Prerequisites​

Before starting, ensure you have:

  1. FeatureSignals running with a target project created (see Installation)
  2. LaunchDarkly Admin API token with *:admin scope and read access to your source project
  3. Your LaunchDarkly project key (found in Project Settings)
  4. Your FeatureSignals target project ID (found in Project Settings → General)
  5. A test environment to validate the migration before touching production

Obtaining a LaunchDarkly API Token​

  1. Log in to your LaunchDarkly dashboard
  2. Navigate to Settings → Authorization
  3. Click Create Token
  4. Set the name to featuresignals-migration
  5. Select the Admin role (or *:admin for fine-grained scopes)
  6. Click Create Token
  7. Copy the token immediately — it will not be shown again

Step 1: Discover Available Providers​

Verify that the LaunchDarkly importer is registered:

curl -X POST https://api.featuresignals.com/v1/migration/providers \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json"

Expected response includes launchdarkly in the providers list:

{
"providers": [
{
"name": "launchdarkly",
"display_name": "LaunchDarkly",
"capabilities": ["flags", "environments", "segments"]
}
]
}

Step 2: Validate Connection​

Test your LaunchDarkly API credentials:

curl -X POST https://api.featuresignals.com/v1/migration/connect \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"provider": "launchdarkly",
"api_key": "api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"base_url": "https://app.launchdarkly.com",
"project_key": "your-ld-project-key"
}'

A successful response indicates the API can reach LaunchDarkly and has the necessary permissions:

{
"status": "connected",
"provider": "launchdarkly",
"project_key": "your-ld-project-key"
}

Step 3: Analyze the Source​

Before importing, run a dry-run analysis to see what will be migrated:

curl -X POST https://api.featuresignals.com/v1/migration/analyze \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"provider": "launchdarkly",
"api_key": "api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"base_url": "https://app.launchdarkly.com",
"project_key": "your-ld-project-key",
"target_project_id": "proj_abc123"
}'

Response:

{
"status": "analyzed",
"summary": {
"total_flags": 47,
"total_environments": 4,
"total_segments": 12,
"source_system": "launchdarkly"
},
"details": {
"boolean_flags": 35,
"string_flags": 8,
"number_flags": 3,
"json_flags": 1,
"environments": ["development", "staging", "production", "canary"]
}
}

Step 4: Execute the Migration​

Once you've reviewed the analysis, start the migration:

curl -X POST https://api.featuresignals.com/v1/migration/execute \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"provider": "launchdarkly",
"api_key": "api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"base_url": "https://app.launchdarkly.com",
"project_key": "your-ld-project-key",
"target_project_id": "proj_abc123"
}'

Response:

{
"status": "started",
"migration_id": "mig_xxx123",
"total_flags": 47,
"total_environments": 4
}

Step 5: Monitor Progress​

Check migration status:

curl -X GET https://api.featuresignals.com/v1/migration/status/mig_xxx123 \
-H "Authorization: Bearer YOUR_JWT"

Response during migration:

{
"status": "in_progress",
"migration_id": "mig_xxx123",
"flags_imported": 23,
"flags_total": 47,
"environments_imported": 4,
"segments_imported": 8,
"errors": []
}

Mapping Reference​

Flag Types​

LaunchDarkly types are mapped to FeatureSignals types as follows:

LaunchDarkly KindFeatureSignals FlagTypeNotes
booleanbooleanTwo variations: true, false
stringstringVariations preserved as values
numbernumberNumeric variations preserved
jsonjsonJSON variations preserved

Flag Categories​

LaunchDarkly does not have a built-in category system, so all imported flags are assigned release as the default category. You can adjust this in the FeatureSignals dashboard after migration.

Operator Mapping​

LaunchDarkly OperatorFeatureSignals OperatorNegation
ininnotIn when negated
startsWithstartsWithDirect mapping
endsWithendsWithDirect mapping
containscontainsDirect mapping
greaterThangtDirect mapping
greaterOrEqualgteDirect mapping
lessThanltDirect mapping
lessOrEquallteDirect mapping
segmentMatchN/AMapped to SegmentKeys
before, afterlt, gtMapped to generic operators
semVerEqualeqMapped to basic equals
semVerGreaterThangtMapped to basic greater-than
semVerLessThanltMapped to basic less-than

Individual Targeting​

LaunchDarkly individual targets (users/contexts) are mapped to targeting rules:

LD: Target["user-1", "user-2"] → Variation: 1
FS: Rule { condition: key in ["user-1", "user-2"], value: <variation value> }

Percentage Rollouts​

LaunchDarkly's 0–100,000 weight scale is converted to FeatureSignals' basis points (0–10,000):

LD rollout weight: 50000 (50%)
FS percentage: 5000 (5000 basis points = 50%)

Post-Migration Verification​

Verify Flag Counts​

curl -X GET https://api.featuresignals.com/v1/projects/proj_abc123/flags \
-H "Authorization: Bearer YOUR_JWT"

Verify a Specific Flag's State​

curl -X GET https://api.featuresignals.com/v1/projects/proj_abc123/flags/new-checkout \
-H "Authorization: Bearer YOUR_JWT"

Test Flag Evaluation​

import { FeatureSignalsClient } from '@featuresignals/node';

const client = new FeatureSignalsClient('fs_srv_...', {
envKey: 'production',
baseURL: 'https://api.featuresignals.com',
});

await client.waitForReady();

// Compare with LaunchDarkly evaluation
const value = client.boolVariation('new-checkout', {
key: 'user-123',
attributes: { country: 'US', beta: true }
}, false);

console.log('Evaluated value:', value);

Known Limitations​

  1. Segment imports: LaunchDarkly segments referenced via segmentMatch are mapped to SegmentKeys but the segment definitions themselves must be re-created manually in FeatureSignals.
  2. Prerequisite chains: Deeply nested prerequisite chains are flattened. Each flag lists its prerequisites but circular dependencies are not detected.
  3. Audit history: Change history from LaunchDarkly is not migrated. Audit logs start fresh in FeatureSignals.
  4. Custom roles & teams: Team member permissions and custom roles must be re-configured.
  5. Expired flags: Archived flags are skipped by default. If you need them, unarchive before migration.

Next Steps​