Config
Remote Config — versions and rollback
GET /api/v1/config/items/:id/versions returns the append-only version log; POST /api/v1/config/items/:id/rollback writes a new version whose snapshot matches a target — non-destructive.
Every change to a Config item creates a new immutable version row. The version log is append-only — there's no in-place mutation, and rollback is just a new row whose snapshot matches an old one.
The endpoints below back the dashboard's history view + the CLI's sankofa config history and sankofa config rollback.
List versions
Authentication
Authorization: Bearer <jwt> — Viewer role minimum.
Path parameters
idstring (UUID)RequiredResponse
{
"versions": [
{
"id": "ver_abc123",
"item_id": "itm_xyz",
"version_number": 7,
"action": "rolled_back",
"value_snapshot": "200",
"rule_snapshot": "{\"cohort\":\"Pro plan customers\",\"value\":200}",
"actor_id": "user_456",
"actor_email": "[email protected]",
"note": "revert accidental bump",
"created_at": "2026-05-09T14:32:01.482Z"
},
{
"id": "ver_abc122",
"item_id": "itm_xyz",
"version_number": 6,
"action": "changed",
"value_snapshot": "500",
"rule_snapshot": null,
"actor_id": "user_456",
"actor_email": "[email protected]",
"note": null,
"created_at": "2026-05-09T13:15:42.100Z"
},
{
"id": "ver_abc121",
"item_id": "itm_xyz",
"version_number": 5,
"action": "created",
"value_snapshot": "200",
"rule_snapshot": null,
"actor_id": "user_123",
"actor_email": "[email protected]",
"note": "initial Pro tier override",
"created_at": "2026-04-12T10:00:00Z"
}
]
}Fields
idstring (UUID)item_idstring (UUID)version_numberintegeractionstringvalue_snapshotstring (serialized JSON)rule_snapshotstring (serialized JSON) | nullactor_idstringactor_emailstringnotestringcreated_atstring (ISO8601)The response is ordered by version_number DESC (newest first) and capped at the most recent 100 versions. For older history, contact support — we can export the full log on request.
Roll back to a previous version
Authentication
Authorization: Bearer <jwt> — Editor role minimum.
Request body
version_idstring (UUID)RequirednotestringExample request
curl -X POST https://api.sankofa.dev/api/v1/config/items/itm_xyz/rollback \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJI..." \
-d '{
"version_id": "ver_abc121",
"note": "revert accidental bump"
}'The CLI equivalent:
sankofa config rollback max_upload_mb 5 --note "revert accidental bump"(The CLI takes the version number, not UUID, and looks up the matching version row first.)
Response
200 OK:
{
"item": {
"id": "itm_xyz",
"project_id": "proj_abc",
"environment": "live",
"key": "max_upload_mb",
"type": "int",
"default_value": "200",
"description": "Max upload size in MB",
"is_archived": false,
"current_version": 8,
"created_at": "2026-04-12T10:00:00Z",
"updated_at": "2026-05-09T14:32:01.482Z"
},
"version": {
"id": "ver_new789",
"item_id": "itm_xyz",
"version_number": 8,
"action": "rolled_back",
"value_snapshot": "200",
"rule_snapshot": null,
"actor_id": "user_456",
"actor_email": "[email protected]",
"note": "revert accidental bump",
"created_at": "2026-05-09T14:32:01.482Z"
},
"rule": null
}Errors
| Status | Body | When |
|---|---|---|
400 | {"error": "version_id is required"} | Missing in body |
401 | {"error": "Invalid token"} | JWT missing / expired |
403 | {"error": "Permission denied"} | JWT role isn't Editor or higher |
404 | {"error": "Item not found"} | Item ID in URL doesn't exist |
404 | {"error": "Version not found for this item"} | version_id exists but doesn't belong to this item |
Non-destructive semantics
Rollback creates a new version row whose value_snapshot and rule_snapshot match the target version. The version timeline is append-only — you never lose history.
| Before rollback | After rollback |
|---|---|
| Versions 1, 2, 3, 4, 5, 6, 7 | Versions 1, 2, 3, 4, 5, 6, 7, 8 (new row, snapshot = version 5) |
Rolling back a rollback works the same way — pick the version you want to restore, post version_id, and a new row appears.
This means:
- Audit trail is preserved. The "rolled_back" action is recorded with actor + reason.
- Reverting accidental rollbacks is one click. Pick the pre-rollback version (typically
version_number - 1of the rollback), post again. - Time-travel debugging — you can fetch any historical version's snapshot and diff against current.
Side effects
When the rollback succeeds:
- Item state updates —
default_value,current_version, and any rule on the item move to match the target snapshot. - ETag invalidates — the Config module's ETag changes, so connected SDKs get the new value on their next handshake (≤ 30 seconds).
- Outbound webhook fires —
config.item.rolled_backevent is emitted to subscribers configured for that pattern. See Webhooks. - Audit log records the rollback — actor, reason, target version, timestamp.