# Solicitudes en la web del cliente

Recoge una solicitud, completa las verificaciones de identidad y recupera la carga de documentos de forma segura.

## Operaciones REST y permisos de las claves

| Tarea | Permisos de la clave | Empieza aquí |
| --- | --- | --- |
| Usar el cupo de solicitudes del servidor del cliente o la transferencia eID alojada | `write:applications` en una clave de servidor independiente | `POST /api/v1/public/units/{slug}/application-eid/handoffs` |

## Crea un flujo de solicitudes alojado por el cliente

Usa la recepción REST versionada si alojas un formulario de solicitud en tu propia web. Parte del origen confirmado de la API indicado en la guía de conexión. Este flujo es independiente de las consultas de solicitudes del arrendador y de las herramientas MCP; una clave de empresa nunca sustituye la identidad ni el consentimiento del solicitante.

### 1. Carga el formulario actual antes de recoger una solicitud.

Consulta `GET /api/v1/public/units/{slug}/application-form?language=sv` (también `en` o `es`). Conserva los valores devueltos de `unitSlug`, `listingPeriodId` y el `formVersion` opaco. Presenta el `inputSchema` publicado, las `rules` condicionales, `policies.confirmations`, los requisitos documentales e `identityGate`; no los recrees a partir de suposiciones. Enlaza las rutas de privacidad y condiciones proporcionadas mediante el origen confirmado de la aplicación web de Homito, no el de la API. Comprueba `canApply` y `unavailableReason`; consultar un formulario no crea ni reabre una convocatoria de solicitudes.

Usa `POST /api/v1/public/units/{slug}/application-form/validate` para recibir comentarios sobre un borrador. La validación comprueba las reglas compartidas de campos, las confirmaciones y los metadatos documentales declarados frente a una instantánea del formulario de solo lectura. No crea solicitudes ni auditorías de consentimiento, no necesita una clave de envío, no verifica bytes de archivos ni inicia llamadas a proveedores de identidad o evaluación. El envío definitivo se valida de nuevo.

### 2. Mantén cada credencial en su función.

| Credencial | Finalidad | Límite |
| --- | --- | --- |
| `X-Homito-Customer-Key` | Una clave de servidor independiente con `write:applications` para el cupo compartido del cliente y la transferencia eID alojada | Solo en el servidor; de la misma empresa que la unidad anunciada; no sustituye las capacidades del solicitante |
| `X-Application-Submission-Key` | Un UUID v4 generado antes del primer envío | Conserva la misma clave y el cuerpo y los archivos aceptados tras perder una respuesta; no es un `Idempotency-Key` del arrendador |
| `X-Application-Receipt` | JSON con `kind: "public"`, el slug original de la unidad como `target` y la clave de envío original como `key` | Envíalo en la cabecera de recibo, nunca en una URL; solo acredita la existencia de la confirmación y el estado de los adjuntos |
| `X-Application-Upload-Key` | Una capacidad independiente generada con 32 bytes aleatorios criptográficamente seguros codificados como 64 caracteres hexadecimales en minúscula | Obligatoria para las operaciones de carga del manifiesto; un recibo, un ID de solicitud o una clave de empresa no la sustituyen |

La clave del cliente cambia el cupo de tráfico compartido de la empresa; no concede acceso a otra empresa ni sustituye los permisos de recibo o carga. Una clave proporcionada que no sea válida se rechaza, en lugar de recurrir al acceso público. Mantén las claves de empresa en el servidor y todas las capacidades fuera de cachés públicas, URL, registros y conversaciones con agentes. Consérvalas en la sesión protegida del formulario del visitante antes de la primera solicitud de red.

### 3. Completa la transferencia de identidad requerida y envía una sola vez.

Cuando `identityGate.required` sea true, usa la transferencia eID alojada publicada. Un administrador registra primero el callback HTTPS exacto del cliente mediante el plano de control autenticado existente. En el servidor del cliente, vincula un `state` aleatorio, un verificador PKCE privado y su desafío `S256` a la sesión protegida del formulario del visitante actual, al formulario y convocatoria actuales y a la clave de envío original. Crea la transferencia en `POST /api/v1/public/units/{slug}/application-eid/handoffs` con la clave de servidor de la misma empresa y el contrato de formulario devuelto.

Guarda el estado de la transferencia antes de redirigir a la URL de autorización devuelta y respeta su `expiresAt`. En el callback registrado, valida `state` frente a la sesión de ese mismo visitante; una búsqueda global del estado no basta. Mantén privados el código y el verificador, evita registrar los parámetros de consulta del callback y redirige inmediatamente a una URL limpia del formulario. Una clave, una cuenta, el indicador `hasNoPersonalNumber` o una afirmación del cliente no permiten omitir la eID requerida.

Envía `POST /api/v1/applications` como `multipart/form-data` con `X-Homito-Application-Contract: 1` y el `X-Application-Submission-Key` original. Indica un `Content-Length` exacto; se rechaza si falta o no coincide. Incluye `unitSlug`, `listingPeriodId`, `formVersion` y los campos JSON de `application`. Cuando sea necesario, envía el campo JSON independiente `identityHandoff` con `handoffId`, `code` y `codeVerifier`; nunca lo incluyas dentro de `application` ni expongas el verificador al código del navegador. Si el envío utiliza una transferencia, envía también la misma clave de cliente original que la creó; otra clave de la misma empresa no puede sustituirla. La recepción JSON heredada no proporciona este contrato versionado.

