# Migrate an OpenAI-compatible client

Source: https://cicora.ai/en/docs/migration

## Run a complete SDK example

You can keep the official OpenAI SDK for methods covered by the cicora.ai compatibility surface. You need Node.js, npm, `curl`, and `jq`. Start with one non-streaming Chat Completions request, meaning one complete JSON response rather than incremental output.



### Install the SDK

```bash
npm install openai
```



### Set the key and choose a current model

Query text models that accept the `max_tokens` used by the example. The checks below require `available == true` and exclude image endpoint entries.

```bash
set -euo pipefail

export CICORA_API_KEY="sk_..."

if ! MODELS_JSON="$(
  curl --fail-with-body --silent --show-error "https://api.cicora.ai/v1/models?output_modalities=text&supported_parameters=max_tokens" \
    -H "Authorization: Bearer $CICORA_API_KEY"
)"; then
  printf '%s\n' "$MODELS_JSON" >&2
  exit 1
fi

if ! CICORA_MODEL="$(
  jq -er '
    first(
      .data[]
      | select(
          .available == true
          and ((.architecture.output_modalities // []) | index("text"))
          and ((.supported_parameters // []) | index("max_tokens"))
          and ((.supported_endpoint_types // []) | all(. != "image-generation" and . != "image-edit"))
        )
      | .id
    )
  ' <<<"$MODELS_JSON"
)"; then
  printf 'No available text model with max_tokens found in /v1/models.\n' >&2
  exit 1
fi

export CICORA_MODEL
printf '%s\n' "$CICORA_MODEL"
```



### Create the client and request

Save this as `migrate.mjs`:

```js
import OpenAI from "openai";

const apiKey = process.env.CICORA_API_KEY;
const model = process.env.CICORA_MODEL;

if (!apiKey || !model) {
  throw new Error("Set CICORA_API_KEY and CICORA_MODEL");
}

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.cicora.ai/v1"
});

const completion = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "Reply with ok" }],
  max_tokens: 64
});

console.log(completion.choices[0]?.message?.content);
```



### Run it and read the output

```bash
node migrate.mjs
```

A successful run prints the text from `choices[0].message.content`, for example:

```text
ok
```



*An existing OpenAI-compatible client changes only its base URL and key.*

*Keep the SDK; point supported methods to cicora.ai.*

## Move configurations to cicora.ai

Use `https://api.cicora.ai/v1` for every Cicora OpenAI-compatible configuration.

## Understand the compatibility boundary

Changing `baseURL` does not make every OpenAI endpoint available. The example works because `client.chat.completions.create()` maps to the published `POST /v1/chat/completions` contract. cicora.ai also publishes `POST /v1/responses`, Conversations, model discovery, and documented image endpoints. Embeddings and audio transcription and translation endpoints are not published.

Check the method used by your SDK or tool before migrating it. If it requires an unpublished endpoint and cannot switch to Chat Completions or Messages, changing the URL alone will not make it compatible.

## Keep discovery in your setup

Refresh `GET /v1/models` instead of treating a model ID copied from this example as permanent. Use each model's current availability and `supported_parameters` when building optional request fields.

A **public error code** is the client-facing value in an API error, usually `error.code`. Record it with the HTTP status when diagnosing a migration; do not use private transport details.

## Troubleshooting


**The SDK returns 401**


Confirm that `CICORA_API_KEY` is available to the process and that `baseURL`
is exactly `https://api.cicora.ai/v1`. Verify the key separately with `GET
        /v1/models` and never print the full value.


**The request path contains /v1 twice**


Keep `/v1` in `baseURL` and call the SDK method normally. Do not also put
`/v1/chat/completions` into a setting that expects only the base URL.


**The client calls /v1/responses or another endpoint**


`/v1/responses` is published; follow [Responses and
Conversations](/en/docs/responses) and add only the documented portable
fields. For another unpublished endpoint, configure the client to use Chat
Completions or Messages when appropriate, or choose a documented compatible
tool.


**The sample model is unavailable or rejects an option**


Select an available ID from `GET /v1/models` and compare the request with
that entry's `supported_parameters`. Do not substitute `max_tokens` or
another field as a universal workaround.

## FAQ

### What is cicora.ai?

cicora.ai is a multi-model AI workspace and compatible API for chat, text, images, video, coding tools, and teams. Start with the [overview](/en.md), [documentation](/en/docs.md), or [model catalog](/en/models.md).

### How is usage billed?

Confirmed usage is charged in USD from the active workspace's prepaid balance. Use the live catalog for current model prices and the [usage guide](/en/docs/usage-costs.md) for settlement details.

### Which protocols and integrations are available?

Documentation covers OpenAI-compatible Chat Completions and Responses, Anthropic Messages, image interfaces, Claude Code, OpenCode, and Codex CLI. Compatibility does not imply support for every upstream parameter.

### Which sources are authoritative and current?

For model IDs, availability, capabilities, limits, and prices, use the [live catalog](/en/models.md). For API behavior, use the matching [documentation page](/en/docs.md). Never include API keys, private workspace data, or preview URLs in public documents.
