Один файл без зависимостей. Работает в Node 18+ и современных браузерах (встроенные fetch, FormData, Web Crypto). Версия - 1.7.2 (константа SDK_VERSION в файле). Для каждой операции API в клиенте есть метод.
Установка
Скачать grem.ts и положить в проект. Пакета в npm нет.
Быстрый старт
import { GremClient } from './grem';
const grem = new GremClient({ apiKey: process.env.GREM_API_KEY });
const { estimate } = await grem.valuation.express({
propertyCategory: 'apartment',
addressText: 'Torstraße 12, Berlin',
totalArea: 64,
});
console.log(estimate.pricePoint, estimate.currency);
Базовый адрес по умолчанию - https://developers.grem.capital/api/v1 (параметр baseUrl). Ключ создается в кабинете: API → Ключи.
Задачи
Каждый ответ задачи содержит jobId (API 1.6.0+). jobs.wait опрашивает статус (бесплатно), пока state не станет completed, partial, failed или action_required. Неудачный запрос статуса (сеть, 5xx, 429) его не останавливает: опрос продолжается, до 5 сбоев подряд:
const job = await grem.text.generate({ city: 'Berlin', totalArea: 64, language: 'en' });
const done = await grem.jobs.wait('text/generate', job.jobId);
if (done.state === 'failed') throw new Error('generation failed');
Проверка отчета ждет подтверждения:
const form = new FormData();
form.append('file', pdfBlob, 'report.pdf');
const review = await grem.valuation.review(form);
await grem.valuation.confirmReview(review.jobId);
const result = await grem.jobs.wait('valuation/review', review.jobId);
Что есть в клиенте
| Группа | Методы |
|---|---|
| ключ | ping(), me() |
| оценка | valuation.express, valuation.report, valuation.rent, valuation.review, valuation.confirmReview |
| контент | content.generate, text.generate, video.create, video.music, video.endings, avatar.video, avatar.list |
| материалы | site.create, pdf.create, flyer.create, draft.extract, photo.enhance, floorplan.create |
| справочники | voices(), toolsStatus(), works(), dictionaries() |
| задачи и файлы | jobs.get, jobs.wait, artifact(path, { range, signal }), uploads.images |
| объекты | objects.list, get, create, update, delete, setAgency, setAssignee |
| лиды | leads.list, get, create, update, delete, matches, claim, returnToPool |
| Swipe Match | match.list, get, events, sendEvent |
| вебхуки | webhooks.list, create, update, delete, test, deliveries, resend |
| чат | chat.createSession, listSessions, updateSession, deleteSession, sendMessage, history, stream |
Инструменты с файлами принимают FormData - как заполнять поля, описано на странице каждого инструмента.
Песочница
С тестовым ключом опция simulate задаёт результат каждого имитированного вызова (Песочница):
const grem = new GremClient({ apiKey: process.env.GREM_TEST_KEY, simulate: 'failed' });
С боевым ключом опция simulate выбрасывает ошибку при создании клиента: вызов выполнился бы по-настоящему.
По умолчанию запрос ждёт до 5 минут (timeoutMs): запуск инструмента может идти больше минуты, а обрыв не останавливает ни работу, ни её оплату.
Клиент повторяет отклоненный вызов после Retry-After, до maxRetries раз (по умолчанию 3, не больше 10; 0 отключает повторы): 429 - всегда, вызов с превышением лимита не обработан; 503 - для чтения, а для записи только с Idempotency-Key: 503 может прийти уже после побочного эффекта. Retry-After больше 30 секунд клиент не ждет: ошибка выбрасывается сразу. Ваш AbortSignal прерывает и паузу. Передайте этот signal в request(). Без Retry-After паузы - 1, 2, 4 ... секунды. Загрузка (uploads.images) после 503 не повторяется (после 429 повторяется: загрузка не обработана). Переданный signal заменяет тайм-аут запроса (timeoutMs).
Ошибки
Ответ 4xx или 5xx бросает GremError с полями status, title, detail, code, errors, requestId и retryAfter (для 429 и 503):
import { GremError } from './grem';
try {
await grem.text.generate({});
} catch (e) {
if (e instanceof GremError) console.error(e.status, e.title, e.code, e.detail);
}
Безопасные повторы
Последний аргумент методов, которые запускают инструмент или создают запись, - Idempotency-Key:
await grem.pdf.create({ pdfName: 'Presentation', objectId, language: 'ENG' }, crypto.randomUUID());
uploads.images тоже принимает ключ, но API его игнорирует: повторная загрузка сохраняет файлы еще раз.
Вебхуки
import { verifyWebhookSignature } from './grem';
app.post('/grem-hook', express.raw({ type: 'application/json' }), async (req, res) => {
const raw = req.body.toString('utf8');
const ok = await verifyWebhookSignature(process.env.GREM_WEBHOOK_SECRET, req.get('x-grem-signature'), raw);
if (!ok) return res.status(400).send('invalid signature');
const event = JSON.parse(raw);
res.sendStatus(200);
});
Чат
const session = await grem.chat.createSession();
const stop = new AbortController();
const streaming = grem.chat
.stream(session.sessionId, (data) => console.log(data), stop.signal)
.catch((e) => { if (e.name !== 'AbortError') throw e; }); // stop.abort() rejects with AbortError
await grem.chat.sendMessage(session.sessionId, 'Привет!');
stop.abort(); // when you no longer need the stream
await streaming;
Без третьего аргумента sendMessage отправляет случайный clientMessageId (и в Node 18). Чтобы безопасно повторить сообщение, передайте свой id и используйте его повторно: на один id отвечают и списывают один раз.