# Referencia del SDK

> API del SDK: lecturas v1 y creación v2 gobernada por la política de pedidos generada.

Paquete `@zafirox/client-sdk` (ESM, TypeScript). Todo se importa desde `index.js`. Esta página describe el SDK actualizado en el repositorio; el CDN 1.1.0 actualmente publicado todavía contiene la antigua creación v1. La versión nueva se publicará después del backend v2.

## `ZafiroxClient`

```ts
new ZafiroxClient(options: ZafiroxClientOptions)
```

| Opción | Tipo | Por defecto | Detalle |
| --- | --- | --- | --- |
| `baseUrl` | `string` | — | Origen de la API, `https://datacenterv2.joyeriazafirox.com` |
| `tokenProvider` | `TokenProvider` | — | Quien entrega el access token |
| `fetch` | `FetchLike` | `globalThis.fetch` | Implementación de `fetch` a usar |
| `timeoutMs` | `number` | `15000` | Timeout por petición (`AbortController`) |
| `apiVersion` | `string` | `'1'` | Versión esperada para lecturas v1; `orders.create()` valida v2 por separado |
| `retry` | `{ maxAttempts?, backoffMs?, maxRetryAfterMs? }` | `2`, `250`, `5000` | Política de reintentos |
| `maxResponseBytes` | `number` | `1048576` | Tamaño máximo aceptado de respuesta |
| `onRequestId` | `(id: string) => void` | — | Recibe el `X-Request-Id` de cada respuesta |

Propiedades: `orders`, `account`, `portfolio`, `apiVersion`.

## Proveedores de token

```ts
createClientCredentialsProvider({
  tokenUrl, clientId, clientSecret, scope?, fetch?, timeoutMs?, refreshSkewSeconds?,
}): TokenProvider

createStaticTokenProvider(accessToken: string): TokenProvider

createAuthorizationCodeProvider({
  apiUrl, clientId, redirectUri, scope, clientSecret?, storage?, storageKey?,
  fetch?, timeoutMs?, refreshSkewSeconds?, now?, randomBytes?, sha256?,
}): AuthorizationCodeProvider

interface TokenProvider { getAccessToken(): Promise<string>; }

interface AuthorizationCodeProvider extends TokenProvider {
  createAuthorizationUrl(options?: { state?, loginHint?, prompt?: 'select_account' | 'login' }): Promise<string>;
  login(options?): Promise<void>;                       // sólo navegador: redirige
  completeLogin(callbackUrl: string | URL): Promise<AuthorizationCodeSession>;
  isAuthenticated(): boolean;
  session(): AuthorizationCodeSession | null;           // { scope, expiresAt, hasRefreshToken }
  logout(): void;
}
```

`createClientCredentialsProvider` es sólo para backends; cachea el token, lo renueva `refreshSkewSeconds` (30) antes de expirar y mantiene una única renovación en vuelo. Un rechazo del token endpoint lanza `AuthenticationError` con el `error_code` del catálogo.

`createAuthorizationCodeProvider` implementa «Acceder con Zafirox» (Authorization Code + PKCE S256): genera `code_verifier` y `state`, los guarda en `storage` (`sessionStorage` en el navegador, memoria en Node), redirige a `GET /oauth/authorize`, canjea el `code` en `completeLogin` y renueva con `refresh_token` (rotación) cuando el access token expira. Errores de `completeLogin`: `access_denied`, `STATE_MISMATCH`, `AUTHORIZATION_NOT_PENDING`, `INVALID_CALLBACK`; un refresh rechazado (`INVALID_GRANT`) borra la sesión local. `clientSecret` sólo en servidores (cliente confidencial, HTTP Basic). También se exportan `generatePkce()`, `base64UrlEncode()` y `requestToken()` para flujos a medida.

## `client.orders`

```ts
orders.create(input: CreateOrderInput, options?: { idempotencyKey?: string }): Promise<OrderCreationResult> // POST /api/v2/client/orders
orders.list(query?: OrderListQuery): Promise<Page<Order>>
```

`CreateOrderInput`: `initialDate`, `deliveryDate`, `jewel`, `performedBy?`, `orderSubjectUid?`, `urgentAuthorizationCode?`.
`JewelInput`: campos requeridos `customerId`, `customer`, `phone`, `type`, `material`, `designRefs`; opcionales `invoiceName`, `goldType`, `goldQuality`, `style`, `observations`, `details`, `requires3dDesign`, `nona`, `individualWeight`, `nonaGender`, `measurementSystem`, `measurements`, `widths`, `marking`, `weight`, `stones`. Los pesos y anchos decimales son cadenas exactas. `designRefs` contiene de 1 a 5 referencias opacas devueltas por `POST /api/v2/client/order-designs`; el SDK no ofrece carga de archivos ni edición de órdenes.

