> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praxa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrate Praxa with TypeScript

> Use @praxa/sdk for Integration Gateway missions and native fetch for Execution Fabric tasks, with typed tests and production safeguards.

TypeScript has two first-class integration paths:

* use `@praxa/sdk` with a deployment-specific Integration Gateway;
* use Node.js `fetch` with the public Execution Fabric API.

Choose one plane per operation.

## Prerequisites

* Node.js 20 or newer
* TypeScript with ESM (`NodeNext` or `ESNext`)
* either a Gateway origin and delegated OAuth token, or a personal Execution
  Fabric API key

## Path A: Integration Gateway SDK

```bash theme={null}
npm install @praxa/sdk@0.3.0
```

```ts src/mission.ts theme={null}
import { randomUUID } from "node:crypto";
import { PraxaClient } from "@praxa/sdk";

const client = new PraxaClient({
  baseUrl: process.env.PRAXA_BASE_URL!,
  accessToken: () => process.env.PRAXA_ACCESS_TOKEN!,
});

const mission = await client.createMission(
  {
    goalSpec: { task: "Prepare the weekly review" },
    resourceBudget: {
      maximumSteps: 12,
      maximumToolCalls: 8,
      maximumElapsedMs: 120_000,
      maximumParallelism: 2,
    },
  },
  randomUUID(),
);

for await (const event of client.missionEvents(mission.runId)) {
  console.log(event.id, event.event);
}

console.log(await client.getMission(mission.runId));
```

Continue with the [SDK quickstart](/sdk/quickstart) for token refresh,
reconnectable SSE, injected-fetch tests, and the complete acceptance matrix.

## Path B: Execution Fabric

```ts src/task.ts theme={null}
import { createHash, randomUUID } from "node:crypto";

const apiKey = process.env.PRAXA_API_KEY;
if (!apiKey) throw new Error("PRAXA_API_KEY is required");

const logicalRequestId = randomUUID();
const idempotencyKey =
  "typescript:" + createHash("sha256").update(logicalRequestId).digest("hex");

const response = await fetch("https://api.praxa.io/v1/execute", {
  method: "POST",
  headers: {
    authorization: `Bearer ${apiKey}`,
    "content-type": "application/json",
    "idempotency-key": idempotencyKey,
  },
  body: JSON.stringify({
    apiVersion: "v1",
    requestId: idempotencyKey,
    mode: "task",
    task: { input: "Summarize the incident and propose the next safe action." },
    idempotencyKey,
  }),
});

if (!response.ok) {
  throw new Error(`Praxa admission failed with HTTP ${response.status}`);
}

const run = await response.json();
console.log(run.run_id, run.status);
```

Store the key with the exact request body before the first attempt. A `202`
response proves durable admission, not task completion.

## Add a terminal readback

```ts theme={null}
const terminal = new Set(["completed", "failed", "cancelled"]);
let current = run;

while (!terminal.has(current.status)) {
  await new Promise((resolve) => setTimeout(resolve, 1_000));
  const read = await fetch(
    `https://api.praxa.io/v1/runs/${encodeURIComponent(current.run_id)}`,
    {
      headers: { authorization: `Bearer ${apiKey}` },
      cache: "no-store",
    },
  );
  if (!read.ok) throw new Error(`Run read failed with HTTP ${read.status}`);
  current = await read.json();
}
```

The key needs `execute:write` for admission and `runs:read` for readback.

## Test with native fakes

Use Node's test runner and inject `fetch` into your own client wrapper. Assert:

* exact URL, method, header, and body;
* one stable key for repeated logical input;
* no token in returned values or logs;
* `401`, `403`, `409`, `429`, and network errors remain distinguishable;
* polling stops only at a public terminal state.

```bash theme={null}
node --test
```

## Troubleshooting

| Symptom                         | Fix                                                                       |
| ------------------------------- | ------------------------------------------------------------------------- |
| ESM import failure              | Set `"type": "module"` and use NodeNext/ESNext                            |
| `fetch` missing                 | Upgrade to Node 20 or provide an explicit implementation to `PraxaClient` |
| SDK request returns `401`       | Refresh the delegated Gateway token; do not substitute a Fabric key       |
| Fabric request returns `403`    | Verify key tenant and exact scopes                                        |
| Replay creates conflict         | Restore the original body associated with the key                         |
| Process exits while stream runs | Keep the async iterator alive and forward an explicit abort signal        |

## Best practices

* Reuse one configured SDK or HTTP client per process.
* Keep all credentials in the server runtime.
* Use `AbortSignal` and an overall deadline.
* Redact authorization and customer payloads from telemetry.
* Bound concurrency and respect `429` responses.
* Reconcile every ambiguous mutation through exact replay or readback.

<CardGroup cols={2}>
  <Card title="Next.js" icon="n" href="/tutorials/nextjs">
    Put the TypeScript client behind an authenticated App Router boundary.
  </Card>

  <Card title="Nuxt" icon="n" href="/tutorials/nuxt">
    Use a Nitro server route and private runtime config.
  </Card>
</CardGroup>

## Optimize for production

* Reuse one configured HTTP or SDK client per process and bound concurrent upstream work.
* Prefer durable admission plus asynchronous readback over holding application requests open.
* Cache only non-sensitive, tenant-scoped reads within their documented freshness window.
* Measure p50/p95 latency, admission-to-terminal time, retries, conflicts, and connection reuse before tuning.

Optimize only after the correctness and isolation matrix passes. Lower latency or cost is not an improvement if verified outcomes, authority checks, or recovery rates regress.

## Cleanup and next steps

1. Revoke the disposable Praxa key and require a later request to fail.
2. Remove synthetic application records and any temporary environment files.
3. Cancel or archive unresolved test runs according to the application policy.
4. Retain only redacted request, run, and verification identifiers needed for the test record.

After cleanup, run the [shared integration test matrix](/tutorials/test-your-integration) and record any environment-specific check that remains pending.

## Frequently asked questions

### What proves this tutorial works?

The minimum observable result is that the correct SDK or Fabric plane executes with server-owned credentials and exact replay behavior. A compile, package import, mocked response, or initial admission alone does not prove the complete workflow.

### Can a browser, mobile app, or model prompt hold the credential?

No. Browser and mobile bundles are inspectable. Keep the Praxa credential in the trusted application backend.

### How should an ambiguous mutation be retried?

Persist the exact logical input and idempotency key before the first attempt. Reconcile through authoritative readback or replay the exact request with that same key before creating new work.

### What should we monitor after release?

Monitor application auth failures, upstream status/code, latency, retries, replay conflicts, and terminal run outcomes. Alert on authorization bypass, cross-tenant disclosure, repeated conflicts, or cleanup failure.
