Using the REST API
This plugin includes support for activating and deactivating branches via the REST API in addition to conventional creation, modification, and deletion operations.
API Token Required
You'll need a valid NetBox REST API token to follow any of the examples shown here. API tokens can be provisioned by navigating to the API tokens list in the user menu.
Creating a Branch
Branches are created in a manner similar to most objects in NetBox. A POST request (including a valid authentication token) is sent to the branches/ API endpoint with the desired attributes, such as name and description:
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
http://netbox:8000/api/plugins/branching/branches/ \
--data '{"name": "Branch 1", "description": "My new branch"}'{
"id": 2,
"url": "http://netbox:8000/api/plugins/branching/branches/2/",
"display": "Branch 1",
"name": "Branch 1",
"status": "new",
"owner": {
"id": 1,
"url": "http://netbox:8000/api/users/users/1/",
"display": "admin",
"username": "admin"
},
"description": "My new branch",
"schema_id": "td5smq0f",
"last_sync": null,
"merged_time": null,
"merged_by": null,
"comments": "",
"tags": [],
"custom_fields": {},
"created": "2024-08-12T17:07:46.196956Z",
"last_updated": "2024-08-12T17:07:46.196970Z"
}Once a new branch has been created, it will be provisioned automatically, just as when one is created via the web UI. The branch's status will show "ready" when provisioning has completed.
Once provisioned, branches can be modified and deleted via the /api/plugins/branching/branches/<id>/ endpoint, similar to most objects in NetBox.
Activating a Branch
Unlike the web UI, where a user's selected branch remains active until it is changed, the desired branch must be specified with each REST API request. This is accomplished by including the X-NetBox-Branch HTTP header specifying the branch's schema ID.
X-NetBox-Branch: $SCHEMA_IDSchema IDs
The schema ID for a branch can be found in its REST API representation or on its detail view in the web UI. This is a pseudorandom eight-character alphanumeric identifier generated automatically when a branch is created. Note that the value passed to the HTTP header does not include the branch_ prefix, which comprises part of the schema's name in the underlying database.
The example below returns all site objects that exist within the branch with schema ID td5smq0f:
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
-H "X-NetBox-Branch: td5smq0f" \
http://netbox:8000/api/dcim/sites/The branch is effectively "deactivated" for future API requests by simply omitting the header.
The X-NetBox-Branch header is required only when making changes to NetBox objects within the context of an active branch. It is not required when creating, modifying, or deleting a branch itself.
Branch Actions
Several REST API endpoints are provided to handle the lifecycle actions associated with a branch:
| Endpoint | Method | Description |
|---|---|---|
/api/plugins/branching/branches/<id>/sync/ | POST | Synchronize changes from main into the branch |
/api/plugins/branching/branches/<id>/merge/ | POST | Merge a branch into main |
/api/plugins/branching/branches/<id>/revert/ | POST | Revert a previously merged branch |
/api/plugins/branching/branches/<id>/archive/ | POST | Archive a merged branch (deprovisions its schema) |
/api/plugins/branching/branches/<id>/recover/ | POST | Reset a branch stuck in a transitional status |
To synchronize updates from main into a branch, send a POST request to the desired branch's sync/ endpoint.
The sync/, merge/, and revert/ endpoints accept a commit argument: setting this to false performs a dry run, where the changes are automatically rolled back at the end of the job. (This can be helpful to check for potential errors before committing to a set of changes.) The default value is true.
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
http://netbox:8000/api/plugins/branching/branches/2/sync/ \
--data '{"commit": true}'If successful, this will return data about the background job that has been enqueued to handle the synchronization of data. This job can be queried to determine the progress of the synchronization.
{
"id": 4,
"url": "http://netbox:8000/api/core/jobs/4/",
"display_url": "http://netbox:8000/core/jobs/4/",
"display": "f0c6dea2-d5bb-4683-851e-2ac705510af4",
"object_type": "netbox_branching.branch",
"object_id": 2,
"name": "Sync branch",
"status": {
"value": "pending",
"label": "Pending"
},
"created": "2024-08-12T17:27:57.448405Z",
"scheduled": null,
"interval": null,
"started": null,
"completed": null,
"user": {
"id": 1,
"url": "http://netbox:8000/api/users/users/1/",
"display": "admin",
"username": "admin"
},
"data": null,
"error": "",
"job_id": "f0c6dea2-d5bb-4683-851e-2ac705510af4"
}This same pattern can be followed to merge and revert branches via their respective API endpoints, listed above.
The archive/ endpoint differs slightly: it does not enqueue a background job, but rather archives the branch synchronously and returns the updated branch representation. The branch must be in the merged state for this action to succeed.
The recover/ endpoint likewise acts synchronously and returns the updated branch representation. It resets a branch which is stuck in a transitional status (provisioning, syncing, migrating, merging or reverting) because the background job responsible for it is no longer running - typically because its worker was killed. See auto_recover_stuck_branches for the status each operation is reset to.
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
http://netbox:8000/api/plugins/branching/branches/2/recover/The request is rejected with a 400 response if the branch is not in a transitional status, or if a job for the branch still appears to be running. Pass {"force": true} to recover the branch regardless - do so only when you are certain the operation has stopped, as resetting the status of a branch which is still being worked on may allow a second, conflicting operation to be started against it.
Pass {"retry": true} to re-run the interrupted operation once the status has been reset, rather than leaving the branch for someone to act on again. This is honoured only for branches stuck in syncing or migrating: both operate solely on the branch's own schema and take no parameters which the recovery cannot reconstruct. It is ignored for merging and reverting, which write to main and whose dry-run flag is not recorded on the job, and for provisioning, which cannot be resumed. The web UI offers the same option, unticked by default, on its recovery page.
Additional Endpoints
The plugin also exposes the following read-only endpoints:
| Endpoint | Method | Description |
|---|---|---|
/api/plugins/branching/branch-events/ | GET | List the event history (provision, sync, migrate, merge, revert, archive) for all branches |
/api/plugins/branching/branch-events/<id>/ | GET | Retrieve a single branch event |
/api/plugins/branching/changes/ | GET | List all ChangeDiff records across branches |
/api/plugins/branching/changes/<id>/ | GET | Retrieve a single ChangeDiff record |
/api/plugins/branching/branchable-models/ | GET | List every model registered for branching support |
Syncing & Merging with Conflicts
If conflicting changes exist on a branch (i.e. the same object has been modified in both main and the branch since the last sync), the sync/ and merge/ endpoints will return HTTP 409 Conflict rather than enqueuing a job. The response body includes a detail message and a conflicts list describing each conflicting change:
{
"detail": "All conflicts must be acknowledged before this action can proceed.",
"conflicts": [
{
"id": 6,
"object_type": "dcim.site",
"object_id": 31,
"object_repr": "s1",
"action": {
"value": "update",
"label": "Updated"
},
"conflicts": ["description", "tags"],
"conflicting_data": {
"original": {
"description": "",
"tags": []
},
"branch": {
"description": "abc",
"tags": ["Alpha", "Foxtrot"]
},
"main": {
"description": "def",
"tags": ["Alpha", "Charlie", "Delta"]
}
},
"last_updated": "2024-08-12T17:07:10.442432Z"
}
]
}Each entry in conflicts corresponds to a ChangeDiff record and includes:
| Field | Description |
|---|---|
id | The ChangeDiff PK |
object_type | The type of the affected object (e.g. dcim.site) |
object_id | The PK of the affected object |
object_repr | Human-readable name of the affected object |
action | The change action on the branch (create, update, or delete) |
conflicts | List of field names where the branch and main have diverged |
conflicting_data | Three-way view of the conflicting fields: original (at branch creation), branch (branch value), main (current main value) |
Full details for any conflict can be retrieved via the change diff endpoint:
curl -X GET \
-H "Authorization: Token $TOKEN" \
-H "Accept: application/json; indent=4" \
http://netbox:8000/api/plugins/branching/changes/6/Acknowledging Conflicts
To proceed despite conflicts, include "acknowledge_conflicts": true in the request body. This signals that you have reviewed the conflicts and accept that the branch's version of the affected fields will overwrite whatever is currently in main.
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
http://netbox:8000/api/plugins/branching/branches/2/sync/ \
--data '{"commit": true, "acknowledge_conflicts": true}'Acknowledging conflicts means the branch's version of all conflicting fields will overwrite whatever is currently in main. Review conflicting_data carefully before proceeding.