@vennyx/solicrm
v0.3.0
Published
Official TypeScript SDK for the SoliCRM API — typed access to contacts, companies, deals, pipelines, activities, tasks, notes, saved views and cross-resource search.
Readme
@vennyx/solicrm
Official TypeScript SDK for the SoliCRM API — typed access to contacts, companies, deals, pipelines, activities, tasks, notes, saved views and cross-resource search.
Every request/response type is derived from the same zod schemas the server uses, so
the SDK cannot drift from the API. Types are bundled into this package; you do not need
any additional @types/*.
Install
npm install @vennyx/solicrm
# or
bun add @vennyx/solicrmRequires Node.js ≥ 18 (or Bun). ESM only — this package has no CommonJS build.
TypeScript setup
The bundled dist/index.d.ts is self-contained, but it references the standard fetch
globals (Response, RequestInit, AbortSignal), so your tsconfig.json needs either
"types": ["node"] or "lib": [..., "DOM"]. Both moduleResolution: "nodenext" and
"bundler" are verified to resolve this package with zero errors.
Quick start
import { SolicrmClient } from '@vennyx/solicrm'
const solicrm = new SolicrmClient({
apiKey: process.env.SOLICRM_API_KEY!, // "scrm_..." — created in the SoliCRM dashboard
tenantId: process.env.SOLICRM_TENANT_ID!, // the tenant the key belongs to
// baseUrl: 'https://api.solicrm.com' // default
})
const page = await solicrm.contacts.list({
limit: 25,
sort: { field: 'created_at', direction: 'desc' }, // keys come from FIELD_CATALOG
filters: { status: 'active' },
})
for (const contact of page.items) {
console.log(contact.id, contact.firstName, contact.lastName, contact.status)
}
const created = await solicrm.contacts.create({
firstName: 'Ada',
lastName: 'Lovelace',
status: 'lead',
})
await solicrm.contacts.update(created.id, { jobTitle: 'Analyst' })Resources
contacts, companies, deals, pipelines, activities, tasks, notes, views
(saved views) and search (cross-resource). Escape hatch for anything not modelled yet:
solicrm.http.requestJson(method, path, schema, options) / solicrm.http.requestVoid(...).
Keyset pagination
List endpoints are cursor based (items / hasMore / nextCursor). Each resource has a
listAll() generator that walks every page, and the underlying paginateAll helper is
exported for custom endpoints. Both fail loudly instead of silently truncating when the
server reports hasMore: true with a null cursor, or repeats a cursor:
for await (const deal of solicrm.deals.listAll({ limit: 100 })) {
console.log(deal.id, deal.amount) // amount is a string — see "Money and dates"
}import { paginateAll } from '@vennyx/solicrm'
for await (const item of paginateAll((q) => solicrm.contacts.list(q), { limit: 100 })) {
console.log(item.id)
}Errors
| Class | Meaning |
| --- | --- |
| SolicrmError | Client-side misuse (missing apiKey, unknown sort field, aborted request) |
| SolicrmApiError | Server returned a non-2xx status — .status, .message, .body |
| SolicrmResponseError | Server returned 2xx but the body did not match the contract schema |
SolicrmApiError.message carries the server's message verbatim, in Turkish (SoliCRM's
API speaks Turkish to end users). .body is the raw parsed body, so richer envelopes
survive intact — the plan-limit response of contacts.create, for example, is
{ code, limit, planCode } with no error field at all:
import { readContactLimitReached, SolicrmApiError } from '@vennyx/solicrm'
try {
await solicrm.contacts.create({ firstName: 'Grace', lastName: 'Hopper', status: 'lead' })
} catch (error) {
if (error instanceof SolicrmApiError && error.status === 402) {
const limit = readContactLimitReached(error.body)
console.log(limit?.code, limit?.limit, limit?.planCode)
}
}Money and dates
Money fields (amount, annualRevenue) are numeric(18,2) columns and the API returns
them as strings; timestamps are ISO 8601 strings. The SDK passes both through
untouched and performs no arithmetic — it never calls parseFloat/Number. Pick your own
decimal and date libraries (SoliCRM itself uses bignumber.js and luxon).
Custom fetch and retries
const solicrm = new SolicrmClient({
apiKey,
tenantId,
fetch: myInstrumentedFetch,
retry: { maxAttempts: 3 },
})Retries use exponential backoff with half jitter and only apply to retryable statuses.
Authorization cannot be overridden through headers.
Authentication: API key only
apiKey is the only credential this SDK takes, and it must be a tenant API key
(scrm_…). SoliCRM's hosted MCP endpoint also accepts short-lived OAuth 2.1 access
tokens, but those are obtained through a browser sign-in flow that this SDK does not
implement — use an API key for programmatic access. (See
@vennyx/solicrm-mcp if you want the
OAuth path for an AI agent.)
Related
@vennyx/solicrm-mcp— MCP server that exposes the same operations as tools for AI agents, over stdio with an API key or hosted athttps://api.solicrm.com/mcpwith OAuth.
License
MIT © Vennyx A.Ş.
