Подключите AITUNNEL к популярным инструментам разработки и автоматизации.

DeepSeek HarnessАгентный харнесс DeepSeek с рабочими пространствами и плагинами — модели через AITUNNEL
Claude CodeТерминальный агент Anthropic — правит код, запускает команды и git-операции через AITUNNEL
Codex CLICodex CLIOpen-source агент OpenAI в терминале — правки кода, ревью и автоматизация задач
Cursor IDEAI-редактор на VS Code с автодополнением Tab, агентом Composer и моделями через AITUNNEL
Qwen CodeCLI-агент для Qwen3-Coder — навигация по проекту, сборка фич и правки через AITUNNEL
OpenCodeОткрытый агент для терминала и IDE с поддержкой 75+ LLM-провайдеров
n8nВизуальная автоматизация с AI-агентами, RAG и интеграциями — модели через AITUNNEL
ClineAI-агент для VS Code — правки файлов, команды в терминале и браузер с подтверждением шагов
OpenClawAI-агент для Telegram, Discord и Slack — модели через один API-ключ AITUNNEL
Все инструменты

Вход через AITunnel (OAuth PKCE)

Ваше приложение получает API-ключ пользователя AITunnel без ручного копирования: человек входит в аккаунт, задаёт лимит и нажимает «Разрешить».

Страница согласия
https://aitunnel.ru/panel/authorize с callback_url и code_challenge
Обмен кода
POST https://api.aitunnel.ru/v1/aitunnel/auth/keys, без авторизации, CORS открыт
Срок кода
10 минут, один раз
Результат
Обычный ключ sk-aitunnel-… аккаунта пользователя, помеченный адресом приложения

Это поток OAuth 2.0 authorization code с PKCE (RFC 7636), как у OpenRouter. Регистрировать приложение не нужно, секрета клиента нет. Подходит для ботов, CLI, сайтов и плагинов.

Как это работает

  1. Приложение создаёт случайный code_verifier (43–128 символов A–Z a–z 0–9 - . _ ~) и хранит его у себя. Наружу отдаёт code_challenge = base64url(SHA-256(verifier)) без =.
  2. Приложение открывает пользователю страницу согласия с callback_url и code_challenge.
  3. Пользователь входит в AITunnel, видит адрес приложения и нажимает «Разрешить». Браузер возвращается на callback_url?code=…, а при отказе — на callback_url?error=access_denied.
  4. Приложение отправляет code и code_verifier в POST /v1/aitunnel/auth/keys и получает ключ.

Шаг 1. Страница согласия

URL
https://aitunnel.ru/panel/authorize
  ?callback_url=https://myapp.example.com/aitunnel/callback
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
callback_url
Куда вернуть браузер. Только https:// или http://localhost / 127.0.0.1 (для CLI). Без логина и пароля в адресе и без #фрагмента. Ваши параметры (например, state) сохраняются, code добавляется к ним
code_challenge
Для S256 — base64url(SHA-256(code_verifier)), 43 символа. Для plain — сам verifier
code_challenge_method
S256 (по умолчанию, рекомендуется) или plain
JavaScript
// 1. Перед входом: verifier храните у себя, наружу уходит только challenge
const base64url = (bytes) =>
  btoa(String.fromCharCode(...bytes))
    .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
const challenge = base64url(new Uint8Array(digest));
sessionStorage.setItem("aitunnel_verifier", verifier);

// 2. Отправьте пользователя на страницу согласия
const url = new URL("https://aitunnel.ru/panel/authorize");
url.searchParams.set("callback_url", "https://myapp.example.com/aitunnel/callback");
url.searchParams.set("code_challenge", challenge);
url.searchParams.set("code_challenge_method", "S256");
location.href = url.href;

Шаг 2. Обмен кода на ключ

JavaScript
// 3. На странице callback_url: обменяйте code на ключ
const params = new URLSearchParams(location.search);
if (params.get("error") === "access_denied") {
  // пользователь нажал «Отказать»
} else {
  const res = await fetch("https://api.aitunnel.ru/v1/aitunnel/auth/keys", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      code: params.get("code"),
      code_verifier: sessionStorage.getItem("aitunnel_verifier"),
      code_challenge_method: "S256",
    }),
  });
  const { key } = await res.json(); // sk-aitunnel-…
  sessionStorage.removeItem("aitunnel_verifier");
}

