Un archivo, sin dependencias. Funciona en Node 18+ y en navegadores modernos (fetch, FormData y Web Crypto integrados). Versión 1.7.2 (la constante SDK_VERSION del archivo). El cliente tiene un método para cada operación de la API.
Instalación
Descargue grem.ts y colóquelo en su proyecto. No hay paquete npm.
Inicio rápido
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);
La dirección base por defecto es https://developers.grem.capital/api/v1 (opción baseUrl). Cree una clave en el panel: API → Claves.
Trabajos
Cada respuesta de un trabajo lleva jobId (API 1.6.0+). jobs.wait consulta el estado (gratis) hasta que state sea completed, partial, failed o action_required. Una consulta fallida (red, 5xx, 429) no lo detiene: sigue consultando, hasta 5 fallos seguidos:
const job = await grem.text.generate({ city: 'Berlin', totalArea: 64, language: 'es' });
const done = await grem.jobs.wait('text/generate', job.jobId);
if (done.state === 'failed') throw new Error('generation failed');
Una revisión de informe espera confirmación:
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);
Qué contiene el cliente
| Grupo | Métodos |
|---|---|
| clave | ping(), me() |
| valoración | valuation.express, valuation.report, valuation.rent, valuation.review, valuation.confirmReview |
| contenido | content.generate, text.generate, video.create, video.music, video.endings, avatar.video, avatar.list |
| materiales | site.create, pdf.create, flyer.create, draft.extract, photo.enhance, floorplan.create |
| referencia | voices(), toolsStatus(), works(), dictionaries() |
| trabajos y archivos | jobs.get, jobs.wait, artifact(path, { range, signal }), uploads.images |
| inmuebles | objects.list, get, create, update, delete, setAgency, setAssignee |
| leads | leads.list, get, create, update, delete, matches, claim, returnToPool |
| Swipe Match | match.list, get, events, sendEvent |
| webhooks | webhooks.list, create, update, delete, test, deliveries, resend |
| chat | chat.createSession, listSessions, updateSession, deleteSession, sendMessage, history, stream |
Las herramientas con archivos reciben FormData: cómo rellenarlo se describe en la página de cada herramienta.
Sandbox
Con una clave de prueba, la opción simulate elige el resultado de cada llamada simulada (Sandbox):
const grem = new GremClient({ apiKey: process.env.GREM_TEST_KEY, simulate: 'failed' });
Con una clave real, la opción simulate lanza un error al crear el cliente: la llamada se ejecutaría de verdad.
Por defecto las solicitudes esperan hasta 5 minutos (timeoutMs): lanzar una herramienta puede tardar más de un minuto, y cortar antes no detiene ni el trabajo ni su cobro.
El cliente repite una llamada rechazada tras Retry-After, hasta maxRetries veces (3 por defecto, 10 como máximo; 0 desactiva las repeticiones): 429 siempre, porque una llamada por encima del límite no se procesó; 503 en las lecturas, y en las escrituras solo si pasa un Idempotency-Key, porque un 503 puede llegar después de un efecto. Un Retry-After de más de 30 segundos no se espera: el error se lanza enseguida. Su AbortSignal también cancela la pausa. Pase ese signal a request(). Sin Retry-After las pausas son de 1, 2, 4 ... segundos. Una subida (uploads.images) no se repite tras un 503 (tras un 429 sí: la subida no se procesó). Un signal que pase sustituye el tiempo límite de la solicitud (timeoutMs).
Errores
Una respuesta 4xx o 5xx lanza un GremError con status, title, detail, code, errors, requestId y retryAfter (para 429 y 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);
}
Repeticiones seguras
El último argumento de los métodos que lanzan una herramienta o crean un registro es el Idempotency-Key:
await grem.pdf.create({ pdfName: 'Presentación', objectId, language: 'SPA' }, crypto.randomUUID());
uploads.images también acepta una clave, pero la API la ignora: una subida repetida guarda los archivos otra vez.
Webhooks
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);
});
Chat
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, '¡Hola!');
stop.abort(); // when you no longer need the stream
await streaming;
Sin el tercer argumento, sendMessage envía un clientMessageId aleatorio (también en Node 18). Para repetir un mensaje con seguridad, pase su propio id y reutilícelo: el mismo id se responde y se cobra una sola vez.