# 🔄 Assistants API Compatibility Shim (`nexgendata/assistants-api-compatibility-shim`) Actor

Drop-in proxy keeping OpenAI Assistants API calls working past the August 26, 2026 sunset, plus a Thread→Conversation migration utility.

- **URL**: https://apify.com/nexgendata/assistants-api-compatibility-shim.md
- **Developed by:** [Stephan Corbeil](https://apify.com/nexgendata) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event + usage

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## 🔄 Assistants API Compatibility Shim

> **102 days until OpenAI Assistants API shutdown.**
> (Hard sunset: **August 26, 2026** — countdown computed against 2026-05-16.)

Your code uses `POST /v1/threads`, `POST /v1/threads/{id}/runs`, and the rest of the OpenAI Assistants API. OpenAI is sunsetting that surface on **August 26, 2026** in favor of the **Responses API**, which has a completely different shape (no threads, different message format, different tool wiring, different run lifecycle).

This actor is a drop-in HTTP proxy that keeps your Assistants-shaped calls working — and a one-shot migration utility for moving thread history into Responses `conversations` cleanly.

---

### ⏳ The sunset countdown

| | |
|---|---|
| Hard cutoff date | **2026-08-26** |
| As of 2026-05-16 | **102 days remaining** |
| Affected surface | `/v1/assistants`, `/v1/threads`, `/v1/threads/{id}/messages`, `/v1/threads/{id}/runs`, `/v1/threads/{id}/runs/{id}` |
| Replacement surface | `/v1/responses`, `/v1/conversations` |
| What breaks if you do nothing | Every hardcoded SDK call against `openai.beta.threads.*` or `openai.beta.assistants.*` returns 410 Gone after the cutoff |

If you own a SaaS, an internal automation, a Zapier/Make/n8n step, or a customer-facing chatbot that was built against the Assistants API in 2024–2025, you have one of two options:

1. **Rewrite every integration before the deadline.** Industry estimate: 5–50 dev-days per integration at $150–300/hr (≈ $6k–$120k per codebase).
2. **Point your client at this shim's URL instead of `api.openai.com`.** Zero code change. Buys you runway. Comes with a migration utility for when you're ready to cut over cleanly.

---

### 🧭 Two ways to use it

#### 1. Live proxy — zero code change

Swap your client's base URL. That's it.

```diff
- base_url = "https://api.openai.com/v1"
+ base_url = "https://<actor-id>.apify.actor/v1"
````

Your existing `client.beta.threads.create(...)`, `client.beta.threads.messages.create(...)`, `client.beta.threads.runs.create(...)` calls keep working. Internally, the shim:

1. Receives the Assistants-shaped request
2. Translates it to the Responses API shape
3. Calls OpenAI on your behalf using **your own API key** (passed via the standard `Authorization: Bearer sk-...` header — never stored)
4. Translates the response back to the Assistants shape your code expects
5. Returns it

#### 2. Migration utility — one-shot bulk cutover

When you're ready to cut over to native Responses calls, use the migration utility to port thread history.

```bash
curl -X POST "https://<actor-id>.apify.actor/v1/migrate?source_thread_id=thread_abc123" \
  -H "Authorization: Bearer sk-..."

curl -X POST "https://<actor-id>.apify.actor/v1/bulk_migrate" \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"thread_ids":["thread_abc","thread_def","thread_ghi"]}'
```

You get back a `conversation_id` per thread, with the message history seeded into the new Responses conversation. Replay or branch from there with native Responses calls. Old threads are not deleted.

***

### 🏗 Architecture

```
┌──────────────┐   Assistants-shape    ┌────────────────────────┐    Responses-shape    ┌─────────────┐
│ Your client  │  ───────────────────▶ │  Apify Standby actor   │ ───────────────────▶ │ api.openai  │
│ (unchanged)  │  Authorization: Bearer│  (this shim)           │   Same Bearer token  │   .com/v1   │
│  SDK / cURL  │                       │  • translate request   │                       │ /responses  │
│              │  ◀─────────────────── │  • call upstream       │ ◀─────────────────── │             │
└──────────────┘   Assistants-shape    │  • translate response  │   Responses-shape     └─────────────┘
                                       │  • thread state in KV  │
                                       └────────────────────────┘
                                                  │
                                                  ▼ (optional)
                                       ┌────────────────────────┐
                                       │ Apify Key-Value Store  │  thread_id → conversation_id
                                       │ (survives restarts)    │  message history mirror
                                       └────────────────────────┘
