Docs

TypeScript SDK

Last updated: 2026-10-034 min read

One file, no dependencies. Works in Node 18+ and modern browsers (built-in fetch, FormData, Web Crypto). Version 1.7.2 (the SDK_VERSION constant in the file). The client has a method for every API operation.

Install

Download grem.ts and put it in your project. There is no npm package.

Quickstart

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);

The default base address is https://developers.grem.capital/api/v1 (option baseUrl). Create a key in the cabinet: API → Keys.

Jobs

Every job response carries jobId (API 1.6.0+). jobs.wait polls the status (free) until state is completed, partial, failed or action_required. A failed poll (network, 5xx, 429) does not stop it: it keeps polling, up to 5 failures in a row:

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');

A report review waits for confirmation:

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);

What the client has

GroupMethods
keyping(), me()
valuationvaluation.express, valuation.report, valuation.rent, valuation.review, valuation.confirmReview
contentcontent.generate, text.generate, video.create, video.music, video.endings, avatar.video, avatar.list
materialssite.create, pdf.create, flyer.create, draft.extract, photo.enhance, floorplan.create
referencevoices(), toolsStatus(), works(), dictionaries()
jobs and filesjobs.get, jobs.wait, artifact(path, { range, signal }), uploads.images
listingsobjects.list, get, create, update, delete, setAgency, setAssignee
leadsleads.list, get, create, update, delete, matches, claim, returnToPool
Swipe Matchmatch.list, get, events, sendEvent
webhookswebhooks.list, create, update, delete, test, deliveries, resend
chatchat.createSession, listSessions, updateSession, deleteSession, sendMessage, history, stream

Tools with files take FormData - how to fill it is described on each tool's page.

Sandbox

With a test key the simulate option picks the outcome of every simulated call (Sandbox):

const grem = new GremClient({ apiKey: process.env.GREM_TEST_KEY, simulate: 'failed' });

With a live key the simulate option throws when the client is created: the call would run for real.

Requests wait up to 5 minutes by default (timeoutMs): starting a tool can take over a minute, and giving up early stops neither the work nor its charge.

The client repeats a refused call after Retry-After, up to maxRetries times (default 3, at most 10; 0 turns retries off): 429 always - a rate-limited call was not processed; 503 for reads, and for writes only when you pass an Idempotency-Key - a 503 can follow a side effect. A Retry-After over 30 seconds is not waited for: the error is thrown at once. Your AbortSignal also cancels the pause. Pass that signal to request(). Without Retry-After the pauses are 1, 2, 4 ... seconds. An upload (uploads.images) is not repeated after 503 (after 429 it is: the upload was not processed). A signal you pass replaces the request timeout (timeoutMs).

Errors

A 4xx or 5xx answer throws a GremError with status, title, detail, code, errors, requestId and retryAfter (for 429 and 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);
}

Safe retries

The last argument of methods that start a tool or create a record is the Idempotency-Key:

await grem.pdf.create({ pdfName: 'Presentation', objectId, language: 'ENG' }, crypto.randomUUID());

uploads.images takes a key too, but the API ignores it: a repeated upload stores the files again.

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, 'Hello!');
stop.abort(); // when you no longer need the stream
await streaming;

Without the third argument sendMessage sends a random clientMessageId (Node 18 included). To retry a message safely, pass your own id and reuse it: the same id is answered and charged once.

Was this article helpful?