API FernieID
для разработчиков
Единая система авторизации, баланса и AI для всех проектов FernieX. Один API-ключ — доступ к аутентификации через Telegram-бота, балансам пользователей и AI-модели Aurin. Ниже — всё, что нужно, чтобы подключить своё приложение за 10 минут.
01 Введение
FernieID — это система авторизации и баланса, общая для всех проектов FernieX: Telegram-бота, AI-чата, игровой платформы, Minecraft-сервера и сторонних приложений разработчиков. API-ключ даёт вашему приложению доступ ко всем этим возможностям через один REST-интерфейс.
Что умеет API
- Аутентификация — вход пользователей FernieID с двухфакторным подтверждением через Telegram.
- Баланс — чтение и изменение баланса пользователя (DC, семена и другие валюты бота).
- AI-чат — доступ к модели Aurin (на базе Mistral) с потоковыми ответами и автоматическим поиском в интернете.
- Уведомления — отправка сообщений пользователю в Telegram от имени вашего приложения.
- Веб-поиск — обёртка над поисковой выдачей для использования в своих ботах и приложениях.
https://ferniex-id.vercel.app — все примеры ниже используют этот адрес.02 Быстрый старт
Путь от нуля до первого запроса — четыре шага.
Зарегистрируйся в FernieID
Создай аккаунт в личном кабинете и привяжи Telegram — без него не будут работать 2FA и уведомления.
Создай API-ключ
Раздел «API-ключи» → «Создать ключ» → укажи название и тип приложения. Ключ формата fid_... выдаётся один раз, скопируй его сразу.
Подключи SDK или делай запросы напрямую
Для веба проще всего подключить fernieid-sdk.js — он берёт на себя окно логина, 2FA и обёртки над баланс/AI эндпоинтами. Для бэкенда и ботов — просто вызывай REST-эндпоинты.
Проверь ключ
Сделай тестовый запрос на /api/apikeys/verify — если вернулся success: true, всё готово.
03 Получение API-ключа
Ключи создаются и управляются в личном кабинете FernieID, раздел «API-ключи». Никакого отдельного консольного доступа — всё через тот же аккаунт, которым пользуется человек.
Типы приложений
При создании ключа нужно выбрать тип — это влияет только на отображение в списке и внутреннюю аналитику, на права доступа не влияет.
action: "add" в /api/edit/balance) доступно только ключам с типом «Бот». Остальные типы могут только списывать (spend).Лимиты
- Максимум 10 ключей на один аккаунт (для администраторов сайта лимит снят).
- Полный текст ключа показывается только при создании — дальше он маскируется в интерфейсе.
- Каждый ключ можно переименовать, изменить тип и приложение, посмотреть логи запросов или удалить.
04 Подключение SDK
Для браузерных приложений проще всего подключить готовый скрипт fernieid-sdk.js — он рисует окно входа с 2FA, хранит сессию в localStorage и даёт удобные методы для баланса и AI.
<script src="/sdk/fernieid-sdk.js"></script>
<script>
FernieID.init('fid_твой_ключ_здесь');
</script>
Открыть окно входа
Метод возвращает промис, который резолвится данными пользователя после успешного входа (включая прохождение 2FA, если Telegram привязан).
const user = await FernieID.login();
// { success: true, userId, username, role, telegramLinked }
console.log('Вошёл как', user.username);
Методы SDK — справочник
localStorage без похода на сервер.['dc','seeds','balance']. Если Telegram не привязан — вернёт ссылку на привязку.action — "add" (только для бот-ключей) или "spend". currency — "dc" или "seeds".callbacks: { onToken, onDone, onError }.05 Аутентификация
Вход происходит по логину и паролю FernieID. Если у пользователя привязан Telegram, сервер сразу отправляет 6-значный код и ожидает подтверждения — это обязательный второй фактор, обойти его нельзя.
Проверяет логин/пароль. Если Telegram привязан — отправляет код и возвращает require2fa: true, вход завершается только после /api/auth/verify-2fa. Если Telegram не привязан — пускает сразу.
| Поле | Тип | Описание |
|---|---|---|
| apiKeyобязателен | string | Твой API-ключ |
| usernameобязателен | string | Логин FernieID |
| passwordобязателен | string | Пароль |
Значения username и password берутся из полей формы логина на твоей странице — их вводит сам пользователь, в коде они не прописываются заранее.
<input id="loginUsername" type="text" placeholder="Логин">
<input id="loginPassword" type="password" placeholder="Пароль">
<button id="loginBtn">Войти</button>
const API_KEY = 'fid_твой_ключ'; // хранится на бэкенде, не в клиентском коде
document.getElementById('loginBtn').onclick = async () => {
// значения берём из того, что реально ввёл пользователь на странице
const username = document.getElementById('loginUsername').value.trim();
const password = document.getElementById('loginPassword').value;
if (!username || !password) return alert('Заполни логин и пароль');
const res = await fetch('https://ferniex-id.vercel.app/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: API_KEY, username, password })
});
const data = await res.json();
// { success: true, require2fa: true, userId: 42, telegramLinked: true }
if (data.require2fa) {
// показываем пользователю поле для ввода кода из Telegram
showCodeInput(data.userId);
}
};
const API_KEY: string = 'fid_твой_ключ'; // хранится на бэкенде, не в клиентском коде
interface LoginResponse {
success: boolean;
require2fa?: boolean;
userId?: number;
telegramLinked?: boolean;
}
document.getElementById('loginBtn')!.addEventListener('click', async () => {
const username = (document.getElementById('loginUsername') as HTMLInputElement).value.trim();
const password = (document.getElementById('loginPassword') as HTMLInputElement).value;
if (!username || !password) return alert('Заполни логин и пароль');
const res = await fetch('https://ferniex-id.vercel.app/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: API_KEY, username, password })
});
const data: LoginResponse = await res.json();
if (data.require2fa) {
showCodeInput(data.userId!);
}
});
import requests
API_KEY = "fid_твой_ключ"
def login(username: str, password: str) -> dict:
res = requests.post(
"https://ferniex-id.vercel.app/api/auth/login",
json={"apiKey": API_KEY, "username": username, "password": password},
)
data = res.json()
# { "success": True, "require2fa": True, "userId": 42, "telegramLinked": True }
if data.get("require2fa"):
# дальше передай data["userId"] в verify_2fa()
pass
return data
'fernie' и '••••••••' из старого примера — это то, что попадёт в переменные из полей формы. Захардкоженных значений в реальном коде быть не должно, логин и пароль всегда приходят от пользователя.06 Подтверждение 2FA
Код приходит в Telegram-бот пользователя, действителен 5 минут, одноразовый.
Финальный шаг входа. При успехе возвращает полные данные пользователя, а в Telegram улетает уведомление об успешном входе.
| Поле | Тип | Описание |
|---|---|---|
| apiKeyобязателен | string | Твой API-ключ |
| userIdобязателен | number | Из ответа /api/auth/login |
| codeобязателен | string | 6-значный код из Telegram |
Функция showCodeInput(userId) из предыдущего шага показывает поле для кода — значение code берётся из него, когда пользователь ввёл цифры и нажал подтвердить.
<input id="twoFaCode" maxlength="6" placeholder="000000">
<button id="confirmCodeBtn">Подтвердить</button>
function showCodeInput(userId) {
document.getElementById('confirmCodeBtn').onclick = async () => {
// код берём из того, что пользователь реально ввёл в поле
const code = document.getElementById('twoFaCode').value.trim();
if (code.length !== 6) return alert('Введи 6-значный код из Telegram');
const res = await fetch('https://ferniex-id.vercel.app/api/auth/verify-2fa', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: API_KEY, userId, code })
});
const data = await res.json();
// { success: true, userId: 42, username: 'fernie', role: 'user' }
if (data.success) {
// сохраняем сессию, открываем личный кабинет и т.д.
}
};
}
interface VerifyResponse {
success: boolean;
userId?: number;
username?: string;
role?: string;
}
function showCodeInput(userId: number): void {
document.getElementById('confirmCodeBtn')!.addEventListener('click', async () => {
const code = (document.getElementById('twoFaCode') as HTMLInputElement).value.trim();
if (code.length !== 6) return alert('Введи 6-значный код из Telegram');
const res = await fetch('https://ferniex-id.vercel.app/api/auth/verify-2fa', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey: API_KEY, userId, code })
});
const data: VerifyResponse = await res.json();
if (data.success) {
// сохраняем сессию, открываем личный кабинет и т.д.
}
});
}
def verify_2fa(user_id: int, code: str) -> dict:
res = requests.post(
"https://ferniex-id.vercel.app/api/auth/verify-2fa",
json={"apiKey": API_KEY, "userId": user_id, "code": code},
)
data = res.json()
# { "success": True, "userId": 42, "username": "fernie", "role": "user" }
return data
'482913' из старого примера было просто иллюстрацией формата — в реальном коде так вставлять код нельзя, он всегда приходит из поля ввода.07 Проверка API-ключа
Используется, чтобы убедиться, что ключ валиден и активен — например, перед открытием окна логина.
Заодно увеличивает счётчики requests_total / requests_today у ключа и пишет запись в его лог.
| Поле | Тип | Описание |
|---|---|---|
| keyобязателен | string | API-ключ для проверки |
const data = await fetch('https://ferniex-id.vercel.app/api/apikeys/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'fid_твой_ключ' })
}).then(r => r.json());
// { success: true, userId, name, appName, type }
interface VerifyKeyResponse {
success: boolean;
userId?: number;
name?: string;
appName?: string;
type?: string;
}
const data: VerifyKeyResponse = await fetch('https://ferniex-id.vercel.app/api/apikeys/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'fid_твой_ключ' })
}).then(r => r.json());
import requests
data = requests.post(
"https://ferniex-id.vercel.app/api/apikeys/verify",
json={"key": "fid_твой_ключ"},
).json()
# { "success": True, "userId": ..., "name": ..., "appName": ..., "type": ... }
08 Получить баланс
Читает баланс пользователя напрямую из Telegram-бота (DC, семена, игровая валюта и т.д.) — данные всегда актуальны на момент запроса.
Пользователь должен быть найден по username и иметь привязанный Telegram — иначе вернётся ошибка no_telegram со ссылкой на привязку.
| Поле | Тип | Описание |
|---|---|---|
| apiKeyобязателен | string | Твой API-ключ |
| usernameобязателен | string | Логин пользователя FernieID |
| fieldsопционально | string[] | Например ['dc','seeds']. По умолчанию — все известные поля. |
const data = await fetch('https://ferniex-id.vercel.app/api/balance/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
username: 'fernie',
fields: ['dc', 'seeds']
})
}).then(r => r.json());
// { success: true, username: 'fernie', dc: 4200, seeds: 15300 }
interface BalanceResponse {
success: boolean;
username?: string;
dc?: number;
seeds?: number;
}
const data: BalanceResponse = await fetch('https://ferniex-id.vercel.app/api/balance/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
username: 'fernie',
fields: ['dc', 'seeds']
})
}).then(r => r.json());
data = requests.post(
"https://ferniex-id.vercel.app/api/balance/get",
json={
"apiKey": "fid_твой_ключ",
"username": "fernie",
"fields": ["dc", "seeds"],
},
).json()
# { "success": True, "username": "fernie", "dc": 4200, "seeds": 15300 }
09 Изменить баланс
Списывает или начисляет валюту через бота. Все операции логируются в историю ключа.
| Поле | Тип | Описание |
|---|---|---|
| apiKeyобязателен | string | Твой API-ключ |
| usernameобязателен | string | Логин пользователя |
| currencyобязателен | string | "dc" или "seeds" |
| amountобязателен | number | Сумма операции |
| actionобязателен | string | "add" (только тип «Бот») или "spend" |
const data = await fetch('https://ferniex-id.vercel.app/api/edit/balance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_бот_ключ',
username: 'fernie',
currency: 'dc',
amount: 500,
action: 'add'
})
}).then(r => r.json());
interface EditBalanceResponse {
success: boolean;
dc?: number;
seeds?: number;
}
const data: EditBalanceResponse = await fetch('https://ferniex-id.vercel.app/api/edit/balance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_бот_ключ',
username: 'fernie',
currency: 'dc',
amount: 500,
action: 'add'
})
}).then(r => r.json());
data = requests.post(
"https://ferniex-id.vercel.app/api/edit/balance",
json={
"apiKey": "fid_твой_бот_ключ",
"username": "fernie",
"currency": "dc",
"amount": 500,
"action": "add",
},
).json()
add) работает только если ключ создан с типом «Бот». Для веб- и других типов ключей сервер вернёт ошибку.10 AI Chat — разовый запрос
Доступ к модели Aurin — ассистенту FernieX-AI поверх Mistral, с автоматическим веб-поиском и чтением ссылок «под капотом». Внешне модель никогда не раскрывает свою базовую платформу.
| Поле | Тип | Описание |
|---|---|---|
| apiKeyобязателен | string | Твой API-ключ |
| messagesобязателен | array | [{ role: 'user', content: '...' }] |
| modelопционально | string | aurin-lite / mistral-medium / aurin / aurin-pro / aurin-custom |
| messages[0]опционально | object | Системный промпт передаётся как сообщение { role: 'system', content: '...' } первым элементом массива messages. Учитывается для всех моделей, но по-настоящему нужен только для aurin-custom — см. раздел 13. |
| userIdопционально | number | ID пользователя FernieID — даёт больший дневной лимит токенов вместо анонимного |
| max_tokensопционально | number | По умолчанию 8192 |
| streamопционально | boolean | false для этого эндпоинта |
const data = await fetch('https://ferniex-id.vercel.app/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
model: 'aurin-lite',
messages: [{ role: 'user', content: 'Привет! Что нового в мире технологий?' }]
})
}).then(r => r.json());
// { choices: [{ message: { role, content } }], usage: { total_tokens } }
interface ChatResponse {
choices: { message: { role: string; content: string } }[];
usage: { total_tokens: number };
}
const data: ChatResponse = await fetch('https://ferniex-id.vercel.app/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
model: 'aurin-lite',
messages: [{ role: 'user', content: 'Привет! Что нового в мире технологий?' }]
})
}).then(r => r.json());
data = requests.post(
"https://ferniex-id.vercel.app/api/chat",
json={
"apiKey": "fid_твой_ключ",
"model": "aurin-lite",
"messages": [{"role": "user", "content": "Привет! Что нового в мире технологий?"}],
},
).json()
# { "choices": [{ "message": { "role", "content" } }], "usage": { "total_tokens" } }
11 AI Chat — потоковый ответ
Тот же эндпоинт /api/chat, но с stream: true — ответ приходит частями по SSE (Server-Sent Events), удобно для «печатающегося» текста в UI.
const res = await fetch('https://ferniex-id.vercel.app/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
model: 'aurin',
stream: true,
messages: [{ role: 'user', content: 'Расскажи историю в 3 предложениях' }]
})
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const raw = line.slice(6).trim();
if (raw === '[DONE]') continue;
const delta = JSON.parse(raw).choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta); // или dom.textContent += delta
}
}
const res = await fetch('https://ferniex-id.vercel.app/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
model: 'aurin',
stream: true,
messages: [{ role: 'user', content: 'Расскажи историю в 3 предложениях' }]
})
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer: string = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop()!;
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const raw = line.slice(6).trim();
if (raw === '[DONE]') continue;
const delta = JSON.parse(raw).choices?.[0]?.delta?.content;
if (delta) console.log(delta);
}
}
import json
import requests
with requests.post(
"https://ferniex-id.vercel.app/api/chat",
json={
"apiKey": "fid_твой_ключ",
"model": "aurin",
"stream": True,
"messages": [{"role": "user", "content": "Расскажи историю в 3 предложениях"}],
},
stream=True,
) as res:
for line in res.iter_lines(decode_unicode=True):
if not line or not line.startswith("data: "):
continue
raw = line[6:].strip()
if raw == "[DONE]":
continue
delta = json.loads(raw)["choices"][0]["delta"].get("content")
if delta:
print(delta, end="", flush=True)
С SDK то же самое пишется в несколько строк:
await FernieID.streamChat(
[{ role: 'user', content: 'Привет!' }],
{
onToken: chunk => console.log(chunk),
onDone: full => console.log('Готово:', full),
onError: msg => console.error(msg)
}
);
12 Лимиты токенов
У AI-чата суточные лимиты в токенах, привязанные к пользователю FernieID (или к самому API-ключу для анонимных запросов).
| Категория | Лимит в сутки |
|---|---|
| Обычный пользователь FernieID | 200 000 токенов |
| Пользователь с подпиской Fernie+ | 1 500 000 токенов |
Анонимный запрос (без userId, только по ключу) | 50 000 токенов на ключ |
Возвращает расход токенов за сегодня и остаток лимита.
const data = await fetch(`https://ferniex-id.vercel.app/api/chat/usage/${userId}`)
.then(r => r.json());
// { success: true, used: 12400, limit: 200000, has_plus: false, remaining: 187600 }
interface UsageResponse {
success: boolean;
used: number;
limit: number;
has_plus: boolean;
remaining: number;
}
const data: UsageResponse = await fetch(`https://ferniex-id.vercel.app/api/chat/usage/${userId}`)
.then(r => r.json());
data = requests.get(
f"https://ferniex-id.vercel.app/api/chat/usage/{user_id}"
).json()
# { "success": True, "used": 12400, "limit": 200000, "has_plus": False, "remaining": 187600 }
13 Aurin-Custom — своя персона
Отдельная модель для случаев, когда готовые персоны Aurin не подходят и приложению нужен ассистент под своим именем, характером и стилем ответа. Вместо системного промпта FernieX-AI по умолчанию модель получает ровно тот промпт, который передашь ты.
Чем отличается от остальных моделей
- Работает на базе
Aurin-medium, как иAurin-medium. - Не подставляет собственную персону («Ты — Aurin…») — вместо неё используется системное сообщение, которое ты передашь первым элементом
messages. - Не несёт зашитых скиллов вроде
netsearch/webfetch— если они нужны, добавляй инструкции сам.
Тот же эндпоинт, что и в разделе 10 — просто передай model: "aurin-custom" и свой system.
| Поле | Тип | Описание |
|---|---|---|
| apiKeyобязателен | string | Твой API-ключ |
| messagesобязателен | array | [{ role: 'user', content: '...' }] |
| modelобязателен | string | Значение "aurin-custom" |
| messages[0]опционально | object | Передай { role: 'system', content: '...' } первым элементом messages — имя, роль, характер и формат ответа, всё, что должна соблюдать модель |
const data = await fetch('https://ferniex-id.vercel.app/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
model: 'aurin-custom',
messages: [
{ role: 'system', content: 'Тебя зовут Марта. Ты дружелюбный консультант книжного магазина, отвечаешь коротко и с лёгким юмором.' },
{ role: 'user', content: 'Посоветуй что почитать вечером' }
]
})
}).then(r => r.json());
// { choices: [{ message: { role: 'assistant', content: '...' } }], usage: {...} }
interface ChatResponse {
choices: { message: { role: string; content: string } }[];
usage: { total_tokens: number };
}
const data: ChatResponse = await fetch('https://ferniex-id.vercel.app/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: 'fid_твой_ключ',
model: 'aurin-custom',
messages: [
{ role: 'system', content: 'Тебя зовут Марта. Ты дружелюбный консультант книжного магазина, отвечаешь коротко и с лёгким юмором.' },
{ role: 'user', content: 'Посоветуй что почитать вечером' }
]
})
}).then(r => r.json());
data = requests.post(
"https://ferniex-id.vercel.app/api/chat",
json={
"apiKey": "fid_твой_ключ",
"model": "aurin-custom",
"messages": [
{"role": "system", "content": "Тебя зовут Марта. Ты дружелюбный консультант книжного магазина, отвечаешь коротко и с лёгким юмором."},
{"role": "user", "content": "Посоветуй что почитать вечером"},
],
},
).json()
# { "choices": [{ "message": { "role": "assistant", "content": "..." } }], "usage": {...} }
role: 'system' модель отвечает от имени «Aurin-Custom» с нейтральным промптом по умолчанию — это не ошибка, просто нечего кастомизировать.14 Веб-поиск
Обёртка над поиском (Serper API с фолбэком на DuckDuckGo) — пригодится, если хочешь дать своему боту доступ к свежим данным без AI-чата.
| Поле | Тип | Описание |
|---|---|---|
| qобязателен | string | Поисковый запрос |
const data = await fetch('https://ferniex-id.vercel.app/api/search?q=погода+в+москве')
.then(r => r.json());
// { success: true, results: [{ title, url, snippet }], query }
interface SearchResult { title: string; url: string; snippet: string; }
interface SearchResponse { success: boolean; results: SearchResult[]; query: string; }
const data: SearchResponse = await fetch('https://ferniex-id.vercel.app/api/search?q=погода+в+москве')
.then(r => r.json());
data = requests.get(
"https://ferniex-id.vercel.app/api/search",
params={"q": "погода в москве"},
).json()
# { "success": True, "results": [{ "title", "url", "snippet" }], "query" }
15 Сообщения в Telegram
Отправка произвольного HTML-сообщения пользователю в Telegram напрямую от имени системы — удобно для собственных уведомлений (например, статус заявки в твоём приложении).
| Поле | Тип | Описание |
|---|---|---|
| telegram_idобязателен | string|number | Telegram ID получателя |
| textобязателен | string | Текст сообщения, поддерживает HTML-разметку Telegram (<b>, <i>, <blockquote>) |
await fetch('https://ferniex-id.vercel.app/api/telegram/send-message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
telegram_id: '7406372338',
text: '✅ <b>Твоя заявка одобрена!</b>'
})
});
async function sendTelegramMessage(telegramId: string, text: string): Promise<void> {
await fetch('https://ferniex-id.vercel.app/api/telegram/send-message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ telegram_id: telegramId, text })
});
}
sendTelegramMessage('7406372338', '✅ <b>Твоя заявка одобрена!</b>');
requests.post(
"https://ferniex-id.vercel.app/api/telegram/send-message",
json={
"telegram_id": "7406372338",
"text": "✅ <b>Твоя заявка одобрена!</b>",
},
)
16 Коды ошибок
Почти все эндпоинты возвращают HTTP 200 с полем success: false и текстом ошибки в error — исключение составляет /api/chat, где используются настоящие HTTP-статусы.
| Ошибка | Где встречается | Что значит |
|---|---|---|
| no_telegram | balance/get, edit/balance, cases/buy | У пользователя не привязан Telegram |
| not_registered | balance/get | Пользователь с таким логином не найден |
| not_in_bot | balance/get | Аккаунт есть в FernieID, но не найден в базе бота |
| invalid_model | chat (HTTP 400) | Указана несуществующая модель |
| jailbreak_detected | chat (HTTP 400) | Сообщение попало под фильтр защиты от джейлбрейков |
| — (HTTP 429) | chat | Превышен суточный лимит токенов |
| — (HTTP 401) | chat | Не передан или недействителен API-ключ |
17 Лимиты и защита
- 10 API-ключей максимум на аккаунт (не действует для сайт-администраторов).
- Коды 2FA и сброса пароля живут 5–10 минут и одноразовые.
- Пополнение баланса (
action: "add") доступно только ключам с типом «Бот». - AI-чат имеет суточные лимиты токенов по трём уровням (см. раздел 12) и фильтр джейлбрейков на входящих сообщениях — включая
aurin-custom. - Каждый запрос через ключ пишется в его лог (IP, время, действие) — историю можно посмотреть в личном кабинете.
18 FAQ
Можно ли использовать ключ на бэкенде и фронтенде одновременно?
Технически да, но операции с балансом (особенно add) лучше держать только на бэкенде — если ключ утечёт из клиентского кода, им сможет воспользоваться кто угодно.
Почему 2FA нельзя отключить для моего приложения?
2FA — часть защиты аккаунта пользователя FernieID, а не настройка конкретного приложения. Если Telegram привязан — код спросят всегда, независимо от того, через какой ключ идёт запрос.
Что будет, если у пользователя нет Telegram?
Вход пройдёт без 2FA, но большинство операций (баланс, уведомления, кейсы, покупки) потребуют привязки — сервер вернёт понятную ошибку no_telegram с готовой ссылкой на привязку.
Можно ли выбрать другую модель для AI-чата?
Да — aurin-lite (быстрая), mistral-medium, aurin (базовая), aurin-pro (с внутренним рассуждением через <thinking>, отдаёт только чистый результат) и aurin-custom (своя персона и системный промпт, см. раздел 13).