Тот же запрос из любого языка:

curl
curl https://api.aitunnel.ru/v1/aitunnel/auth/keys \
  -H "Content-Type: application/json" \
  -d '{
    "code": "Qm9vZ2llV29vZ2llLi4uY29kZS1mcm9tLWNhbGxiYWNr",
    "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
    "code_challenge_method": "S256"
  }'
Ответ 200
{
  "key": "sk-aitunnel-AbCdEfGhIjKlMnOpQrStUvWxYz012345",
  "user_id": 4242,
  "key_id": 917,
  "name": "myapp.example.com"
}

Пример: сервер бота

Если callback_url ведёт на ваш сервер (Telegram-бот, бэкенд сайта), verifier хранится на сервере, а пользователь приходит по ссылке из чата:

Python
# Сервер приложения (например, Telegram-бота): callback_url ведёт сюда
import base64, hashlib, secrets
import requests

def start_login(state_store, chat_id):
    verifier = secrets.token_urlsafe(32)
    challenge = base64.urlsafe_b64encode(
        hashlib.sha256(verifier.encode()).digest()
    ).rstrip(b"=").decode()
    state = secrets.token_urlsafe(16)
    state_store[state] = {"verifier": verifier, "chat_id": chat_id}
    callback = f"https://bot.example.com/aitunnel/callback?state={state}"
    return (
        "https://aitunnel.ru/panel/authorize"
        f"?callback_url={requests.utils.quote(callback, safe='')}"
        f"&code_challenge={challenge}&code_challenge_method=S256"
    )

def on_callback(state_store, state, code):
    login = state_store.pop(state)
    answer = requests.post(
        "https://api.aitunnel.ru/v1/aitunnel/auth/keys",
        json={"code": code, "code_verifier": login["verifier"]},
        timeout=30,
    ).json()
    return login["chat_id"], answer["key"]  # сохраните ключ за этим пользователем

Что видит пользователь

  • Страница согласия показывает адрес приложения — хост callback_url — и аккаунт, от имени которого выдаётся ключ.
  • Пользователь задаёт ключу название (по умолчанию — хост приложения), лимит расходов в рублях и срок: без срока, 30 дней, 90 дней или 1 год.
  • В разделе API-ключи у такого ключа метка с адресом приложения. Его можно поставить на паузу, изменить лимит или удалить, как любой другой ключ. Удалённый ключ сразу перестаёт работать (401).

Ошибки

400 invalid_code
Код неизвестен, истёк (10 минут), уже использован или code_verifier не подходит. Код сгорает после первой попытки, даже неудачной — начните вход заново
402 balance_required
Пока код ждал обмена, баланс аккаунта закончился
409 keys_limit_reached
У аккаунта уже максимум ключей
429
Слишком много неудачных обменов с вашего адреса
«Ссылка для входа неверна»
На странице согласия: callback_url не https, содержит #, логин или пароль, или нет code_challenge

Если у пользователя нулевой баланс или не подтверждена почта, страница согласия скажет об этом сразу и ключ не выдаст: пользователь пополнит баланс и войдёт в приложении снова.

Безопасность

  • code из адреса бесполезен без code_verifier: перехватить ключ по ссылке нельзя. Используйте S256 — с plain verifier виден в адресе страницы согласия.
  • Приложение не регистрируется, поэтому пользователь доверяет адресу, который видит. Показывайте в своём интерфейсе, что вход идёт через AITunnel, и не открывайте страницу согласия без действия пользователя.
  • Ключ приложения ничем не отличается от обычного: он видит баланс аккаунта (GET /aitunnel/balance) и свои запросы, но не может управлять аккаунтом, видеть другие ключи или пополнять баланс. Для управления аккаунтом из кода нужен ключ управления.
  • Выдавать ключ может только сам владелец аккаунта: сотрудник поддержки, вошедший в аккаунт, этого сделать не может.