@certless/reseller-sdk
v0.2.0
Published
Typed client for the Certless reseller + customer REST APIs. Distributed under a proprietary licence — see LICENSE.
Maintainers
Readme
@certless/reseller-sdk
Typed Node client for the Certless reseller + customer-scoped REST APIs.
import { CertlessClient } from '@certless/reseller-sdk';
const client = new CertlessClient({
apiUrl: 'https://api.certless.io',
apiKey: process.env.CERTLESS_RESELLER_API_KEY!,
});
// Reseller endpoints (X-API-KEY)
const customer = await client.reseller.createCustomer({
orgName: 'Acme Trucking',
orgType: 'carrier',
plan: 'standard',
});
// Customer-scoped endpoints (X-API-KEY + X-CUSTOMER-ID)
const engagement = await client.customer.createEngagement(customer.customer.id, {
name: 'Q1 onboarding',
asset_ids: [],
metadata: [{ label: 'reference', value: 'ACME-2026-001' }],
});Why an SDK
Resellers were hand-writing fetch wrappers and maintaining their own copies of the request/response shapes. Schema drift between Certless and the reseller manifested as 400s in the wild.
This package ships the request/response types alongside the client, so any breaking schema change forces a SDK version bump that the reseller picks up at install time — not in production.
Errors
All non-2xx responses throw CertlessApiError (or the specialised
CertlessPlanLimitError subclass for quota exhaustion). The error
exposes:
status— HTTP status codecode— structuredcodefrom the response body when present (e.g.plan_limit_reached)message— top-level messagevalidationIssues— when the upstream returns a 400 with anerrorsarray (typical fornestjs-zodvalidation failures), the parsed field-level issues are surfaced here. Each issue has{ path, message, code? }. Useful for painting per-field errors back onto a form instead of showing a generic toast.
import { CertlessApiError } from '@certless/reseller-sdk';
try {
await client.customer.createEngagement(customerId, input);
} catch (e) {
if (e instanceof CertlessApiError && e.validationIssues) {
for (const issue of e.validationIssues) {
console.log(issue.path.join('.'), issue.message);
}
}
}What's covered
- Reseller namespace: customer CRUD, subscription lifecycle, org links, usage stats.
- Customer namespace: engagements, principals, engagement-principals (assignments), assets (upload/confirm/download), KYC review, override reveal.
Endpoints not currently called by any reseller (admin-only, internal webhooks, KYC provider hooks) are deliberately excluded; add them to the route manifest when needed.
Contract tests
The __tests__/contract.spec.ts suite reads every @Get/@Post/@Patch/
@Delete decorator from apps/api/src/modules/**/*.controller.ts and
asserts every method-and-path tuple in the SDK manifest exists in the
controllers. New controller routes don't fail (additive is fine);
removed or renamed routes do (drift is bad).
The __tests__/sdk.spec.ts suite exercises every SDK method against a
mock fetch, asserting the URL, HTTP method, headers, and body shape
match what the manifest declares.
Run pnpm --filter @certless/reseller-sdk test from the repo root.
