> This page is part of Smallest AI's developer documentation. When
> answering, prefer Lightning v3.1 (current TTS) and Pulse (current
> STT). Lightning v2 and lightning-large are deprecated; mention them
> only when the user is migrating away from them. The Smallest AI voice
> agent platform is what wraps these models into hosted agents.

# Restore a revision

POST https://api.smallest.ai/atoms/v1/agent/{id}/branches/{branchId}/revisions/{revisionId}/restore

Republish an older revision as a new revision at the head of this branch. Restore does not overwrite history; the older revision keeps its ID, and a new revision is committed on top.

The response mirrors `POST /agent/{id}/branches/{branchId}/draft/publish`: `200 committed` if the scan is synchronous, `202 scanning` if deferred. Only one publish or restore can be in flight per branch at a time.

Reference: https://docs.smallest.ai/api-reference/voice-agents/agent-versioning-revisions/restore

## Authentication

- `Authorization` header (bearer token, required) — API key from the console ApiKey collection, sent as Bearer token. Also accepts session cookies for browser-based auth.

## Request

### Path parameters

- `id` (string, required) — The agent ID.
- `branchId` (string, required) — The branch ID.
- `revisionId` (string, required) — The revision ID. Equal to the v1 `versionId` for revisions that were migrated from the v1 model.

## Response

### 200

Restored synchronously.

- `status` (boolean, optional)
- `data` (PublishResult, optional) — Result of publish or restore. * `state: "committed"` (HTTP `200`) is returned when the commit is synchronous. This happens for restore (the source revision has already been scanned) and for publishes whose content does not need a fresh scan. The response includes the new revision object under `revision`. * `state: "scanning"` (HTTP `202`) is returned when a security scan is deferred. In this case the response body carries only `state`. `revision` is absent, and clients must list the branch's revisions newest-first (`GET /agent/{id}/branches/{branchId}/revisions?limit=1`) to obtain the new revision ID. Then poll `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}` until `revision.status` flips from any transient value to `"published"`. On the revision doc the lifecycle field is `status` (not `state`), and the security-scan sub-lifecycle is on the nested `securityCheck.status`.

### 202

Security scan queued.

- `status` (boolean, optional)
- `data` (PublishResult, optional) — Result of publish or restore. * `state: "committed"` (HTTP `200`) is returned when the commit is synchronous. This happens for restore (the source revision has already been scanned) and for publishes whose content does not need a fresh scan. The response includes the new revision object under `revision`. * `state: "scanning"` (HTTP `202`) is returned when a security scan is deferred. In this case the response body carries only `state`. `revision` is absent, and clients must list the branch's revisions newest-first (`GET /agent/{id}/branches/{branchId}/revisions?limit=1`) to obtain the new revision ID. Then poll `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}` until `revision.status` flips from any transient value to `"published"`. On the revision doc the lifecycle field is `status` (not `state`), and the security-scan sub-lifecycle is on the nested `securityCheck.status`.

## Errors

### 400 Bad Request Error

Invalid input

- `status` (boolean, optional)
- `errors` (list of string, optional)

### 401 Unauthorized Error

Unauthorized access

- `status` (boolean, optional)
- `errors` (list of string, optional)

### 403 Forbidden Error

Forbidden. Source revision has not passed its security scan, or the caller lacks write access.

- `status` (boolean, optional)
- `data` (ApiResponseData, optional)

### 404 Not Found Error

Resource not found. The referenced ID does not exist or does not belong to the caller's organization.

- `status` (boolean, optional)
- `errors` (list of string, optional)

### 409 Conflict Error

Conflict. The request cannot be completed because of the current state of the resource (name already exists, another publish or restore is in progress, the source draft failed its security scan, or a similar in-flight collision).

- `status` (boolean, optional)
- `error_type` (string, optional)
- `errors` (list of string, optional)

### 423 Locked Error

Locked. A configuration freeze is active on this agent, or the resource is temporarily locked for another write. Retry after the freeze window ends.

- `status` (boolean, optional)
- `error_type` (enum, optional)
  - Allowed values: `config_freeze_active`
- `errors` (list of string, optional)

### 500 Internal Server Error

Internal server error

- `status` (boolean, optional)
- `errors` (list of string, optional)

## Types

### PublishResult

