> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# AI assistants: create, deploy, and run the lifecycle

> Drive the full assistant (agent) lifecycle over the Orbit API — create, list, fetch, update, deploy, and delete — with one assistant_id reused across every call, and full request/response envelopes to copy.

# AI assistants

An **assistant** is an Orbit AI agent: a named system prompt, model, and tool set that you bind to a channel when you deploy it. This page is the worked reference for the assistant lifecycle — create, list, fetch, update, deploy, undeploy, and delete — with one `assistant_id` carried across the whole sequence. Field-level reference lives in the [Agents API](/api-reference/agents); this page walks the sequence end to end.

**Base path:** `/api/v1/agents`

**Authentication:** API key (`X-API-Key`) or session JWT. Create, update, deploy, undeploy, and delete require an `owner`, `admin`, or `developer` role; list and fetch are open to any authenticated role.

Every response below carries the full `{ data, meta }` envelope — `meta.request_id` and `meta.timestamp` on the success path, a labelled error envelope on the failure path — the same floor as [SDK sample coverage policy](/api-reference/sdk-language-policy).

## Lifecycle worked sequence

Create the assistant once, reuse its `assistant_id` for everything afterward. Nothing seeds channel binding on create — the agent lands as a `draft`, and `deploy` flips it `active`.

### 1. Create the assistant

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/agents" \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Order support assistant",
      "type": "chatbot",
      "model": "claude-sonnet-4-6",
      "system_prompt": "You answer order-status questions for Acme Kitchens. Cite the order id in every reply.",
      "tools": [
        {
          "name": "lookup_order",
          "description": "Fetch one order by id",
          "parameters": {
            "type": "object",
            "properties": { "order_id": { "type": "string" } },
            "required": ["order_id"]
          }
        }
      ],
      "safety_config": { "pii_redaction": true, "content_filter": true }
    }'
  ```

  ```typescript Node.js theme={null}
  import { Orbit } from "@devotel-orbit/node";

  const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

  const created = await orbit.request("POST", "/agents", {
    name: "Order support assistant",
    type: "chatbot",
    model: "claude-sonnet-4-6",
    system_prompt:
      "You answer order-status questions for Acme Kitchens. Cite the order id in every reply.",
    tools: [
      {
        name: "lookup_order",
        description: "Fetch one order by id",
        parameters: {
          type: "object",
          properties: { order_id: { type: "string" } },
          required: ["order_id"],
        },
      },
    ],
    safety_config: { pii_redaction: true, content_filter: true },
  });

  const assistantId = created.data.id;
  ```

  ```python Python theme={null}
  import os
  import requests

  headers = {
      "X-API-Key": os.environ["ORBIT_API_KEY"],
      "Content-Type": "application/json",
  }

  body = {
      "name": "Order support assistant",
      "type": "chatbot",
      "model": "claude-sonnet-4-6",
      "system_prompt": "You answer order-status questions for Acme Kitchens. Cite the order id in every reply.",
      "tools": [
          {
              "name": "lookup_order",
              "description": "Fetch one order by id",
              "parameters": {
                  "type": "object",
                  "properties": {"order_id": {"type": "string"}},
                  "required": ["order_id"],
              },
          }
      ],
      "safety_config": {"pii_redaction": True, "content_filter": True},
  }

  res = requests.post(
      "https://api.orbit.devotel.io/api/v1/agents",
      headers=headers,
      json=body,
  )
  assistant_id = res.json()["data"]["id"]
  ```

  ```go Go theme={null}
  package main

  import (
  	"bytes"
  	"encoding/json"
  	"net/http"
  	"os"
  )

  func main() {
  	body := []byte(`{
  	  "name": "Order support assistant",
  	  "type": "chatbot",
  	  "model": "claude-sonnet-4-6",
  	  "system_prompt": "You answer order-status questions for Acme Kitchens. Cite the order id in every reply.",
  	  "tools": [{
  	    "name": "lookup_order",
  	    "description": "Fetch one order by id",
  	    "parameters": {
  	      "type": "object",
  	      "properties": { "order_id": { "type": "string" } },
  	      "required": ["order_id"]
  	    }
  	  }],
  	  "safety_config": { "pii_redaction": true, "content_filter": true }
  	}`)
  	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/agents", bytes.NewReader(body))
  	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
  	req.Header.Set("Content-Type", "application/json")
  	res, _ := http.DefaultClient.Do(req)
  	defer res.Body.Close()
  	var out struct {
  		Data struct {
  			ID string `json:"id"`
  		} `json:"data"`
  	}
  	json.NewDecoder(res.Body).Decode(&out)
  	// out.Data.ID is the assistant_id reused below
  }
  ```

  ```ruby Ruby theme={null}
  require "net/http"
  require "json"

  uri = URI("https://api.orbit.devotel.io/api/v1/agents")
  req = Net::HTTP::Post.new(uri)
  req["X-API-Key"] = ENV["ORBIT_API_KEY"]
  req["Content-Type"] = "application/json"
  req.body = JSON.dump(
    name: "Order support assistant",
    type: "chatbot",
    model: "claude-sonnet-4-6",
    system_prompt: "You answer order-status questions for Acme Kitchens. Cite the order id in every reply.",
    tools: [{
      name: "lookup_order",
      description: "Fetch one order by id",
      parameters: {
        type: "object",
        properties: { order_id: { type: "string" } },
        required: ["order_id"]
      }
    }],
    safety_config: { pii_redaction: true, content_filter: true }
  )
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
  assistant_id = JSON.parse(res.body).dig("data", "id")
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init("https://api.orbit.devotel.io/api/v1/agents");
  $body = json_encode([
    "name" => "Order support assistant",
    "type" => "chatbot",
    "model" => "claude-sonnet-4-6",
    "system_prompt" => "You answer order-status questions for Acme Kitchens. Cite the order id in every reply.",
    "tools" => [[
      "name" => "lookup_order",
      "description" => "Fetch one order by id",
      "parameters" => [
        "type" => "object",
        "properties" => ["order_id" => ["type" => "string"]],
        "required" => ["order_id"],
      ],
    ]],
    "safety_config" => ["pii_redaction" => true, "content_filter" => true],
  ]);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $body,
    CURLOPT_HTTPHEADER => [
      "X-API-Key: " . getenv("ORBIT_API_KEY"),
      "Content-Type: application/json",
    ],
  ]);
  $res = json_decode(curl_exec($ch), true);
  $assistantId = $res["data"]["id"];
  ?>
  ```
</CodeGroup>

Response `201`:

```json theme={null}
{
  "data": {
    "id": "agt_8f2e41abcc9d70",
    "name": "Order support assistant",
    "type": "chatbot",
    "model": "claude-sonnet-4-6",
    "status": "draft",
    "created_at": "2026-09-15T13:11:02.418Z"
  },
  "meta": {
    "request_id": "req_agc_9d31ef2",
    "timestamp": "2026-09-15T13:11:02.418Z"
  }
}
```

Carry the returned `assistant_id` (`agt_8f2e41abcc9d70`) through the rest of this page — it is the same id you pass to fetch, update, deploy, undeploy, and delete.

### 2. Deploy the assistant to a channel

`deploy` binds a channel and moves the assistant out of `draft` into `active`. The channel name is one of `webhook`, `sms`, `whatsapp`, `voice`, or `rcs`; `webhook_url` is required only for `webhook` channels, and a `phone_number` is required only for SMS / WhatsApp / voice.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/agents/agt_8f2e41abcc9d70/deploy" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "sms", "phone_number": "+14155550100" }'
```

