@vennyx/solihr
v0.1.0
Published
Official TypeScript SDK for the SoliHR Public API — a typed, PAT-authenticated client for the curated, versioned SoliHR HR platform surface (people, leave, time, finance, reporting).
Readme
@vennyx/solihr
The official TypeScript SDK for the SoliHR Public API — a typed, promise-based client generated from SoliHR's curated, versioned public OpenAPI surface (people, leave, time tracking, finance, and reporting).
- Fully typed request params and responses for every public operation.
- One personal access token (PAT) authorizes both REST and MCP.
- Thin ergonomic helpers plus a low-level client for the complete surface.
- ESM, zero heavy runtime dependencies (a single tiny typed
fetchwrapper).
The SoliHR Public API is a deliberate, stable subset of the platform. Operations in it evolve additively within the
v1line; breaking changes ship only under a new version. Everything else is internal and unsupported for external use.
Install
npm install @vennyx/solihr
# or: pnpm add @vennyx/solihr / yarn add @vennyx/solihrRequires Node.js >= 22 (or any runtime with a global fetch, including modern
browsers and edge runtimes).
Authentication
The SDK authenticates with a personal access token (PAT). Mint one in the
SoliHR web app under Ayarlar > API erişimi (Settings > API access). The full
token (solihr_pat_...) is shown only once at creation — store it securely.
A PAT acts as the SoliHR user that minted it: its effective permissions are
that user's role intersected with the scopes granted to the token (for example
people:read, people:write, time:write, finance:read, mcp:use). A token
can never exceed the permissions of the user behind it. Treat a PAT like a
password: never commit it, and load it from an environment variable or secret store.
Quick start
import { createSolihrClient } from "@vennyx/solihr";
const solihr = createSolihrClient({
token: process.env.SOLIHR_TOKEN!, // solihr_pat_...
});
// List employees (a typed GET).
const { data, error } = await solihr.people.list({ pageSize: 25 });
if (error) {
throw new Error(`SoliHR request failed: ${JSON.stringify(error)}`);
}
console.log(data?.items);createSolihrClient accepts:
| Option | Type | Default | Description |
| --------- | ------------------------ | -------------------------- | ---------------------------------------------- |
| token | string | — | Your PAT (solihr_pat_...). Required. |
| baseUrl | string | https://solihr.com/v1 | Override the API base URL. |
| headers | Record<string, string> | — | Extra default headers merged into requests. |
| fetch | typeof fetch | global fetch | Custom fetch implementation. |
Responses
Every call resolves to { data, error, response } (from
openapi-fetch):
data— the typed success body (present on 2xx).error— the typed error body (present on non-2xx).response— the rawResponse.
const { data, error, response } = await solihr.people.get("<employee-id>");
if (response.status === 404) {
// handle "not found"
}Asynchronous writes
Writes are asynchronous commands: they return an accepted result with an
operationId. Poll the operation until it reaches a terminal state, then read the
projection. The SDK ships a waitForOperation helper for exactly this:
const created = await solihr.people.create({
firstName: "Ada",
lastName: "Lovelace",
legalEntityId: "<legal-entity-id>",
employmentStartDate: "2026-01-01",
});
if (created.data) {
const status = await solihr.waitForOperation(created.data.operationId, {
intervalMs: 1000,
timeoutMs: 30_000,
});
if (status.status === "failed") {
throw new Error(`Command failed: ${status.errorCode}`);
}
// status.aggregateId / status.aggregateVersion now reflect the applied change.
}Commands accept an idempotency key so retries are safe. One is generated per call when omitted; pass a stable key yourself if you intend to retry the same command:
await solihr.people.create(employee, { idempotencyKey: "employee-import-42" });The full public surface
The ergonomic namespaces (people, me, operations) cover the most common
flows. Every public operation is reachable — fully typed by path and method — on
the low-level client at solihr.request:
// e.g. list leave requests, record a time entry, run a report — all typed:
const leave = await solihr.request.GET("/leave/requests");
const overtime = await solihr.request.POST("/time/overtime", {
body: {
/* CreateOvertimeDto */
},
params: { header: { "Idempotency-Key": crypto.randomUUID() } },
});Request and response shapes are exported for reuse:
import type { components, paths } from "@vennyx/solihr";
type Employee = components["schemas"]["EmployeeDetailDto"];
type PeopleListResponse =
paths["/people"]["get"]["responses"][200]["content"]["application/json"];Errors
createSolihrClient throws a SolihrError when constructed without a token.
waitForOperation rejects with a SolihrError on transport failure, timeout, or
abort. HTTP-level failures are returned as the typed error on each call (they are
not thrown), so you can branch on response.status.
Reference
- Developer portal: https://solihr.com/developers
- REST API docs: https://solihr.com/api
- MCP docs: https://solihr.com/mcp
- OpenAPI document: https://solihr.com/openapi/public.json
License
MIT © VENNYX YAZILIM DANIŞMANLIK A.Ş.
