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

# TypeScript SDK configuration and behavior

> Every option, default, retry rule, error mapping and method of the TypeScript SDK, each checked against the code.

This page states exactly how `@parmana/sdk` behaves. If a guide and this page disagree, this page is right, and please report the guide. Every value here was read from the SDK source, and the retry behavior was tested against a server that fails on purpose.

## Create a client

```typescript theme={null}
import { ParmanaClient, type Configuration } from "@parmana/sdk";

const configuration: Configuration = {
  endpoint: "https://your-parmana.example.com",
  apiKey: "your API key",
};

const client = new ParmanaClient(configuration);
```

From 1.1.6 the client builds a default `HttpTransport` from the configuration you pass, so `timeout`, `retryPolicy`, `apiKey` and `userAgent` all apply. Two notes:

1. **On 1.1.5 and earlier, always pass `transport`.** Those versions throw `ConfigurationError: Transport is required.` when it is missing, even though the `Configuration` type marks it optional. Use 1.1.6 or later to omit it.
2. **If you supply your own transport, build it from the same configuration object.** `HttpTransport` reads `timeout` and `retryPolicy` from the configuration it is constructed with, not from the client. If you write `new HttpTransport({ endpoint, apiKey })` next to a `retryPolicy` on the client, the transport never sees the retry policy and **nothing is retried**, with no error. Omit `transport` and this cannot happen.

## Configuration options

| Option        | Type                 | Default                   | What it does                                                                                                                                                                     |
| ------------- | -------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `endpoint`    | string               | None, required            | The base URL of the Parmana server, without a trailing slash. Every path is appended to it as written. An empty value throws `ConfigurationError: Runtime endpoint is required.` |
| `apiKey`      | string               | None                      | When set, every request carries `Authorization: Bearer <apiKey>`. When it is not set, no `Authorization` header is sent and a server with authentication on answers `401`.       |
| `timeout`     | number, milliseconds | `30000`                   | A request that has not finished in this time is aborted and throws `TimeoutError`. Read by the transport, see rule 2 above.                                                      |
| `retryPolicy` | `RetryPolicy`        | Retries off               | See the next section. Read by the transport, see rule 2 above.                                                                                                                   |
| `transport`   | `Transport`          | A default `HttpTransport` | The object that sends requests. Omit it in 1.1.6 and later. Required in 1.1.5 and earlier.                                                                                       |
| `userAgent`   | string               | None                      | Sent as the `User-Agent` header from 1.1.6. Before 1.1.6 it was declared but never sent.                                                                                         |

Every request also carries `Content-Type: application/json`.

## Retries

Retries are **off by default**. They run only when both `retryPolicy.enabled` is `true` and `maxAttempts` is above zero.

| `RetryPolicy` field | Default                     | Meaning                                                                                                   |
| ------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- |
| `enabled`           | `false`                     | Must be `true` for any retry to happen.                                                                   |
| `maxAttempts`       | `0`                         | The number of retries after the first attempt. `3` makes up to 4 requests in total.                       |
| `initialDelayMs`    | `1000`                      | The delay before the first retry.                                                                         |
| `maxDelayMs`        | `30000`                     | The ceiling for any delay.                                                                                |
| `strategy`          | `RetryStrategy.EXPONENTIAL` | `EXPONENTIAL` doubles the delay on each retry, up to `maxDelayMs`. `FIXED` always waits `initialDelayMs`. |

What is retried:

| Request                                                                                                                                                      | Retried?                                                                                |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `GET` requests (`health()`, `version()`, `transaction()`, `transactions()`, `trustRecord()`, `getLatestVerification()`, `refusalRecord()`)                   | Yes, on a network failure, a timeout, a `429` or a `5xx`. Nothing else.                 |
| `POST` requests (`execute()`, `createTransaction()`, `verify()`, `replay()`, `receipt()`, `validatePolicy()`, `verifyRefusalRecord()`, `verifyAuditEvent()`) | **Never.** This is deliberate, because retrying `execute()` could repeat a real action. |

Tested: a server that returns `503` twice and then `200`, and a client with `enabled: true` and `maxAttempts: 3`, made 3 requests and returned success. With a supplied transport built from a partial configuration the same client made 1 request and threw `InternalServerError`.

## Errors

Every non 2xx response, and every failure to get one, is thrown as a typed error. There is no path where a failure looks like success.