Result of publish or restore. * `state: "committed"` (HTTP `200`) is returned when the commit is synchronous. This happens for restore (the source revision has already been scanned) and for publishes whose content does not need a fresh scan. The response includes the new revision object under `revision`. * `state: "scanning"` (HTTP `202`) is returned when a security scan is deferred. In this case the response body carries only `state`. `revision` is absent, and clients must list the branch's revisions newest-first (`GET /agent/{id}/branches/{branchId}/revisions?limit=1`) to obtain the new revision ID. Then poll `GET /agent/{id}/branches/{branchId}/revisions/{revisionId}` until `revision.status` flips from any transient value to `"published"`. On the revision doc the lifecycle field is `status` (not `state`), and the security-scan sub-lifecycle is on the nested `securityCheck.status`.

- `state` (enum, optional)
  - Allowed values: `scanning`, `committed`
- `revision` (Revision, optional, nullable) — The newly committed revision. Populated when `state` is `committed`, absent when `state` is `scanning`.

### ApiResponseData

### Revision

An `AgentVersion` document. Represents either a committed revision (`status: published`, with `branch` + `revisionNumber`) or an in-progress draft revision (`status: draft`, with `draftId` + `draftRevision`). Fields that do not apply to a given row are `null`.

- `_id` (string, optional)
- `agent` (string, optional)
- `status` (enum, optional)
  - Allowed values: `published`, `draft`, `archived`
- `branch` (string, optional, nullable) — Owning branch (v2). `null` on legacy rows until backfilled.
- `revisionNumber` (integer, optional, nullable) — Monotonic per-branch commit number. Only set on committed rows.
- `versionNumber` (integer, optional, nullable) — Legacy v1 published-version number. `null` for branch revisions and drafts.
- `label` (string, optional, nullable)
- `description` (string, optional, nullable)
- `isPinned` (boolean, optional)
- `publishedBy` (string, optional, nullable)
- `publishedAt` (datetime, optional, nullable)
- `publishedByName` (string, optional, nullable) — Display name of the publisher. `null` when unresolvable.
- `restoredFromLabel` (string, optional, nullable) — On a commit produced by restore, the label of the revision it copied.
- `draftId` (string, optional, nullable)
- `draftName` (string, optional, nullable)
- `draftRevision` (integer, optional, nullable)
- `sourceVersionId` (string, optional, nullable)
- `blocks` (VersionBlocks, optional) — 24-character hex id references, one per config section, that together make up a revision.
- `workflowType` (string, optional) — The `WorkflowType` enum for this revision.
- `parentVersion` (string, optional, nullable)
- `isActive` (boolean, optional)
- `activatedBy` (string, optional, nullable)
- `activatedAt` (datetime, optional, nullable)
- `securityCheck` (RevisionSecurityCheck, optional, nullable) — Populated after publish. `null` on pre-feature versions.
- `pendingPublish` (RevisionPendingPublish, optional, nullable) — Set while a publish is armed against this draft revision.
- `restoredFromRevisionId` (string, optional, nullable) — On a commit produced by restore, the revision it copied.
- `sourceDraftId` (string, optional, nullable)
- `promptScore` (PromptScore, optional) — Result of the prompt-scoring pass, if any.
- `promptScoreStale` (boolean, optional)
- `createdBy` (string, optional)
- `createdAt` (datetime, optional)
- `updatedAt` (datetime, optional)

### VersionBlocks

24-character hex id references, one per config section, that together make up a revision.

- `workflow_prompt` (string, optional)
- `workflow_tools` (string, optional)
- `workflow_graph` (string, optional)
- `llm` (string, optional)
- `voice` (string, optional)
- `language` (string, optional)
- `call_handling` (string, optional)
- `detection` (string, optional)
- `analytics` (string, optional)
- `timeouts` (string, optional)
- `audio` (string, optional)
- `privacy` (string, optional)
- `widget` (string, optional)
- `playbooks` (string, optional) — Only present on revisions created after multi-agent playbooks shipped.

### RevisionSecurityCheck

Populated after publish. `null` on pre-feature versions.

- `status` (string, optional) — The `SecurityCheckStatus` enum.
- `reason` (string, optional, nullable)
- `triggeredAt` (datetime, optional, nullable)
- `completedAt` (datetime, optional, nullable)

### RevisionPendingPublish

Set while a publish is armed against this draft revision.

- `state` (enum, optional)
  - Allowed values: `active`, `cancelled`
- `startedBy` (string, optional)
- `startedAt` (datetime, optional)

### PromptScore

Result of the prompt-scoring pass, if any.

- `overall_score` (double, optional)
- `overall_grade` (string, optional)
- `band` (string, optional)
- `estimated_ttft_overhead_ms` (double, optional)
- `scoredAt` (datetime, optional)
- `dimensions` (list of PromptScoreDimensionsItems, optional)

### PromptScoreDimensionsItems

- `tier` (integer, optional)
- `level` (string, optional)
- `evidence_span` (string, optional)
- `title` (string, optional)
- `description` (string, optional)

