Running switchback tests¶
Info
Learn the concepts and fundamentals of switchback tests in the Explanation page.
A switchback test is an experiment that runs in the background and compares the results of two instances: baseline vs. candidate. The experiment switches back and forth between the two instances, when a run is made to the app, hence the name.
This how-to guide explains how to interact with switchback tests using the
/v1/applications/{application_id}/experiments/switchback endpoints. Go to the
reference section to see all the available parameters
for each endpoint.
Create a switchback test¶
Use the POST /v1/applications/{application_id}/experiments/switchback
endpoint to create a new switchback test for an application. A switchback
test requires you to define the number of units that it is going to run
for, via the units field of the generate_random_plan object. Each unit is a
time duration in minutes, defined by the unit_duration_minutes field. Lastly,
you must define two instances to compare against each other, using
the comparison field: a baseline_instance_id and a candidate_instance_id.
The endpoint also requires an id and name in the request payload, so
generate one yourself if you don't want to choose custom values:
SWITCHBACK_TEST_ID="switchback-$(openssl rand -hex 4)"
curl -s -X POST "https://api.cloud.nextmv.io/v1/applications/uncanny-rodent/experiments/switchback" \
-H "Authorization: Bearer ${NEXTMV_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"id\": \"${SWITCHBACK_TEST_ID}\", \"name\": \"${SWITCHBACK_TEST_ID}\", \"comparison\": {\"baseline_instance_id\": \"production\", \"candidate_instance_id\": \"staging\"}, \"generate_random_plan\": {\"unit_duration_minutes\": 2, \"units\": 5}}" \
| jq '.'
{
"id": "switchback-1a51bae6",
"name": "switchback-1a51bae6",
"description": "",
"created_at": "2026-07-29T15:07:10.009201462Z",
"updated_at": "2026-07-29T15:07:10.009201462Z",
"status": "draft",
"comparison": {
"baseline_instance_id": "production",
"candidate_instance_id": "staging"
},
"plan": {
"units": [
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 0
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 1
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 2
},
{
"duration_minutes": 2,
"instance_id": "production",
"index": 3
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 4
}
]
}
}
The call above uses a randomly generated ID, and the same identifier for the
switchback test’s name. The name of the test is used as a
human-readable label. You can pass different values for the id and name
fields in the payload to specify a custom ID and name for the switchback
test. For example:
curl -s -X POST "https://api.cloud.nextmv.io/v1/applications/uncanny-rodent/experiments/switchback" \
-H "Authorization: Bearer ${NEXTMV_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"id": "fluffy-switchback-test", "name": "Fluffy Switchback Test", "comparison": {"baseline_instance_id": "production", "candidate_instance_id": "staging"}, "generate_random_plan": {"unit_duration_minutes": 2, "units": 5}}' \
| jq '.'
{
"id": "fluffy-switchback-test",
"name": "Fluffy Switchback Test",
"description": "",
"created_at": "2026-07-29T15:02:55.634178557Z",
"updated_at": "2026-07-29T15:02:55.634178557Z",
"status": "draft",
"comparison": {
"baseline_instance_id": "production",
"candidate_instance_id": "staging"
},
"plan": {
"units": [
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 0
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 1
},
{
"duration_minutes": 2,
"instance_id": "production",
"index": 2
},
{
"duration_minutes": 2,
"instance_id": "production",
"index": 3
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 4
}
]
}
}
The POST /v1/applications/{application_id}/experiments/switchback
endpoint creates a switchback test in draft mode. Once the test
is created, you must start it.
Start a switchback test¶
As mentioned above, when a switchback test is created, it is in draft mode. A switchback test must be started so that it can begin executing runs and collecting data. There are two ways to start a switchback test:
- Using the
startfield (an ISO-8601 datetime string) insidegenerate_random_plan, with thePOST /v1/applications/{application_id}/experiments/switchbackendpoint. This will start the switchback test at the scheduled time. - Using the
PUT /v1/applications/{application_id}/experiments/switchback/{switchback_id}/startendpoint. This will start the switchback test as soon as the request is made.
As an example, here is how to start a switchback test using the endpoint:
Once a switchback test has started, you can stop it at any time or wait for it to complete based on the termination events defined when the test was created.
To generate data for the test, submit runs to the application using the
baseline_instance_id (production in this example) as the instance_id
query parameter, the same way described in the
Run Cloud applications guide. As explained in the
Explanation page, Nextmv Cloud automatically routes each run to
whichever instance the currently active plan unit specifies, and links the
resulting run to this switchback test.
Get a switchback test¶
Info
The best way to view and interact with switchback test results is in the Nextmv Console.
Use the
GET /v1/applications/{application_id}/experiments/switchback/{switchback_id}/metadata
endpoint to retrieve the metadata for a switchback test, using the
switchback test ID.
Once the status of the switchback test is completed, you can get its
plan and comparison details using the
GET /v1/applications/{application_id}/experiments/switchback/{switchback_id}
endpoint. If the test hasn't completed, you can still get partial
results.
A switchback test that has already started can be stopped at any
time, or it will stop automatically when the termination events are fulfilled.
Once a switchback test is stopped, then its status will move to
completed.
{
"id": "fluffy-switchback-test",
"name": "Fluffy Switchback Test",
"description": "",
"created_at": "2026-07-29T15:02:55.634178557Z",
"updated_at": "2026-07-29T15:02:56.174454Z",
"started_at": "2026-07-29T15:02:56.174454Z",
"status": "started",
"comparison": {
"baseline_instance_id": "production",
"candidate_instance_id": "staging"
},
"plan": {
"start": "2026-07-29T15:02:56.174454Z",
"units": [
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 0
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 1
},
{
"duration_minutes": 2,
"instance_id": "production",
"index": 2
},
{
"duration_minutes": 2,
"instance_id": "production",
"index": 3
},
{
"duration_minutes": 2,
"instance_id": "staging",
"index": 4
}
]
},
"grouped_distributional_summaries": [
{
"group_keys": [
"instanceID",
"versionID"
],
"group_values": [
"staging",
"v0.0.2"
],
"indicator_keys": [
"value"
],
"indicator_distributions": {
"value": {
"min": 1.23,
"max": 1.23,
"count": 1,
"mean": 1.23,
"std": "nan",
"shifted_geometric_mean": {
"value": 1.2300000000000022,
"shift": 10
},
"percentiles": {
"p01": 1.23,
"p05": 1.23,
"p10": 1.23,
"p25": 1.23,
"p50": 1.23,
"p75": 1.23,
"p90": 1.23,
"p95": 1.23,
"p99": 1.23
}
}
},
"number_of_runs_total": 1
}
]
}
Use the
GET /v1/applications/{application_id}/experiments/switchback/{switchback_id}/runs
endpoint to retrieve the runs that were made for the test. This endpoint
is paginated, so the snippet below always uses pagination: it passes
pagereturn=true to receive a next_page_token in the response, and keeps
requesting pages by passing that token back as pagetoken until no token is
returned.
RUNS="[]"
PAGE_TOKEN=""
while :; do
RESPONSE=$(curl -s -G "https://api.cloud.nextmv.io/v1/applications/uncanny-rodent/experiments/switchback/fluffy-switchback-test/runs" \
-H "Authorization: Bearer ${NEXTMV_API_KEY}" \
--data-urlencode "pagereturn=true" \
--data-urlencode "pagetoken=${PAGE_TOKEN}")
RUNS=$(jq -n --argjson existing "$RUNS" --argjson page "$(echo "$RESPONSE" | jq '.runs')" '$existing + $page')
PAGE_TOKEN=$(echo "$RESPONSE" | jq -r '.next_page_token // empty')
[ -z "$PAGE_TOKEN" ] && break
done
echo "$RUNS" | jq '.'
[
{
"id": "staging-It0LqQEDg",
"user_email": "sebastian@nextmv.io",
"name": "",
"description": "",
"created_at": "2026-07-29T15:03:36.174454Z",
"application_id": "uncanny-rodent",
"application_instance_id": "staging",
"application_version_id": "v0.0.2",
"run_type": {
"type": "",
"definition_id": "",
"reference_id": ""
},
"execution_class": "6c9500mb870s",
"queuing_priority": 6,
"queuing_disabled": false,
"runtime": "python-3_11",
"status": "succeeded",
"status_v2": "succeeded",
"metrics": {
"status": "succeeded",
"indicators": [
{
"name": "value",
"value": 1.23
},
{
"name": "metadata.duration",
"value": 4.534
}
]
},
"options": {
"details": "true"
},
"options_summary": [
{
"name": "details",
"value": "true",
"source": "version"
}
],
"switchback_experiment": {
"experiment_id": "fluffy-switchback-test",
"plan_unit_index": 0
}
}
]
You can list all switchback tests in the application using the
GET /v1/applications/{application_id}/experiments/switchback
endpoint. This endpoint is paginated, so the snippet below always uses
pagination.
ITEMS="[]"
PAGE_TOKEN=""
while :; do
RESPONSE=$(curl -s -G "https://api.cloud.nextmv.io/v1/applications/uncanny-rodent/experiments/switchback" \
-H "Authorization: Bearer ${NEXTMV_API_KEY}" \
--data-urlencode "pagereturn=true" \
--data-urlencode "pagetoken=${PAGE_TOKEN}")
ITEMS=$(jq -n --argjson existing "$ITEMS" --argjson page "$(echo "$RESPONSE" | jq '.items')" '$existing + $page')
PAGE_TOKEN=$(echo "$RESPONSE" | jq -r '.next_page_token // empty')
[ -z "$PAGE_TOKEN" ] && break
done
echo "$ITEMS" | jq '.'
[
{
"id": "fluffy-switchback-test",
"name": "Fluffy Switchback Test",
"description": "",
"created_at": "2026-07-29T15:02:55.634178557Z",
"updated_at": "2026-07-29T15:02:56.174454Z",
"status": "started",
"started_at": "2026-07-29T15:02:56.174454Z",
"comparison": {
"baseline_instance_id": "production",
"candidate_instance_id": "staging"
}
},
{
"id": "switchback-330prr2m",
"name": "switchback-330prr2m",
"description": "",
"created_at": "2026-07-26T23:14:52.918111217Z",
"updated_at": "2026-07-26T23:14:52.918111217Z",
"status": "draft",
"comparison": {
"baseline_instance_id": "production",
"candidate_instance_id": "staging"
}
}
]
Stop a switchback test¶
A switchback test will continue to execute until it stops, which is equivalent to it being completed. There are two ways to stop a switchback test:
- Using the
unit_duration_minutesandunitsfields with thePOST /v1/applications/{application_id}/experiments/switchbackendpoint. Termination events will cause the switchback test to stop. - Using the
PUT /v1/applications/{application_id}/experiments/switchback/{switchback_id}/stopendpoint. This will stop the switchback test as soon as the request is made.
When using the stop endpoint, you must pass the intent field in the request
payload to track if you want to complete or cancel the switchback test.
As an example, here is how to stop a switchback test and mark it as
completed:
Update a switchback test¶
You can update attributes of a switchback test with the
PATCH /v1/applications/{application_id}/experiments/switchback/{switchback_id}
endpoint, such as its:
- Name
- Description
The endpoint returns the updated switchback test attributes. You cannot update the ID of a switchback test.
curl -s -X PATCH "https://api.cloud.nextmv.io/v1/applications/uncanny-rodent/experiments/switchback/fluffy-switchback-test" \
-H "Authorization: Bearer ${NEXTMV_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Switchback Test Name", "description": "Updated description for the switchback test"}' \
| jq '.'
Delete a switchback test¶
Warning
Deleting a switchback test is irreversible. All the data associated with the switchback test will be permanently deleted.
Delete a switchback test using the
DELETE /v1/applications/{application_id}/experiments/switchback/{switchback_id}
endpoint.