Response `200` — `status` is `active` and `config.deployment` carries the channel binding:

```json theme={null}
{
  "data": {
    "id": "agt_8f2e41abcc9d70",
    "status": "active",
    "config": {
      "deployment": {
        "channel": "sms",
        "phone_number": "+14155550100",
        "deployed_at": "2026-09-15T13:14:50.117Z"
      }
    }
  },
  "meta": {
    "request_id": "req_dpl_77a1b2",
    "timestamp": "2026-09-15T13:14:50.117Z"
  }
}
```

### 3. List to confirm the assistant is live

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/agents?status=active&limit=1" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Response `200` — the envelope's `meta.pagination` carries the `cursor` and `has_more` pair you use to walk the full list:

```json theme={null}
{
  "data": [
    {
      "id": "agt_8f2e41abcc9d70",
      "name": "Order support assistant",
      "type": "chatbot",
      "model": "claude-sonnet-4-6",
      "status": "active",
      "created_at": "2026-09-15T13:11:02.418Z",
      "active_conversations": 1,
      "conversation_count": 4,
      "total_tokens": 981,
      "version": 1
    }
  ],
  "meta": {
    "request_id": "req_agl_201d88x",
    "timestamp": "2026-09-15T13:16:22.044Z",
    "pagination": {
      "cursor": "2026-09-15T13:11:02.418Z|agt_8f2e41abcc9d70",
      "has_more": false,
      "total": 1
    }
  }
}
```

