---
title: "Remote Control"
id: "660"
type: "page"
slug: "remote-control"
published_at: "2026-06-07T17:50:40+00:00"
modified_at: "2026-06-10T10:46:36+00:00"
url: "https://xedant.com/code/docs/remote-control"
markdown_url: "https://xedant.com/code/docs/remote-control.md"
excerpt: "Remote Control lets you drive Xedant Code programmatically via a simple HTTP API. Create chats,…"
---

# Remote Control

[https://xedant.com/code/docs/remote-control.md](https://xedant.com/code/docs/remote-control.md)

Remote Control lets you drive Xedant Code programmatically via a simple HTTP API. Create chats, send prompts, and retrieve AI responses — all without opening a browser. This makes it perfect for CI/CD pipelines, automated workflows, and integrating AI capabilities into your existing systems.

The API is ideal for adding **“Edit with AI”** buttons to any application. Instead of building a complex integration with the OpenAI API or an agentic framework from scratch, you simply send an HTTP request to Xedant Code — which already has skills, validation, hooks, environments, and the full power of Claude Code built in. Your system gets AI-assisted code editing with zero additional complexity.

Combined with [Skills](/code/docs/skills)
 and [Validation](/code/docs/validation)
, Remote Control can be significantly more effective than integrating the OpenAI API or any agentic framework from scratch. Skills provide domain-specific context and workflows, while validation ensures every change is checked and fixed either automatically via AutoFix feature or at least manually reviewed.

## Setup

Remote Control requires a single environment variable: `XEDANT_CODE_API_KEY`. This variable holds the SHA256 hash of your API key — the plain-text key is never stored on the server.

### Step 1: Generate an API Key Hash

Choose an API key (any string you want) and compute its SHA256 hash. The server will compare incoming keys by hashing them and matching against this value:

```
# Linux / macOS
echo -n "my-secret-api-key" | sha256sum | awk '{print $1}'
# Output: a1b2c3d4e5f6... (64-character hex string)
```

You can also use the SHA256 generator on the [login page](/code/docs/getting-started)
 — click “SHA256 generator” below the Sign In button.

### Step 2: Set the Environment Variable

Set the hash as `XEDANT_CODE_API_KEY` on your Xedant Code instance:

```
# Docker (docker-compose.yml)
services:
  code:
    environment:
      - XEDANT_CODE_API_KEY=a1b2c3d4e5f6...

# Linux / macOS
export XEDANT_CODE_API_KEY=a1b2c3d4e5f6...

# Windows PowerShell
$env:XEDANT_CODE_API_KEY="a1b2c3d4e5f6..."
```

If the variable is not set, the API returns `503 Service Unavailable` — it’s disabled by default for security.

## Authentication

All requests to the Remote API must include the `X-API-Key` header with your **plain-text** API key. The server hashes it automatically and compares with the stored value. This means the actual key never touches the disk — only its hash does.

```
X-API-Key: my-secret-api-key
```

- **401 Unauthorized** — missing or invalid API key
- **503 Service Unavailable** — API disabled (`XEDANT_CODE_API_KEY` not set)

## Create Chat

```
POST /api/remote/chats
```

Creates a new chat and optionally sends an initial prompt. The prompt is enqueued and processed automatically by the message queue.

### Request Body

- `prompt` (string, optional) — initial user message to send
- `title` (string, optional) — chat title (defaults to “Remote API Chat”)
- `environment` (string, optional) — environment name to use for this chat (see [Environments](/code/docs/environments) )

### Response (201 Created)

```
{
  "chatId": 123,
  "url": "/123",
  "status": "queued",
  "createdAt": "2026-06-07T17:50:00Z"
}
```

The `status` is `"queued"` when a prompt is provided (message is waiting in the queue) or `"created"` when no prompt is given.

### Example

```
curl -X POST http://localhost:5173/api/remote/chats \
  -H "X-API-Key: my-secret-api-key" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Fix the null reference in UserService.cs", "title": "Bug Fix"}'
```

## Chat Status

```
GET /api/remote/chats/{chatId}/status
```

Returns the current processing status, message count, total cost, and error details if something went wrong.

### Response (200 OK)

```
{
  "chatId": 123,
  "status": "processing",
  "title": "Bug Fix",
  "createdAt": "2026-06-07T17:50:00Z",
  "updatedAt": "2026-06-07T17:52:30Z",
  "messageCount": 4,
  "totalCost": 0.035,
  "error": null
}
```

**Status values:**

- `created` — chat exists, no messages sent yet
- `queued` — messages waiting in the queue
- `processing` — Claude Code is actively working
- `completed` — all messages processed, chat idle
- `error` — an error occurred (details in the `error` field)

### Example

```
curl http://localhost:5173/api/remote/chats/123/status \
  -H "X-API-Key: my-secret-api-key"
```

## Chat Messages

```
GET /api/remote/chats/{chatId}/messages?since={timestamp}
```

Retrieves all messages in a chat. Use the optional `since` query parameter (ISO 8601 timestamp) for incremental updates — only messages created at or after that time are returned.

### Response (200 OK)

```
{
  "chatId": 123,
  "messages": [
    {
      "id": 456,
      "role": "user",
      "content": "Fix the null reference in UserService.cs",
      "createdAt": "2026-06-07T17:50:00Z",
      "tokens": { "input": 250, "output": 0 },
      "cost": 0.0001
    },
    {
      "id": 457,
      "role": "assistant",
      "content": "I found the null reference issue...",
      "createdAt": "2026-06-07T17:50:15Z",
      "tokens": { "input": 1800, "output": 420 },
      "cost": 0.0320
    }
  ],
  "since": "2026-06-07T17:50:00Z"
}
```

Each message includes the role (`user`, `assistant`, or `system`), content, timestamps, token counts, and cost. The `since` parameter is echoed back in the response for convenience.

### Example

```
# Get all messages
curl http://localhost:5173/api/remote/chats/123/messages \
  -H "X-API-Key: my-secret-api-key"

# Get only new messages since a timestamp
curl "http://localhost:5173/api/remote/chats/123/messages?since=2026-06-07T17:51:00Z" \
  -H "X-API-Key: my-secret-api-key"
```

## How It Works Internally

When you create a chat with a prompt, the following happens inside Xedant Code:

1. **Chat created** — a new chat record is stored in the database with the title, environment, and timestamp
2. **Message enqueued** — the prompt is placed into the message queue (a database-backed queue processed every 1 second)
3. **Claude Code instance started** — the message queue processor picks up the message and creates a Claude Code instance for the project
4. **Processing** — Claude Code reads the prompt, applies the active skill and hooks, uses tools to read/edit files, and generates a response
5. **Validation** — if [Validation](/code/docs/validation) is configured, build results and checks are automatically evaluated
6. **Complete** — the response is stored as an assistant message, status changes to `completed`

The `environment` parameter selects the AI model and configuration from your [Environments](/code/docs/environments)
 setup. This means you can use different models or settings for different API callers — for example, a fast model for quick edits and a powerful model for complex refactors.

## Polling Pattern

The recommended way to use the API is a simple polling loop: create a chat, poll status until completion, then retrieve messages.

```
import time, requests

API_KEY = "my-secret-api-key"
BASE = "http://localhost:5173/api/remote"

# 1. Create chat with a prompt
resp = requests.post(f"{BASE}/chats",
    headers={"X-API-Key": API_KEY},
    json={"prompt": "Add error handling to the login endpoint",
          "title": "Error Handling", "environment": "production"})
chat_id = resp.json()["chatId"]

# 2. Poll until completed
while True:
    status = requests.get(f"{BASE}/chats/{chat_id}/status",
        headers={"X-API-Key": API_KEY}).json()
    if status["status"] == "completed":
        break
    elif status["status"] == "error":
        raise Exception(status["error"])
    time.sleep(2)

# 3. Get the assistant's response
messages = requests.get(f"{BASE}/chats/{chat_id}/messages",
    headers={"X-API-Key": API_KEY}).json()["messages"]
for msg in messages:
    if msg["role"] == "assistant":
        print(msg["content"])
```

For long-running tasks, use the `since` parameter to fetch only new messages incrementally instead of re-fetching the entire history each time.

## Integration with Existing Systems

Remote Control is the simplest path to adding AI-assisted code editing to any application. Compared to integrating the OpenAI API or building an agentic framework from scratch, Xedant Code gives you:

- **No prompt engineering required** — [Skills](/code/docs/skills) provide pre-built domain context, instructions, and workflows. Ask the model to load a skill and the AI already knows what to do.
- **Automatic validation** — [Validation](/code/docs/validation) runs builds, linting, and custom checks after every change. Your integration doesn’t need to verify results — Xedant Code does it for you.
- **Full Claude Code capabilities** — file reading, editing, git operations, terminal commands, web search, and MCP server access. Everything Claude Code can do is available through the API.
- **Safety guardrails** — [Hooks](/code/docs/hooks) prevent dangerous commands and enforce policies automatically.
- **Three HTTP calls** — create chat, poll status, get messages. That’s the entire integration surface.

A typical “Edit with AI” button in your app would POST to `/api/remote/chats` with a descriptive prompt, poll `/api/remote/chats/{id}/status` until complete, then display the result. The AI handles reading files, understanding context, making changes, and running validation — your app just handles the HTTP calls.

## Error Responses

All errors follow a consistent JSON format:

```
{
  "error": "ErrorType",
  "message": "Human-readable description",
  "details": "Additional context (optional)"
}
```

- `Unauthorized` — invalid or missing API key (401)
- `NotFound` — chat ID does not exist (404)
- `Remote API is disabled` — `XEDANT_CODE_API_KEY` not set (503)
- `InternalServerError` — unexpected server error (500)

**[← .xedant Folder](/code/docs/xedant-folder)**
