Change control is critical to the success of any ongoing project and especially fundamental to software development. It is used to ensure that changes are introduced in a controlled and coordinated manner, reducing the risk of unnecessary changes and ensuring minimal disruption to services.
Currently, Split supports change control on segment and split objects. If change control is enabled in your environment, then API calls to modify these objects directly without creating a change request returns a 403 stating that Approvals are required, for example:
{
"code": 403,
"message": "Approvals are required. orgId=id-org-UUID envId=id-envronment-UUID",
"details": "",
"transactionId": "22ugt6f5igp"
}
Prerequisites
- Download cURL. This application is a free HTTP API client that we will use to make API calls in this document. It should be installed already if you are on a Mac or a Linux machine. If you are more comfortable with other ways to call HTTP endpoints or other HTTP clients, you should be able to follow along. It is a command line tool, so you need to have basic familiarity with the CMD.exe command prompt on Windows or Terminal emulators on Mac or Linux machines.
- You need to create an Admin API key. You can create this by navigating to the Admin Settings, select Workspace Settings, and then API Keys. Click the Create API key button in the top right of the screen. The following page displays:
Select Admin as this tutorial’s API key needs to be for the Admin API. Give it a name, and optionally restrict it to environments and workspaces that you are using the API key for. Once you click Create, an API key is available for your use:
Note: For this document, we are using $apiKey to replace the actual API key that we gathered previously.
Additionally, there is a requirement to have this API Key set as an approver in order to use it to approve change requests. To do that, do the following:
- Navigate to the Admin Settings, select Workspace Settings and then Workspaces.
- Click View under Actions next to the workspace you are interested in setting up this API key as the approver for.
- In the next window, click Edit to edit the specific environment where you want to set up the API key as an approver.
-
In the Edit Environment page, select Require approval for changes.
This displays the following radio buttons:If you choose Let submitters choose their own approver(s), the API cannot be used to approve change requests, but it can still create them and set user account email addresses as approvers.
If you have selected Restrict who can approve, you must explicitly select the API key as an approver, such as below:
- Click Save. The next screen shows that the environment requires approvals.
For both of the following sections, we also need the workspace id and environment id. Those can be collected from the Split user interface or from API calls. Using our API key we can call to the workspaces endpoint as follows:
curl --location --request GET 'https://api.split.io/internal/api/v2/workspaces' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey‘
To retrieve our list of workspaces and their ids:
{
"objects": [
{
"name": "Default",
"type": "workspace",
"id": "id-defaultWorkspace-UUID",
"requiresTitleAndComments": false
},
………
],
"offset": 0,
"limit": 10,
"totalCount": 3
}
Then, using that workspace id, we can get our environments:
curl --location --request GET 'https://api.split.io/internal/api/v2/environments/ws/id-defaultWorkspace-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey‘
This returns the following data:
[
{
"name": "Prod-Default",
"id": "id-prodEnv-UUID",
"production": true
},
{
"name": "Staging-Default",
"id": "id-stgEnv-UUID",
"production": false
}
]
To do this using the user interface, go to Admin Settings, select Workspace Settings then Workspaces to see the workspace names and ids.
Click View under Actions on the workspace of your choosing to see the environments and their id values.
Change requests with segments
Note: Not all of the below actions can be done with the Admin API. Specifically updating owners and tags.
What requires a change request |
What does NOT require a change request |
Adding keys to a segment |
Creating a segment in a workspace |
Removing keys from a segment |
Adding an initial empty segment definition to an environment |
|
Updating segment tags |
|
Updating segment owners |
|
Updating segment descriptions |
|
Removing an empty segment definition from an environment |
Deleting a segment |
Creating or deleting segment keys via the Admin API both require a HTTP POST request. All change requests go to the changeRequest endpoint. Before submitting the change request, you must gather the data required.
Object Property |
Description |
||||||
segment.name |
The name of the segment being updated |
||||||
segment.keys |
The segment keys to use as part of the operation |
||||||
operationType |
|
||||||
title |
The title of the change request |
||||||
comment |
Any change request comments |
||||||
approvers |
|
In the following scenario, we plan on adding the accounts “Annie’s Apples” and “Bob’s Berries” to our segment beta_accounts. This object will look like the following:
{
"segment":{"name":"beta_accounts", "keys":["Annie's Apples","Bob's Berries"]},
"operationType":"CREATE",
"title":"Adding new beta accounts",
"comment":"Processed via Admin API",
"approvers":[]
}
Then to make the call to create the change request will look like this.
curl --location --request POST 'https://api.split.io/internal/api/v2/changeRequests/ws/id-defaultWorkspace-UUID/environments/id-prodEnv-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey' \
--data-raw '{
"segment":{"name":"beta_accounts", "keys":["Annie'\''s Apples","Bob'\''s Berries"]},
"operationType":"CREATE",
"title":"Adding new beta accounts",
"comment":"Processed via Admin API",
"approvers":[]
}
'
The result shows the successful creation of the change request:
{
"split": null,
"segment": {
"name": "beta_accounts",
"keys": [
"Annie's Apples",
"Bob's Berries"
]
},
"id": "id-cr-UUID",
"status": "REQUESTED",
"title": "Adding new beta accounts",
"comment": null,
"workspace": {
"id": "id-defaultWorkspace-UUID",
"type": "workspace"
},
"approvers": [
"admin-api-key"
],
"operationType": "CREATE",
"comments": [
{
"comment": "Processed via Admin API",
"user": "admin-api-key",
"role": "SUBMITTER",
"timestamp": 1646219795355
}
],
"rolloutStatus": null,
"rolloutStatusTimestamp": null
}
To confirm this change request, we can call the GET on the changeRequests endpoint:
curl --location --request GET 'https://api.split.io/internal/api/v2/changeRequests' \
--header 'Authorization: Bearer $apiKey’‘
We then see the REQUESTED status change request:
{
"data": [
{
"split": null,
"segment": {
"name": "beta_accounts",
"keys": [
"Annie's Apples",
"Bob's Berries"
]
},
"id": "id-cr-UUID",
"status": "REQUESTED",
"title": "Adding new beta accounts",
"comment": null,
"workspace": {
"id": "id-defaultWorkspace-UUID",
"type": "workspace"
},
"approvers": [
"admin-api-key"
],
"operationType": "CREATE",
"comments": [
{
"comment": "Processed via Admin API",
"user": "admin-api-key",
"role": "SUBMITTER",
"timestamp": 1646219795355
}
],
"rolloutStatus": null,
"rolloutStatusTimestamp": null
}
],
"nextMarker": null,
"previousMarker": null,
"limit": 20,
"count": 1
}
The GET supports pagination using the nextMarker and previousMarker as optional query parameters. It also is possible to also get APPROVED, REJECTED, WITHDRAWN, and PUBLISHED change requests using the status query parameter.
In the Split user interface, we can also confirm the pending change request exists:
At this point, if the API key is not set as an approver, the only other thing we can do with it is withdraw the change request.
curl --location --request PUT 'https://api.split.io/internal/api/v2/changeRequests/id-cr-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey‘ \
--data-raw '{
"status":"WITHDRAWN",
"comment":"CR withdrawn via Admin API"
}
'
Now if we call the GET like we did previously, you won’t see the change request.
curl --location --request GET 'https://api.split.io/internal/api/v2/changeRequests' \
--header 'Authorization: Bearer $apiKey‘
This call returns the following.:
{
"data": [],
"nextMarker": null,
"previousMarker": null,
"limit": 20,
"count": 0
}
To see a withdrawn change request, use this endpoint to see a single change request. To do this, we need to put the change request id into the endpoint url:
curl --location --request GET 'https://api.split.io/internal/api/v2/changeRequests/id-cr-UUID’ \
--header 'Authorization: Bearer $apiKey’
This returns the change request showing the WITHDRAWN status. Notice the comments objects at the bottom also showing the history of comments as well. In the case of change request management, it is helpful to have meaningful comments.
{
"split": null,
"segment": {
"name": "beta_accounts",
"keys": [
"Annie's Apples",
"Bob's Berries"
]
},
"id": "id-cr-UUID",
"status": "WITHDRAWN",
"title": "Adding new beta accounts",
"comment": null,
"workspace": {
"id": "id-",
"type": "workspace"
},
"approvers": [
"admin-api-key"
],
"operationType": "CREATE",
"comments": [
{
"comment": "Processed via Admin API",
"user": "admin-api-key",
"role": "SUBMITTER",
"timestamp": 1646219795355
},
{
"comment": "CR withdrawn via Admin API",
"user": "admin-api-key",
"role": "SUBMITTER",
"timestamp": 1646221275904
}
],
"rolloutStatus": null,
"rolloutStatusTimestamp": null
}
Note: Another way to see this is to explicitly call to list all WITHDRAWN change requests.
curl --location --request GET 'https://api.split.io/internal/api/v2/changeRequests?status=WITHDRAWN' \
--header 'Authorization: Bearer $apiKey‘
If the Admin API is set as an approver, you can approve the change request by setting the status to APPROVED instead of WITHDRAWN as shown below:
curl --location --request PUT 'https://api.split.io/internal/api/v2/changeRequests/id-cr-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey‘ \
--data-raw '{
"status":"APPROVED",
"comment":"CR approved via Admin API"
}
'
The status changes to PUBLISHED if the change request is successfully approved.
{
"split": null,
"segment": {
"name": "beta_accounts",
"keys": [
"Annie's Apples",
"Bob's Berries"
]
},
"id": "id-cr-UUID",
"status": "PUBLISHED",
"title": "Adding new beta accounts",
"comment": null,
…..
}
It is important to note that you cannot use the same API key to submit and approve the same request. In that scenario you would need 2 API keys. One API key to submit requests and the other set up as an approver.
Change Requests with Splits
Change requests with splits have a few more options than change requests with segments.
Note: Not all of the below actions can be done with the Admin API at the time of this writing. Specifically, updating owners is not possible.
What requires a change request |
What does NOT require a change request |
Changing a split definition in an environment |
Creating a new split in a workspace |
Adding a split definition to an environment |
Updating split tags |
Removing a split definition from an environment |
Updating split owners |
Killing or reactivating a split |
Updating a split description |
Updating a split rollout status |
|
Deleting a split with no targeting rules |
The same changeRequest endpoint is used for split change requests. However, the object sent in the body of the request is different. We need to gather the following information to build the object to send:
Object Property |
Description |
||||||||||
split |
The split definition object. The existing definition for a split can be retrieved by using a HTTP GET call on the splits endpoint. For convenience the only object property required for KILL, RESTORE, or ARCHIVE operations is the split.name object and property. UPDATE and CREATE operations require the entire new split definition to use. |
||||||||||
operationType |
|
||||||||||
title |
The title of the change request |
||||||||||
comment |
Any change request comments |
||||||||||
rolloutStatus.id |
The ID of the rollout status. This is not needed for KILL operations |
||||||||||
approvers |
|
For this exercise, you are going to take an existing split definition and create a new, different split called copy_of_onboarding_flow with the rollout percentage changed from 50/50 to 80/20 on/off.
The split we are using is called new_onboarding_flow. In the Split user interface, the default rule shows a 50/50 rollout:
The first thing we need to do is to create the workspace level split in order to add a split definition to our environment. This call creates the new split with a simple description.
curl --location --request POST 'https://api.split.io/internal/api/v2/splits/ws/557c90d0-7c44-11ec-97df-eafbc0e90433/trafficTypes/user/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer bbka4th38dlfh1cmn3ron8s36a7s2r3jpp9q' \
--data-raw '{
"name": "copy_of_onboarding_flow",
"description": "New Split"
}'
You don’t need the split id here so just having a successful result of the api call will be sufficient.
Rather than trying to create the split definition object we want from scratch, it’s much easier to retrieve the existing split object and make the necessary modifications.
As such, we first get the split definition.
curl --location --request GET 'https://api.split.io/internal/api/v2/splits/ws/id-defaultWorkspace-UUID/new_onboarding_flow/environments/id-prodEnv-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey'
The returned value is the full split definition with id, name, environment, and trafficType along with the treatment definitions. However, for what you need, you can ignore that. The properties we need are the ones below that are returned.
{
…..
"name": "new_onboarding_flow",
….
"treatments": [
{
"name": "on",
"description": ""
},
{
"name": "off",
"description": ""
}
],
"defaultTreatment": "off",
"trafficAllocation": 100,
"rules": [
{
"buckets": [
{
"treatment": "on",
"size": 100
}
],
"condition": {
"combiner": "AND",
"matchers": [
{
"type": "IN_SEGMENT",
"string": "employees"
}
]
}
}
],
"defaultRule": [
{
"treatment": "on",
"size": 50
},
{
"treatment": "off",
"size": 50
}
],
….
}
Split Property |
Description |
name |
Split name |
treatments |
Name and description of treatments |
defaultTreatment |
Name of default treatment |
trafficAllocation |
0-100, percentage of traffic allocated to the split |
rules |
Targeting rule objects |
defaultRule |
Default targeting rule |
Now that we have our split object, we are going to change the defaultRule object to give the on treatment a size of 80 and the off treatment a size of 20. The object now looks like the following:
{
"name": "copy_of_onboarding_flow",
"treatments": [ {"name": "on", "description": ""}, {"name": "off", "description": ""} ],
"defaultTreatment": "off",
"trafficAllocation": 100,
"rules": [
{
"buckets": [ {"treatment": "on", "size": 100} ],
"condition": {
"combiner": "AND",
"matchers": [ {"type": "IN_SEGMENT", "string": "employees"} ]
}
}
],
"defaultRule": [ {"treatment": "on", "size": 80}, {"treatment": "off", "size": 20} ]
}
The other piece of information that we need is the rollout status we want to use. Getting the rollout status ids can be done with this HTTP GET request.
curl --location --request GET 'https://api.split.io/internal/api/v2/rolloutStatuses?wsId=id-defaultWorkspace-UUID' \
--header 'Authorization: Bearer $apiKey‘
This returns the rollout statuses. In our case, we are interested in the Ramping status.
[
……
{
"id": "id-ramping-UUID",
"name": "Ramping",
"description": "Splits that are turned on for a small percentage of users to make sure no performance issues or larger issues come up"
},
{
"id": "id-experimenting-UUID",
"name": "Experimenting",
"description": "Splits that have are ramped for max power in an experiment to get results as quickly as possible"
},
……
]
Putting this object into our change request would look like the following:
{
"split": {
"name": "copy_of_onboarding_flow",
"treatments": [ {"name": "on", "description": ""}, {"name": "off", "description": ""} ],
"defaultTreatment": "off",
"trafficAllocation": 100,
"rules": [
{
"buckets": [ {"treatment": "on", "size": 100} ],
"condition": {
"combiner": "AND",
"matchers": [ {"type": "IN_SEGMENT", "string": "employees"} ]
}
}
],
"defaultRule": [ {"treatment": "on", "size": 80}, {"treatment": "off", "size": 20} ]
},
"operationType": "CREATE",
"title": "New rollout split copy",
"comment": "Copy of the new_onboarding_split with updated rollout percentage",
"rolloutStatus": {"id": "id-ramping-UUID"},
"approvers": []
}
With our object created and information gathered, now you can submit this as a CREATE change request.
curl --location --request POST 'https://api.split.io/internal/api/v2/changeRequests/ws/id-defaultWorkspace-UUID/environments/id-defaultEnvironment-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey‘ \
--data-raw '{
"split": {
"name": "copy_of_onboarding_flow",
"treatments": [ {"name": "on", "description": ""}, {"name": "off", "description": ""} ],
"defaultTreatment": "off",
"trafficAllocation": 100,
"rules": [
{
"buckets": [ {"treatment": "on", "size": 100} ],
"condition": {
"combiner": "AND",
"matchers": [ {"type": "IN_SEGMENT", "string": "employees"} ]
}
}
],
"defaultRule": [ {"treatment": "on", "size": 80}, {"treatment": "off", "size": 20} ]
},
"operationType": "CREATE",
"title": "New rollout split copy",
"comment": "Copy of the new_onboarding_split with updated rollout percentage",
"rolloutStatus": {"id": "id-ramping-UUID"},
"approvers": []
}'
The returned value shows the change request and the change request id.
{
"split": {
……
"name": "copy_of_onboarding_flow",
……
"killed": false,
"treatments": [ {"name": "on", "description": ""}, {"name": "off", "description": ""} ],
"defaultTreatment": "off",
"trafficAllocation": 100,
"rules": [
{
"buckets": [ {"treatment": "on", "size": 100} ],
"condition": {
"combiner": "AND",
"matchers": [ {"type": "IN_SEGMENT", "string": "employees"} ]
}
}
],
"defaultRule": [ {"treatment": "on", "size": 80}, {"treatment": "off", "size": 20} ],
"openChangeRequestId": "id-cr-UUID"
},
"segment": null,
"id": "id-cr-UUID",
"status": "REQUESTED",
"title": "New rollout split copy",
……..
"operationType": "CREATE",
……..
"rolloutStatusTimestamp": null
}
As with segments change requests, Split doesn’t allow for the same API key or user to approve a change request that it submitted. This can be approved either by a different Admin API Key or by a user manually.
Once approved, let’s say we want to update the split to a 90/10 rollout. You need to create a change request of operationType UPDATE. The only difference between UPDATE and CREATE is that UPDATE operations act upon existing split definitions.
For example, this is an UPDATE call to update the existing split we created to a new rollout percentage.
curl --location --request POST 'https://api.split.io/internal/api/v2/changeRequests/ws/id-defaultWorkspace-UUID/environments/id-defaultEnvironment-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey‘ \
--data-raw '{
"split": {
"name": "copy_of_onboarding_flow",
"treatments": [ {"name": "on", "description": ""}, {"name": "off", "description": ""} ],
"defaultTreatment": "off",
"trafficAllocation": 100,
"rules": [
{
"buckets": [ {"treatment": "on", "size": 100} ],
"condition": {
"combiner": "AND",
"matchers": [ {"type": "IN_SEGMENT", "string": "employees"} ]
}
}
],
"defaultRule": [ {"treatment": "on", "size": 90}, {"treatment": "off", "size": 10} ]
},
"operationType": "UPDATE",
"title": "New rollout split percentage",
"comment": "updated rollout percentage",
"rolloutStatus": {"id": "id-ramping-UUID"},
"approvers": []
}'
The Admin API can also be used for creating change requests to kill a split. For this, you don’t need the split definition, only the split name. You would kill the split in the case of alerts showing performance problems, for example.
curl --location --request POST 'https://api.split.io/internal/api/v2/changeRequests/ws/id-defaultWorkspace-UUID/environments/id-prodEnv-UUID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $apiKey' \
--data-raw '{
"split": {"name":"new_onboarding_flow"},
"operationType":"KILL",
"title":"Killed Split",
"comment":"Seeing some performance problems",
"approvers":[]
}'
The response shows the proposed split definition with a split.killed property equal to true.
This same API call can be used to create a change request to RESTORE the killed split to its state before the kill or ARCHIVE the split to remove it from the environment entirely just by changing the operationType object property.
You’ve learned all about change requests via the Admin API in Split.
External references
For more information, refer to the Admin API Guide section on Change Requests.
There are also wrappers for multiple programming languages that have already been built for your convenience. Refer to API wrappers examples for more information.
A Postman API collection for the public Admin API endpoints is available for those who are interested in using the free Postman tool for API testing and development.
Comments
0 comments
Please sign in to leave a comment.