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

# TypeScript SDK

> Use the safefetch npm package to send, inspect, and manage actions from TypeScript or JavaScript.

## Installation

```bash theme={null}
npm install safefetch
```

## Quick setup

The easiest way to get started is with the `safeFetch()` convenience function. It reads your API key from the `SAFEFETCH_API_KEY` environment variable automatically.

```bash theme={null}
export SAFEFETCH_API_KEY=sf_live_...
```

```ts theme={null}
import { safeFetch } from "safefetch";

const action = await safeFetch({
  url: "https://api.example.com/send-email",
  method: "POST",
  body: { to: "user@example.com", subject: "Hello" },
});

console.log(action.id);     // act_...
console.log(action.status); // "pending" | "completed" | ...
```

## `safeFetch()` — convenience function

`safeFetch(opts)` sends an action using a shared instance configured from environment variables. It accepts all [SendOptions](#sendoptions) and returns a `Promise<Action>`.

The function also has shorthand methods for the other operations:

| Method                  | Description                                  |
| ----------------------- | -------------------------------------------- |
| `safeFetch(opts)`       | Send a new action                            |
| `safeFetch.get(id)`     | Fetch an action by ID                        |
| `safeFetch.list(opts?)` | List actions                                 |
| `safeFetch.approve(id)` | Approve an awaiting-approval action          |
| `safeFetch.cancel(id)`  | Cancel a pending or awaiting-approval action |
| `safeFetch.retry(id)`   | Retry a failed or cancelled action           |

```ts theme={null}
// Get a single action
const action = await safeFetch.get("act_abc123");

// List recent actions
const { data, total } = await safeFetch.list({ status: "pending", limit: 10 });

// Approve an action waiting for human review
await safeFetch.approve("act_abc123");

// Cancel an action
await safeFetch.cancel("act_abc123");

// Retry a failed action (creates a new action)
const retried = await safeFetch.retry("act_abc123");
```

## `new SafeFetch()` — class API

Use the class directly when you need multiple instances, custom base URLs, or explicit key management.

```ts theme={null}
import { SafeFetch } from "safefetch";

const sf = new SafeFetch({
  apiKey: process.env.SAFEFETCH_API_KEY!,
  // baseUrl: "https://api.safefetch.dev", // optional override
});

const action = await sf.send({
  url: "https://api.example.com/send-email",
  method: "POST",
  body: { to: "user@example.com" },
});
```

The class exposes the same set of methods: `send`, `get`, `list`, `approve`, `cancel`, `retry`.

## SendOptions

All options for `safeFetch()` / `sf.send()`:

| Field            | Type                                    | Default  | Description                                                            |
| ---------------- | --------------------------------------- | -------- | ---------------------------------------------------------------------- |
| `url`            | `string`                                | required | Target URL to dispatch to                                              |
| `method`         | `GET \| POST \| PUT \| PATCH \| DELETE` | `"POST"` | HTTP method                                                            |
| `body`           | `Record<string, unknown>`               | —        | Request body, serialised as JSON                                       |
| `headers`        | `Record<string, string>`                | —        | Additional headers forwarded to the target                             |
| `approve`        | `boolean`                               | `false`  | When `true`, the action pauses for human approval before dispatch      |
| `retries`        | `number`                                | `3`      | Max delivery retries on failure                                        |
| `dedupe`         | `string`                                | —        | Deduplication key — actions with the same key within 24 h are de-duped |
| `callback`       | `string`                                | —        | URL notified when the action finishes                                  |
| `idempotencyKey` | `string`                                | —        | Sent as the `Idempotency-Key` header                                   |
| `sync`           | `boolean`                               | `false`  | Poll until the action completes and return the final action object     |
| `syncTimeout`    | `number`                                | `30000`  | Polling timeout in ms when `sync` is true                              |

<Note>
  `sync: true` cannot be combined with `approve: true` — actions waiting for human approval cannot be awaited synchronously.
</Note>

### Sync mode

Pass `sync: true` to wait for the action to finish before returning:

```ts theme={null}
const action = await safeFetch({
  url: "https://api.example.com/process",
  method: "POST",
  body: { input: "data" },
  sync: true,
  syncTimeout: 60_000, // 60 s
});

// action.status is guaranteed to be "completed" here
console.log(action.response_code); // e.g. 200
```

### Human approval

Pass `approve: true` to require a human to approve the action before it is dispatched:

```ts theme={null}
const action = await safeFetch({
  url: "https://api.example.com/delete-account",
  method: "DELETE",
  body: { userId: "u_123" },
  approve: true,
});

// action.status === "awaiting_approval"
// Approve later:
await safeFetch.approve(action.id);
```

See [Human Approval](/guides/human-approval) for the full workflow.

## ListOptions

Options for `safeFetch.list()` / `sf.list()`:

| Field    | Type           | Default | Description               |
| -------- | -------------- | ------- | ------------------------- |
| `status` | `ActionStatus` | —       | Filter by status          |
| `limit`  | `number`       | `20`    | Maximum results to return |
| `offset` | `number`       | `0`     | Pagination offset         |

## The `Action` object

Every method returns an `Action` object:

| Field               | Type             | Description                                              |
| ------------------- | ---------------- | -------------------------------------------------------- |
| `id`                | `string`         | Action ID (`act_...`)                                    |
| `status`            | `ActionStatus`   | Current status                                           |
| `url`               | `string`         | Target URL                                               |
| `method`            | `string`         | HTTP method                                              |
| `body`              | `object \| null` | Request body                                             |
| `headers`           | `object \| null` | Forwarded headers                                        |
| `approve`           | `boolean`        | Whether approval was required                            |
| `approved_at`       | `string \| null` | ISO timestamp when approved                              |
| `attempts`          | `number`         | Number of dispatch attempts made                         |
| `retries`           | `number`         | Max retries configured                                   |
| `retries_remaining` | `number`         | Retries left                                             |
| `dedupe`            | `string \| null` | Deduplication key                                        |
| `deduplicated`      | `boolean`        | `true` if this action was a duplicate of an existing one |
| `response_code`     | `number \| null` | HTTP status from the target                              |
| `response_body`     | `unknown`        | Response body from the target                            |
| `duration_ms`       | `number \| null` | Time taken for the last dispatch attempt                 |
| `last_error`        | `string \| null` | Error message from the last failed attempt               |
| `created_at`        | `string`         | ISO creation timestamp                                   |
| `finished_at`       | `string \| null` | ISO completion timestamp                                 |
| `callback`          | `string \| null` | Callback URL                                             |

`ActionStatus` values: `pending`, `active`, `awaiting_approval`, `completed`, `failed`, `cancelled`.

## Error handling

The SDK throws `SafeFetchError` for all failures:

```ts theme={null}
import { safeFetch, SafeFetchError } from "safefetch";

try {
  await safeFetch({ url: "https://api.example.com/action", method: "POST" });
} catch (err) {
  if (err instanceof SafeFetchError) {
    console.error(err.message);
    console.error(err.source);       // "target" | "dispatch"
    console.error(err.statusCode);   // HTTP status from SafeFetch API
    console.error(err.errorCode);    // machine-readable code, e.g. "RATE_LIMITED"

    if (err.source === "target") {
      // The target endpoint returned a non-2xx response
      console.error(err.targetResponse?.code);  // target HTTP status
      console.error(err.targetResponse?.body);  // target response body
    }

    // The action object at the time of failure, if available
    console.error(err.action?.id);
  }
}
```

`SafeFetchError` properties:

| Property         | Type                          | Description                                                                       |
| ---------------- | ----------------------------- | --------------------------------------------------------------------------------- |
| `message`        | `string`                      | Human-readable error description                                                  |
| `source`         | `"target" \| "dispatch"`      | `"target"` = non-2xx from your endpoint; `"dispatch"` = network/API/timeout error |
| `statusCode`     | `number \| undefined`         | HTTP status returned by the SafeFetch API                                         |
| `errorCode`      | `string \| undefined`         | Machine-readable code from the SafeFetch API                                      |
| `targetResponse` | `{ code, body } \| undefined` | Present when `source === "target"`                                                |
| `action`         | `Action \| undefined`         | Action object at the time of the error                                            |

## TypeScript types

All types are exported from the `safefetch` package:

```ts theme={null}
import type {
  Action,
  ActionStatus,
  SendOptions,
  ListOptions,
  ListResponse,
  SafeFetchOptions,
} from "safefetch";
```

## Environment variables

| Variable             | Description                                      |
| -------------------- | ------------------------------------------------ |
| `SAFEFETCH_API_KEY`  | Your API key (required when using `safeFetch()`) |
| `SAFEFETCH_BASE_URL` | Override the API base URL (optional)             |