### 4. Fetch one assistant by id

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/agents/agt_8f2e41abcc9d70" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Same envelope as the list, without pagination. Use this form when you already know the id — there is no separate "assistant detail" resource.

### 5. Update the system prompt, tools, or safety config in place

`PATCH` rewrites only the fields you send. Bump the prompt or safety configuration without redeploying.

```bash theme={null}
curl -X PATCH "https://api.orbit.devotel.io/api/v1/agents/agt_8f2e41abcc9d70" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "system_prompt": "You answer order-status questions for Acme Kitchens. Cite the order id in every reply, and link the tracking URL from the order record." }'
```

Response `200`:

```json theme={null}
{
  "data": {
    "id": "agt_8f2e41abcc9d70",
    "version": 2,
    "updated_at": "2026-09-15T13:24:18.508Z"
  },
  "meta": {
    "request_id": "req_agp_31ed47c",
    "timestamp": "2026-09-15T13:24:18.508Z"
  }
}
```

### 6. Undeploy, then delete

`undeploy` drops the channel binding and flips `status` back to `draft`. Delete the assistant only when it is no longer needed — conversation history stays on the tenant.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/agents/agt_8f2e41abcc9d70/undeploy" \
  -H "X-API-Key: dv_live_sk_your_key_here"

curl -X DELETE "https://api.orbit.devotel.io/api/v1/agents/agt_8f2e41abcc9d70" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Undeploy returns `200` with the same envelope. Delete returns `204 No Content` with an empty body.

## Error envelopes

An unknown id — whether passed to fetch, update, deploy, undeploy, or delete — answers `404` with a labelled error envelope, not a bare status:

```json 404 Error theme={null}
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Assistant agt_8f2e41abcc9d70 was not found in this organization.",
    "status": 404
  },
  "meta": {
    "request_id": "req_age_41d0b8",
    "timestamp": "2026-09-15T13:30:02.447Z"
  }
}
```

Validation failures on create come back as `422 VALIDATION_ERROR` — one entry per field, with the same `meta` block. A cross-tenant id returns the same `404 NOT_FOUND` as one that never existed, so existence cannot be probed across organizations. If you receive a `403 INSUFFICIENT_PERMISSIONS`, the caller's role is below `developer`; only owner, admin, and developer may write assistants.

## Escape hatches beyond the Node.js and Python samples

Every public SDK ships an untyped request method that reaches any endpoint when a typed helper does not exist yet — the same semantics as the [Agents API](/api-reference/agents) page's Python escape hatch. The per-language idiom:

* **Go** — plain `net/http`; set `X-API-Key` + `Content-Type` and decode the `{ data, meta }` envelope directly.
* **Ruby** — `Net::HTTP` over the host + path pair; `JSON.parse` the envelope.
* **PHP** — cURL with `json_encode` bodies; `json_decode` returns the envelope as an associative array.
* **Java / C#** — `client.RequestAsync` / `client.request` per the [SDK language policy](/api-reference/sdk-language-policy)'s escape-hatch table; the route shape above applies verbatim.

See also [Agents API](/api-reference/agents) for the full field catalogue and [Agent templates](/api-reference/assistant-templates) for the pre-built-into-assistant flow.