Usa `documents.maxExternalBodyBytes` del formulario para la solicitud multipart completa. Los archivos incluidos directamente utilizan los campos de adjunto publicados; los documentos mayores utilizan `attachmentManifest`, sin archivos incluidos directamente y con la clave de carga independiente. Sigue `documents.manifestSchema`, `maxFiles`, `maxFileBytes`, `allowedMimeTypes` y `acceptedByteFormats`, incluidos los requisitos del solicitante principal y de cada cosolicitante. Un manifiesto declara archivos previstos; no demuestra que se hayan recibido.

### 4. Consulta el recibo antes de decidir qué repetir.

Consulta `GET /api/v1/applications/receipt` con la cabecera de recibo original. `received` significa que la solicitud se confirmó; inspecciona su estado independiente de `attachments` en lugar de crear otro envío. Una repetición idéntica ya confirmada devuelve el recibo original antes de comprobar de nuevo una prueba de identidad de un solo uso consumida o caducada. `unknown` puede significar que no se envió, que sigue procesándose o que se revirtió: aplica esperas limitadas y, al repetir, utiliza la solicitud idéntica con la clave original. `removed` es terminal; detén las repeticiones antiguas. Consultar recibos no concede acceso al perfil del solicitante, la descarga de documentos, resultados de evaluación ni derechos de decisión. `notifications: unknown` no demuestra la entrega de un correo.

### 5. Carga los bytes declarados y finaliza.

Las cuatro operaciones siguientes requieren el `X-Application-Upload-Key` original. Conserva cada ID de carga del manifiesto y los bytes y el hash originales de cada archivo tras una interrupción.

| Operación | Qué confirma |
| --- | --- |
| `GET /api/v1/applications/{applicationId}/uploads` | El indicador `finalized` actual y el estado `pending`, `received`, `expired` o `stale` de cada archivo declarado |
| `POST /api/v1/applications/{applicationId}/uploads/{uploadId}/url` | Una URL PUT de corta duración con las cabeceras exactas, o un resultado ya `received` que no necesita PUT |
| `POST /api/v1/applications/{applicationId}/uploads/{uploadId}/complete` | Se comprobaron los bytes reales, la longitud, SHA-256, el formato de bytes/MIME y la vinculación actual del solicitante, y se confirmó el adjunto |
| `POST /api/v1/applications/{applicationId}/finalize` | Todos los documentos declarados están confirmados en el almacén documental habitual; de lo contrario, se rechaza la operación |

Carga solo en la URL exacta devuelta, con su método, cabeceras y longitud de bytes exactos. Nunca envíes la clave de empresa ni las capacidades del solicitante al almacenamiento de objetos, sigas redirecciones ni registres la URL firmada. Un PUT correcto por sí solo no es una finalización. Un proxy de carga en el servidor del cliente evita asumir que esté configurado CORS para cargar directamente del navegador al almacenamiento.

Tras perder la respuesta de un PUT, prueba `complete` para el mismo ID de carga antes de volver a cargar. Ante `upload_missing`, obtén otra URL para ese mismo ID, carga los bytes originales y completa la operación. Renovar una URL PUT caducada no renueva la capacidad de carga subyacente. Detente si caduca la capacidad o cambia el alcance del solicitante o de la convocatoria; cambiar un archivo o una persona requiere el flujo de corrección o finalización expresamente autorizado, no una nueva clave de envío ni modificar un manifiesto aceptado.

### Recupera según el código de error, sin crear una segunda solicitud.

| Error | Siguiente acción |
| --- | --- |
| `eid_required` (403) | Completa el flujo de identidad alojado aprobado; no inventes una forma de omitirlo |
| `form_version_changed` / `application_round_changed` (409) | Vuelve a cargar el contrato actual y pide al solicitante que confirme los datos modificados antes de un nuevo envío aceptado; conserva cualquier recibo existente |
| `submission_payload_changed` (409) | Concilia el cuerpo y los archivos aceptados originales; no crees automáticamente otra operación |
| `upload_subject_changed` (409), acceso de carga obsoleto o caducado | Detén la carga o finalización y utiliza la vía de finalización autorizada para el solicitante actual |
| `submission_removed` (410) | Deja de repetir el envío eliminado |
| Error de validación de archivo o formato (400/411/413/415/422) | Corrige el problema de la solicitud o del documento indicado antes de una repetición autorizada |
| 429, 503 o una respuesta perdida | Respeta `Retry-After` cuando se proporcione, conserva el punto de recuperación y consulta el recibo o progreso antes de repetir con la clave original |

`application.submitted` y `application.intake_finalized` son eventos webhook distintos. Verifícalos y elimina duplicados como indica la guía de webhooks, y utiliza el recibo o progreso para conocer el estado actual. Ni un evento ni la finalización acreditan la entrega de correo, una decisión de arrendamiento o una evaluación completada. Mantén los datos del solicitante y las capacidades de carga y recibo fuera de las cachés públicas de anuncios.

## Mantén los datos privados fuera de las cachés públicas

Mantén los datos personales, los recibos y las capacidades de carga fuera de las cachés públicas de anuncios. Las consultas de inquilinos y solicitudes devuelven deliberadamente proyecciones operativas mínimas; no deduzcas campos privados ausentes ni conclusiones de evaluación. Distingue los valores null o desconocidos de cero o de un resultado completado.
