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

# Ensamblar una carpeta de contrato

> Fusiona JSON de contrato multiarchivo en un manifest totalmente validado. Acceso anónimo permitido.

**`POST`** `https://api-datahub.octoparse.com/v1/data-apps/contract/assemble`

Autenticación: ninguna. Acceso anónimo permitido.

Fusiona una carpeta de contrato (mapa de archivos por ruta relativa, orden conservado) en un manifest autocontenido. Este endpoint es la única fuente de verdad de las reglas de ensamblado. Lo usan el asistente de importación del portal y el self-check del skill de contrato.

Archivos de slot: `dataapp.json` / `input_schema.json` / `output_schema.json` / `runtime.json` / `pricing.json` / `examples.json` / `README.md` (nombres sin distinción de mayúsculas; formas con punto como `input.schema.json` se aceptan y normalizan en `picked`). Precios y ejemplos también pueden ir en línea en `dataapp.json`. El manifest fusionado pasa por la validación completa del lado autor (mismas reglas que Validar un manifest). Entregable solo cuando `issues` está vacío. Las anomalías recuperables (archivos faltantes, colisiones de nombre entre capas, archivos no-contrato ignorados, desajuste de nombre de carpeta, etc.) vuelven como `warnings` estructurados (`code` + `params`, localizados por la capa de presentación) y no son errores.

Fallos duros (`400`): un archivo de slot no es JSON válido, tiene forma incorrecta (`examples.json` debe ser array, etc.) o un solo archivo supera 1MB / el total 4MB. Soft: anomalías recuperables vuelven como `warnings`.

El 8.º archivo opcional `i18n.json` (`{source, locales}`, hasta 4MB) **no** se fusiona en el manifest: se valida en forma y se devuelve por separado en `translations` para que el llamador decida cuándo guardar overlays. Sin el archivo, `translations` es `null`.

Acceso anónimo permitido: el ensamblado es función pura del cuerpo, no lee ni escribe datos de plataforma, y los límites de tamaño son la única puerta.

## Solicitud

### Cuerpo de la solicitud

<ParamField body="files" type="object" required>
  Mapa de archivos. Claves = rutas relativas; valores = texto del archivo.
</ParamField>

### Ejemplo de solicitud

```bash theme={null}
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"files": {"dataapp.json": "{\"spec_version\": \"0.2\", \"identity\": {…}}", "input_schema.json": "{\"type\": \"object\", …}", "output_schema.json": "{\"id_field\": \"review_id\", …}", "runtime.json": "{\"kind\": \"api\", …}", "pricing.json": "{\"events\": [...]}", "examples.json": "[...]", "README.md": "# Product review lookup"}}' \
  "https://api-datahub.octoparse.com/v1/data-apps/contract/assemble"
```

## Respuesta

### 200 correcto

```json theme={null}
{
  "data": {
    "manifest": {
      "spec_version": "0.2",
      "identity": {
        "app_name": "reviews-query",
        "name": "Product review lookup"
      },
      "…": "…"
    },
    "picked": {
      "dataapp.json": "dataapp.json",
      "input_schema.json": "input_schema.json"
    },
    "issues": [],
    "warnings": [
      {
        "code": "contract-folder-name-mismatch",
        "params": {
          "expected": "reviews-query",
          "actual": "reviews"
        }
      }
    ],
    "card": {
      "app_name": "reviews-query",
      "name": "Product review lookup"
    },
    "translations": null
  }
}
```

La carga útil va envuelta en `data`. Campos:

<ResponseField name="manifest" type="object" required>
  Objeto manifest fusionado.
</ResponseField>

<ResponseField name="picked" type="string[]">
  Mapa de archivos de slot realmente reconocidos.
</ResponseField>

<ResponseField name="folder_name" type="string">
  —
</ResponseField>

<ResponseField name="issues" type="object[]">
  Incidencias de validación. Vacío significa entregable.

  <Expandable title="campos">
    <ResponseField name="path" type="string" required>
      —
    </ResponseField>

    <ResponseField name="message" type="string" required>
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="warnings" type="object[]">
  Avisos estructurados.

  <Expandable title="campos">
    <ResponseField name="code" type="string" required>
      —
    </ResponseField>

    <ResponseField name="params" type="object">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="translations" type="object">
  Contenido parseado de `i18n.json`, o `null` si no hay.

  <Expandable title="campos">
    <ResponseField name="source" type="string" required>
      —
    </ResponseField>

    <ResponseField name="locales" type="object">
      —
    </ResponseField>
  </Expandable>
</ResponseField>

### Errores

| HTTP | `code`                  | `category`      | Descripción                                                                                                     |
| ---- | ----------------------- | --------------- | --------------------------------------------------------------------------------------------------------------- |
| 400  | `contract-file-invalid` | `invalid_input` | Un archivo de contrato no es JSON válido, tiene forma incorrecta o supera 1MB; o falta `source` en `i18n.json`. |
| 400  | `contract-empty`        | `invalid_input` | El juego de archivos no contiene slots de contrato.                                                             |
| 400  | `locale-invalid`        | `invalid_input` | El código de locale no es válido.                                                                               |
| 413  | `payload-too-large`     | `invalid_input` | El cuerpo supera el límite de tamaño.                                                                           |

Las respuestas de error usan `{"error": {code, category, message, retryable}}`. Véase <a href="/docs/es/datahub/api/reference/introduction#errors">Errores</a>.

## Bibliotecas cliente

Los SDK de Python y JavaScript aún no encapsulan este endpoint. Llame a REST directamente.
