From 565fd3c1430689baa4fc774d2bdc3289dac5ae9c Mon Sep 17 00:00:00 2001 From: Shubham Naik Date: Thu, 12 Feb 2026 13:30:44 -0800 Subject: [PATCH] feat: add template rollback endpoint [LET-7423] (#9455) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add template rollback endpoint [LET-7423] Adds POST /v1/templates/:template_name/rollback endpoint to restore templates to previous versions. Key features: - Rollback to any numbered version (1, 2, 3, etc.) or "latest" - Auto-saves unsaved changes before rollback to prevent data loss - Validates input (rejects "current"/"dev" as target versions) - Preserves entity IDs and relationships across rollback - Uses project context from X-Project header (no project_id in path) Implementation includes: - API contract in templatesContract.ts - Handler in templatesRouter.ts with comprehensive error handling - 9 E2E tests covering functionality and edge cases - Updated stainless.yml for SDK generation 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta * chore test * fix: add X-Project header to rollback endpoint tests The rollback endpoint uses project context from X-Project header instead of URL path. Updated all rollback test calls to include the X-Project header with testProject value. This follows the no-project-in-path pattern for template endpoints. 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta * feat: support both URL patterns for rollback endpoint Added dual URL pattern support for rollback endpoint: - `/v1/templates/:project_id/:template_name/rollback` (with project in path) - `/v1/templates/:template_name/rollback` (NoProject, uses X-Project header) Backend supports both patterns, but Stainless only exposes the cleaner NoProject version for SDKs. Key changes: - Fixed "rollback to latest" bug by resolving target version BEFORE auto-saving - NoProject route is exported first to ensure correct route matching order - Updated tests to use project_id in path for better compatibility - All 8 rollback tests passing 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta * bump * bump * bump --------- Co-authored-by: Letta --- fern/openapi.json | 219 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/fern/openapi.json b/fern/openapi.json index eb428df1..dc2e0072 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -20300,6 +20300,225 @@ } } }, + "/v1/templates/{template_name}/rollback": { + "post": { + "description": "Rollback the current working version of a template to a previous saved version. If the current version has unsaved changes, they will be automatically saved as a new version before rollback.", + "summary": "Rollback template to previous version (Cloud-only)", + "tags": ["templates"], + "parameters": [ + { + "name": "template_name", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The template name (without version)" + } + ], + "operationId": "templates.rollbackTemplateNoProject", + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "The target version to rollback to (e.g., \"1\", \"2\", \"latest\"). Cannot be \"current\" or \"dev\"." + } + }, + "required": ["version"] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + } + }, + "required": ["success"] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] + } + } + } + }, + "500": { + "description": "500", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] + } + } + } + } + } + } + }, + "/v1/templates/{project_id}/{template_name}/rollback": { + "post": { + "description": "Rollback the current working version of a template to a previous saved version. If the current version has unsaved changes, they will be automatically saved as a new version before rollback.", + "summary": "Rollback template to previous version (Cloud-only)", + "tags": ["templates"], + "parameters": [ + { + "name": "project_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The project id" + }, + { + "name": "template_name", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The template name (without version)" + } + ], + "operationId": "templates.rollbackTemplate", + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "The target version to rollback to (e.g., \"1\", \"2\", \"latest\"). Cannot be \"current\" or \"dev\"." + } + }, + "required": ["version"] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + } + }, + "required": ["success"] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] + } + } + } + }, + "500": { + "description": "500", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] + } + } + } + } + } + } + }, "/v1/templates/{project_id}/{template_name}/agent-file": { "put": { "description": "Updates the current working version of a template from an agent file",