| What happened                                 | Error class              | Notes                                                             |
| --------------------------------------------- | ------------------------ | ----------------------------------------------------------------- |
| HTTP `400`                                    | `ValidationError`        | The request is malformed.                                         |
| HTTP `401`                                    | `AuthenticationError`    | The key is missing or wrong.                                      |
| HTTP `403` with code `POLICY_DENIED`          | `ExecutionRejectedError` | A policy refused. This is final.                                  |
| HTTP `403` with code `CAPABILITY_NOT_ALLOWED` | `AuthorizationError`     | The server's code is on `serverCode`.                             |
| HTTP `403` with no code                       | `AuthorizationError`     | The key may not assert that principal.                            |
| HTTP `404`                                    | `NotFoundError`          |                                                                   |
| HTTP `409`                                    | `ConflictError`          | The `businessTransactionId` was already used.                     |
| HTTP `429`                                    | `RateLimitError`         | `retryAfterSeconds` carries the `Retry-After` value when present. |
| Any other non 2xx, including `500` and `503`  | `InternalServerError`    | From 1.1.3, `serverCode` carries the server's code, see below.    |
| The connection failed                         | `NetworkError`           |                                                                   |
| The request timed out                         | `TimeoutError`           |                                                                   |
| A missing `endpoint`                          | `ConfigurationError`     | Thrown by the constructor.                                        |

Two `InternalServerError` cases need different handling, and from 1.1.3 you can tell them apart with `error.serverCode`:

| `serverCode`                        | Meaning                                                                  | Do                                              |
| ----------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------- |
| `SIGNING_UNAVAILABLE` (503)         | Refused before release. **Nothing was executed.**                        | Retry later with a new `businessTransactionId`. |
| `EXECUTION_RECORD_INCOMPLETE` (500) | The action **was** released but its signed record could not be produced. | **Do not resubmit.** Reconcile first.           |

On 1.1.2 and earlier `serverCode` is not on `InternalServerError`, so read the code from the error message.

## Methods

| Method                                    | Request                   | Returns                                                               |
| ----------------------------------------- | ------------------------- | --------------------------------------------------------------------- |
| `execute(transaction)`                    | `POST /execute`           | The signed Execution Trust Record.                                    |
| `createTransaction(transaction)`          | `POST /transactions`      | The same record, from the second entry point.                         |
| `verify(id)`                              | `POST /verify`            | A `Verification`.                                                     |
| `getLatestVerification(id)`               | `GET /verification/{id}`  | The latest `Verification`.                                            |
| `replay(id)`                              | `POST /replay`            | A `ReplayResult`.                                                     |
| `receipt(id)`                             | `POST /receipt`           | A `Receipt`.                                                          |
| `transaction(id)`                         | `GET /transactions/{id}`  | One `BusinessTransaction`.                                            |
| `transactions(page, pageSize)`            | `GET /transactions`       | A page of transactions. The defaults are page `1` and page size `25`. |
| `trustRecord(id)`                         | `GET /trust-records/{id}` | The Execution Trust Record.                                           |
| `validatePolicy(policyId, policyVersion)` | `POST /policies/validate` | A `PolicyValidationResult`.                                           |
| `refusalRecord(id)`                       | `GET /refusal/{id}`       | A `RefusalRecord`.                                                    |
| `verifyRefusalRecord(record)`             | `POST /refusal/verify`    | `true` or `false`.                                                    |
| `verifyAuditEvent(event, signature)`      | `POST /audit/verify`      | `true` or `false`.                                                    |
| `health()`                                | `GET /health`             | The health status.                                                    |
| `version()`                               | `GET /version`            | The version information.                                              |
| `endpoint()`                              | None                      | The configured endpoint, as a string.                                 |

The full signature of every method is in the [API reference](/sdks/reference/typescript/classes/ParmanaClient).

## Idempotency

`businessTransactionId` is the idempotency key. Sending the same one twice returns `409` (`ConflictError`). The helper `createBusinessTransaction()` generates the identifiers and keeps the linked ones consistent, so use it instead of writing them by hand. The SDK never generates a `businessTransactionId` for you when you build the object yourself, and it never retries a `POST`, so a single call is a single attempt.

## Where to go next

For a client wrapper with logging and a deployment checklist read [TypeScript SDK in production](/guides/typescript-sdk-production). For an agent that must fail closed read [TypeScript SDK for AI agents](/guides/typescript-sdk-ai-agents). For the exact rules an agent follows read [Integrate Parmana: specification for AI agents](/agents/integrate).