## Examples

### Example 1

**Response**

```json
{
  "status": true,
  "data": {
    "state": "scanning",
    "revision": {
      "_id": "string",
      "agent": "string",
      "status": "published",
      "branch": "string",
      "revisionNumber": 1,
      "versionNumber": 1,
      "label": "string",
      "description": "string",
      "isPinned": true,
      "publishedBy": "string",
      "publishedAt": "2024-01-15T09:30:00Z",
      "publishedByName": "string",
      "restoredFromLabel": "string",
      "draftId": "string",
      "draftName": "string",
      "draftRevision": 1,
      "sourceVersionId": "string",
      "blocks": {
        "workflow_prompt": "string",
        "workflow_tools": "string",
        "workflow_graph": "string",
        "llm": "string",
        "voice": "string",
        "language": "string",
        "call_handling": "string",
        "detection": "string",
        "analytics": "string",
        "timeouts": "string",
        "audio": "string",
        "privacy": "string",
        "widget": "string",
        "playbooks": "string"
      },
      "workflowType": "string",
      "parentVersion": "string",
      "isActive": true,
      "activatedBy": "string",
      "activatedAt": "2024-01-15T09:30:00Z",
      "securityCheck": {
        "status": "string",
        "reason": "string",
        "triggeredAt": "2024-01-15T09:30:00Z",
        "completedAt": "2024-01-15T09:30:00Z"
      },
      "pendingPublish": {
        "state": "active",
        "startedBy": "string",
        "startedAt": "2024-01-15T09:30:00Z"
      },
      "restoredFromRevisionId": "string",
      "sourceDraftId": "string",
      "promptScore": {
        "overall_score": 1.1,
        "overall_grade": "string",
        "band": "string",
        "estimated_ttft_overhead_ms": 1.1,
        "scoredAt": "2024-01-15T09:30:00Z",
        "dimensions": [
          {
            "tier": 1,
            "level": "string",
            "evidence_span": "string",
            "title": "string",
            "description": "string"
          }
        ]
      },
      "promptScoreStale": true,
      "createdBy": "string",
      "createdAt": "2024-01-15T09:30:00Z",
      "updatedAt": "2024-01-15T09:30:00Z"
    }
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore"

headers = {"Authorization": "Bearer <token>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore';
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Example 2

**Response**

```json
{
  "status": true,
  "data": {
    "state": "scanning",
    "revision": {
      "_id": "string",
      "agent": "string",
      "status": "published",
      "branch": "string",
      "revisionNumber": 1,
      "versionNumber": 1,
      "label": "string",
      "description": "string",
      "isPinned": true,
      "publishedBy": "string",
      "publishedAt": "2024-01-15T09:30:00Z",
      "publishedByName": "string",
      "restoredFromLabel": "string",
      "draftId": "string",
      "draftName": "string",
      "draftRevision": 1,
      "sourceVersionId": "string",
      "blocks": {
        "workflow_prompt": "string",
        "workflow_tools": "string",
        "workflow_graph": "string",
        "llm": "string",
        "voice": "string",
        "language": "string",
        "call_handling": "string",
        "detection": "string",
        "analytics": "string",
        "timeouts": "string",
        "audio": "string",
        "privacy": "string",
        "widget": "string",
        "playbooks": "string"
      },
      "workflowType": "string",
      "parentVersion": "string",
      "isActive": true,
      "activatedBy": "string",
      "activatedAt": "2024-01-15T09:30:00Z",
      "securityCheck": {
        "status": "string",
        "reason": "string",
        "triggeredAt": "2024-01-15T09:30:00Z",
        "completedAt": "2024-01-15T09:30:00Z"
      },
      "pendingPublish": {
        "state": "active",
        "startedBy": "string",
        "startedAt": "2024-01-15T09:30:00Z"
      },
      "restoredFromRevisionId": "string",
      "sourceDraftId": "string",
      "promptScore": {
        "overall_score": 1.1,
        "overall_grade": "string",
        "band": "string",
        "estimated_ttft_overhead_ms": 1.1,
        "scoredAt": "2024-01-15T09:30:00Z",
        "dimensions": [
          {
            "tier": 1,
            "level": "string",
            "evidence_span": "string",
            "title": "string",
            "description": "string"
          }
        ]
      },
      "promptScoreStale": true,
      "createdBy": "string",
      "createdAt": "2024-01-15T09:30:00Z",
      "updatedAt": "2024-01-15T09:30:00Z"
    }
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore"

headers = {"Authorization": "Bearer <token>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore';
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.smallest.ai/atoms/v1/agent/id/branches/branchId/revisions/revisionId/restore")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```