{"openapi":"3.1.0","info":{"title":"API de Miralotu","version":"1.0.0","description":"Proyectos, propuestas, avances y pagos de tu empresa. Montos que envías: en pesos. Montos que recibes: en centavos (campos «…Cents»)."},"servers":[{"url":"https://miralotu.com/api/v1"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Llave de API (mlt_…) creada en Integraciones"}}},"paths":{"/projects":{"get":{"tags":["Proyectos"],"summary":"Listar proyectos","description":"Permiso: `projects:read`.","parameters":[{"name":"externalRef","in":"query","description":"Filtra por la referencia del sistema externo (p. ej. el folio de tu cotización).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"projects":[{"id":"3a87e7ad-95ba-465d-a820-2bebdd02f0fc","slug":"aceros-del-norte-k3m9x","externalRef":"CP-2026-0001","name":"Nave industrial","clientName":"Aceros del Norte","status":"draft","quoteType":"items","paymentDays":5,"currency":"MXN","totalCents":14036000,"portalUrl":"https://miralotu.com/p/aceros-del-norte-k3m9x","progress":0,"hoursUsed":0,"paidCents":0}]}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}},"post":{"tags":["Proyectos"],"summary":"Crear proyecto","description":"Por conceptos (`quoteType: \"items\"`) con `items` y `milestones`, o por horas (`quoteType: \"hours\"`) con `hoursPerMonth`, `months` y `hourlyRate`. `externalRef` es tu identificador (único por empresa): con él puedes consultar el proyecto como `/projects/ext:{externalRef}`. `managedBy` (p. ej. `\"Picoi\"`) indica que tu sistema administra avances, etapas y cobros: en el panel de Miralotu se ven pero no se editan.\n\nPermiso: `projects:write`.","requestBody":{"required":true,"content":{"application/json":{"example":{"externalRef":"CP-2026-0001","managedBy":"Picoi","name":"Nave industrial","clientName":"Aceros del Norte","description":"Instalación eléctrica de la nave 2.","quoteType":"items","taxRate":16,"paymentDays":5,"startDate":"2026-10-05","clientContactName":"Jorge Ramírez","clientEmail":"compras@aceros.mx","items":[{"description":"Instalación eléctrica tablero principal","quantity":1,"unit":"lote","unitPrice":85000},{"description":"Cableado y canalización","quantity":300,"unit":"m","unitPrice":120}],"discounts":[{"label":"Descuento cliente fundador","type":"fixed","value":5000}],"milestones":[{"label":"Anticipo","trigger":"acceptance","amountType":"percent","percent":50},{"label":"Al 50 % de avance","trigger":"progress","triggerProgress":50,"amountType":"percent","percent":30},{"label":"Contra entrega","trigger":"delivery","amountType":"percent","percent":20}],"stages":[{"name":"Ingeniería","description":"Planos y memoria de cálculo","startDate":"2026-10-05","endDate":"2026-10-20"}]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"project":{"id":"3a87e7ad-95ba-465d-a820-2bebdd02f0fc","slug":"aceros-del-norte-k3m9x","externalRef":"CP-2026-0001","name":"Nave industrial","clientName":"Aceros del Norte","status":"draft","quoteType":"items","paymentDays":5,"currency":"MXN","totalCents":14036000,"portalUrl":"https://miralotu.com/p/aceros-del-norte-k3m9x"}}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}":{"get":{"tags":["Proyectos"],"summary":"Detalle del proyecto","description":"`{id}` es el slug o `ext:{externalRef}`. Incluye etapas, conceptos, hitos (con su monto y si ya se cumplió), pagos, avances, propuestas y accesos del cliente.\n\nPermiso: `projects:read`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"project":{"id":"3a87e7ad-95ba-465d-a820-2bebdd02f0fc","slug":"aceros-del-norte-k3m9x","externalRef":"CP-2026-0001","name":"Nave industrial","clientName":"Aceros del Norte","status":"draft","quoteType":"items","paymentDays":5,"currency":"MXN","totalCents":14036000,"portalUrl":"https://miralotu.com/p/aceros-del-norte-k3m9x"},"stats":{"progress":40,"hoursUsed":12,"hoursRemaining":0,"paidCents":7018000,"totalCents":14036000},"stages":[{"position":1,"name":"Ingeniería","status":"done","progress":100}],"items":[{"position":1,"description":"Instalación eléctrica tablero principal","unit":"lote","quantity":1,"unitPriceCents":8500000},{"position":2,"description":"Tornillo 1/4","unit":"pza","quantity":500,"unitPriceCents":12.34}],"milestones":[{"position":1,"label":"Anticipo","trigger":"acceptance","condition":"Al aceptar la propuesta","amountType":"percent","percent":50,"amountCents":7018000,"reachedAt":"2026-10-02"}],"payments":[{"id":"…","concept":"Anticipo","amountCents":7018000,"dueDate":"2026-10-07","condition":null,"automatic":true,"paidAt":null}],"updates":[],"proposals":[{"folio":"P-2026-0001","status":"accepted","totalCents":14036000,"acceptedName":"Jorge Ramírez"}],"viewers":[],"pendingInvitations":[]}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}},"patch":{"tags":["Proyectos"],"summary":"Editar proyecto","description":"Solo cambia los campos enviados. Si envías `items` o `milestones`, reemplazan los actuales. Para marcarlo entregado: `{ \"status\": \"completed\" }` (cumple los hitos «contra entrega»).\n\nPermiso: `projects:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"status":"active","items":[{"description":"Tablero principal","quantity":1,"unitPrice":90000}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/stages":{"put":{"tags":["Proyectos"],"summary":"Reemplazar etapas","description":"Estimación de cada etapa: `estimate` + `unit` (`hours` · `days` · `weeks` · `months`), o `hoursEstimated` (horas). En cotización por horas se convierte a horas con las equivalencias de la empresa (8 h/día, 40 h/semana, 160 h/mes por defecto); por conceptos es solo la duración.\n\nPermiso: `projects:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"stages":[{"name":"Ingeniería","estimate":2,"unit":"weeks","startDate":"2026-10-05","endDate":"2026-10-20","status":"in_progress","progress":30}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"count":1}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/stages/{position}":{"patch":{"tags":["Proyectos"],"summary":"Actualizar una etapa","description":"Por su número (1, 2, 3…). El avance de las etapas cumple los hitos «al llegar a % de avance».\n\nPermiso: `projects:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}},{"name":"position","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"status":"done"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"stage":{"position":1,"status":"done","progress":100}}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/proposals":{"post":{"tags":["Propuestas"],"summary":"Enviar propuesta","description":"Genera la propuesta con folio y la manda por correo al contacto del cliente con un link personal. El cliente la acepta (nombre, correo verificado y firma con Face ID o huella) o la rechaza con motivos; te avisamos por webhook. Las anteriores sin responder quedan `superseded`. `cc` (opcional, hasta 10): correos que reciben su propio link solo para ver la propuesta; no la aceptan, rechazan ni descargan. `folio` (opcional): el folio de tu sistema, para que coincida en los dos lados; si no lo mandas, se asigna P-AAAA-NNNN. La propuesta sale con el logo y los datos de la empresa de la llave (Configuración o `PUT /settings`).\n\nPermiso: `proposals:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"to":"compras@aceros.mx","cc":["direccion@aceros.mx"],"message":"Te comparto la propuesta que platicamos.","folio":"PRY-2026-0002"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"proposal":{"id":"…","folio":"P-2026-0001","status":"sent","totalCents":14036000},"url":"https://miralotu.com/propuesta/…","emailed":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}},"get":{"tags":["Propuestas"],"summary":"Estado de las propuestas","description":"`status`: sent · viewed · accepted · rejected · superseded.\n\nPermiso: `projects:read`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"proposals":[{"folio":"P-2026-0001","status":"viewed","viewedAt":"2026-10-02T21:53:00.000Z"}]}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/settings":{"get":{"tags":["Empresa"],"summary":"Datos de la empresa","description":"Nombre, datos fiscales, contacto y logo con los que salen la propuesta, el portal y los correos.\n\nPermiso: `projects:read`.","responses":{"200":{"description":"OK","content":{"application/json":{"example":{"settings":{"tradeName":"Aceros del Norte","legalName":"Aceros del Norte SA de CV","taxId":"ANO010101AB1","logoUrl":"https://miralotu.com/api/brand/logo?w=…"}}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}},"put":{"tags":["Empresa"],"summary":"Sincronizar datos de la empresa","description":"Para que la propuesta, el portal y los correos salgan con los datos de tu sistema (p. ej. cada tenant). Solo se cambian los campos enviados. `logo`: `{ mime: image/png | image/jpeg | image/webp, data: base64 }` (máx. 5 MB; si es el mismo que la vez pasada no se vuelve a procesar) o `null` para quitarlo. No funciona con llaves de la empresa principal.\n\nPermiso: `settings:write`.","requestBody":{"required":true,"content":{"application/json":{"example":{"tradeName":"Aceros del Norte","legalName":"Aceros del Norte SA de CV","taxId":"ANO010101AB1","taxRegime":"601 - General de Ley Personas Morales","taxZip":"64000","address":"Av. Constitución 100, Monterrey, N.L.","phone":"8112345678","email":"ventas@aceros.mx","logo":{"mime":"image/png","data":"iVBORw0KGgo…"}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"logoUpdated":true,"settings":{"tradeName":"Aceros del Norte","logoUrl":"https://miralotu.com/api/brand/logo?w=…"}}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/updates":{"post":{"tags":["Avances y solicitudes"],"summary":"Publicar avance","description":"El cliente lo ve al momento en su portal y recibe un aviso por correo y notificación (sin el detalle). `files`: hasta 10 adjuntos `{ name, mime, base64 }` — imágenes (10 MB, se ven dentro del avance), PDF, Word, Excel, PowerPoint, CSV, ZIP, DWG/DXF o video (25 MB); el tipo se toma de la extensión de `name`. (`images` sigue funcionando.) Con `externalRef` (el id del avance en tu sistema) es idempotente: si ya existe se actualiza y responde 200 con `created: false`; al actualizar no se manda correo ni se crean solicitudes, y las imágenes solo se reemplazan si mandas nuevas.\n\nPermiso: `updates:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"externalRef":"picoi-update-019f…","date":"2026-10-03","hours":6,"stage":1,"stageProgress":60,"title":"Levantamiento en sitio","summary":"Se midió la nave y se ubicaron los puntos de carga.","items":[{"text":"Levantamiento de cargas","done":true}],"next":["Diagrama unifilar"],"requests":[{"question":"¿Aprueban la ubicación del tablero?","type":"approval"}]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"ok":true,"id":"…","created":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/updates/{updateId}":{"delete":{"tags":["Avances y solicitudes"],"summary":"Borrar avance","description":"`{updateId}`: id del avance o `ext:{externalRef}`. Las solicitudes y lo que contestó el cliente se conservan.\n\nPermiso: `updates:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/requests":{"get":{"tags":["Avances y solicitudes"],"summary":"Solicitudes y respuestas","description":"Permiso: `projects:read`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"requests":[{"id":"…","question":"¿Aprueban la ubicación del tablero?","type":"approval","status":"answered"}]}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}},"post":{"tags":["Avances y solicitudes"],"summary":"Crear solicitud","description":"`type`: choice · multiple · text · file · approval. `update` (id o `ext:{externalRef}` de un avance) la liga a ese avance. Con `externalRef` es idempotente: si ya existe y el cliente no ha contestado, se actualiza (200). Al crearse se avisa al cliente por correo y notificación (`notify: false` para no avisar). La respuesta del cliente llega por el webhook `request.answered` con tu `externalRef`.\n\nPermiso: `requests:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"externalRef":"picoi-req-0042","update":"ext:picoi-update-019f…","question":"Envíanos el plano arquitectónico","type":"file","description":"PDF o DWG"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"ok":true,"id":"…","created":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/requests/{requestId}":{"patch":{"tags":["Avances y solicitudes"],"summary":"Cerrar o reabrir solicitud","description":"`{requestId}`: id o `ext:{externalRef}`. Cerrada, el cliente ya no puede cambiar su respuesta.\n\nPermiso: `requests:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"status":"closed"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/payments":{"post":{"tags":["Pagos y accesos"],"summary":"Agregar pago al calendario","description":"Pagos aparte de los automáticos (por periodo o por hito). Monto en pesos. Con `externalRef` es idempotente (si ya existe y no está pagado, se actualiza).\n\nPermiso: `payments:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"externalRef":"picoi-cxc-0012","concept":"Extra: tablero adicional","amount":18500,"dueDate":"2026-11-15"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"ok":true,"id":"…"}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/payments/{paymentId}/record":{"post":{"tags":["Pagos y accesos"],"summary":"Registrar pago recibido","description":"`{paymentId}`: id del pago, `ext:{externalRef}`, `milestone:{n}` (pago del hito n) o `period:{n}` (pago del mes o etapa n). `amount` en pesos solo si difiere del monto. Volver a mandarlo actualiza el registro. El cliente ve el pago como «Pagado» y se dispara el webhook `payment.recorded`. Para deshacerlo: `DELETE` a la misma ruta.\n\nPermiso: `payments:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"paidAt":"2026-10-06","amount":107276.8,"reference":"SPEI 0912"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"id":"…","paidAt":"2026-10-06","paidAmountCents":10727680}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/projects/{id}/invitations":{"post":{"tags":["Pagos y accesos"],"summary":"Invitar al portal","description":"Manda el link personal (de un solo uso) para que la persona active su acceso con Face ID o huella.\n\nPermiso: `invitations:write`.","parameters":[{"name":"id","in":"path","required":true,"description":"Slug del proyecto o ext:{externalRef}","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"Ana López","email":"ana@aceros.mx","role":"Administración"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"ok":true,"url":"https://miralotu.com/p/…?t=…","emailed":true}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}},"/workspaces":{"post":{"tags":["Plataforma"],"summary":"Dar de alta una empresa","description":"Con la llave del servidor, o con la de una cuenta con **plan Integrador** (permiso `workspaces:write`). Crea la empresa, invita a su responsable por correo y devuelve una llave de API para esa empresa (solo se muestra aquí). Un integrador da de alta así a cada negocio que conecta: cada empresa integrada tiene su marca, no tiene límite de API y se cobra al integrador por mes desde su alta. `freeMonths` (opcional): meses gratis de promoción.\n\nRequiere la llave del servidor.","requestBody":{"required":true,"content":{"application/json":{"example":{"name":"Opsiris","ownerName":"Yamili","ownerEmail":"admin@opsiris.mx","externalRef":"picoi-tenant-123","freeMonths":2}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"workspace":{"id":"…","name":"Opsiris","externalRef":"picoi-tenant-123"},"owner":{"email":"admin@opsiris.mx","emailed":true},"apiKey":"mlt_…"}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}},"get":{"tags":["Plataforma"],"summary":"Listar empresas","description":"Con la llave del servidor, todas; con la de un integrador, solo las empresas que integra.\n\nRequiere la llave del servidor.","responses":{"200":{"description":"OK","content":{"application/json":{"example":{"workspaces":[{"id":"…","name":"Opsiris","externalRef":"picoi-tenant-123","disabled":false,"projects":3}]}}}},"401":{"description":"Falta la llave o es inválida"},"403":{"description":"La llave no tiene el permiso"},"404":{"description":"No existe (o es de otra empresa)"},"422":{"description":"Datos inválidos: { error }"},"429":{"description":"Más de 120 peticiones por minuto, o se alcanzó el límite mensual de llamadas del plan"}}}}},"webhooks":{"evento":{"post":{"summary":"Aviso de Miralotu a tu URL","description":"Eventos: proposal.sent, proposal.viewed, proposal.accepted, proposal.rejected, project.authorized, update.published, request.answered, payment.recorded, viewer.registered. Encabezados: Miralotu-Event, Miralotu-Delivery, Miralotu-Signature (t=…,v1=HMAC-SHA256 de \"t.cuerpo\").","requestBody":{"content":{"application/json":{"example":{"id":"5b1f…","event":"proposal.accepted","createdAt":"2026-10-02T22:00:00.000Z","workspaceId":"…","data":{"project":{"id":"…","slug":"aceros-del-norte-k3m9x","externalRef":"CP-2026-0001","name":"Nave industrial","status":"active","totalCents":14036000},"viewer":{"id":"…","name":"Jorge Ramírez","email":"compras@aceros.mx"},"proposal":{"folio":"P-2026-0001","status":"accepted","acceptedName":"Jorge Ramírez","acceptedDevice":"Android · Chrome · Huella","contentHash":"9f2c…"}}}}}},"responses":{"200":{"description":"Responde 2xx para confirmar la entrega"}}}}}}