npm package: @kernprotocol/sdk
Description: Official Kern TypeScript SDK
Latest version: 0.2.0

# @kernprotocol/sdk

Official TypeScript SDK for Kern API (Node 18+, fetch-based).

## Install

```bash
npm install @kernprotocol/sdk
```

## 5-minute Quickstart

Set env vars:

```bash
export KERN_BASE_URL="https://api.kernprotocol.com"
export KERN_API_KEY="<your-api-key>"
```

### 1) Initialize client + create task

```ts
import { KernClient } from "@kernprotocol/sdk";

const client = new KernClient({
  baseUrl: process.env.KERN_BASE_URL!,
  apiKey: process.env.KERN_API_KEY!,
});

const created = await client.tasks.create({
  title: "Storefront verification",
  instructions: "Upload one clear photo of the storefront",
  location: { lat: -12.0464, lng: -77.0428, radius_m: 75 },
});

const taskId = created.id ?? created.task_id;
```

### 2) Get upload URL

```ts
const workerId = "worker-quickstart-001";

const uploadMeta = await client.tasks.getEvidenceUploadUrl({
  taskId,
  workerId,
  contentType: "image/jpeg",
});

// uploadMeta: { upload_url, object_key, expires_in_seconds }
```

### 3) Upload file (Node)

```ts
import fs from "node:fs/promises";

const bytes = await fs.readFile("./proof.jpg");
const putRes = await fetch(uploadMeta.upload_url, {
  method: "PUT",
  headers: { "Content-Type": "image/jpeg" },
  body: bytes,
});

if (!putRes.ok) throw new Error(`Upload failed: ${putRes.status}`);
```

### 4) Upload file (Browser)

```ts
// file: File from <input type="file" />
const putRes = await fetch(uploadMeta.upload_url, {
  method: "PUT",
  headers: { "Content-Type": file.type || "application/octet-stream" },
  body: file,
});

if (!putRes.ok) throw new Error(`Upload failed: ${putRes.status}`);
```

### 5) Submit evidence

```ts
await client.tasks.submitEvidence({
  taskId,
  workerId,
  objectKey: uploadMeta.object_key,
  lat: -12.0464,
  lng: -77.0428,
  timestampIso: new Date().toISOString(),
});
```

### 6) Wait for final decision

```ts
const finalTask = await client.tasks.waitForFinalDecision({
  taskId,
  timeoutMs: 5 * 60_000,
  pollIntervalMs: 2_000,
});

console.log("Final status:", finalTask.status); // COMPLETED | REJECTED | CANCELED
```

---

## Core API

```ts
new KernClient({ baseUrl, apiKey, timeoutMs?, maxRetries?, retryOnStatuses? })
```

- `tasks.create(...)`
- `tasks.get(taskId)`
- `tasks.events(taskId)`
- `tasks.getEvidenceUploadUrl(...)`
- `tasks.submitEvidence(...)`
- `tasks.waitForFinalDecision(...)`

Existing worker lifecycle methods are still available (`worker.token/claim/start/evidence/complete`).

## Error handling

SDK throws `KernError`:

- `status`
- `code`
- `message`
- `requestId` (from `X-Request-Id` response header)
- `details`

Backward compatibility: `raw` remains as a deprecated alias of `details`.

## Retries, timeout, idempotency

- Default timeout: **20s** per request.
- Retries default to **429/503**, with exponential backoff + jitter.
- `Retry-After` header is respected when present.
- Retries are only attempted for **safe/idempotent** requests:
  - safe methods (`GET`, `HEAD`, `OPTIONS`) OR
  - requests with `Idempotency-Key`.
- Unsafe calls should include `Idempotency-Key` for safe retries.