# Entrega DDEX > Envía lanzamientos como DDEX ERN: prueba un lote con POST /ddex/validate, entrégalo con POST /ddex o subidas directas, usa SFTP y lee el informe. Los lanzamientos entran en HILLS Music como lotes DDEX ERN. La API no crea ni edita lanzamientos sueltos campo a campo. Envías el mismo lote que enviarías a una plataforma, por la API o por SFTP. Cada lanzamiento del lote se convierte en un borrador de tu catálogo y pasa por la revisión y la entrega a las plataformas como de costumbre. ## Versiones de ERN | Versión | Estado | | --- | --- | | ERN 4.3 (`http://ddex.net/xml/ern/43`) | Recomendada | | 4.2, 4.1 | Aceptadas | | 3.8.2, 3.8.1, 3.8, 3.7 | Aceptadas. El informe advierte de que la versión es antigua. | | Cualquier otra | Rechazada | Todas las versiones aceptadas pasan por las mismas comprobaciones y la misma importación. Un lote es un zip con uno o varios archivos XML `NewReleaseMessage` y el audio, las portadas y los videos a los que remiten. ## Ámbitos | Ámbito | Permite | | --- | --- | | `ddex:validate` | Solo pruebas. Ideal para CI. | | `ddex:write` | Pruebas, entregas, subidas directas y estado de las entregas. | ## 1. Prueba primero `POST /ddex/validate` ejecuta todas las comprobaciones de una entrega: el XML, las reglas de HILLS, los archivos de audio, portada y video, los UPC e ISRC frente a tu catálogo, los géneros, los territorios y los acuerdos. **No se crea nada**: ni lanzamientos, ni grabaciones, ni artistas, ni códigos, ni entregas. Envía un `.zip` o un único `.xml`. ```bash curl -X POST "https://api.hillsmusic.com/v1/ddex/validate?member_id=41" \ -H "Authorization: Bearer $HILLS_API_KEY" \ -H "Content-Type: application/zip" \ --data-binary @batch-0001.zip ``` ```js import { readFile } from "node:fs/promises"; const res = await fetch("https://api.hillsmusic.com/v1/ddex/validate?member_id=41", { method: "POST", headers: { Authorization: `Bearer ${process.env.HILLS_API_KEY}`, "Content-Type": "application/zip" }, body: await readFile("batch-0001.zip"), }); const { data } = await res.json(); console.log(data.verdict, data.summary); for (const c of data.checks.filter((c) => c.status === "error")) console.log(c.field, c.message); ``` `member_id` es el artista o sello al que corresponde el lote. Omítelo para tu propio catálogo. Para un archivo XML, envía `Content-Type: application/xml`. También puedes enviar `multipart/form-data` con el campo `file` (hasta 512 MB). La respuesta incluye la versión del ERN, un resumen, cada comprobación por fase (`pass`, `warning`, `error`, `skipped`) y un veredicto: | Veredicto | Significa | | --- | --- | | `would_import` | Sin problemas. Se convertiría en un borrador. | | `would_import_with_warnings` | Se importaría. Lee las advertencias. | | `would_be_flagged` | Quedaría a la espera del equipo de HILLS: una retirada, o una actualización de un lanzamiento en revisión o publicado. | | `would_be_rejected` | Tiene errores. No se crearía nada para ese lanzamiento. | Un lote con errores responde igualmente `200`. El veredicto indica qué ha fallado. Un lote demasiado grande para probarlo responde `413 too_large`, y muchas pruebas a la vez pueden responder `429 busy`. Además, cada prueba aparece durante 90 días en la página DDEX de la aplicación web, en **Test reports**. ## 2. Entrega `POST /ddex` acepta el zip como cuerpo en bruto (`Content-Type: application/zip`, hasta 20 GB) o en el campo multipart `file` (hasta 512 MB). Responde `202` de inmediato: ```bash curl -X POST "https://api.hillsmusic.com/v1/ddex?member_id=41&name=batch-0001.zip" \ -H "Authorization: Bearer $HILLS_API_KEY" \ -H "Content-Type: application/zip" \ --data-binary @batch-0001.zip # {"data":{"id":311,"status":"queued","environment":"production","status_url":"/v1/ddex/311"}} ``` Para archivos grandes, es preferible una subida directa. ## Subidas directas para lotes grandes Una subida directa envía el archivo directamente al almacenamiento en lugar de pasar por la API. 1. **Inicia.** `POST /ddex/uploads` con `name` y `size` en bytes (hasta 20 GB), y `member_id` si hace falta. 2. **Sube.** Hasta 64 MB recibes una sola `upload.url`: envía a ella el archivo completo con `PUT`. Por encima de 64 MB recibes `upload.part_size` y una URL por parte. Envía con `PUT` cada fragmento de `part_size` bytes a su URL (el último fragmento es más corto). Las partes pueden ir en cualquier orden y en paralelo. Estas URL están firmadas: no envíes ninguna cabecera `Authorization` a ellas. 3. **Confirma.** `POST /ddex/uploads/{id}/commit`. Comprueba que todas las partes llegaron con el tamaño correcto y después pone el lote en cola igual que `POST /ddex` (`202`). ```js import { open, stat } from "node:fs/promises"; const H = { Authorization: `Bearer ${process.env.HILLS_API_KEY}`, "Content-Type": "application/json" }; const API = "https://api.hillsmusic.com/v1"; const file = "batch-0001.zip"; const { size } = await stat(file); const start = await fetch(`${API}/ddex/uploads`, { method: "POST", headers: H, body: JSON.stringify({ name: file, size, member_id: 41 }), }).then((r) => r.json()); const { id, upload } = start.data; const fh = await open(file); if (upload.url) { await fetch(upload.url, { method: "PUT", body: await fh.readFile() }); } else { for (const p of upload.parts) { const offset = (p.part_number - 1) * upload.part_size; const buf = Buffer.alloc(Math.min(upload.part_size, size - offset)); await fh.read(buf, 0, buf.length, offset); const r = await fetch(p.url, { method: "PUT", body: buf }); if (!r.ok) throw new Error(`part ${p.part_number}: ${r.status}`); } } await fh.close(); const commit = await fetch(`${API}/ddex/uploads/${id}/commit`, { method: "POST", headers: H }); console.log(commit.status, await commit.json()); // 202, then poll status_url ``` Las URL de las partes funcionan durante 6 horas. `GET /ddex/uploads/{id}` da URL nuevas para las partes que aún faltan, con `received_parts` y `parts_total`. Una confirmación con partes que faltan responde `409 upload_incomplete` y enumera `missing` y `wrong_size`: envíalas y vuelve a confirmar. `DELETE /ddex/uploads/{id}` descarta una subida que no vayas a terminar. Las subidas sin confirmar se eliminan a los 7 días. ## Por SFTP Cada buzón DDEX es un único acceso SFTP con dos carpetas. Recibes el acceso con tu buzón, en la página DDEX de la aplicación web. | Carpeta | Qué ocurre | | --- | --- | | `/production//` | Se entrega. En cuanto llega el marcador, se importa el lote. | | `/test//` | Se prueba, como con `POST /ddex/validate`. No se crea nada. El informe se escribe a su lado: `/test/.report.txt` y `.report.json`. | Sube el XML y sus archivos a la carpeta del lote y, al final, el marcador: `BatchComplete_*.xml` o cualquier archivo `*.complete`. También se prueba un único `/test/.xml` o `.zip`. Cualquier otro archivo en `/test`, como `ping.txt`, es una prueba de conexión. Una carpeta de lote de producción que nunca recibe su marcador se elimina a los 7 días. Lo que haya en `/test` se elimina en el mismo plazo. Consulta [Carpeta de pruebas](https://developer.hillsmusic.com/es/reference/test-folder/). ## 3. Lee el informe de entrega Consulta `GET /ddex/{id}` cada 10–30 segundos, o suscríbete al [webhook](https://developer.hillsmusic.com/es/guides/webhooks/) `ddex.import.imported`. El estado pasa por `queued` → `parsing` → `validating` → `importing` y termina en: - `done`: todos los lanzamientos se importaron; - `partial`: algunos lanzamientos quedaron excluidos; - `failed`: no se importó nada. ```bash curl "https://api.hillsmusic.com/v1/ddex/311" \ -H "Authorization: Bearer $HILLS_API_KEY" ``` La respuesta incluye recuentos y un elemento por lanzamiento, cada uno con su `outcome`, su `release_id` y sus `errors` (cada error indica su campo DDEX). `GET /ddex` enumera tus últimas 100 entregas. ## Qué se crea o se marca | Resultado | Cuándo | | --- | --- | | `created` | Un lanzamiento nuevo. Se convierte en un borrador del catálogo de `member_id`. | | `updated` | El UPC ya es un borrador (o un lanzamiento devuelto para cambios) en ese catálogo. Se actualiza ese borrador. | | `flagged` | Una retirada, o una actualización de un lanzamiento en revisión o publicado. No se aplica. Lo revisa el equipo de HILLS. | | `left_out` | El lanzamiento tenía errores. No se creó nada para él. Corrígelo y vuelve a entregarlo. | | `test` | Un mensaje marcado como `TestMessage`: se comprueba y no se crea nada. | Un ISRC que ya tienes reutiliza esa grabación. Un ISRC que pertenece a otra cuenta deja el lanzamiento excluido. Después, los borradores pasan por la revisión como de costumbre: envíalos desde la aplicación web o pide a HILLS Music que active el envío automático para tu buzón. Síguelos con [GET /releases](https://developer.hillsmusic.com/es/reference/releases/) y con webhooks como `release.approved` y `release.live`. Todos los parámetros están en la [referencia de Entregas DDEX](https://developer.hillsmusic.com/es/reference/ddex-deliveries/). Fuente: https://developer.hillsmusic.com/es/guides/ddex/