npm package: @fincore/sdk
Description: TypeScript SDK for Fin-Core hosted tenant APIs.
Latest version: 0.1.1

# @fincore/sdk

TypeScript SDK for tenant-facing Fin-Core APIs.

## Runtime support

- Node 18+ required
- Node 20+ recommended

## Module system

This package is intended for ESM + TypeScript.

- `require()` is not supported
- use `"type": "module"` in the consumer app
- use TypeScript with `module: "NodeNext"` and `moduleResolution: "NodeNext"`

## Current high-level client surface

The current happy-path wrappers are:

- `client.hello()`
- `client.bootstrap.get()`
- `client.health.get()`
- `client.plan.get()`
- `client.apiKeys.list()`
- `client.apiKeys.create(body)`
- `client.apiKeys.rotate(keyId, body)`
- `client.apiKeys.revoke(keyId)`
- `client.usage.summary(query?)`
- `client.usage.metering(query?)`
- `client.usage.getSummary(query?)`
- `client.usage.getMetering(query?)`
- `client.billing.list(query?)`
- `client.billing.get(billingPeriodId)`
- `client.deepThought.getInsight(query?)`
- `client.discovery.listChains(query?)`
- `client.discovery.listSuites(query?)`
- `client.discovery.listDeployments(query?)`
- `client.discovery.getDeployment(deploymentId)`
- `client.discovery.getOfficialRailDeployments(query?)`

These map to the current tenant routes:

- `/console/bootstrap`
- `/console/health`
- `/console/plan`
- `/console/api-keys`
- `/console/usage`
- `/console/billing/periods`
- `/console/deepthought/insight`
- `/chains`
- `/contracts/suites`
- `/contracts/deployments`
- `/contracts/deployments/:deploymentId`
- `/contracts/official-rail/deployments`

Discovery route visibility rules:

- official contract-platform rows are visible to all authenticated tenants
- tenant custom suites/deployments are only visible to the tenant that registered them
- discovery is read-only; it does not enable custom authoring or contract writes

## Consumer quickstart

Install from npm (publish path):

```bash
npm install @fincore/sdk
npm i -D typescript tsx @types/node
```

Artifact fallback used for pre-publish validation:

```bash
npm i ./fincore-sdk-0.1.0.tgz
npm i -D typescript tsx @types/node
```

Add `.env`:

```bash
FINCORE_BASE_URL=https://api.example.com
FINCORE_API_KEY=YOUR_API_KEY
```

Add `package.json`:

```json
{
  "name": "fincore-consumer",
  "private": true,
  "type": "module",
  "scripts": {
    "smoke": "tsx --env-file=.env src/index.ts"
  },
  "dependencies": {
    "@fincore/sdk": "^0.1.0"
  },
  "devDependencies": {
    "@types/node": "^24.0.0",
    "tsx": "^4.21.0",
    "typescript": "^5.9.0"
  }
}
```

Add `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": ["node"],
    "outDir": "dist"
  },
  "include": ["src/**/*.ts"]
}
```

Add `src/index.ts`:

```ts
import { FinCoreClient, HttpError, RateLimitError } from "@fincore/sdk";

const client = new FinCoreClient({
  apiKey: process.env.FINCORE_API_KEY,
});

try {
  const hello = await client.hello();

  console.log("Fin-Core status:", (hello.health.data as any)?.overall?.status ?? "unknown");
  console.log("Tenant:", hello.tenantId ?? "unknown");
  console.log("Plan:", hello.planTier ?? "unknown");
  console.log("Usage view:", (hello.usage.data as any)?.view ?? "summary");
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log("Retry after:", err.resetAt?.toISOString() ?? "soon");
  } else if (err instanceof HttpError) {
    console.log("Fin-Core request failed:", err.message);
    console.log("Next step:", err.actionHint ?? "Inspect the request id and retry.");
  } else {
    throw err;
  }
}
```

Run the smoke check:

```bash
npm run smoke
```

Distribution note:

- The intended public install path is `npm install @fincore/sdk`.
- Phase 20 CI validates the same publish-shaped artifact path before the first registry release.
- For basic reads, `baseUrl` now defaults from `FINCORE_BASE_URL`, browser origin, or `http://localhost:8080`.

## Windows / offline zip bundle

For restricted environments, a secondary `.zip` bundle can be assembled from the built SDK.

Validated offline path:

- unzip `fincore-sdk-<version>-offline.zip`
- set `FINCORE_BASE_URL` and `FINCORE_API_KEY`
- run `node .\samples\hello-world.mjs` from the unzipped bundle

PowerShell helper:

```powershell
$env:FINCORE_BASE_URL = "https://YOUR_FINCORE_BASE_URL"
$env:FINCORE_API_KEY = "YOUR_API_KEY"
.\samples\hello-world.ps1
```

Notes:

- this zip path reuses the same built `dist/` output as npm
- it is a secondary manual-install channel, not a separate SDK surface
- see `docs/public/windows-offline-quickstart.md` for the tested Windows flow

## Idempotent write example

For writes, include `Idempotency-Key` explicitly:

```ts
import { FinCoreClient } from "@fincore/sdk";

const client = new FinCoreClient({
  apiKey: process.env.FINCORE_API_KEY,
});

const result = await client.transport.request({
  method: "POST",
  path: "/intents",
  headers: {
    "Idempotency-Key": `idem_${Date.now()}`,
  },
  body: {
    intentType: "deposit",
    params: {
      subject: "IndividualVault",
      assetsIn: "1",
    },
  },
});

console.log("intent", result.meta.status, result.data);
```

Note:

- the current SDK surface does not expose `client.requestWithRetry(...)`
- retry behavior is handled by `client.transport.request(...)` plus the configured `autoRetry` policy
- for any write retry, `Idempotency-Key` is required

## Expected output

For a healthy read smoke:

```text
Fin-Core status: ok
Tenant: hackathon-demo
Plan: pro
Usage view: summary
```

For a successful write:

```text
intent 200 { ... }
```

If credentials are invalid, expect an HTTP auth error rather than a success payload.

## Included consumer template

This package includes a minimal example scaffold in:

`examples/consumer-template/`

It contains:

- `package.json`
- `tsconfig.json`
- `.env.example`
- `src/index.ts`

## Notes

- This SDK relies on the Node runtime `fetch` implementation.
- If you are using Node 18, ensure your runtime environment provides global `fetch`.
- For the smoothest setup, use Node 20+ with ESM enabled from the start.
- Tenant control-plane visibility is tier-aware. Community tenants should expect summary usage only, while higher tiers may expose billing period visibility and read-only DeepThought insight.

## Hello World

The canonical hosted Hello World loop is:

1. Construct the client with `baseUrl` and `apiKey`
2. Call `client.health.get()`
3. Call `client.bootstrap.get()`
4. Call `client.usage.getSummary()`
5. Open the tenant console `/usage` page to confirm the round-trip

Higher-tier reads such as billing period visibility and DeepThought insight should come after this first success path, not before it.