```

The actor runs in **Apify Standby mode** — it's an always-on HTTP server, not a one-shot scrape. You get a stable hostname (`https://<actor-id>.apify.actor`), the container stays warm under load, and idles down after 180 s of inactivity.

***

### 🔀 Translation table

The translation logic is the heart of the shim. Every Assistants concept maps to a Responses concept:

| Assistants API concept | Responses API concept | Notes |
|---|---|---|
| `thread_id` | `conversation_id` | Created lazily on first run; the shim stores the mapping. |
| `message.content` (array of `{type:"text", text:{value}}`) | `input` (string, or array of `{role, content}`) | Multi-part text gets joined when possible; image parts pass through as `input_image`. |
| `assistant_id` + `assistant.tools` | Request-time `tools` array | The shim keeps assistant configs in memory + KV; injects them on every run. |
| `assistant.instructions` | `instructions` field on the Responses request | Direct passthrough. |
| `run.status` lifecycle (`queued → in_progress → completed`) | Single synchronous Responses call | The shim returns a `completed` run immediately — Responses is not long-polling. |
| `run_steps` | Reassembled from `response.output[]` items | Tool calls and message outputs are flattened into the run-step shape your client expects. |
| Assistants `tools: [{type:"function", function:{...}}]` | Responses `tools: [{type:"function", name, description, parameters}]` | Schema repacks; semantics identical. |
| Assistants `tools: [{type:"code_interpreter"}]` | Responses `tools: [{type:"code_interpreter", container:{type:"auto"}}]` | Translates, but containers are auto-provisioned — explicit container IDs aren't carried over. |
| Assistants `tools: [{type:"file_search", vector_store_ids:[...]}]` | **Not supported in v1** | Vector stores don't migrate 1:1; see "Unsupported" below. |
| Assistants `messages.attachments` (file\_ids) | `input_image` / `input_file` parts | File-id passthrough for images; arbitrary file attachments → v2. |
| Assistants `metadata` | `metadata` | Stored and round-tripped. |

***

### ✅ Supported endpoints (v1)

- `POST /v1/threads` — create thread
- `GET  /v1/threads/{thread_id}/messages` — list messages
- `POST /v1/threads/{thread_id}/messages` — append message
- `POST /v1/threads/{thread_id}/runs` — synchronous run (returns `completed`)
- `GET  /v1/threads/{thread_id}/runs/{run_id}` — get run
- `POST /v1/assistants` — create assistant (stored in actor state)
- `GET  /v1/models` — passthrough to OpenAI
- `POST /v1/migrate?source_thread_id=...` — migrate one thread
- `POST /v1/bulk_migrate` — migrate up to thousands of threads in one call

### 🚧 Not yet supported (v2 roadmap)

