{"openapi":"3.1.0","info":{"title":"API de Vialo","version":"1.1.0","description":"Lectura y carga de los datos de un laboratorio. Cada token actúa en nombre de la persona que lo creó, solo en su laboratorio y con su rol máximo: un token Lector solo lee, uno Editor también carga y modifica. Todo cambio queda en el historial del registro como «vía API». Límites: 60 pedidos por minuto para leer y 20 para escribir, por token."},"servers":[{"url":"https://vialo.juanpaladea.com.ar/api/v1"}],"security":[{"token":[]}],"components":{"securitySchemes":{"token":{"type":"http","scheme":"bearer","description":"Token de Vialo (trz_…), creado en Configuración › Acceso por API."}}},"paths":{"/lab":{"get":{"tags":["Laboratorio"],"summary":"Laboratorio del token","description":"El laboratorio (con su logo), sus módulos habilitados, su plan y qué puede hacer el token. Sirve para probar que el token anda y para saber si el laboratorio puede cargar datos (can_write: false = solo lectura).","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"timezone":{"type":"string"},"logo_url":{"description":"URL pública del logo; null si el laboratorio no subió uno.","type":["string","null"]},"modules":{"type":"array","items":{"type":"string"}},"subscription":{"anyOf":[{"type":"object","properties":{"plan":{"type":"string","enum":["basico","laboratorio","instituto"]},"status":{"type":"string","enum":["trialing","active","past_due","read_only","canceled"],"description":"trialing (en prueba), active, past_due (pago atrasado: se sigue cargando), read_only o canceled."},"trial_ends_at":{"description":"Hasta cuándo dura la prueba (si está en prueba).","type":["string","null"]},"current_period_end":{"description":"Hasta cuándo está pago el plan, si se sabe.","type":["string","null"]},"can_write":{"type":"boolean","description":"false: solo lectura (prueba vencida, sin plan o dado de baja). Se puede leer y exportar, pero cargar responde 403 read_only."}},"required":["plan","status","trial_ends_at","current_period_end","can_write"],"additionalProperties":false},{"type":"null"}]},"token":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string","enum":["viewer","editor","admin"]},"role_label":{"type":"string"},"expires_at":{"type":"string"}},"required":["name","role","role_label","expires_at"],"additionalProperties":false}},"required":["id","name","slug","timezone","logo_url","modules","subscription","token"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/support":{"get":{"tags":["Soporte"],"summary":"Mis consultas de soporte","description":"Las consultas que la persona dueña del token escribió al equipo de Vialo desde este laboratorio (por la Ayuda, la API o el MCP), con su estado. Las más nuevas primero; hasta 50.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string"},"category":{"type":"string","enum":["question","problem","idea","billing"]},"subject":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["new","answered","closed"],"description":"new (recibida), answered (respondida por mail) o closed (cerrada)."},"via":{"type":"string","enum":["web","api","mcp"]}},"required":["id","created_at","category","subject","message","status","via"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Soporte"],"summary":"Escribir una consulta de soporte","description":"Le manda una consulta al equipo de Vialo (no a la administración del laboratorio), igual que el formulario «Escribinos» de la Ayuda: llega por mail y al panel de la plataforma, y se responde por mail a la persona dueña del token. Hasta 5 por hora por persona. Funciona aunque el laboratorio esté en solo lectura.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"category":{"type":"string","enum":["question","problem","idea","billing"],"description":"question (una duda), problem (algo no anda), idea (una idea o pedido) o billing (plan y facturación)."},"subject":{"type":"string","minLength":3,"maxLength":160,"description":"Asunto corto, de 3 a 160 caracteres."},"message":{"type":"string","minLength":10,"maxLength":4000,"description":"Qué se quería hacer, en qué pantalla y qué pasó; de 10 a 4000 caracteres."},"locale":{"default":"es","description":"En qué idioma responder: es (por defecto) o en.","type":"string","enum":["es","en"]}},"required":["category","subject","message"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"created_at":{"type":"string"},"notified":{"type":"boolean","description":"Si salió el aviso por mail. Aunque sea false, la consulta quedó guardada y el equipo la ve."}},"required":["id","created_at","notified"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/fields":{"get":{"tags":["Biobanco"],"summary":"Campos de la muestra","description":"Campos base y personalizados, con su tipo y opciones: dicen qué significa cada clave de custom_fields.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: los campos personalizados aparecen así en custom_fields."},"label":{"type":"string"},"type":{"type":"string","enum":["text","long_text","number","date","boolean","select","multiselect"]},"custom":{"type":"boolean","description":"true si lo definió el laboratorio."},"required":{"type":"boolean"},"unit":{"type":["string","null"]},"help":{"type":["string","null"]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Opciones vigentes, para las listas."},"archived":{"type":"boolean"}},"required":["key","label","type","custom","required","unit","help","options","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/sample-types":{"get":{"tags":["Biobanco"],"summary":"Tipos de muestra","description":"","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"code_prefix":{"type":"string"},"default_unit":{"type":["string","null"]},"archived":{"type":"boolean"}},"required":["id","name","code_prefix","default_unit","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples":{"get":{"tags":["Biobanco"],"summary":"Buscar muestras","description":"Las muestras vigentes que cumplen todos los filtros, paginadas. Es la misma búsqueda que el listado de la web.","parameters":[{"name":"q","in":"query","required":false,"description":"Texto libre: código, donante, notas y campos personalizados de texto.","schema":{"description":"Texto libre: código, donante, notas y campos personalizados de texto.","type":"string","maxLength":200}},{"name":"type","in":"query","required":false,"description":"Tipos de muestra, por id o por nombre, separados por coma (cualquiera de ellos).","schema":{"description":"Tipos de muestra, por id o por nombre, separados por coma (cualquiera de ellos).","type":"string"}},{"name":"status","in":"query","required":false,"description":"Estados separados por coma: available, in_use, depleted, discarded, transferred. Por defecto, todos.","schema":{"description":"Estados separados por coma: available, in_use, depleted, discarded, transferred. Por defecto, todos.","type":"string"}},{"name":"location","in":"query","required":false,"description":"Ubicaciones por id (sala, freezer, rack o caja): incluye lo que está adentro.","schema":{"description":"Ubicaciones por id (sala, freezer, rack o caja): incluye lo que está adentro.","type":"string"}},{"name":"donor","in":"query","required":false,"description":"Parte del código de paciente o donante.","schema":{"description":"Parte del código de paciente o donante.","type":"string","maxLength":200}},{"name":"collected_from","in":"query","required":false,"description":"Extraídas desde esta fecha (AAAA-MM-DD).","schema":{"description":"Extraídas desde esta fecha (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"collected_to","in":"query","required":false,"description":"Extraídas hasta esta fecha (AAAA-MM-DD).","schema":{"description":"Extraídas hasta esta fecha (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"created_from","in":"query","required":false,"description":"Cargadas desde esta fecha (AAAA-MM-DD).","schema":{"description":"Cargadas desde esta fecha (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"created_to","in":"query","required":false,"description":"Cargadas hasta esta fecha (AAAA-MM-DD).","schema":{"description":"Cargadas hasta esta fecha (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"sort","in":"query","required":false,"description":"Campo por el que ordenar; con «-» adelante, de mayor a menor. Ej.: «-collection_date», «code», «cf.lote». Por defecto, «-created_at».","schema":{"description":"Campo por el que ordenar; con «-» adelante, de mayor a menor. Ej.: «-collection_date», «code», «cf.lote». Por defecto, «-created_at».","type":"string","maxLength":80}},{"name":"page","in":"query","required":false,"description":"Página, desde 1.","schema":{"default":1,"description":"Página, desde 1.","type":"integer","minimum":1,"maximum":9007199254740991}},{"name":"limit","in":"query","required":false,"description":"Resultados por página (hasta 200).","schema":{"default":50,"description":"Resultados por página (hasta 200).","type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","page","limit","total"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Biobanco"],"summary":"Cargar muestras","description":"Hasta 100 muestras por pedido; se crean todas juntas o ninguna. El código lo asigna Vialo con el formato del laboratorio. Con una caja y sin posición, cada muestra va al primer lugar libre. Conviene mandar «Idempotency-Key» para poder reintentar sin duplicar.\n\nPide un token con rol Editor.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"samples":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","minLength":1,"maxLength":80,"description":"Tipo de muestra, por id o por nombre (ver /biobank/sample-types)."},"status":{"default":"available","type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]{0,39}$"},{"type":"null"}],"description":"Código pseudonimizado del donante; null lo borra."},"collection_date":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Fecha de extracción (AAAA-MM-DD)."},{"type":"null"}]},"quantity":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Cantidad (volumen, masa…); null la borra."},"unit":{"type":"string","maxLength":20,"description":"Unidad de la cantidad (mL, µL, mg…)."},"notes":{"type":"string","maxLength":2000,"description":"Notas."},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"box_id":{"description":"Caja donde se guarda (ver /biobank/locations); null = sin ubicar.","anyOf":[{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},{"type":"null"}]},"position":{"description":"Posición en la caja: «B4» (filas con letras) o «2-4». Sin posición, va al primer lugar libre.","type":"string","maxLength":10}},"required":["type"]},"description":"Las muestras a cargar. Se crean todas juntas o ninguna."}},"required":["samples"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples/{id}":{"get":{"tags":["Biobanco"],"summary":"Ver una muestra","description":"Por id o por código (por ejemplo PL-2026-00042), con su ubicación, la muestra de origen y sus alícuotas.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"aliquots":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."}},"required":["id","code","status","quantity","unit","placement"],"additionalProperties":false}}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version","aliquots"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"tags":["Biobanco"],"summary":"Editar una muestra","description":"Por id o por código. Solo cambian los campos que se mandan; en custom_fields, solo las claves que se mandan. Pide la «version» que devolvió la API: si alguien la cambió después, responde 409. Para moverla o cambiar su estado están /move y /status.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"type":{"type":"string","minLength":1,"maxLength":80,"description":"Tipo de muestra, por id o por nombre (ver /biobank/sample-types)."},"donor_code":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]{0,39}$"},{"type":"null"}],"description":"Código pseudonimizado del donante; null lo borra."},"collection_date":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Fecha de extracción (AAAA-MM-DD)."},{"type":"null"}]},"quantity":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Cantidad (volumen, masa…); null la borra."},"unit":{"type":"string","maxLength":20,"description":"Unidad de la cantidad (mL, µL, mg…)."},"notes":{"type":"string","maxLength":2000,"description":"Notas."},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["version"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"tags":["Biobanco"],"summary":"Borrar una muestra","description":"Borrado lógico, como en la web: deja de aparecer y la administración la puede restaurar desde «Borradas». No se ofrece por MCP.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"deleted":{"type":"boolean","const":true}},"required":["id","code","deleted"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples/{id}/history":{"get":{"tags":["Biobanco"],"summary":"Historial de una muestra","description":"Quién hizo cada cambio, cuándo y por dónde, del más nuevo al más viejo.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"action":{"type":"string","enum":["insert","update","soft_delete","restore","delete","move","status_change"]},"actor":{"type":"string"},"via":{"description":"«vía API · <token>» si no se hizo desde la web.","type":["string","null"]},"changed_fields":{"type":"array","items":{"type":"string"}},"old":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"new":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]}},"required":["at","action","actor","via","changed_fields","old","new"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples/{id}/attachments":{"get":{"tags":["Biobanco"],"summary":"Adjuntos de una muestra","description":"Cada adjunto trae un enlace de descarga que vence a los 5 minutos.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"file_name":{"type":"string"},"mime_type":{"type":"string"},"size_bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_at":{"type":"string"},"uploaded_by":{"type":"string"},"download_url":{"description":"Enlace de descarga que vence a los 5 minutos.","type":["string","null"]}},"required":["id","file_name","mime_type","size_bytes","created_at","uploaded_by","download_url"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/locations":{"get":{"tags":["Biobanco"],"summary":"Ubicaciones","description":"Salas, equipos, racks y cajas con su ruta completa. En las cajas, la capacidad y cuántos lugares están ocupados.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"parent_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"kind":{"type":"string","enum":["room","freezer","fridge","ln2_tank","rack","box"],"description":"Sala, freezer, heladera, tanque de nitrógeno, rack o caja."},"name":{"type":"string"},"path":{"type":"string"},"temperature_c":{"type":["number","null"]},"box":{"anyOf":[{"type":"object","properties":{"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cols":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"capacity":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["rows","cols","capacity","used"],"additionalProperties":false},{"type":"null"}],"description":"Solo para cajas."}},"required":["id","parent_id","kind","name","path","temperature_c","box"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/boxes/{id}":{"get":{"tags":["Biobanco"],"summary":"Grilla de una caja","description":"Todas las posiciones de la caja, ocupadas o libres, en orden.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"path":{"type":"string"},"rows":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cols":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"free":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"positions":{"type":"array","items":{"type":"object","properties":{"position":{"type":"string"},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sample":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"string"},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."}},"required":["id","code","type","status"],"additionalProperties":false},{"type":"null"}]}},"required":["position","row","col","sample"],"additionalProperties":false}}},"required":["id","name","path","rows","cols","free","positions"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples/{id}/move":{"post":{"tags":["Biobanco"],"summary":"Mover una muestra","description":"A otra caja o posición (sin posición, al primer lugar libre de la caja), o sin ubicar con «box_id»: null. Vialo valida que el lugar esté libre y que la caja exista.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"box_id":{"anyOf":[{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},{"type":"null"}]},"position":{"description":"Posición en la caja: «B4» (filas con letras) o «2-4». Sin posición, va al primer lugar libre.","type":"string","maxLength":10}},"required":["box_id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples/{id}/status":{"post":{"tags":["Biobanco"],"summary":"Cambiar el estado de una muestra","description":"Disponible, en uso, agotada, descartada o transferida. Agotada, descartada y transferida liberan su lugar en la caja.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."}},"required":["status"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/biobank/samples/{id}/aliquots":{"post":{"tags":["Biobanco"],"summary":"Crear alícuotas","description":"Alícuotas de una muestra: mismo tipo, donante, fecha y campos personalizados, con código propio. Con una caja, van a sus primeras posiciones libres. Se crean todas juntas o ninguna.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"count":{"type":"integer","minimum":1,"maximum":50,"description":"Cuántas alícuotas crear."},"quantity":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Cantidad (volumen, masa…); null la borra."},"unit":{"type":"string","maxLength":20,"description":"Unidad de cada alícuota. Por defecto, la de la muestra de origen."},"box_id":{"description":"Caja donde van, en sus primeras posiciones libres; sin caja, quedan sin ubicar.","anyOf":[{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},{"type":"null"}]}},"required":["count"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["available","in_use","depleted","discarded","transferred"],"description":"available = disponible, in_use = en uso, depleted = agotada, discarded = descartada, transferred = transferida."},"donor_code":{"description":"Código pseudonimizado.","type":["string","null"]},"collection_date":{"description":"Fecha de extracción (AAAA-MM-DD).","type":["string","null"]},"quantity":{"type":["number","null"]},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"placement":{"anyOf":[{"type":"object","properties":{"box_id":{"type":"string","description":"Identificador (UUID)."},"box":{"type":"string","description":"Nombre de la caja."},"path":{"type":"string","description":"Ruta completa, por ejemplo «Sala 2 › Freezer -80 °C › Rack 3 › Caja 12»."},"position":{"type":"string","description":"Posición legible: «B4» (filas con letras) o «2-4»."},"row":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"col":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["box_id","box","path","position","row","col"],"additionalProperties":false},{"type":"null"}],"description":"Dónde está guardada; null si no está ubicada en una caja."},"parent":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":["string","null"]}},"required":["id","code"],"additionalProperties":false},{"type":"null"}],"description":"Muestra de origen, si es una alícuota o derivada."},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","type","status","donor_code","collection_date","quantity","unit","notes","placement","parent","custom_fields","created_at","updated_at","version"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/fields":{"get":{"tags":["Equipos"],"summary":"Campos del equipo","description":"Campos base y personalizados, con su tipo y opciones: dicen qué significa cada clave de custom_fields.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: los campos personalizados aparecen así en custom_fields."},"label":{"type":"string"},"type":{"type":"string","enum":["text","long_text","number","date","boolean","select","multiselect"]},"custom":{"type":"boolean","description":"true si lo definió el laboratorio."},"required":{"type":"boolean"},"unit":{"type":["string","null"]},"help":{"type":["string","null"]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Opciones vigentes, para las listas."},"archived":{"type":"boolean"}},"required":["key","label","type","custom","required","unit","help","options","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/types":{"get":{"tags":["Equipos"],"summary":"Tipos de equipo","description":"","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"description":{"type":"string"},"archived":{"type":"boolean"}},"required":["id","name","description","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/providers":{"get":{"tags":["Equipos"],"summary":"Proveedores","description":"Servicio técnico y calibraciones, con sus datos de contacto.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"contact_name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"archived":{"type":"boolean"}},"required":["id","name","contact_name","phone","email","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items":{"get":{"tags":["Equipos"],"summary":"Buscar equipos","description":"Los equipos vigentes que cumplen todos los filtros, paginados. Es la misma búsqueda que el listado de la web.","parameters":[{"name":"q","in":"query","required":false,"description":"Texto libre: código, nombre, marca, modelo, n.º de serie o de inventario.","schema":{"description":"Texto libre: código, nombre, marca, modelo, n.º de serie o de inventario.","type":"string","maxLength":200}},{"name":"type","in":"query","required":false,"description":"Tipos de equipo, por id o por nombre, separados por coma (cualquiera de ellos).","schema":{"description":"Tipos de equipo, por id o por nombre, separados por coma (cualquiera de ellos).","type":"string"}},{"name":"status","in":"query","required":false,"description":"Estados separados por coma: in_service, out_of_service, retired. Por defecto, todos.","schema":{"description":"Estados separados por coma: in_service, out_of_service, retired. Por defecto, todos.","type":"string"}},{"name":"room","in":"query","required":false,"description":"Salas separadas por coma (sin importar mayúsculas).","schema":{"description":"Salas separadas por coma (sin importar mayúsculas).","type":"string"}},{"name":"responsible","in":"query","required":false,"description":"Ids de las personas responsables, separados por coma.","schema":{"description":"Ids de las personas responsables, separados por coma.","type":"string"}},{"name":"attention","in":"query","required":false,"description":"due = con algo vencido o por vencer, overdue = con algo vencido, open_failure = con una falla abierta.","schema":{"description":"due = con algo vencido o por vencer, overdue = con algo vencido, open_failure = con una falla abierta.","type":"string","enum":["due","overdue","open_failure"]}},{"name":"sort","in":"query","required":false,"description":"Campo por el que ordenar; con «-» adelante, de mayor a menor. Ej.: «next_due», «-purchase_date», «name». Por defecto, «code».","schema":{"description":"Campo por el que ordenar; con «-» adelante, de mayor a menor. Ej.: «next_due», «-purchase_date», «name». Por defecto, «code».","type":"string","maxLength":80}},{"name":"page","in":"query","required":false,"description":"Página, desde 1.","schema":{"default":1,"description":"Página, desde 1.","type":"integer","minimum":1,"maximum":9007199254740991}},{"name":"limit","in":"query","required":false,"description":"Resultados por página (hasta 200).","schema":{"default":50,"description":"Resultados por página (hasta 200).","type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["in_service","out_of_service","retired"],"description":"in_service = en servicio, out_of_service = fuera de servicio, retired = dado de baja."},"status_reason":{"type":"string","description":"Por qué está fuera de servicio o dado de baja."},"brand":{"type":"string"},"model":{"type":"string"},"serial_number":{"type":"string"},"asset_number":{"type":"string","description":"N.º de inventario o de patrimonio."},"room":{"type":"string"},"place":{"type":"string"},"responsible":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"provider":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Servicio técnico habitual."},"purchase_date":{"type":["string","null"]},"warranty_until":{"type":["string","null"]},"next_due":{"anyOf":[{"type":"object","properties":{"due_on":{"type":["string","null"]},"status":{"type":"string","enum":["overdue","due_soon","ok","unscheduled"],"description":"overdue = vencida, due_soon = por vencer, ok = al día, unscheduled = sin fecha."}},"required":["due_on","status"],"additionalProperties":false},{"type":"null"}],"description":"La tarea del plan que vence primero y el peor estado del plan; null si no tiene plan."},"notes":{"type":"string"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","name","type","status","status_reason","brand","model","serial_number","asset_number","room","place","responsible","provider","purchase_date","warranty_until","next_due","notes","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","page","limit","total"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Equipos"],"summary":"Crear un equipo","description":"Da de alta un equipo con las mismas reglas que el formulario de la web. El código lo asigna el laboratorio (EQ-0001…). Tipo y servicio técnico van por id o por nombre.\n\nPide un token con rol Editor.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nombre del equipo."},"type":{"type":"string","minLength":1,"maxLength":120,"description":"Tipo de equipo, por id o por nombre (ver …/equipment/types)."},"status":{"description":"in_service = en servicio (por defecto), out_of_service = fuera de servicio, retired = dado de baja.","type":"string","enum":["in_service","out_of_service","retired"]},"status_reason":{"description":"Por qué está fuera de servicio o dado de baja: obligatorio en esos estados. En servicio no se guarda.","type":"string","maxLength":500},"brand":{"description":"Marca.","type":"string","maxLength":80},"model":{"description":"Modelo.","type":"string","maxLength":80},"serial_number":{"description":"N.º de serie.","type":"string","maxLength":80},"asset_number":{"description":"N.º de inventario o de patrimonio.","type":"string","maxLength":80},"room":{"description":"Sala.","type":"string","maxLength":80},"place":{"description":"Lugar dentro de la sala (mesada, estante…).","type":"string","maxLength":120},"responsible_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Miembro responsable: su id (el de responsible o person en las respuestas), su nombre o su email. null lo deja sin asignar."},{"type":"null"}]},"provider":{"description":"Servicio técnico habitual, por id o por nombre (ver …/equipment/providers). null lo quita.","anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"purchase_date":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Fecha de compra (AAAA-MM-DD)."},{"type":"null"}]},"warranty_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Garantía hasta (AAAA-MM-DD)."},{"type":"null"}]},"biobank_location_id":{"description":"Freezer, heladera o tanque del Biobanco que es este equipo. Si no mandás sala, toma la de esa ubicación.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"notes":{"description":"Notas.","type":"string","maxLength":2000},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["name","type"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["in_service","out_of_service","retired"],"description":"in_service = en servicio, out_of_service = fuera de servicio, retired = dado de baja."},"status_reason":{"type":"string","description":"Por qué está fuera de servicio o dado de baja."},"brand":{"type":"string"},"model":{"type":"string"},"serial_number":{"type":"string"},"asset_number":{"type":"string","description":"N.º de inventario o de patrimonio."},"room":{"type":"string"},"place":{"type":"string"},"responsible":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"provider":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Servicio técnico habitual."},"purchase_date":{"type":["string","null"]},"warranty_until":{"type":["string","null"]},"next_due":{"anyOf":[{"type":"object","properties":{"due_on":{"type":["string","null"]},"status":{"type":"string","enum":["overdue","due_soon","ok","unscheduled"],"description":"overdue = vencida, due_soon = por vencer, ok = al día, unscheduled = sin fecha."}},"required":["due_on","status"],"additionalProperties":false},{"type":"null"}],"description":"La tarea del plan que vence primero y el peor estado del plan; null si no tiene plan."},"notes":{"type":"string"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","name","type","status","status_reason","brand","model","serial_number","asset_number","room","place","responsible","provider","purchase_date","warranty_until","next_due","notes","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items/{id}":{"get":{"tags":["Equipos"],"summary":"Ver un equipo","description":"Por id o por código (por ejemplo EQ-0004), con su plan de mantenimiento, sus fallas abiertas y quién lo está usando.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["in_service","out_of_service","retired"],"description":"in_service = en servicio, out_of_service = fuera de servicio, retired = dado de baja."},"status_reason":{"type":"string","description":"Por qué está fuera de servicio o dado de baja."},"brand":{"type":"string"},"model":{"type":"string"},"serial_number":{"type":"string"},"asset_number":{"type":"string","description":"N.º de inventario o de patrimonio."},"room":{"type":"string"},"place":{"type":"string"},"responsible":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"provider":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Servicio técnico habitual."},"purchase_date":{"type":["string","null"]},"warranty_until":{"type":["string","null"]},"next_due":{"anyOf":[{"type":"object","properties":{"due_on":{"type":["string","null"]},"status":{"type":"string","enum":["overdue","due_soon","ok","unscheduled"],"description":"overdue = vencida, due_soon = por vencer, ok = al día, unscheduled = sin fecha."}},"required":["due_on","status"],"additionalProperties":false},{"type":"null"}],"description":"La tarea del plan que vence primero y el peor estado del plan; null si no tiene plan."},"notes":{"type":"string"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"biobank_location_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}],"description":"Freezer, heladera o tanque del Biobanco vinculado."},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other"],"description":"maintenance = mantenimiento, calibration = calibración, verification = verificación, cleaning = limpieza, other = otra."},"title":{"type":"string"},"every":{"type":"string","description":"Frecuencia legible: «Anual», «Cada 6 meses»."},"interval_value":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"interval_unit":{"type":"string","enum":["day","week","month","year"]},"performed_by":{"type":"string","enum":["internal","external"],"description":"internal = el laboratorio, external = el servicio técnico."},"last_done":{"type":["string","null"]},"due_on":{"type":["string","null"]},"due_status":{"type":"string","enum":["overdue","due_soon","ok","unscheduled"],"description":"overdue = vencida, due_soon = por vencer, ok = al día, unscheduled = sin fecha."}},"required":["id","kind","title","every","interval_value","interval_unit","performed_by","last_done","due_on","due_status"],"additionalProperties":false},"description":"Plan de mantenimiento vigente."},"open_failures":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other","failure","repair","note"],"description":"Qué se registró. Las fallas tienen severity y resolved_on."},"task_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"performed_on":{"type":"string","description":"Fecha (AAAA-MM-DD)."},"performed_by":{"description":"Quién lo hizo: un miembro, el servicio técnico o un técnico.","type":["string","null"]},"detail":{"type":"string"},"result":{"anyOf":[{"type":"string","enum":["pass","fail"]},{"type":"null"}],"description":"Calibraciones y verificaciones: pass = conforme, fail = no conforme."},"valid_until":{"type":["string","null"]},"severity":{"anyOf":[{"type":"string","enum":["high","low"]},{"type":"null"}],"description":"Fallas: high = impide usarlo, low = se puede seguir usando."},"resolved_on":{"type":["string","null"]},"resolution":{"type":["string","null"]},"cost":{"type":["number","null"]},"currency":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","kind","task_id","performed_on","performed_by","detail","result","valid_until","severity","resolved_on","resolution","cost","currency","created_at"],"additionalProperties":false}},"in_use_by":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Quién lo está usando ahora, si alguien."}},"required":["id","code","name","type","status","status_reason","brand","model","serial_number","asset_number","room","place","responsible","provider","purchase_date","warranty_until","next_due","notes","custom_fields","created_at","updated_at","version","biobank_location_id","tasks","open_failures","in_use_by"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"tags":["Equipos"],"summary":"Modificar un equipo","description":"Por id o por código. Cambia solo los campos que mandás (en custom_fields, solo las claves que mandás); el resto queda como estaba. Pide la «version» que devolvió la API: si alguien lo cambió después, responde 409.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nombre del equipo."},"type":{"type":"string","minLength":1,"maxLength":120,"description":"Tipo de equipo, por id o por nombre (ver …/equipment/types)."},"status":{"description":"in_service = en servicio (por defecto), out_of_service = fuera de servicio, retired = dado de baja.","type":"string","enum":["in_service","out_of_service","retired"]},"status_reason":{"description":"Por qué está fuera de servicio o dado de baja: obligatorio en esos estados. En servicio no se guarda.","type":"string","maxLength":500},"brand":{"description":"Marca.","type":"string","maxLength":80},"model":{"description":"Modelo.","type":"string","maxLength":80},"serial_number":{"description":"N.º de serie.","type":"string","maxLength":80},"asset_number":{"description":"N.º de inventario o de patrimonio.","type":"string","maxLength":80},"room":{"description":"Sala.","type":"string","maxLength":80},"place":{"description":"Lugar dentro de la sala (mesada, estante…).","type":"string","maxLength":120},"responsible_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":200,"description":"Miembro responsable: su id (el de responsible o person en las respuestas), su nombre o su email. null lo deja sin asignar."},{"type":"null"}]},"provider":{"description":"Servicio técnico habitual, por id o por nombre (ver …/equipment/providers). null lo quita.","anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"purchase_date":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Fecha de compra (AAAA-MM-DD)."},{"type":"null"}]},"warranty_until":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Garantía hasta (AAAA-MM-DD)."},{"type":"null"}]},"biobank_location_id":{"description":"Freezer, heladera o tanque del Biobanco que es este equipo. Si no mandás sala, toma la de esa ubicación.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"notes":{"description":"Notas.","type":"string","maxLength":2000},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["version"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"status":{"type":"string","enum":["in_service","out_of_service","retired"],"description":"in_service = en servicio, out_of_service = fuera de servicio, retired = dado de baja."},"status_reason":{"type":"string","description":"Por qué está fuera de servicio o dado de baja."},"brand":{"type":"string"},"model":{"type":"string"},"serial_number":{"type":"string"},"asset_number":{"type":"string","description":"N.º de inventario o de patrimonio."},"room":{"type":"string"},"place":{"type":"string"},"responsible":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"provider":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Servicio técnico habitual."},"purchase_date":{"type":["string","null"]},"warranty_until":{"type":["string","null"]},"next_due":{"anyOf":[{"type":"object","properties":{"due_on":{"type":["string","null"]},"status":{"type":"string","enum":["overdue","due_soon","ok","unscheduled"],"description":"overdue = vencida, due_soon = por vencer, ok = al día, unscheduled = sin fecha."}},"required":["due_on","status"],"additionalProperties":false},{"type":"null"}],"description":"La tarea del plan que vence primero y el peor estado del plan; null si no tiene plan."},"notes":{"type":"string"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Valores de los campos personalizados por clave. Las listas devuelven el texto de la opción."},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","name","type","status","status_reason","brand","model","serial_number","asset_number","room","place","responsible","provider","purchase_date","warranty_until","next_due","notes","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items/{id}/events":{"get":{"tags":["Equipos"],"summary":"Registro de un equipo","description":"Mantenimientos, calibraciones, verificaciones, limpiezas, fallas, reparaciones y notas, del más reciente al más viejo (hasta 500).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other","failure","repair","note"],"description":"Qué se registró. Las fallas tienen severity y resolved_on."},"task_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"performed_on":{"type":"string","description":"Fecha (AAAA-MM-DD)."},"performed_by":{"description":"Quién lo hizo: un miembro, el servicio técnico o un técnico.","type":["string","null"]},"detail":{"type":"string"},"result":{"anyOf":[{"type":"string","enum":["pass","fail"]},{"type":"null"}],"description":"Calibraciones y verificaciones: pass = conforme, fail = no conforme."},"valid_until":{"type":["string","null"]},"severity":{"anyOf":[{"type":"string","enum":["high","low"]},{"type":"null"}],"description":"Fallas: high = impide usarlo, low = se puede seguir usando."},"resolved_on":{"type":["string","null"]},"resolution":{"type":["string","null"]},"cost":{"type":["number","null"]},"currency":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","kind","task_id","performed_on","performed_by","detail","result","valid_until","severity","resolved_on","resolution","cost","currency","created_at"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Equipos"],"summary":"Registrar un mantenimiento","description":"Suma al registro del equipo un mantenimiento, calibración, verificación, limpieza, reparación, nota u otra tarea. Con task_id cuenta para el plan de mantenimiento: una calibración o verificación no conforme deja la tarea pendiente.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","repair","other","note"],"description":"maintenance = mantenimiento, calibration = calibración, verification = verificación, cleaning = limpieza, repair = reparación, other = otra tarea, note = nota. Las fallas van por …/failures."},"task_id":{"description":"Tarea del plan de mantenimiento que se cumplió (ver tasks en la ficha del equipo): así se recalcula su vencimiento.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"performed_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo se hizo (AAAA-MM-DD). Por defecto, hoy."},"performed_by_user_id":{"type":"string","minLength":1,"maxLength":200,"description":"Miembro que lo hizo: id, nombre o email. Por defecto, la persona del token, salvo que mandes provider o technician_name."},"provider":{"description":"Si lo hizo el servicio técnico: el proveedor, por id o por nombre (ver …/providers).","type":"string","maxLength":120},"technician_name":{"description":"Si lo hizo el servicio técnico: nombre del técnico.","type":"string","maxLength":120},"detail":{"description":"Qué se hizo, observaciones.","type":"string","maxLength":4000},"result":{"description":"Calibraciones y verificaciones (obligatorio): pass = conforme, fail = no conforme. No conforme deja la tarea pendiente.","type":"string","enum":["pass","fail"]},"valid_until":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Calibraciones y verificaciones: hasta cuándo vale (AAAA-MM-DD)."},"cost":{"description":"Costo, con hasta 2 decimales.","type":"number","minimum":0},"currency":{"description":"Moneda del costo (ARS, USD…). Por defecto, ARS.","type":"string","pattern":"^[A-Z]{3}$"},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["kind"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other","failure","repair","note"],"description":"Qué se registró. Las fallas tienen severity y resolved_on."},"task_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"performed_on":{"type":"string","description":"Fecha (AAAA-MM-DD)."},"performed_by":{"description":"Quién lo hizo: un miembro, el servicio técnico o un técnico.","type":["string","null"]},"detail":{"type":"string"},"result":{"anyOf":[{"type":"string","enum":["pass","fail"]},{"type":"null"}],"description":"Calibraciones y verificaciones: pass = conforme, fail = no conforme."},"valid_until":{"type":["string","null"]},"severity":{"anyOf":[{"type":"string","enum":["high","low"]},{"type":"null"}],"description":"Fallas: high = impide usarlo, low = se puede seguir usando."},"resolved_on":{"type":["string","null"]},"resolution":{"type":["string","null"]},"cost":{"type":["number","null"]},"currency":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","kind","task_id","performed_on","performed_by","detail","result","valid_until","severity","resolved_on","resolution","cost","currency","created_at"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items/{id}/history":{"get":{"tags":["Equipos"],"summary":"Historial de un equipo","description":"Quién cambió los datos del equipo, cuándo y por dónde, del más nuevo al más viejo.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"action":{"type":"string","enum":["insert","update","soft_delete","restore","delete","move","status_change"]},"actor":{"type":"string"},"via":{"description":"«vía API · <token>» si no se hizo desde la web.","type":["string","null"]},"changed_fields":{"type":"array","items":{"type":"string"}},"old":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"new":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]}},"required":["at","action","actor","via","changed_fields","old","new"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items/{id}/attachments":{"get":{"tags":["Equipos"],"summary":"Documentos de un equipo","description":"Manuales, certificados y otros archivos del equipo. Cada uno trae un enlace de descarga que vence a los 5 minutos.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"file_name":{"type":"string"},"mime_type":{"type":"string"},"size_bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_at":{"type":"string"},"uploaded_by":{"type":"string"},"download_url":{"description":"Enlace de descarga que vence a los 5 minutos.","type":["string","null"]}},"required":["id","file_name","mime_type","size_bytes","created_at","uploaded_by","download_url"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/due":{"get":{"tags":["Equipos"],"summary":"Vencimientos","description":"Las tareas del plan de mantenimiento de todos los equipos, por fecha de vencimiento. Por defecto, solo lo vencido y lo que está por vencer.","parameters":[{"name":"status","in":"query","required":false,"description":"Estados del vencimiento separados por coma: overdue, due_soon, ok, unscheduled. Por defecto, overdue y due_soon.","schema":{"description":"Estados del vencimiento separados por coma: overdue, due_soon, ok, unscheduled. Por defecto, overdue y due_soon.","type":"string"}},{"name":"until","in":"query","required":false,"description":"Solo lo que vence hasta esta fecha (AAAA-MM-DD).","schema":{"description":"Solo lo que vence hasta esta fecha (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other"],"description":"maintenance = mantenimiento, calibration = calibración, verification = verificación, cleaning = limpieza, other = otra."},"title":{"type":"string"},"every":{"type":"string","description":"Frecuencia legible: «Anual», «Cada 6 meses»."},"interval_value":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"interval_unit":{"type":"string","enum":["day","week","month","year"]},"performed_by":{"type":"string","enum":["internal","external"],"description":"internal = el laboratorio, external = el servicio técnico."},"last_done":{"type":["string","null"]},"due_on":{"type":["string","null"]},"due_status":{"type":"string","enum":["overdue","due_soon","ok","unscheduled"],"description":"overdue = vencida, due_soon = por vencer, ok = al día, unscheduled = sin fecha."},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false}},"required":["id","kind","title","every","interval_value","interval_unit","performed_by","last_done","due_on","due_status","equipment"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/failures":{"get":{"tags":["Equipos"],"summary":"Fallas abiertas","description":"Las fallas sin cerrar de todos los equipos, de la más vieja a la más nueva.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other","failure","repair","note"],"description":"Qué se registró. Las fallas tienen severity y resolved_on."},"task_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"performed_on":{"type":"string","description":"Fecha (AAAA-MM-DD)."},"performed_by":{"description":"Quién lo hizo: un miembro, el servicio técnico o un técnico.","type":["string","null"]},"detail":{"type":"string"},"result":{"anyOf":[{"type":"string","enum":["pass","fail"]},{"type":"null"}],"description":"Calibraciones y verificaciones: pass = conforme, fail = no conforme."},"valid_until":{"type":["string","null"]},"severity":{"anyOf":[{"type":"string","enum":["high","low"]},{"type":"null"}],"description":"Fallas: high = impide usarlo, low = se puede seguir usando."},"resolved_on":{"type":["string","null"]},"resolution":{"type":["string","null"]},"cost":{"type":["number","null"]},"currency":{"type":["string","null"]},"created_at":{"type":"string"},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false},"reported_by":{"type":["string","null"]}},"required":["id","kind","task_id","performed_on","performed_by","detail","result","valid_until","severity","resolved_on","resolution","cost","currency","created_at","equipment","reported_by"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/bookings":{"get":{"tags":["Equipos"],"summary":"Reservas","description":"Reservas pendientes y confirmadas que tocan el rango pedido, en orden. Las horas van en UTC; la zona del laboratorio está en /lab.","parameters":[{"name":"from","in":"query","required":false,"description":"Desde este día (AAAA-MM-DD, en la hora del laboratorio). Por defecto, hoy.","schema":{"description":"Desde este día (AAAA-MM-DD, en la hora del laboratorio). Por defecto, hoy.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Hasta este día inclusive. Por defecto, 7 días desde «from». Hasta 3 meses.","schema":{"description":"Hasta este día inclusive. Por defecto, 7 días desde «from». Hasta 3 meses.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"equipment","in":"query","required":false,"description":"Un equipo, por id o por código.","schema":{"description":"Un equipo, por id o por código.","type":"string","maxLength":80}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"starts_at":{"type":"string","description":"Instante ISO 8601 (UTC)."},"ends_at":{"type":"string"},"purpose":{"type":"string"},"status":{"type":"string","enum":["pending","confirmed","rejected","cancelled"],"description":"pending = pendiente de aprobación, confirmed = confirmada, rejected = rechazada, cancelled = cancelada."}},"required":["id","equipment","person","starts_at","ends_at","purpose","status"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Equipos"],"summary":"Reservar un equipo","description":"Reserva un horario, o el mismo horario varios días con repeat. Si el equipo pide aprobación, queda pendiente (pero nadie más puede tomar ese horario). En una serie, los días que no se pueden (chocan con otra reserva, fuera de horario…) vuelven en rejected y los demás se reservan.\n\nPide un token con rol Editor.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"equipment":{"type":"string","minLength":1,"maxLength":80,"description":"El equipo, por id o por código (EQ-0004)."},"starts_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Inicio: instante ISO 8601 con zona (2026-10-27T10:00:00-03:00 o …Z)."},"ends_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Fin, con el mismo formato."},"purpose":{"description":"Para qué se reserva.","type":"string","maxLength":300},"person_id":{"type":"string","minLength":1,"maxLength":200,"description":"Para reservar a nombre de otra persona (id, nombre o email; lo puede hacer quien es Editor o aprueba ese equipo). Por defecto, la persona del token."},"repeat":{"description":"Repetir el mismo horario (en la hora del laboratorio): weekly = cada semana, weekdays = de lunes a viernes, daily = todos los días. Hasta 60 reservas por serie.","type":"string","enum":["weekly","weekdays","daily"]},"repeat_until":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Con repeat: último día de la serie (AAAA-MM-DD), inclusive."}},"required":["equipment","starts_at","ends_at"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"created":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"starts_at":{"type":"string","description":"Instante ISO 8601 (UTC)."},"ends_at":{"type":"string"},"purpose":{"type":"string"},"status":{"type":"string","enum":["pending","confirmed","rejected","cancelled"],"description":"pending = pendiente de aprobación, confirmed = confirmada, rejected = rechazada, cancelled = cancelada."}},"required":["id","equipment","person","starts_at","ends_at","purpose","status"],"additionalProperties":false},"description":"Las reservas que se hicieron."},"rejected":{"type":"array","items":{"type":"object","properties":{"starts_at":{"type":"string"},"error":{"type":"string"}},"required":["starts_at","error"],"additionalProperties":false},"description":"Solo en las series: los días que no se pudieron reservar y por qué."}},"required":["created","rejected"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/usage":{"get":{"tags":["Equipos"],"summary":"Registro de uso","description":"Quién usó cada equipo, cuándo y cuánto tiempo, del más reciente al más viejo (hasta 1000).","parameters":[{"name":"from","in":"query","required":false,"description":"Usos que empezaron desde este día (AAAA-MM-DD). Por defecto, hace 30 días.","schema":{"description":"Usos que empezaron desde este día (AAAA-MM-DD). Por defecto, hace 30 días.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Hasta este día inclusive. Por defecto, hoy. Hasta 3 meses.","schema":{"description":"Hasta este día inclusive. Por defecto, hoy. Hasta 3 meses.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"equipment","in":"query","required":false,"description":"Un equipo, por id o por código.","schema":{"description":"Un equipo, por id o por código.","type":"string","maxLength":80}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"started_at":{"type":"string"},"ended_at":{"description":"null mientras se está usando.","type":["string","null"]},"minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"purpose":{"type":"string"},"notes":{"type":"string"},"outcome":{"anyOf":[{"type":"string","enum":["ok","problem"]},{"type":"null"}],"description":"ok = sin problemas, problem = con un problema (abre una falla)."}},"required":["id","equipment","person","started_at","ended_at","minutes","purpose","notes","outcome"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items/{id}/failures":{"post":{"tags":["Equipos"],"summary":"Reportar una falla","description":"Abre una falla del equipo. Con take_out_of_service queda fuera de servicio (no se puede usar ni reservar) hasta que se cierre.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"detail":{"type":"string","minLength":1,"maxLength":4000,"description":"Qué pasó con el equipo."},"severity":{"default":"high","description":"high = impide usarlo (por defecto), low = se puede seguir usando.","type":"string","enum":["high","low"]},"take_out_of_service":{"default":false,"description":"true deja el equipo fuera de servicio hasta que se cierre la falla.","type":"boolean"},"performed_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo pasó (AAAA-MM-DD). Por defecto, hoy."}},"required":["detail"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other","failure","repair","note"],"description":"Qué se registró. Las fallas tienen severity y resolved_on."},"task_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"performed_on":{"type":"string","description":"Fecha (AAAA-MM-DD)."},"performed_by":{"description":"Quién lo hizo: un miembro, el servicio técnico o un técnico.","type":["string","null"]},"detail":{"type":"string"},"result":{"anyOf":[{"type":"string","enum":["pass","fail"]},{"type":"null"}],"description":"Calibraciones y verificaciones: pass = conforme, fail = no conforme."},"valid_until":{"type":["string","null"]},"severity":{"anyOf":[{"type":"string","enum":["high","low"]},{"type":"null"}],"description":"Fallas: high = impide usarlo, low = se puede seguir usando."},"resolved_on":{"type":["string","null"]},"resolution":{"type":["string","null"]},"cost":{"type":["number","null"]},"currency":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","kind","task_id","performed_on","performed_by","detail","result","valid_until","severity","resolved_on","resolution","cost","currency","created_at"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/failures/{id}/resolve":{"post":{"tags":["Equipos"],"summary":"Cerrar una falla","description":"Cierra una falla abierta (ver …/equipment/failures). Con back_in_service el equipo vuelve a estar en servicio, salvo que tenga otras fallas abiertas.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"resolution":{"type":"string","minLength":1,"maxLength":4000,"description":"Cómo se resolvió."},"resolved_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo se resolvió (AAAA-MM-DD). Por defecto, hoy."},"back_in_service":{"default":false,"description":"true vuelve a poner el equipo en servicio, si no le quedan otras fallas abiertas.","type":"boolean"}},"required":["resolution"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["maintenance","calibration","verification","cleaning","other","failure","repair","note"],"description":"Qué se registró. Las fallas tienen severity y resolved_on."},"task_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}]},"performed_on":{"type":"string","description":"Fecha (AAAA-MM-DD)."},"performed_by":{"description":"Quién lo hizo: un miembro, el servicio técnico o un técnico.","type":["string","null"]},"detail":{"type":"string"},"result":{"anyOf":[{"type":"string","enum":["pass","fail"]},{"type":"null"}],"description":"Calibraciones y verificaciones: pass = conforme, fail = no conforme."},"valid_until":{"type":["string","null"]},"severity":{"anyOf":[{"type":"string","enum":["high","low"]},{"type":"null"}],"description":"Fallas: high = impide usarlo, low = se puede seguir usando."},"resolved_on":{"type":["string","null"]},"resolution":{"type":["string","null"]},"cost":{"type":["number","null"]},"currency":{"type":["string","null"]},"created_at":{"type":"string"},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["in_service","out_of_service","retired"],"description":"in_service = en servicio, out_of_service = fuera de servicio, retired = dado de baja."}},"required":["id","code","name","status"],"additionalProperties":false},"other_open_failures":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Otras fallas abiertas del equipo: si hay, no vuelve a estar en servicio."}},"required":["id","kind","task_id","performed_on","performed_by","detail","result","valid_until","severity","resolved_on","resolution","cost","currency","created_at","equipment","other_open_failures"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/bookings/{id}/cancel":{"post":{"tags":["Equipos"],"summary":"Cancelar una reserva","description":"Cancela una reserva pendiente o confirmada y libera el horario: la propia, o la de otra persona si el token es Editor o aprueba ese equipo. Con scope «following», también las siguientes de la misma serie.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reason":{"description":"Por qué se cancela (opcional).","type":"string","maxLength":500},"scope":{"default":"one","description":"one = solo esta reserva (por defecto); following = esta y las siguientes de la misma serie.","type":"string","enum":["one","following"]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"cancelled":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"starts_at":{"type":"string","description":"Instante ISO 8601 (UTC)."},"ends_at":{"type":"string"},"purpose":{"type":"string"},"status":{"type":"string","enum":["pending","confirmed","rejected","cancelled"],"description":"pending = pendiente de aprobación, confirmed = confirmada, rejected = rechazada, cancelled = cancelada."}},"required":["id","equipment","person","starts_at","ends_at","purpose","status"],"additionalProperties":false}}},"required":["cancelled"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/equipment/items/{id}/usage":{"post":{"tags":["Equipos"],"summary":"Registrar un uso","description":"Carga un uso que ya terminó, como «Registrar un uso» de la web. Si queda dentro de una reserva, se vincula solo. Con outcome «problem» también reporta la falla.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"started_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Cuándo empezó: instante ISO 8601 con zona (2026-10-24T09:00:00-03:00 o …Z)."},"ended_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Cuándo terminó, con el mismo formato."},"person_id":{"type":"string","minLength":1,"maxLength":200,"description":"Quién lo usó, si no es la persona del token: id, nombre o email (lo puede cargar quien es Editor)."},"purpose":{"description":"Para qué se usó.","type":"string","maxLength":300},"notes":{"description":"Observaciones del uso.","type":"string","maxLength":2000},"outcome":{"default":"ok","description":"ok = sin problemas (por defecto), problem = con un problema: abre una falla con failure_detail.","type":"string","enum":["ok","problem"]},"failure_detail":{"description":"Con outcome problem (obligatorio): qué pasó con el equipo.","type":"string","maxLength":4000},"severity":{"default":"high","description":"high = impide usarlo (por defecto), low = se puede seguir usando.","type":"string","enum":["high","low"]},"take_out_of_service":{"default":false,"description":"true deja el equipo fuera de servicio hasta que se cierre la falla.","type":"boolean"},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["started_at","ended_at"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"started_at":{"type":"string"},"ended_at":{"description":"null mientras se está usando.","type":["string","null"]},"minutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"purpose":{"type":"string"},"notes":{"type":"string"},"outcome":{"anyOf":[{"type":"string","enum":["ok","problem"]},{"type":"null"}],"description":"ok = sin problemas, problem = con un problema (abre una falla)."},"failure_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}],"description":"La falla que se abrió por el problema, si hubo uno."}},"required":["id","equipment","person","started_at","ended_at","minutes","purpose","notes","outcome","failure_id"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/fields":{"get":{"tags":["Insumos"],"summary":"Campos del artículo","description":"Campos base y personalizados de los artículos, con su tipo y opciones: dicen qué significa cada clave de custom_fields. En lot_custom_fields, los campos personalizados de los lotes.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: los campos personalizados aparecen así en custom_fields."},"label":{"type":"string"},"type":{"type":"string","enum":["text","long_text","number","date","boolean","select","multiselect"]},"custom":{"type":"boolean","description":"true si lo definió el laboratorio."},"required":{"type":"boolean"},"unit":{"type":["string","null"]},"help":{"type":["string","null"]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Opciones vigentes, para las listas."},"archived":{"type":"boolean"}},"required":["key","label","type","custom","required","unit","help","options","archived"],"additionalProperties":false}},"lot_custom_fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Clave estable: los campos personalizados aparecen así en custom_fields."},"label":{"type":"string"},"type":{"type":"string","enum":["text","long_text","number","date","boolean","select","multiselect"]},"custom":{"type":"boolean","description":"true si lo definió el laboratorio."},"required":{"type":"boolean"},"unit":{"type":["string","null"]},"help":{"type":["string","null"]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Opciones vigentes, para las listas."},"archived":{"type":"boolean"}},"required":["key","label","type","custom","required","unit","help","options","archived"],"additionalProperties":false}}},"required":["data","lot_custom_fields"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/categories":{"get":{"tags":["Insumos"],"summary":"Categorías de insumos","description":"","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"description":{"type":"string"},"archived":{"type":"boolean"}},"required":["id","name","description","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/places":{"get":{"tags":["Insumos"],"summary":"Lugares de guardado","description":"Dónde se guardan los insumos, como un árbol (sala › heladera › estante), con su conservación y el equipo vinculado.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"path":{"type":"string","description":"El camino desde la sala: «Laboratorio › Heladera › Estante 2»."},"parent_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}],"description":"El lugar que lo contiene."},"kind":{"type":"string","description":"room = sala, cabinet = armario, fridge = heladera, freezer, shelf = estante, drawer = cajón, box = caja, other = otro."},"storage":{"type":"string","enum":["room_temp","fridge","freezer_20","freezer_80","ln2","other"],"description":"room_temp = temperatura ambiente, fridge = heladera (2 a 8 °C), freezer_20 = freezer −20 °C, freezer_80 = freezer −80 °C, ln2 = nitrógeno líquido, other = otra condición."},"equipment_item_id":{"anyOf":[{"type":"string","description":"Identificador (UUID)."},{"type":"null"}],"description":"El equipo de Equipos que es este lugar (una heladera, un freezer)."},"notes":{"type":"string"},"archived":{"type":"boolean"}},"required":["id","name","path","parent_id","kind","storage","equipment_item_id","notes","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/suppliers":{"get":{"tags":["Insumos"],"summary":"Proveedores de insumos","description":"Con sus datos de contacto y el n.º de cliente del laboratorio.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"contact_name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"website":{"type":"string"},"customer_number":{"type":"string","description":"El n.º de cliente del laboratorio en el proveedor."},"notes":{"type":"string"},"archived":{"type":"boolean"}},"required":["id","name","contact_name","phone","email","website","customer_number","notes","archived"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/items":{"get":{"tags":["Insumos"],"summary":"Buscar insumos","description":"Los artículos vigentes que cumplen todos los filtros, paginados, con su stock. Es la misma búsqueda que el inventario de la web.","parameters":[{"name":"q","in":"query","required":false,"description":"Texto libre: código, nombre, marca, n.º de catálogo, n.º CAS, o el código o n.º de un lote.","schema":{"description":"Texto libre: código, nombre, marca, n.º de catálogo, n.º CAS, o el código o n.º de un lote.","type":"string","maxLength":200}},{"name":"category","in":"query","required":false,"description":"Categorías, por id o por nombre, separadas por coma (cualquiera de ellas).","schema":{"description":"Categorías, por id o por nombre, separadas por coma (cualquiera de ellas).","type":"string"}},{"name":"place","in":"query","required":false,"description":"Lugares, por id, nombre o camino (Sala › Heladera), separados por coma: el lugar habitual o donde hay un lote con stock, incluido lo que tiene adentro.","schema":{"description":"Lugares, por id, nombre o camino (Sala › Heladera), separados por coma: el lugar habitual o donde hay un lote con stock, incluido lo que tiene adentro.","type":"string"}},{"name":"supplier","in":"query","required":false,"description":"Proveedores habituales, por id o por nombre, separados por coma.","schema":{"description":"Proveedores habituales, por id o por nombre, separados por coma.","type":"string"}},{"name":"storage","in":"query","required":false,"description":"Conservación, separadas por coma: room_temp, fridge, freezer_20, freezer_80, ln2, other.","schema":{"description":"Conservación, separadas por coma: room_temp, fridge, freezer_20, freezer_80, ln2, other.","type":"string"}},{"name":"attention","in":"query","required":false,"description":"any = vencido, por vencer o para pedir; reorder = en el punto de pedido; due = con lotes vencidos o por vencer; expired = con lotes vencidos; out = sin stock.","schema":{"description":"any = vencido, por vencer o para pedir; reorder = en el punto de pedido; due = con lotes vencidos o por vencer; expired = con lotes vencidos; out = sin stock.","type":"string","enum":["any","reorder","due","expired","out"]}},{"name":"controlled","in":"query","required":false,"description":"yes = solo las sustancias controladas, no = sin ellas.","schema":{"description":"yes = solo las sustancias controladas, no = sin ellas.","type":"string","enum":["yes","no"]}},{"name":"archived","in":"query","required":false,"description":"include = también los archivados, only = solo los archivados. Por defecto, sin archivados.","schema":{"description":"include = también los archivados, only = solo los archivados. Por defecto, sin archivados.","type":"string","enum":["include","only"]}},{"name":"sort","in":"query","required":false,"description":"Campo por el que ordenar; con «-» adelante, de mayor a menor. Ej.: «name», «stock», «next_expiry», «-created_at». Por defecto, «name».","schema":{"description":"Campo por el que ordenar; con «-» adelante, de mayor a menor. Ej.: «name», «stock», «next_expiry», «-created_at». Por defecto, «name».","type":"string","maxLength":80}},{"name":"page","in":"query","required":false,"description":"Página, desde 1.","schema":{"default":1,"description":"Página, desde 1.","type":"integer","minimum":1,"maximum":9007199254740991}},{"name":"limit","in":"query","required":false,"description":"Cuántos resultados (hasta 200).","schema":{"default":50,"description":"Cuántos resultados (hasta 200).","type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}]},"presentation":{"type":"string"},"brand":{"type":"string"},"catalog_number":{"type":"string"},"cas_number":{"type":"string"},"unit":{"type":"string","description":"En qué se cuenta: frasco, caja, mL…"},"reorder_point":{"type":["number","null"]},"target_stock":{"type":["number","null"]},"storage":{"type":"string","enum":["room_temp","fridge","freezer_20","freezer_80","ln2","other"],"description":"room_temp = temperatura ambiente, fridge = heladera (2 a 8 °C), freezer_20 = freezer −20 °C, freezer_80 = freezer −80 °C, ln2 = nitrógeno líquido, other = otra condición."},"storage_notes":{"type":"string"},"default_place":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"path":{"type":"string"}},"required":["id","name","path"],"additionalProperties":false},{"type":"null"}],"description":"Lugar habitual."},"supplier":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Proveedor habitual."},"hazards":{"type":"array","items":{"type":"string"},"description":"Pictogramas del SGA (GHS01 a GHS09)."},"controlled":{"type":"boolean","description":"Sustancia controlada: cada movimiento pide un motivo."},"opened_shelf_life_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Vida útil una vez abierto, en días."},"warn_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Con cuántos días de anticipación avisa un vencimiento."},"responsible":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"notes":{"type":"string"},"stock":{"type":"object","properties":{"available":{"type":"number","description":"Lo que hay, sin contar los lotes vencidos."},"expired":{"type":"number","description":"Lo que hay en lotes vencidos."},"lots":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Lotes con stock."},"next_expiry":{"description":"El vencimiento más cercano de un lote con stock.","type":["string","null"]},"expiry_status":{"anyOf":[{"type":"string","enum":["expired","due_soon","ok"]},{"type":"null"}],"description":"El peor estado de sus lotes con stock; null si no tiene."},"below_reorder":{"type":"boolean","description":"true si llegó al punto de pedido (aparece en «Para pedir»)."}},"required":["available","expired","lots","next_expiry","expiry_status","below_reorder"],"additionalProperties":false,"description":"Stock calculado de los lotes y sus movimientos, en la unidad del artículo."},"on_order":{"anyOf":[{"type":"object","properties":{"order_id":{"type":"string","description":"Identificador (UUID)."},"status":{"type":"string","enum":["requested","ordered"]},"pending":{"type":"number","description":"Lo que falta llegar."}},"required":["order_id","status","pending"],"additionalProperties":false},{"type":"null"}],"description":"El pedido abierto del artículo; null si no hay."},"archived":{"type":"boolean"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","name","category","presentation","brand","catalog_number","cas_number","unit","reorder_point","target_stock","storage","storage_notes","default_place","supplier","hazards","controlled","opened_shelf_life_days","warn_days","responsible","notes","stock","on_order","archived","custom_fields","created_at","updated_at","version"],"additionalProperties":false}},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","page","limit","total"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Insumos"],"summary":"Cargar un insumo","description":"Da de alta un artículo del inventario. El código lo genera Vialo con el formato del laboratorio. El stock se carga después, con un ingreso (POST …/reagents/lots).\n\nPide un token con rol Editor.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nombre del artículo."},"unit":{"type":"string","minLength":1,"maxLength":30,"description":"En qué se cuenta el stock: unidad, frasco, caja, kit, determinaciones, mL, g…"},"category":{"description":"Categoría, por id o por nombre (ver …/reagents/categories). null la quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"presentation":{"type":"string","maxLength":80,"description":"Presentación: «frasco de 1 L», «caja x 96»…"},"brand":{"type":"string","maxLength":80,"description":"Marca."},"catalog_number":{"type":"string","maxLength":80,"description":"N.º de catálogo del fabricante."},"cas_number":{"type":"string","maxLength":20,"description":"N.º CAS con guiones, como 64-17-5 (se verifica el dígito de control)."},"reorder_point":{"description":"Punto de pedido: con ese stock o menos, el artículo aparece en «Para pedir». null lo quita.","anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"target_stock":{"description":"Stock objetivo: hasta cuánto conviene pedir. Igual o mayor que el punto de pedido.","anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"storage":{"description":"Conservación: room_temp, fridge, freezer_20, freezer_80, ln2, other. Por defecto, room_temp.","type":"string","enum":["room_temp","fridge","freezer_20","freezer_80","ln2","other"]},"storage_notes":{"type":"string","maxLength":300,"description":"Indicaciones de conservación."},"default_place":{"description":"Lugar habitual, por id, nombre o camino (Sala › Heladera; ver …/reagents/places). null lo quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"supplier":{"description":"Proveedor habitual, por id o por nombre (ver …/reagents/suppliers). null lo quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"hazards":{"description":"Pictogramas del SGA: GHS01 a GHS09.","type":"array","items":{"type":"string","enum":["GHS01","GHS02","GHS03","GHS04","GHS05","GHS06","GHS07","GHS08","GHS09"]}},"controlled":{"description":"Sustancia controlada (precursor químico): cada movimiento va a pedir un motivo.","type":"boolean"},"opened_shelf_life_days":{"description":"Vida útil una vez abierto, en días (1 a 3650). null: no cambia al abrirlo.","anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"warn_days":{"description":"Con cuántos días de anticipación avisa un vencimiento (0 a 365). Por defecto, 30.","anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"responsible_id":{"description":"Quien se ocupa de pedirlo: el id de un miembro (ver /lab). null lo quita.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"notes":{"type":"string","maxLength":2000,"description":"Notas."},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["name","unit"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"archived":{"type":"boolean"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","name","archived","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/items/{id}":{"get":{"tags":["Insumos"],"summary":"Ver un insumo","description":"Por id o por código (por ejemplo RE-0004), con su stock, sus lotes y su pedido abierto.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}]},"presentation":{"type":"string"},"brand":{"type":"string"},"catalog_number":{"type":"string"},"cas_number":{"type":"string"},"unit":{"type":"string","description":"En qué se cuenta: frasco, caja, mL…"},"reorder_point":{"type":["number","null"]},"target_stock":{"type":["number","null"]},"storage":{"type":"string","enum":["room_temp","fridge","freezer_20","freezer_80","ln2","other"],"description":"room_temp = temperatura ambiente, fridge = heladera (2 a 8 °C), freezer_20 = freezer −20 °C, freezer_80 = freezer −80 °C, ln2 = nitrógeno líquido, other = otra condición."},"storage_notes":{"type":"string"},"default_place":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"path":{"type":"string"}},"required":["id","name","path"],"additionalProperties":false},{"type":"null"}],"description":"Lugar habitual."},"supplier":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Proveedor habitual."},"hazards":{"type":"array","items":{"type":"string"},"description":"Pictogramas del SGA (GHS01 a GHS09)."},"controlled":{"type":"boolean","description":"Sustancia controlada: cada movimiento pide un motivo."},"opened_shelf_life_days":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Vida útil una vez abierto, en días."},"warn_days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Con cuántos días de anticipación avisa un vencimiento."},"responsible":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"notes":{"type":"string"},"stock":{"type":"object","properties":{"available":{"type":"number","description":"Lo que hay, sin contar los lotes vencidos."},"expired":{"type":"number","description":"Lo que hay en lotes vencidos."},"lots":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Lotes con stock."},"next_expiry":{"description":"El vencimiento más cercano de un lote con stock.","type":["string","null"]},"expiry_status":{"anyOf":[{"type":"string","enum":["expired","due_soon","ok"]},{"type":"null"}],"description":"El peor estado de sus lotes con stock; null si no tiene."},"below_reorder":{"type":"boolean","description":"true si llegó al punto de pedido (aparece en «Para pedir»)."}},"required":["available","expired","lots","next_expiry","expiry_status","below_reorder"],"additionalProperties":false,"description":"Stock calculado de los lotes y sus movimientos, en la unidad del artículo."},"on_order":{"anyOf":[{"type":"object","properties":{"order_id":{"type":"string","description":"Identificador (UUID)."},"status":{"type":"string","enum":["requested","ordered"]},"pending":{"type":"number","description":"Lo que falta llegar."}},"required":["order_id","status","pending"],"additionalProperties":false},{"type":"null"}],"description":"El pedido abierto del artículo; null si no hay."},"archived":{"type":"boolean"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"created_at":{"type":"string"},"updated_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lots":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string","description":"Código interno del lote (el de la etiqueta)."},"lot_number":{"type":"string","description":"N.º de lote del fabricante."},"quantity":{"type":"number","description":"Lo que queda."},"initial_quantity":{"type":"number"},"expires_on":{"description":"Vencimiento del fabricante.","type":["string","null"]},"effective_expiry":{"description":"El vencimiento que vale: el del fabricante o, si se abrió, el de su vida útil abierto.","type":["string","null"]},"status":{"type":"string","enum":["expired","due_soon","ok","depleted"],"description":"expired = vencido, due_soon = por vencer, ok = al día, depleted = agotado."},"received_on":{"type":"string"},"opened_on":{"type":["string","null"]},"place":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"path":{"type":"string"}},"required":["id","name","path"],"additionalProperties":false},{"type":"null"}]},"place_detail":{"type":"string"},"supplier":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}]},"unit_cost":{"type":["number","null"]},"currency":{"type":"string"},"notes":{"type":"string"},"custom_fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"created_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","lot_number","quantity","initial_quantity","expires_on","effective_expiry","status","received_on","opened_on","place","place_detail","supplier","unit_cost","currency","notes","custom_fields","created_at","version"],"additionalProperties":false},"description":"Primero los que tienen stock, el que vence primero arriba (el que conviene usar); al final los agotados."}},"required":["id","code","name","category","presentation","brand","catalog_number","cas_number","unit","reorder_point","target_stock","storage","storage_notes","default_place","supplier","hazards","controlled","opened_shelf_life_days","warn_days","responsible","notes","stock","on_order","archived","custom_fields","created_at","updated_at","version","lots"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"tags":["Insumos"],"summary":"Modificar un insumo","description":"Cambia solo los campos que se mandan; el resto queda como estaba. Pide la «version» que devolvió la lectura: si alguien lo cambió después, responde 409.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nombre del artículo."},"unit":{"type":"string","minLength":1,"maxLength":30,"description":"En qué se cuenta el stock: unidad, frasco, caja, kit, determinaciones, mL, g…"},"archived":{"description":"true lo archiva (sale del inventario y de «Para pedir», su historia se conserva); false lo vuelve al inventario.","type":"boolean"},"category":{"description":"Categoría, por id o por nombre (ver …/reagents/categories). null la quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"presentation":{"type":"string","maxLength":80,"description":"Presentación: «frasco de 1 L», «caja x 96»…"},"brand":{"type":"string","maxLength":80,"description":"Marca."},"catalog_number":{"type":"string","maxLength":80,"description":"N.º de catálogo del fabricante."},"cas_number":{"type":"string","maxLength":20,"description":"N.º CAS con guiones, como 64-17-5 (se verifica el dígito de control)."},"reorder_point":{"description":"Punto de pedido: con ese stock o menos, el artículo aparece en «Para pedir». null lo quita.","anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"target_stock":{"description":"Stock objetivo: hasta cuánto conviene pedir. Igual o mayor que el punto de pedido.","anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"storage":{"description":"Conservación: room_temp, fridge, freezer_20, freezer_80, ln2, other. Por defecto, room_temp.","type":"string","enum":["room_temp","fridge","freezer_20","freezer_80","ln2","other"]},"storage_notes":{"type":"string","maxLength":300,"description":"Indicaciones de conservación."},"default_place":{"description":"Lugar habitual, por id, nombre o camino (Sala › Heladera; ver …/reagents/places). null lo quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"supplier":{"description":"Proveedor habitual, por id o por nombre (ver …/reagents/suppliers). null lo quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"hazards":{"description":"Pictogramas del SGA: GHS01 a GHS09.","type":"array","items":{"type":"string","enum":["GHS01","GHS02","GHS03","GHS04","GHS05","GHS06","GHS07","GHS08","GHS09"]}},"controlled":{"description":"Sustancia controlada (precursor químico): cada movimiento va a pedir un motivo.","type":"boolean"},"opened_shelf_life_days":{"description":"Vida útil una vez abierto, en días (1 a 3650). null: no cambia al abrirlo.","anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"warn_days":{"description":"Con cuántos días de anticipación avisa un vencimiento (0 a 365). Por defecto, 30.","anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"responsible_id":{"description":"Quien se ocupa de pedirlo: el id de un miembro (ver /lab). null lo quita.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"notes":{"type":"string","maxLength":2000,"description":"Notas."},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["version"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"archived":{"type":"boolean"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","code","name","archived","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/items/{id}/movements":{"get":{"tags":["Insumos"],"summary":"Movimientos de un insumo","description":"Ingresos, consumos, ajustes, descartes y traslados de todos sus lotes, del más reciente al más viejo. Los anulados vienen con voided = true y no cuentan para el stock.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Cuántos resultados (hasta 500).","schema":{"default":100,"description":"Cuántos resultados (hasta 500).","type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"}},"required":["id","code"],"additionalProperties":false},"kind":{"type":"string","enum":["receipt","consumption","adjustment","discard","transfer_out","transfer_in"],"description":"receipt = ingreso, consumption = consumo, adjustment = ajuste, discard = descarte, transfer_out / transfer_in = traslado (salida y entrada)."},"delta":{"type":"number","description":"Cuánto cambió el stock del lote: positivo si entró, negativo si salió."},"occurred_on":{"type":"string"},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"purpose":{"type":"string","description":"Para qué se usó."},"reason":{"type":"string","description":"Motivo (descartes, ajustes y sustancias controladas)."},"used_expired":{"type":"boolean","description":"Se usó un lote vencido."},"voided":{"type":"boolean"},"voided_at":{"type":["string","null"]},"void_reason":{"type":"string"},"created_at":{"type":"string"}},"required":["id","item","lot","kind","delta","occurred_on","person","purpose","reason","used_expired","voided","voided_at","void_reason","created_at"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/items/{id}/history":{"get":{"tags":["Insumos"],"summary":"Historial de un insumo","description":"Quién cambió los datos del artículo, cuándo y por dónde, del más nuevo al más viejo.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"action":{"type":"string","enum":["insert","update","soft_delete","restore","delete","move","status_change"]},"actor":{"type":"string"},"via":{"description":"«vía API · <token>» si no se hizo desde la web.","type":["string","null"]},"changed_fields":{"type":"array","items":{"type":"string"}},"old":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"new":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]}},"required":["at","action","actor","via","changed_fields","old","new"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/items/{id}/attachments":{"get":{"tags":["Insumos"],"summary":"Documentos de un insumo","description":"Fichas de seguridad, certificados de análisis y otros archivos del artículo. Cada uno trae un enlace de descarga que vence a los 5 minutos.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"file_name":{"type":"string"},"mime_type":{"type":"string"},"size_bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_at":{"type":"string"},"uploaded_by":{"type":"string"},"download_url":{"description":"Enlace de descarga que vence a los 5 minutos.","type":["string","null"]}},"required":["id","file_name","mime_type","size_bytes","created_at","uploaded_by","download_url"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/lots":{"get":{"tags":["Insumos"],"summary":"Vencimientos","description":"Lotes con stock de los artículos vigentes, del que vence primero al último. Por defecto, solo los vencidos y los que están por vencer.","parameters":[{"name":"status","in":"query","required":false,"description":"Estados separados por coma: expired, due_soon, ok. Por defecto, expired y due_soon.","schema":{"description":"Estados separados por coma: expired, due_soon, ok. Por defecto, expired y due_soon.","type":"string"}},{"name":"q","in":"query","required":false,"description":"Código interno o n.º del lote, o el nombre, código o n.º de catálogo del artículo.","schema":{"description":"Código interno o n.º del lote, o el nombre, código o n.º de catálogo del artículo.","type":"string","maxLength":200}},{"name":"until","in":"query","required":false,"description":"Solo lo que vence hasta esta fecha (AAAA-MM-DD).","schema":{"description":"Solo lo que vence hasta esta fecha (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"item","in":"query","required":false,"description":"Un artículo, por id o por código.","schema":{"description":"Un artículo, por id o por código.","type":"string","maxLength":80}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"lot_number":{"type":"string"},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"},"controlled":{"type":"boolean"}},"required":["id","code","name","unit","controlled"],"additionalProperties":false},"quantity":{"type":"number"},"expires_on":{"description":"El vencimiento que vale.","type":["string","null"]},"status":{"type":"string","enum":["expired","due_soon","ok","depleted"],"description":"expired = vencido, due_soon = por vencer, ok = al día, depleted = agotado."},"days_left":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Días hasta el vencimiento; negativo si ya venció."},"place":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"path":{"type":"string"}},"required":["id","name","path"],"additionalProperties":false},{"type":"null"}]},"place_detail":{"type":"string"}},"required":["id","code","lot_number","item","quantity","expires_on","status","days_left","place","place_detail"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Insumos"],"summary":"Ingresar stock","description":"Registra lo que llegó: un lote o, con containers, uno por envase (la cantidad se reparte en partes iguales). Vialo genera el código interno de cada lote y el movimiento de ingreso. Con order_id, cuenta para ese pedido.\n\nPide un token con rol Editor.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"item":{"type":"string","minLength":1,"maxLength":80,"description":"El artículo que llegó, por id o por código (RE-0004)."},"quantity":{"type":"number","exclusiveMinimum":0,"description":"Cuánto llegó en total, en la unidad del artículo. Con containers, se reparte en partes iguales."},"containers":{"description":"Un lote por envase, para seguir cada frasco por separado (se abren en fechas distintas): llegaron 3 frascos → 3. Por defecto, 1.","type":"integer","minimum":1,"maximum":50},"lot_number":{"type":"string","maxLength":80,"description":"N.º de lote del fabricante."},"expires_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Vencimiento (AAAA-MM-DD)."},{"type":"null"}]},"received_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo llegó (AAAA-MM-DD). Por defecto, hoy."},"place":{"description":"Dónde se guardó, por id, nombre o camino (Sala › Heladera). Por defecto, el lugar habitual del artículo; null lo deja sin lugar.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"place_detail":{"type":"string","maxLength":120,"description":"Detalle del lugar: estante, caja, posición…"},"supplier":{"description":"Proveedor, por id o por nombre. Por defecto, el del pedido o el habitual del artículo; null lo deja sin proveedor.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"unit_cost":{"description":"Costo por unidad del artículo.","anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"currency":{"description":"Moneda del costo (ISO 4217). Por defecto, ARS.","type":"string","pattern":"^[A-Z]{3}$"},"order_id":{"description":"El pedido abierto que se recibe (ver …/reagents/orders). Cuando llega todo, el pedido queda recibido.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"notes":{"type":"string","maxLength":1000,"description":"Notas."},"custom_fields":{"description":"Campos personalizados por clave (ver …/fields). Las listas aceptan el texto de la opción. null borra el valor; las claves que no se mandan quedan como estaban.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["item","quantity"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lots":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"quantity":{"type":"number"}},"required":["id","code","quantity"],"additionalProperties":false}},"stock":{"type":"object","properties":{"available":{"type":"number"}},"required":["available"],"additionalProperties":false}},"required":["item","lots","stock"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/movements":{"get":{"tags":["Insumos"],"summary":"Libro de movimientos","description":"Todo lo que entró y salió en el laboratorio, del más reciente al más viejo (hasta 300, como la pantalla: filtrá por fechas para ver más atrás). Con controlled = true es el libro de las sustancias controladas.","parameters":[{"name":"item","in":"query","required":false,"description":"Un artículo, por id o por código.","schema":{"description":"Un artículo, por id o por código.","type":"string","maxLength":80}},{"name":"kind","in":"query","required":false,"description":"Un tipo de movimiento: receipt, consumption, adjustment, discard, transfer_out, transfer_in.","schema":{"description":"Un tipo de movimiento: receipt, consumption, adjustment, discard, transfer_out, transfer_in.","type":"string","enum":["receipt","consumption","adjustment","discard","transfer_out","transfer_in"]}},{"name":"person","in":"query","required":false,"description":"Quién lo registró, por id.","schema":{"description":"Quién lo registró, por id.","type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"}},{"name":"from","in":"query","required":false,"description":"Desde este día (AAAA-MM-DD).","schema":{"description":"Desde este día (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Hasta este día inclusive (AAAA-MM-DD).","schema":{"description":"Hasta este día inclusive (AAAA-MM-DD).","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"controlled","in":"query","required":false,"description":"true = solo los de sustancias controladas (el libro de precursores).","schema":{"description":"true = solo los de sustancias controladas (el libro de precursores).","type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"description":"Cuántos resultados (hasta 300).","schema":{"default":300,"description":"Cuántos resultados (hasta 300).","type":"integer","minimum":1,"maximum":300}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"}},"required":["id","code"],"additionalProperties":false},"kind":{"type":"string","enum":["receipt","consumption","adjustment","discard","transfer_out","transfer_in"],"description":"receipt = ingreso, consumption = consumo, adjustment = ajuste, discard = descarte, transfer_out / transfer_in = traslado (salida y entrada)."},"delta":{"type":"number","description":"Cuánto cambió el stock del lote: positivo si entró, negativo si salió."},"occurred_on":{"type":"string"},"person":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"purpose":{"type":"string","description":"Para qué se usó."},"reason":{"type":"string","description":"Motivo (descartes, ajustes y sustancias controladas)."},"used_expired":{"type":"boolean","description":"Se usó un lote vencido."},"voided":{"type":"boolean"},"voided_at":{"type":["string","null"]},"void_reason":{"type":"string"},"created_at":{"type":"string"}},"required":["id","item","lot","kind","delta","occurred_on","person","purpose","reason","used_expired","voided","voided_at","void_reason","created_at"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/orders":{"get":{"tags":["Insumos"],"summary":"Pedidos","description":"Los pedidos abiertos (por pedir y pedidos al proveedor, del más viejo al más nuevo) o los cerrados (del más reciente al más viejo, hasta 200), con lo que ya llegó de cada uno.","parameters":[{"name":"open","in":"query","required":false,"description":"true = los abiertos (por pedir y pedidos), false = los cerrados (recibidos y cancelados). Por defecto, true.","schema":{"default":"true","description":"true = los abiertos (por pedir y pedidos), false = los cerrados (recibidos y cancelados). Por defecto, true.","type":"string","enum":["true","false"]}},{"name":"item","in":"query","required":false,"description":"Un artículo, por id o por código.","schema":{"description":"Un artículo, por id o por código.","type":"string","maxLength":80}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"quantity":{"type":"number"},"received":{"type":"number","description":"Lo que ya se ingresó contra este pedido."},"pending":{"type":"number","description":"Lo que falta llegar."},"supplier":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}]},"status":{"type":"string","enum":["requested","ordered","received","cancelled"],"description":"requested = por pedir (alguien avisó que falta), ordered = pedido al proveedor, received = recibido, cancelled = cancelado."},"requested_by":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Miembro del laboratorio."},"requested_on":{"type":"string"},"ordered_on":{"type":["string","null"]},"expected_on":{"type":["string","null"]},"reference":{"type":"string","description":"N.º de pedido u orden de compra."},"notes":{"type":"string"},"closed_on":{"type":["string","null"]},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","item","quantity","received","pending","supplier","status","requested_by","requested_on","ordered_on","expected_on","reference","notes","closed_on","version"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Insumos"],"summary":"Pedir un insumo","description":"Sin ordered, deja el artículo «por pedir» para quien hace las compras (si ya tiene un pedido abierto, devuelve ese con already_open = true). Con ordered = true, registra un pedido ya hecho al proveedor.\n\nPide un token con rol Editor.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"item":{"type":"string","minLength":1,"maxLength":80,"description":"El artículo a pedir, por id o por código."},"quantity":{"type":"number","exclusiveMinimum":0,"description":"Cuánto pedir, en la unidad del artículo (ver suggested_quantity en …/reagents/to-order)."},"notes":{"type":"string","maxLength":1000,"description":"Notas para quien hace las compras."},"ordered":{"default":false,"description":"true si ya se le pidió al proveedor. Si no, queda «por pedir» y, si el artículo ya tiene un pedido abierto, se devuelve ese en vez de duplicarlo.","type":"boolean"},"supplier":{"description":"Con ordered: el proveedor, por id o por nombre. Por defecto, el habitual del artículo.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"reference":{"type":"string","maxLength":120,"description":"Con ordered: n.º de pedido u orden de compra."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Con ordered: cuándo se espera que llegue (AAAA-MM-DD)."},{"type":"null"}]}},"required":["item","quantity"]}}}},"responses":{"201":{"description":"Creado","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"status":{"type":"string","enum":["requested","ordered","received","cancelled"],"description":"requested = por pedir (alguien avisó que falta), ordered = pedido al proveedor, received = recibido, cancelled = cancelado."},"quantity":{"type":"number"},"already_open":{"type":"boolean","description":"true si el artículo ya tenía un pedido abierto y se devolvió ese (no se duplica)."}},"required":["id","item","status","quantity","already_open"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/to-order":{"get":{"tags":["Insumos"],"summary":"Para pedir","description":"Los artículos vigentes que llegaron a su punto de pedido y no tienen un pedido abierto, por proveedor habitual, con la cantidad que conviene pedir.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"presentation":{"type":"string"},"catalog_number":{"type":"string"},"available":{"type":"number"},"reorder_point":{"type":"number"},"target_stock":{"type":["number","null"]},"suggested_quantity":{"type":"number","description":"Cuánto conviene pedir: hasta el stock objetivo o, sin objetivo, el doble del punto de pedido."},"supplier":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Proveedor habitual."}},"required":["item","presentation","catalog_number","available","reorder_point","target_stock","suggested_quantity","supplier"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/lots/{id}/consume":{"post":{"tags":["Insumos"],"summary":"Registrar un consumo","description":"Descuenta lo que se usó de un lote (por id o por código interno), a nombre de la persona del token. Conviene el lote que vence primero; un lote vencido pide confirm_expired. Las sustancias controladas piden reason.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"quantity":{"type":"number","exclusiveMinimum":0,"description":"Cuánto se usó, en la unidad del artículo."},"occurred_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo (AAAA-MM-DD). Por defecto, hoy."},"purpose":{"type":"string","maxLength":200,"description":"Para qué se usó (protocolo, proyecto, equipo…)."},"reason":{"type":"string","maxLength":500,"description":"Motivo. Obligatorio para las sustancias controladas."},"confirm_expired":{"default":false,"description":"true para registrar el uso de un lote vencido (queda marcado). Sin esto, un lote vencido no se puede usar.","type":"boolean"}},"required":["quantity"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"lot_number":{"type":"string"},"quantity":{"type":"number","description":"Lo que queda en el lote."},"status":{"type":"string","enum":["expired","due_soon","ok","depleted"],"description":"expired = vencido, due_soon = por vencer, ok = al día, depleted = agotado."}},"required":["id","code","lot_number","quantity","status"],"additionalProperties":false},"new_lot":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"quantity":{"type":"number"}},"required":["id","code","quantity"],"additionalProperties":false},{"type":"null"}],"description":"Al trasladar una parte: el lote nuevo que quedó en el otro lugar."},"stock":{"type":"object","properties":{"available":{"type":"number"}},"required":["available"],"additionalProperties":false}},"required":["item","lot","new_lot","stock"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/lots/{id}/discard":{"post":{"tags":["Insumos"],"summary":"Descartar","description":"Retira un lote o una parte (vencido, roto, contaminado), con su motivo. Sin cantidad, el lote entero.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Por qué se descarta (vencido, roto, contaminado…)."},"quantity":{"type":"number","exclusiveMinimum":0,"description":"Cuánto se descarta. Sin cantidad, el lote entero."},"occurred_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo (AAAA-MM-DD). Por defecto, hoy."}},"required":["reason"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"lot_number":{"type":"string"},"quantity":{"type":"number","description":"Lo que queda en el lote."},"status":{"type":"string","enum":["expired","due_soon","ok","depleted"],"description":"expired = vencido, due_soon = por vencer, ok = al día, depleted = agotado."}},"required":["id","code","lot_number","quantity","status"],"additionalProperties":false},"new_lot":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"quantity":{"type":"number"}},"required":["id","code","quantity"],"additionalProperties":false},{"type":"null"}],"description":"Al trasladar una parte: el lote nuevo que quedó en el otro lugar."},"stock":{"type":"object","properties":{"available":{"type":"number"}},"required":["available"],"additionalProperties":false}},"required":["item","lot","new_lot","stock"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/lots/{id}/adjust":{"post":{"tags":["Insumos"],"summary":"Ajustar al recuento","description":"Deja el lote con la cantidad contada (registra la diferencia como un ajuste), con su motivo.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"counted":{"type":"number","minimum":0,"description":"Lo que hay de verdad en el lote, contado (0 si no queda nada)."},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Por qué se ajusta (recuento, error de carga…)."}},"required":["counted","reason"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"lot_number":{"type":"string"},"quantity":{"type":"number","description":"Lo que queda en el lote."},"status":{"type":"string","enum":["expired","due_soon","ok","depleted"],"description":"expired = vencido, due_soon = por vencer, ok = al día, depleted = agotado."}},"required":["id","code","lot_number","quantity","status"],"additionalProperties":false},"new_lot":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"quantity":{"type":"number"}},"required":["id","code","quantity"],"additionalProperties":false},{"type":"null"}],"description":"Al trasladar una parte: el lote nuevo que quedó en el otro lugar."},"stock":{"type":"object","properties":{"available":{"type":"number"}},"required":["available"],"additionalProperties":false}},"required":["item","lot","new_lot","stock"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/lots/{id}/transfer":{"post":{"tags":["Insumos"],"summary":"Trasladar","description":"Mueve un lote a otro lugar. Con una cantidad menor que la que tiene, esa parte queda como un lote nuevo en el otro lugar (new_lot).\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"place":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"A dónde va, por id, nombre o camino (Sala › Heladera; ver …/reagents/places). null lo deja sin lugar."},"place_detail":{"type":"string","maxLength":120,"description":"Detalle del lugar nuevo: estante, caja…"},"quantity":{"type":"number","exclusiveMinimum":0,"description":"Cuánto se traslada. Sin cantidad, el lote entero; una parte queda como un lote nuevo en el otro lugar."}},"required":["place"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"lot_number":{"type":"string"},"quantity":{"type":"number","description":"Lo que queda en el lote."},"status":{"type":"string","enum":["expired","due_soon","ok","depleted"],"description":"expired = vencido, due_soon = por vencer, ok = al día, depleted = agotado."}},"required":["id","code","lot_number","quantity","status"],"additionalProperties":false},"new_lot":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"quantity":{"type":"number"}},"required":["id","code","quantity"],"additionalProperties":false},{"type":"null"}],"description":"Al trasladar una parte: el lote nuevo que quedó en el otro lugar."},"stock":{"type":"object","properties":{"available":{"type":"number"}},"required":["available"],"additionalProperties":false}},"required":["item","lot","new_lot","stock"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/movements/{id}/void":{"post":{"tags":["Insumos"],"summary":"Anular un movimiento","description":"Anula un movimiento cargado por error, con su motivo: deja de contar para el stock pero queda en el libro, marcado como anulado.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Por qué se anula (cargado por error, duplicado…)."}},"required":["reason"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"lot":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"quantity":{"type":"number"}},"required":["id","code","quantity"],"additionalProperties":false},"stock":{"type":"object","properties":{"available":{"type":"number"}},"required":["available"],"additionalProperties":false}},"required":["id","item","lot","stock"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/orders/{id}/ordered":{"post":{"tags":["Insumos"],"summary":"Marcar como pedido","description":"Registra que el pedido se le hizo al proveedor, con su n.º y la fecha estimada de llegada. Sirve también para corregir esos datos. Con «version», responde 409 si alguien lo cambió después de leerlo.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"ordered_on":{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo se le pidió al proveedor (AAAA-MM-DD). Por defecto, hoy."},"expected_on":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$","description":"Cuándo se espera que llegue (AAAA-MM-DD)."},{"type":"null"}]},"reference":{"type":"string","maxLength":120,"description":"N.º de pedido u orden de compra."},"quantity":{"type":"number","exclusiveMinimum":0,"description":"Cuánto se pidió, si cambió. Por defecto, lo que estaba pedido."},"supplier":{"description":"A qué proveedor, por id o por nombre, si cambió. null lo quita.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"notes":{"type":"string","maxLength":1000,"description":"Notas."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"status":{"type":"string","enum":["requested","ordered","received","cancelled"],"description":"requested = por pedir (alguien avisó que falta), ordered = pedido al proveedor, received = recibido, cancelled = cancelado."},"quantity":{"type":"number"},"already_open":{"type":"boolean","description":"true si el artículo ya tenía un pedido abierto y se devolvió ese (no se duplica)."}},"required":["id","item","status","quantity","already_open"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/reagents/orders/{id}/close":{"post":{"tags":["Insumos"],"summary":"Cerrar un pedido","description":"Lo da por recibido (aunque haya llegado menos) o lo cancela. Lo que llega con un ingreso contra el pedido lo cierra solo.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"status":{"type":"string","enum":["received","cancelled"],"description":"received = recibido (aunque haya llegado menos de lo pedido), cancelled = cancelado. Un pedido cerrado solo lo reabre la administración."}},"required":["status"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"item":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"},"unit":{"type":"string"}},"required":["id","code","name","unit"],"additionalProperties":false},"status":{"type":"string","enum":["requested","ordered","received","cancelled"],"description":"requested = por pedir (alguien avisó que falta), ordered = pedido al proveedor, received = recibido, cancelled = cancelado."},"quantity":{"type":"number"},"already_open":{"type":"boolean","description":"true si el artículo ya tenía un pedido abierto y se devolvió ese (no se duplica)."}},"required":["id","item","status","quantity","already_open"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/monitoring/sensors":{"get":{"tags":["Monitoreo"],"summary":"Sensores","description":"Los sensores de temperatura conectados, con su estado ahora, la última lectura y, si está fuera de rango, desde cuándo.","parameters":[{"name":"equipment","in":"query","required":false,"description":"Solo los sensores de un equipo, por id o por código.","schema":{"description":"Solo los sensores de un equipo, por id o por código.","type":"string"}},{"name":"archived","in":"query","required":false,"description":"include suma los desconectados; only, solo ellos.","schema":{"description":"include suma los desconectados; only, solo ellos.","type":"string","enum":["include","only"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false,"description":"El equipo donde está el sensor (módulo Equipos)."},"min_value":{"description":"Mínimo aceptable, en °C.","type":["number","null"]},"max_value":{"description":"Máximo aceptable, en °C.","type":["number","null"]},"range":{"type":"string","description":"El rango en palabras: «Entre 2 y 8 °C»."},"alarm_delay_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutos fuera de rango antes de que cuente como alarma."},"offline_after_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutos sin lecturas antes de avisar que no responde."},"state":{"type":"string","enum":["waiting","offline","alarm","out","ok"],"description":"waiting = nunca mandó una lectura, offline = dejó de mandar (sin datos), alarm = fuera de rango más tiempo que la demora, out = fuera de rango dentro de la demora, ok = en rango."},"latest":{"anyOf":[{"type":"object","properties":{"measured_at":{"type":"string","description":"Instante de la lectura (UTC)."},"value":{"type":"number","description":"Temperatura en °C."}},"required":["measured_at","value"],"additionalProperties":false},{"type":"null"}],"description":"La última lectura, o null si nunca mandó."},"open_excursion":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["high","low"],"description":"high = por encima del máximo, low = por debajo del mínimo."},"started_at":{"type":"string"},"ended_at":{"description":"Vacío mientras sigue fuera de rango.","type":["string","null"]},"peak_value":{"type":"number","description":"La temperatura más alta (high) o más baja (low) que llegó."},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Cuánto duró (o lleva), en minutos."},"acknowledged_at":{"description":"Cuándo se registró qué se hizo. Vacío: sin registrar.","type":["string","null"]},"acknowledged_by":{"type":["string","null"]},"note":{"type":"string","description":"La acción correctiva que se anotó."}},"required":["id","kind","started_at","ended_at","peak_value","minutes","acknowledged_at","acknowledged_by","note"],"additionalProperties":false},{"type":"null"}],"description":"Si ahora está fuera de rango, desde cuándo y hasta dónde llegó."},"key_prefix":{"description":"Los primeros caracteres de su clave vigente (vsn_…).","type":["string","null"]},"archived":{"type":"boolean","description":"true si está desconectado: su clave no funciona."},"created_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","equipment","min_value","max_value","range","alarm_delay_minutes","offline_after_minutes","state","latest","open_excursion","key_prefix","archived","created_at","version"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/monitoring/sensors/{id}":{"get":{"tags":["Monitoreo"],"summary":"Ver un sensor","description":"Por id, o por el código de su equipo si tiene un solo sensor. Con la lectura más baja y la más alta del período y sus alarmas.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"period","in":"query","required":false,"description":"Atajo: 24h, 7d o 30d (hasta ahora). Por defecto, 24h. Se ignora si van from o to.","schema":{"description":"Atajo: 24h, 7d o 30d (hasta ahora). Por defecto, 24h. Se ignora si van from o to.","type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"description":"Desde este día (AAAA-MM-DD, en la hora del laboratorio). Hasta un año.","schema":{"description":"Desde este día (AAAA-MM-DD, en la hora del laboratorio). Hasta un año.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Hasta este día inclusive. Por defecto, hoy.","schema":{"description":"Hasta este día inclusive. Por defecto, hoy.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false,"description":"El equipo donde está el sensor (módulo Equipos)."},"min_value":{"description":"Mínimo aceptable, en °C.","type":["number","null"]},"max_value":{"description":"Máximo aceptable, en °C.","type":["number","null"]},"range":{"type":"string","description":"El rango en palabras: «Entre 2 y 8 °C»."},"alarm_delay_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutos fuera de rango antes de que cuente como alarma."},"offline_after_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutos sin lecturas antes de avisar que no responde."},"state":{"type":"string","enum":["waiting","offline","alarm","out","ok"],"description":"waiting = nunca mandó una lectura, offline = dejó de mandar (sin datos), alarm = fuera de rango más tiempo que la demora, out = fuera de rango dentro de la demora, ok = en rango."},"latest":{"anyOf":[{"type":"object","properties":{"measured_at":{"type":"string","description":"Instante de la lectura (UTC)."},"value":{"type":"number","description":"Temperatura en °C."}},"required":["measured_at","value"],"additionalProperties":false},{"type":"null"}],"description":"La última lectura, o null si nunca mandó."},"open_excursion":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["high","low"],"description":"high = por encima del máximo, low = por debajo del mínimo."},"started_at":{"type":"string"},"ended_at":{"description":"Vacío mientras sigue fuera de rango.","type":["string","null"]},"peak_value":{"type":"number","description":"La temperatura más alta (high) o más baja (low) que llegó."},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Cuánto duró (o lleva), en minutos."},"acknowledged_at":{"description":"Cuándo se registró qué se hizo. Vacío: sin registrar.","type":["string","null"]},"acknowledged_by":{"type":["string","null"]},"note":{"type":"string","description":"La acción correctiva que se anotó."}},"required":["id","kind","started_at","ended_at","peak_value","minutes","acknowledged_at","acknowledged_by","note"],"additionalProperties":false},{"type":"null"}],"description":"Si ahora está fuera de rango, desde cuándo y hasta dónde llegó."},"key_prefix":{"description":"Los primeros caracteres de su clave vigente (vsn_…).","type":["string","null"]},"archived":{"type":"boolean","description":"true si está desconectado: su clave no funciona."},"created_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"period":{"type":"object","properties":{"from":{"type":"string","description":"Inicio del período (UTC)."},"to":{"type":"string","description":"Fin del período (UTC)."},"label":{"type":"string","description":"El período en palabras."}},"required":["from","to","label"],"additionalProperties":false},"lowest":{"anyOf":[{"type":"object","properties":{"measured_at":{"type":"string","description":"Instante de la lectura (UTC)."},"value":{"type":"number","description":"Temperatura en °C."}},"required":["measured_at","value"],"additionalProperties":false},{"type":"null"}],"description":"La lectura más baja del período."},"highest":{"anyOf":[{"type":"object","properties":{"measured_at":{"type":"string","description":"Instante de la lectura (UTC)."},"value":{"type":"number","description":"Temperatura en °C."}},"required":["measured_at","value"],"additionalProperties":false},{"type":"null"}],"description":"La lectura más alta del período."},"alarms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["high","low"],"description":"high = por encima del máximo, low = por debajo del mínimo."},"started_at":{"type":"string"},"ended_at":{"description":"Vacío mientras sigue fuera de rango.","type":["string","null"]},"peak_value":{"type":"number","description":"La temperatura más alta (high) o más baja (low) que llegó."},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Cuánto duró (o lleva), en minutos."},"acknowledged_at":{"description":"Cuándo se registró qué se hizo. Vacío: sin registrar.","type":["string","null"]},"acknowledged_by":{"type":["string","null"]},"note":{"type":"string","description":"La acción correctiva que se anotó."}},"required":["id","kind","started_at","ended_at","peak_value","minutes","acknowledged_at","acknowledged_by","note"],"additionalProperties":false},"description":"Las alarmas del período, de la más nueva a la más vieja."}},"required":["id","name","equipment","min_value","max_value","range","alarm_delay_minutes","offline_after_minutes","state","latest","open_excursion","key_prefix","archived","created_at","version","period","lowest","highest","alarms"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"tags":["Monitoreo"],"summary":"Modificar un sensor","description":"Cambia el nombre, el rango o las demoras de un sensor, o lo desconecta (archived). Cambia solo lo que mandás. Pide la «version» que devolvió la API: si alguien lo cambió después, responde 409.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"La «version» que devolvió la API al leer el registro. Si alguien lo cambió después, la respuesta es 409 y no se pisa nada."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"Nombre del sensor dentro del equipo («Estante de arriba»)."},"min_value":{"description":"Mínimo aceptable. En °C. null lo quita.","anyOf":[{"type":"number","minimum":-273,"maximum":500},{"type":"null"}]},"max_value":{"description":"Máximo aceptable. En °C. null lo quita.","anyOf":[{"type":"number","minimum":-273,"maximum":500},{"type":"null"}]},"alarm_delay_minutes":{"description":"Minutos fuera de rango antes de que cuente como alarma.","type":"integer","minimum":0,"maximum":240},"offline_after_minutes":{"description":"Minutos sin lecturas antes de avisar que no responde.","type":"integer","minimum":5,"maximum":1440},"archived":{"description":"true lo desconecta (su clave deja de funcionar; las lecturas y alarmas quedan); false lo vuelve a conectar.","type":"boolean"}},"required":["version"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false,"description":"El equipo donde está el sensor (módulo Equipos)."},"min_value":{"description":"Mínimo aceptable, en °C.","type":["number","null"]},"max_value":{"description":"Máximo aceptable, en °C.","type":["number","null"]},"range":{"type":"string","description":"El rango en palabras: «Entre 2 y 8 °C»."},"alarm_delay_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutos fuera de rango antes de que cuente como alarma."},"offline_after_minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Minutos sin lecturas antes de avisar que no responde."},"state":{"type":"string","enum":["waiting","offline","alarm","out","ok"],"description":"waiting = nunca mandó una lectura, offline = dejó de mandar (sin datos), alarm = fuera de rango más tiempo que la demora, out = fuera de rango dentro de la demora, ok = en rango."},"latest":{"anyOf":[{"type":"object","properties":{"measured_at":{"type":"string","description":"Instante de la lectura (UTC)."},"value":{"type":"number","description":"Temperatura en °C."}},"required":["measured_at","value"],"additionalProperties":false},{"type":"null"}],"description":"La última lectura, o null si nunca mandó."},"open_excursion":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["high","low"],"description":"high = por encima del máximo, low = por debajo del mínimo."},"started_at":{"type":"string"},"ended_at":{"description":"Vacío mientras sigue fuera de rango.","type":["string","null"]},"peak_value":{"type":"number","description":"La temperatura más alta (high) o más baja (low) que llegó."},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Cuánto duró (o lleva), en minutos."},"acknowledged_at":{"description":"Cuándo se registró qué se hizo. Vacío: sin registrar.","type":["string","null"]},"acknowledged_by":{"type":["string","null"]},"note":{"type":"string","description":"La acción correctiva que se anotó."}},"required":["id","kind","started_at","ended_at","peak_value","minutes","acknowledged_at","acknowledged_by","note"],"additionalProperties":false},{"type":"null"}],"description":"Si ahora está fuera de rango, desde cuándo y hasta dónde llegó."},"key_prefix":{"description":"Los primeros caracteres de su clave vigente (vsn_…).","type":["string","null"]},"archived":{"type":"boolean","description":"true si está desconectado: su clave no funciona."},"created_at":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","equipment","min_value","max_value","range","alarm_delay_minutes","offline_after_minutes","state","latest","open_excursion","key_prefix","archived","created_at","version"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/monitoring/sensors/{id}/readings":{"get":{"tags":["Monitoreo"],"summary":"Lecturas de un sensor","description":"Las lecturas de un período, resumidas por tramos para graficar: de cada tramo, la más baja y la más alta.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"period","in":"query","required":false,"description":"Atajo: 24h, 7d o 30d (hasta ahora). Por defecto, 24h. Se ignora si van from o to.","schema":{"description":"Atajo: 24h, 7d o 30d (hasta ahora). Por defecto, 24h. Se ignora si van from o to.","type":"string","enum":["24h","7d","30d"]}},{"name":"from","in":"query","required":false,"description":"Desde este día (AAAA-MM-DD, en la hora del laboratorio). Hasta un año.","schema":{"description":"Desde este día (AAAA-MM-DD, en la hora del laboratorio). Hasta un año.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Hasta este día inclusive. Por defecto, hoy.","schema":{"description":"Hasta este día inclusive. Por defecto, hoy.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"points","in":"query","required":false,"description":"En cuántos tramos se divide el período (de cada uno vienen la lectura más baja y la más alta). Por defecto, 360.","schema":{"default":360,"description":"En cuántos tramos se divide el período (de cada uno vienen la lectura más baja y la más alta). Por defecto, 360.","type":"integer","minimum":10,"maximum":1000}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"period":{"type":"object","properties":{"from":{"type":"string","description":"Inicio del período (UTC)."},"to":{"type":"string","description":"Fin del período (UTC)."},"label":{"type":"string","description":"El período en palabras."}},"required":["from","to","label"],"additionalProperties":false},"data":{"type":"array","items":{"type":"object","properties":{"measured_at":{"type":"string","description":"Instante de la lectura (UTC)."},"value":{"type":"number","description":"Temperatura en °C."}},"required":["measured_at","value"],"additionalProperties":false},"description":"De la más vieja a la más nueva. El período se divide en tramos y de cada uno vienen la lectura más baja y la más alta: un pico corto no se pierde."}},"required":["period","data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/monitoring/sensors/{id}/history":{"get":{"tags":["Monitoreo"],"summary":"Historial de un sensor","description":"Quién lo conectó, le cambió el rango o lo desconectó, cuándo y por dónde.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string"},"action":{"type":"string","enum":["insert","update","soft_delete","restore","delete","move","status_change"]},"actor":{"type":"string"},"via":{"description":"«vía API · <token>» si no se hizo desde la web.","type":["string","null"]},"changed_fields":{"type":"array","items":{"type":"string"}},"old":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"new":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]}},"required":["at","action","actor","via","changed_fields","old","new"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/monitoring/alarms":{"get":{"tags":["Monitoreo"],"summary":"Alarmas","description":"Cada vez que una temperatura estuvo fuera de rango más tiempo que la demora del sensor, con la acción correctiva registrada, de la más nueva a la más vieja (hasta 500).","parameters":[{"name":"pending","in":"query","required":false,"description":"true: solo las que todavía no tienen registrado qué se hizo.","schema":{"description":"true: solo las que todavía no tienen registrado qué se hizo.","type":"string","enum":["true","false"]}},{"name":"sensor","in":"query","required":false,"description":"Solo las de un sensor: su id, o el código del equipo si tiene uno solo.","schema":{"description":"Solo las de un sensor: su id, o el código del equipo si tiene uno solo.","type":"string"}},{"name":"from","in":"query","required":false,"description":"Las que se cruzan con el período, desde este día (AAAA-MM-DD). Sin from ni to, todas.","schema":{"description":"Las que se cruzan con el período, desde este día (AAAA-MM-DD). Sin from ni to, todas.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Hasta este día inclusive.","schema":{"description":"Hasta este día inclusive.","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["high","low"],"description":"high = por encima del máximo, low = por debajo del mínimo."},"started_at":{"type":"string"},"ended_at":{"description":"Vacío mientras sigue fuera de rango.","type":["string","null"]},"peak_value":{"type":"number","description":"La temperatura más alta (high) o más baja (low) que llegó."},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Cuánto duró (o lleva), en minutos."},"acknowledged_at":{"description":"Cuándo se registró qué se hizo. Vacío: sin registrar.","type":["string","null"]},"acknowledged_by":{"type":["string","null"]},"note":{"type":"string","description":"La acción correctiva que se anotó."},"sensor":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false,"description":"El equipo donde está el sensor (módulo Equipos)."}},"required":["id","kind","started_at","ended_at","peak_value","minutes","acknowledged_at","acknowledged_by","note","sensor","equipment"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/monitoring/alarms/{id}/acknowledge":{"post":{"tags":["Monitoreo"],"summary":"Registrar una alarma","description":"Anota qué se hizo con una alarma (la acción correctiva), a nombre de la persona del token. Cada alarma se registra una sola vez.\n\nPide un token con rol Editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Clave única de la operación (por ejemplo un UUID). Si el pedido se repite con la misma clave en 24 horas, no se vuelve a ejecutar y se recibe la primera respuesta.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"note":{"type":"string","minLength":1,"maxLength":1000,"description":"Qué se hizo: la acción correctiva («Se cerró la puerta y volvió a rango en 20 min»)."}},"required":["note"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"kind":{"type":"string","enum":["high","low"],"description":"high = por encima del máximo, low = por debajo del mínimo."},"started_at":{"type":"string"},"ended_at":{"description":"Vacío mientras sigue fuera de rango.","type":["string","null"]},"peak_value":{"type":"number","description":"La temperatura más alta (high) o más baja (low) que llegó."},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Cuánto duró (o lleva), en minutos."},"acknowledged_at":{"description":"Cuándo se registró qué se hizo. Vacío: sin registrar.","type":["string","null"]},"acknowledged_by":{"type":["string","null"]},"note":{"type":"string","description":"La acción correctiva que se anotó."},"sensor":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"equipment":{"type":"object","properties":{"id":{"type":"string","description":"Identificador (UUID)."},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"additionalProperties":false,"description":"El equipo donde está el sensor (módulo Equipos)."}},"required":["id","kind","started_at","ended_at","peak_value","minutes","acknowledged_at","acknowledged_by","note","sensor","equipment"],"additionalProperties":false}},"required":["data"],"additionalProperties":false}}}},"default":{"description":"Error: 400, 401, 403, 404, 409, 413, 429 o 500, con un código estable y un mensaje en español.","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}}}}