# TypeScript SDK `@shruwd/sdk` is a typed wrapper over the [API](https://shruwd.io/docs/api/api-overview.md). ESM, and no runtime dependencies beyond `fetch`. Its types are generated from the OpenAPI document, so the client and the contract cannot drift apart. ```bash npm install @shruwd/sdk ``` ## Getting started ```ts import { Shruwd } from '@shruwd/sdk'; const shruwd = new Shruwd({ apiKey: process.env.SHRUWD_API_KEY! }); const brand = await shruwd.brands.create({ name: 'Waitlister', domain: 'waitlister.me' }); // Competitors first: the first measurement starts as soon as prompts exist. await shruwd.entities.add(brand.brandId, { name: 'LaunchList', domains: ['getlaunchlist.com'], }); await shruwd.prompts.add(brand.brandId, [ { text: 'best waitlist software for a product launch', intent: 'commercial' }, { text: 'launchlist alternatives', intent: 'comparison' }, ]); ``` Constructing without a key throws immediately rather than failing on the first request. ### Options | Option | Default | | |---|---|---| | `apiKey` | — | Required | | `baseUrl` | `https://shruwd.io/api/v1` | Point at a preview deployment | | `fetch` | global `fetch` | Supply your own | | `maxRetries` | `3` | See below | | `timeoutMs` | — | Per request | | `client` | — | Your app’s name and version, e.g. `acme-reporter/2.1.0`. Sent with the SDK’s own so your usage is attributable. Never a person, an account or a secret | ## Methods | Namespace | Methods | |---|---| | `workspace` | `get()` | | `brands` | `list()` · `create()` · `get()` · `update()` · `archive()` | | `prompts` | `list()` · `add()` · `update()` · `remove()` | | `entities` | `list()` · `add()` · `set()` · `remove()` | | `cycles` | `list()` · `run()` | | `visibility` | `get()` · `series()` | | `crawlers` | `get()` | | `findings` | `list()` · `get()` · `transition()` | | `suggestions` | `list()` · `accept()` · `dismiss()` | | `connections` | `createIngestToken()` | | `setup` | `get()` · `suggest()` | | `answers` | `list()` | List calls unwrap their envelope, so `brands.list()` returns an array rather than `{ brands: [...] }`. ## Reading a metric ```ts const visibility = await shruwd.visibility.get(brand.brandId, { engine: 'google_aio' }); // point, lo and hi are proportions from 0 to 1. const pct = (x: number) => (x * 100).toFixed(1); for (const entity of visibility.entities) { const rate = entity.mentionRate; if (rate.state === 'ok') { console.log(`${entity.canonicalName}: ${pct(rate.point)}% (${pct(rate.lo)}–${pct(rate.hi)}%, n=${rate.n})`); } else { console.log(`${entity.canonicalName}: not enough data yet`); } } ``` Discriminating on `state` is not optional politeness — there is no `point` to read when the state is `insufficient_data`, and treating it as `0` is the single most common way to misreport these numbers. ## Errors Every failure throws a `ShruwdError`. ```ts import { Shruwd, ShruwdError } from '@shruwd/sdk'; try { await shruwd.entities.add(brandId, { name: 'Arc' }); } catch (error) { if (error instanceof ShruwdError && error.code === 'context_terms_required') { await shruwd.entities.add(brandId, { name: 'Arc', contextTerms: ['browser', 'The Browser Company'] }); } else { throw error; } } ``` | Property | | |---|---| | `status` | HTTP status | | `code` | The stable code. Branch on this | | `message` | Human-readable | | `retryable` | Whether retrying could succeed | | `details` | Structured context, when there is any | | `retryAfterSeconds` | Set on a `429` | A failure with no JSON body becomes `http_`, so you always get a code to branch on. ## Retries Handled for you, with backoff: | Response | Retried | |---|---| | `429` | Yes, on every method, honouring `Retry-After` — unless it asks for more than 30 seconds, which is thrown at once for you to schedule | | `5xx` and network errors | Only on `GET`, `PUT` and `DELETE` | | `4xx` other than `429` | Never | `POST` is not retried on `5xx` because it is not idempotent — a brand or a batch of prompts might have been created before the failure. Handle those yourself if you need to. Up to `maxRetries` attempts, then the error is thrown. ## Next steps - [API overview](https://shruwd.io/docs/api/api-overview.md) — the underlying contract - [MCP server](https://shruwd.io/docs/api/mcp-server.md) — the same client as agent tools