- `/v1/vector_stores/*` and `file_search` tool
- `code_interpreter` with explicit container IDs (auto-containers do work)
- Streaming (`stream:true`) on `runs` — synchronous-only in v1
- `assistants.list` / `threads.delete` (low priority — most clients don't need them)

If you hit an unsupported endpoint, the shim returns HTTP 501 with `unsupported_endpoint` so your error handling can detect it cleanly.

***

### 🎯 Use cases

1. **SaaS app with hardcoded Assistants integration.** You shipped a customer-facing AI assistant feature in 2024 against `openai.beta.threads`. Rewriting before August 26 is a full sprint. Swap base URL → done.
2. **Zapier / Make / n8n workflow.** Your no-code automation has an "OpenAI Assistant" step. Point the custom-base-URL field at the shim and the workflow keeps running.
3. **Customer chatbot with persistent thread history.** Per-user `thread_id`s are stored in your DB. You can't lose conversation continuity. The shim preserves `thread_id` semantics; migration utility ports history to `conversation_id` when you're ready.
4. **Internal AI tools (Slack bot, ops dashboard).** Built once, forgotten about, still in production. Shim keeps them alive past sunset while you reprioritize a rewrite.
5. **Multi-tenant AI platform.** You expose "bring your own assistant" to customers. Each tenant's hardcoded Assistants integrations need a stopgap — point them all at the shim.
6. **Agentic frameworks pinned to old OpenAI SDK versions.** LangChain/LlamaIndex chains that wrap `AssistantAgent` keep working without a framework upgrade.

***

### ⚙️ Input schema

| Field | Type | Default | Description |
|---|---|---|---|
| `openaiApiKey` | string (secret) | `""` | Fallback OpenAI key used when an incoming request has no `Authorization` header. Most live-proxy users leave this empty — clients send the key per request. Set it for migration mode if your migration script doesn't send auth. |
| `mode` | enum | `"both"` | `"live-proxy"`, `"migration-only"`, or `"both"`. |
| `persistThreadState` | boolean | `true` | Mirror thread/assistant state to the actor's Key-Value Store so it survives restarts. |

***

### 🚀 Quick start

#### Step 1 — Start the actor in Standby mode

Run the actor once with the default input. Apify keeps the container warm. You'll get a Standby URL like `https://nexgendata--assistants-api-compatibility-shim.apify.actor`.

#### Step 2 — Repoint your client

##### Python (OpenAI SDK)

```python
from openai import OpenAI

client = OpenAI(
    api_key="sk-...",
    base_url="https://<actor-id>.apify.actor/v1",
)

## This call used to go to api.openai.com — now it goes through the shim.
thread = client.beta.threads.create()
client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="What's the capital of France?",
)
run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id="asst_xxx",
)
print(run.status)  ## "completed"
```

##### cURL — live proxy

```bash
## Create a thread
curl -X POST "https://<actor-id>.apify.actor/v1/threads" \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{}'

## Append a user message
curl -X POST "https://<actor-id>.apify.actor/v1/threads/thread_xxx/messages" \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"role":"user","content":"Hello"}'

## Run an assistant
curl -X POST "https://<actor-id>.apify.actor/v1/threads/thread_xxx/runs" \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"assistant_id":"asst_xxx"}'
```

##### cURL — migration

```bash
## One thread
curl -X POST "https://<actor-id>.apify.actor/v1/migrate?source_thread_id=thread_xxx" \
  -H "Authorization: Bearer sk-..."

## Bulk
curl -X POST "https://<actor-id>.apify.actor/v1/bulk_migrate" \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"thread_ids":["thread_a","thread_b","thread_c"]}'
```

##### Python — bulk migration script

```python
import httpx
ids = [...]  ## load from your DB
r = httpx.post(
    "https://<actor-id>.apify.actor/v1/bulk_migrate",
    headers={"Authorization": "Bearer sk-..."},
    json={"thread_ids": ids},
    timeout=600,
)
print(r.json()["success"], "migrated;", r.json()["failed"], "failed")
```

***

### ❓ FAQ

**Q: How long until OpenAI shuts down the Assistants API?**
A: **102 days** as of 2026-05-16. Hard cutoff is 2026-08-26. After that, every `/v1/threads/*` call returns 410 Gone.

**Q: What happens to my thread history if I just keep using the live proxy?**
A: The shim stores thread state (messages, assistant\_id mapping, conversation\_id mapping) in the Apify Key-Value Store. It survives restarts. But the upstream OpenAI threads themselves go away on 2026-08-26 — meaning the shim becomes the new source of truth for history. Run the migration utility before the cutoff to seed the data into Responses conversations.

**Q: Can I migrate without using the live proxy?**
A: Yes — set `mode: "migration-only"` and the live-proxy routes are disabled. You just call `/v1/bulk_migrate` once and get the mapping table.

**Q: What's the latency overhead of the shim?**
A: ~15–40 ms p50 added on top of the OpenAI call (one extra hop, JSON repack). Standby keeps the container warm so there's no cold start after the first request.

**Q: Are `file_search` / vector stores supported?**
A: Not in v1 — they're punted to v2 because vector\_store\_ids don't migrate 1:1 to Responses' file/container model. Hitting `/v1/vector_stores/*` returns HTTP 501 with `unsupported_endpoint`. If you depend on file\_search, migrate those specific call sites manually.

**Q: How does pricing compare to migration consulting?**
A: A typical agency engagement to rewrite Assistants integrations runs $6k–$120k per codebase. The shim is $0.05 per proxied request, $0.20 per thread migrated, and $12 per 100-thread bulk batch. Even a heavy user running 100k requests and migrating 10k threads pays ~$5,000 — and gets a runway, not a one-time rewrite.

**Q: What if OpenAI extends the deadline?**
A: Then you keep running the shim cheaply. There's no commitment — pay-per-event pricing means you pay $0 when your traffic is $0.

**Q: Do I need my own OpenAI API key?**
A: Yes — BYO. The shim is a translation layer; it does not resell OpenAI access. Pass your key in the `Authorization: Bearer sk-...` header on every request (the standard SDK behavior). Keys are never logged or persisted.

**Q: Is the container multi-tenant safe? Can two of my customers share one shim?**
A: Thread state is keyed by `thread_id`, which is globally unique. Two tenants with separate `thread_id`s won't collide. But for strict tenant isolation we recommend one shim run per tenant — Apify makes that cheap.

***

### 🆚 Comparison

| | DIY rewrite | OpenAI's official migration guide | Cleric / Bedrock proxies | **This shim** |
|---|---|---|---|---|
| Code changes required | All call sites | All call sites | Some config | **One base URL line** |
| Engineering effort | 5–50 dev-days | 5–50 dev-days | 1–5 dev-days | **5 minutes** |
| Migrates existing threads | Manual | Manual scripts you write | No | **Yes (bulk utility)** |
| Pricing | $6k–$120k consulting | Free (your time) | $500+/mo flat | **PPE: $0 idle, ~$0.05/request** |
| Hosted | You host | N/A | They host | **Apify hosts** |
| Source available | — | — | Closed | Sourced from this actor |

***

### 🤝 Sister actors from NexGenData

If this shim solved your migration problem, check out our other Apify-hosted AI infra:

- [`nexgendata/news-mcp-server`](https://apify.com/nexgendata/news-mcp-server) — News MCP server for Claude, Cursor, and other MCP clients.
- [`nexgendata/finance-mcp-server`](https://apify.com/nexgendata/finance-mcp-server) — Finance + market data MCP server.
- [`nexgendata/yahoo-finance-mcp-server`](https://apify.com/nexgendata/yahoo-finance-mcp-server) — Yahoo Finance MCP server with tickers, fundamentals, and history.
- [`nexgendata/developer-tools-mcp-server`](https://apify.com/nexgendata/developer-tools-mcp-server) — Developer tooling MCP server.
- [`nexgendata/google-maps-mcp-server`](https://apify.com/nexgendata/google-maps-mcp-server) — Google Maps MCP server for location, geocoding, and place search.

***

### 💸 Don't have an Apify account?

[**Sign up free**](https://www.apify.com/sign-up?fpr=2ayu9b) — **$5 free credits** to host this shim and keep your Assistants integrations alive past the August 2026 sunset.

***

### 📜 License

Source distributed with the actor. BYO OpenAI key — we never store, log, or proxy-decrypt your credentials.

# Actor input Schema

## `openaiApiKey` (type: `string`):

Your OpenAI API key. Optional — most users pass the key per-request via the standard 'Authorization: Bearer sk-...' header from their client (this is the drop-in-proxy use case). Set this here ONLY if you want a default fallback used when a request arrives with no Authorization header (e.g. for the /v1/migrate one-shot mode). Stored as a secret.

## `mode` (type: `string`):

Which subsystem(s) of the shim to expose. 'live-proxy' = Assistants→Responses translation routes only (POST /v1/threads, /v1/threads/{id}/messages, /v1/threads/{id}/runs, /v1/assistants, GET /v1/models). 'migration-only' = just /v1/migrate and /v1/bulk\_migrate. 'both' = everything (recommended).

## `persistThreadState` (type: `boolean`):

When true, thread metadata (thread\_id → conversation\_id mapping, message history) is written to the actor's Key-Value Store so it survives restarts and standby cycles. When false, state lives only in memory — faster, but a standby restart loses all thread mappings. Default true.

## Actor input object example

```json
{
  "mode": "both",
  "persistThreadState": true
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "openaiApiKey": "",
    "mode": "both",
    "persistThreadState": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("nexgendata/assistants-api-compatibility-shim").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "openaiApiKey": "",
    "mode": "both",
    "persistThreadState": True,
}

# Run the Actor and wait for it to finish
run = client.actor("nexgendata/assistants-api-compatibility-shim").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "openaiApiKey": "",
  "mode": "both",
  "persistThreadState": true
}' |
apify call nexgendata/assistants-api-compatibility-shim --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=nexgendata/assistants-api-compatibility-shim",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "🔄 Assistants API Compatibility Shim",
        "description": "Drop-in proxy keeping OpenAI Assistants API calls working past the August 26, 2026 sunset, plus a Thread→Conversation migration utility.",
        "version": "0.0",
        "x-build-id": "6rm3VaynJC9FAv39Y"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/nexgendata~assistants-api-compatibility-shim/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-nexgendata-assistants-api-compatibility-shim",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/nexgendata~assistants-api-compatibility-shim/runs": {
            "post": {
                "operationId": "runs-sync-nexgendata-assistants-api-compatibility-shim",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/nexgendata~assistants-api-compatibility-shim/run-sync": {
            "post": {
                "operationId": "run-sync-nexgendata-assistants-api-compatibility-shim",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "openaiApiKey": {
                        "title": "OpenAI API key (optional fallback)",
                        "type": "string",
                        "description": "Your OpenAI API key. Optional — most users pass the key per-request via the standard 'Authorization: Bearer sk-...' header from their client (this is the drop-in-proxy use case). Set this here ONLY if you want a default fallback used when a request arrives with no Authorization header (e.g. for the /v1/migrate one-shot mode). Stored as a secret."
                    },
                    "mode": {
                        "title": "Operating mode",
                        "enum": [
                            "live-proxy",
                            "migration-only",
                            "both"
                        ],
                        "type": "string",
                        "description": "Which subsystem(s) of the shim to expose. 'live-proxy' = Assistants→Responses translation routes only (POST /v1/threads, /v1/threads/{id}/messages, /v1/threads/{id}/runs, /v1/assistants, GET /v1/models). 'migration-only' = just /v1/migrate and /v1/bulk_migrate. 'both' = everything (recommended).",
                        "default": "both"
                    },
                    "persistThreadState": {
                        "title": "Persist thread state to Apify KV store",
                        "type": "boolean",
                        "description": "When true, thread metadata (thread_id → conversation_id mapping, message history) is written to the actor's Key-Value Store so it survives restarts and standby cycles. When false, state lives only in memory — faster, but a standby restart loses all thread mappings. Default true.",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