`orders.create()` mapea camelCase a snake_case, aplica el perfil generado `external-create-v2`, adjunta `policy_version` y `policy_sha256`, y compara esa identidad con `meta` de la respuesta. Si el payload infringe el contrato, lanza `OrderPolicyInputError` con `violations`, `field`, `code` y `ruleId` antes de enviar. Una versión de política no conocida por el backend responde `409 order_policy.version`. `orders.list()` sigue en `GET /api/v1/client/orders`.

```ts
// Para la nueva versión del SDK, después de publicar backend v2 y obtener una referencia autorizada:
const receipt = await client.orders.create({
  initialDate: '2026-10-01', deliveryDate: '2026-10-15',
  jewel: {
    customerId: '123456789', customer: 'Cliente Demo', phone: '3000000000',
    type: 'Anillo', material: 'Plata', designRefs: [authorizedDesignRef],
  },
}, { idempotencyKey: 'erp-orden-123' });
console.log(receipt.serial, receipt.policyVersion, receipt.policySha256);
```

`ORDER_POLICY_VERSION`, `ORDER_POLICY_SHA256`, `ORDER_POLICY_MANIFEST` y los evaluadores generados también se exportan para inspección. Su versión es independiente de la versión HTTP y del `dataSchemaVersion` histórico de Angular.
`OrderListQuery`: `cursor?`, `limit?`, `status?`, `category?`, `search?`, `startDate?`, `endDate?`, `subjectUid?`.

## `client.account`

```ts
account.get(): Promise<Account>
account.transactions(query?: TransactionQuery): Promise<Page<AccountTransaction>>
```

`TransactionQuery`: `cursor?`, `limit?`, `type?`, `from?`, `to?`.

## `client.portfolio`

```ts
portfolio.get(query?: PortfolioQuery): Promise<Page<PortfolioItem>>
portfolio.payments(query?: PaymentQuery): Promise<Page<Payment>>
```

`PortfolioQuery`: `cursor?`, `limit?`, `status?`, `orderSerial?`, `subjectUid?`.
`PaymentQuery`: `cursor?`, `limit?`, `orderSerial?`, `from?`, `to?`, `subjectUid?`.

## Tipos de resultado

```ts
interface Page<T> { items: readonly T[]; nextCursor: string | null; hasMore: boolean; requestId: string; }

type DecimalString = string;   // "150000.00" histórico o "0.0001" COP operativo
type CivilDate = string;       // "2026-09-20"
type Rfc3339Instant = string;  // "2026-09-15T17:00:00-05:00"

interface Order { serial; subjectUid; status; customer; invoiceName; jewelType; initialDate; deliveryDate; price: DecimalString; currency; performedBy }
interface OrderCreationResult { serial; subjectUid; status; replayed: boolean; initialDate; deliveryDate; requestId; policyVersion; policySha256 }
interface Account { accountId; uid; balance: DecimalString; currency; status; createdAt: Rfc3339Instant | null; updatedAt }
interface AccountTransaction { transactionId; accountId; subaccountId; type; amount; balanceBefore; balanceAfter; description; reference; occurredAt; currency }
interface PortfolioItem { orderSerial; subjectUid; status; customer; description; orderTotal; paidTotal; balance; currency; updatedAt }
interface Payment { paymentId; subjectUid; orderSerials: readonly string[]; amount; currency; paymentType; reference; occurredAt }
```

Los campos camelCase del SDK corresponden uno a uno a los snake_case del [contrato HTTP](/docs/referencia-api).

## Errores

`ZafiroxApiError` y sus subclases (`AuthenticationError`, `AuthorizationError`, `NotFoundError`, `ConflictError`, `ValidationError`, `RateLimitError`, `UpstreamError`, `TransportError`) con `code`, `status`, `requestId`, `retryable`, `field`, `diagnosticId`, `retryAfterSeconds`. El rechazo local de creación es `OrderPolicyInputError`; un rechazo de servidor conserva `errors[0].code` y `field`. Ver [errores y reintentos](/docs/errores).

## Transporte de bajo nivel

`ApiTransport` es público para quien necesite llamar una ruta nueva antes de que el SDK la exponga:

```ts
const transport = new ApiTransport({ baseUrl, tokenProvider });
const { data, meta, requestId } = await transport.request({
  method: 'GET', path: '/api/v1/client/account', idempotent: true,
});
```

Valida el envelope y la `api_version`, aplica timeout, límite de tamaño y reintentos igual que los módulos de alto nivel.

## Utilidades

`generateRequestId()` devuelve un identificador válido para `X-Request-Id` (`sdk-<uuid>`); `CLIENT_API_BASE_PATH` es `'/api/v1/client'` y `CLIENT_ORDER_CREATE_V2_PATH` es `'/api/v2/client/orders'`.
