# Servidor xamiSDK — Servicios (HTTP)

Cuando levantas el servicio con `xamisdk serve`, el xamiSDK expone una API HTTP **local**
(por defecto en `http://127.0.0.1:8300`) para que tus aplicaciones firmen a través de
ella. Es la API que consumen las librerías XamiLib (PHP, Python).

## Autenticación

Todas las rutas `/v1/*` requieren una credencial de aplicación (emitida con
`xamisdk wrapper:create`), enviada en cada petición mediante cabeceras:

```
X-Wrapper-Key: wrp_...
X-Wrapper-Secret: wsec_...
```

## Endpoints

### `POST /v1/pades/sign`
Firma un PDF. Devuelve un `request_id`; el resultado se consulta luego en
`/v1/results/{request_id}`.

Cuerpo (JSON):

| Campo | Tipo | Descripción |
|---|---|---|
| `pdf_base64` | string | PDF en base64 (**requerido**). |
| `credential_key` | string | Credencial a usar (**requerido**). |
| `design_key` | string | Diseño del sello (opcional; usa el por defecto). |
| `variables` | objeto | Valores de las variables del sello. |
| `reason` | string | Razón de la firma. |
| `location` | string | Lugar de la firma. |
| `signer_name` | string | Nombre del firmante. |

Respuesta: `{ "request_id": "...", "status": "PENDING" }`

### `POST /v1/blockchain/sign`
Firma el hash de una transacción EVM. Devuelve un `request_id`.

Cuerpo (JSON):

| Campo | Tipo | Descripción |
|---|---|---|
| `tx_hash` | string | keccak256 de la tx (**requerido**). |
| `credential_key` | string | Credencial EVM / wallet (**requerido**). |
| `chain_id` | entero | Chain id (EIP-155). |

Respuesta: `{ "request_id": "...", "status": "PENDING" }`

### `GET /v1/results/{request_id}`
Consulta el resultado de una operación de firma. Mientras esté pendiente devuelve
`status: PENDING`; al completarse, `status: DONE`. Para PAdES, cuando está `DONE`
incluye el PDF firmado en `pdf_base64`. Para blockchain, incluye `r`, `s`, `v`.

Respuesta (ejemplo PAdES): `{ "status": "DONE", "pdf_base64": "..." }`

### `GET /v1/credentials`
Lista las credenciales disponibles en la caché local.

Respuesta: `{ "credentials": [ ... ] }`

### `GET /v1/designs`
Lista los diseños de sello disponibles en la caché local.

Respuesta: `{ "designs": [ ... ] }`

### `GET /v1/results`
*Desde xamiSDK 0.2.0.* Lista los resultados recientes, del más nuevo al más antiguo.
Existe para recuperar firmas cuyo `request_id` se perdió (proceso reiniciado, push no
recibido); antes eso solo se podía ver con `xamisdk callbacks:list`.

Parámetros: `limit` (por defecto 50).

Respuesta: `{ "results": [ { "request_id": ..., "status": "PENDING|DONE|ERROR", ... } ] }`

### `POST /v1/sync`
*Desde xamiSDK 0.2.0.* Recarga credenciales y diseños desde Xami **sin reiniciar el
servicio**. Equivale a `xamisdk sync`. Necesario porque al dar de alta una credencial o
cambiar un diseño en la consola, el servicio en marcha sigue sirviendo su caché.

Respuesta: `{ "ok": true, "credentials": 4, "designs": 3, "cache": { ... } }`

Devuelve `409` si el equipo no está pareado.

### `GET /v1/errors`
*Desde xamiSDK 0.2.0.* Errores registrados por el servicio local, **sin traceback**. El
detalle técnico completo no sale del equipo del cliente: solo viaja cuando alguien
ejecuta `xamisdk report`.

Parámetros: `limit` (por defecto 20), `request_id` (opcional, filtra por request).

Respuesta: `{ "errors": [ { "error_id": "err_...", "short": "...", "ts": ..., "type": ... } ] }`

### `GET /health`
Estado del servicio. No requiere autenticación de aplicación. Útil para comprobar que el
servicio está arriba, pareado y en qué versión.

Respuesta: `{ "ok": true, "service": "xamisdk-serve", "paired": true, "tenant_id": ..., "version": "0.2.0", "commit": "...", "api_version": 2, "cache": { ... } }`

`api_version` indica la versión del contrato local: `1` para servicios anteriores a
0.2.0 (sin `/v1/sync`, `/v1/results` ni `/v1/errors`) y `2` a partir de ahí. Compruébalo
antes de usar los endpoints nuevos si no controlas la versión instalada.

> **Nota.** El servicio expone además una ruta interna (`/receive`) que usa Xami para
> entregar los resultados de firma en modo push. Es parte del funcionamiento interno del
> ciclo de firma; tu aplicación no la usa.

---

## Errores

Cuando algo falla, la respuesta HTTP trae un mensaje corto y un identificador:

```json
{ "error": "el contenido no es un PDF (empieza por b'<!DOCTYP', se esperaba b'%PDF-')",
  "error_id": "err_a0ff104054" }
```

En la consola del servicio se imprime **una sola línea en rojo** con ese mismo mensaje.
El traceback nunca se muestra: se guarda en `errors.jsonl` y `errors.log` dentro del
directorio de configuración (`~/.xamisdk` por defecto), junto con el contexto y las
versiones de las librerías del entorno.

Ese detalle solo sale del equipo del cliente al ejecutar `xamisdk report <requestId>`,
que lo adjunta al incidente previa confirmación explícita.

Códigos habituales:

| Código | Significado |
|---|---|
| `401` | Falta la credencial de aplicación o está revocada. |
| `409` | El equipo no está pareado con Xami (`xamisdk init`). |
| `422` | La petición es inválida — por ejemplo, el `pdf_base64` no contiene un PDF. |
| `502` | El motor de firma o Xami devolvieron un error. |
| `500` | Fallo inesperado; el detalle queda registrado con su `error_id`. |

### Tolerancia en `pdf_base64`

El servicio corrige por su cuenta los envíos mal formados **cuando la corrección tiene
una sola interpretación posible**, y deja un aviso en el log para que arregles el
cliente en origen:

| Lo que llega | Qué hace |
|---|---|
| `data:application/pdf;base64,JVBERi0...` | Recorta el prefijo del data-URI. |
| Base64 partido en líneas de 76 columnas | Elimina espacios y saltos. |
| Base64 sin el relleno `=` final | Lo completa. |
| Base64 url-safe (`-` y `_`) | Lo traduce. |
| Base64 codificado dos veces | Lo decodifica otra vez. |

Toda corrección se verifica después contra la cabecera `%PDF-`: si no produce un PDF, se
rechaza con `422`. Lo ambiguo nunca se adivina — un HTML de error o un archivo que no es
un PDF devuelven `422` diciendo con qué bytes empieza lo que llegó.
