{"openapi":"3.1.0","info":{"title":"Noti — API para empresas","version":"0.1.0","summary":"Creá tu QR, mostralo y avisale a tu gente.","description":"Guía paso a paso, con ejemplos en curl, Node y Python: `/empresas`.\n\nTodo se autentica con `Authorization: Bearer ntf_…` (el token de tu empresa), salvo lo que dice «panel», que es para una persona de tu equipo con sesión iniciada.\n\n**El 202 de un envío no significa «entregado»**: es a propósito, para que nadie se entere de quién se dio de baja."},"servers":[{"url":"https://noti-api-production.up.railway.app"}],"security":[{"TokenDeEmpresa":[]}],"paths":{"/send":{"post":{"tags":["send"],"summary":"Mandar un aviso","description":"Manda un aviso a quienes se suscribieron a tu empresa: **a todos** (sin destino), **a un QR** (`group`) o **a una persona** (`ref`). Pasá como mucho uno de `to`, `group` y `ref`.\n\nLa respuesta es **siempre** `202 {\"id\"}`, llegue el aviso a mil personas, a una o a ninguna (nadie suscripto, alguien lo silenció o te cortó, te pasaste de un límite). **El 202 no significa «entregado»**: es a propósito, para que nadie se entere de quién se dio de baja.\n\nLímites (pasarse no da error: el aviso simplemente no sale): 60 llamadas por minuto por token, 20 difusiones por hora por empresa, hasta 5.000 personas por difusión y un aviso cada 10 segundos a la misma persona.","operationId":"send_send_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendIn"}}},"required":true},"responses":{"202":{"description":"Recibimos el pedido. No dice si llegó ni a cuántos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendOut"}}}},"400":{"description":"Algún campo no cumple las reglas (título vacío, emoji doble, botón a medias, dos destinos a la vez…)."},"401":{"description":"Falta el token, está mal escrito o fue revocado."}}}},"/company/companies":{"get":{"tags":["company"],"summary":"Mis empresas (panel)","description":"Las empresas a las que entra el mail de la sesión. Lo usa el panel para el selector de empresa; es sólo para una persona del equipo con sesión iniciada, no para un token.","operationId":"my_companies_company_companies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CompanyOut"},"type":"array","title":"Response My Companies Company Companies Get"}}}},"401":{"description":"Sesión inválida o mail sin verificar."}}}},"/company/me":{"get":{"tags":["company"],"summary":"Mi empresa","description":"Tu empresa: nombre, código, el texto que lleva su QR (`noti://s/<código>`) y la página pública `/e/<código>`, lista para imprimir. También sirve para probar que tu token anda.","operationId":"me_company_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyOut"}}}},"401":{"description":"Falta el token, está mal escrito o fue revocado."}}},"patch":{"tags":["company"],"summary":"Cambiar el nombre de la empresa","description":"Cambia el nombre. Se puede aun con la empresa en revisión.","operationId":"edit_me_company_me_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyOut"}}}},"400":{"description":"Algún campo no cumple las reglas."}}}},"/company/signup":{"post":{"tags":["company"],"summary":"Anotar una empresa nueva (queda en revisión)","description":"Cualquier persona con un mail verificado (sesión de Auth0, igual que el\nresto de `/company/*`) puede anotar una empresa. Nace **`pending`**: hasta\nque un admin de Noti la aprueba no tiene QR, no resuelve su código y no\npuede mandar avisos. Sí puede entrar al panel, ver su estado y cambiar su\nnombre. Verificamos a cada empresa para que los avisos que recibe la gente\nsean de confianza.\n\nRepetir el mismo nombre o código devuelve la solicitud que ya estaba (no\nla duplica). Límites: hasta 3 empresas en revisión por persona y un tope de\nsolicitudes por hora por mail y por IP (429).","operationId":"signup_company_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupOut"}}}},"400":{"description":"Algún campo no cumple las reglas."}}}},"/company/groups":{"get":{"tags":["company"],"summary":"Mis QR","description":"Tus QR por grupo (sucursales, temas). `signups` es cuántas personas se sumaron **alguna vez** por ese QR: sólo sube, nunca baja, a propósito. `?archived=true` incluye los archivados.","operationId":"groups_company_groups_get","parameters":[{"name":"archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Archived"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GroupOut"},"title":"Response Groups Company Groups Get"}}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"400":{"description":"Algún campo no cumple las reglas."}}},"post":{"tags":["company"],"summary":"Crear un QR","description":"Crea un QR para un grupo (una sucursal, un tema). Sin `code`, Noti inventa uno corto; con `code`: de 3 a 24 caracteres, minúsculas, números y guiones. Los códigos comparten espacio con los de las empresas.","operationId":"new_group_company_groups_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupOut"}}}},"400":{"description":"El código o el nombre no cumplen las reglas."},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"409":{"description":"Ese código ya está en uso."}}}},"/company/stats":{"get":{"tags":["company"],"summary":"Salud de tu audiencia","description":"Cuántas personas se sumaron en total (por empresa y por QR; sólo sube), cuántos avisos mandaste en los últimos 30 días y, **cuando tu audiencia llega a un mínimo**, una idea aproximada de qué tan bien lo hacés: `calidad` (`excelente`, `buena`, `atencion`, `riesgo`), `rechazo_banda`, `alcance_banda` y `tendencia`, siempre en **bandas**, nunca números exactos, y con consejos. Es una foto que se saca una vez por día: volver a preguntar no la cambia. Noti nunca dice quiénes se fueron ni cuántos.","operationId":"stats_company_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsOut"}}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."}}}},"/company/avisos":{"get":{"tags":["company"],"summary":"Tus avisos y si se vieron","description":"Los avisos que mandaste, del más nuevo al más viejo (se conservan unos meses). **Qué se puede saber y qué no.** Noti nunca dice si un aviso «llegó», «se entregó» o «falló»: eso delataría quién silenció o cortó. Lo único que ves es lo que la persona hizo: abrirlo (`seen_on` en un aviso a una persona, `seen_count` en un aviso a muchos) y tocar el botón (`click_count`). Faltar un dato nunca significa «no lo vio».","operationId":"avisos_company_avisos_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvisoPage"}}}},"400":{"description":"El cursor no es válido."},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."}}}},"/company/avisos/{aviso_id}":{"get":{"tags":["company"],"summary":"Un aviso y si se vio","description":"Pasale el `id` que te devolvió `POST /send`. **Qué se puede saber y qué no.** Noti nunca dice si un aviso «llegó», «se entregó» o «falló»: eso delataría quién silenció o cortó. Lo único que ves es lo que la persona hizo: abrirlo (`seen_on` en un aviso a una persona, `seen_count` en un aviso a muchos) y tocar el botón (`click_count`). Faltar un dato nunca significa «no lo vio».","operationId":"aviso_company_avisos__aviso_id__get","parameters":[{"name":"aviso_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Aviso Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvisoOut"}}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"404":{"description":"No es un aviso tuyo (o ya pasó el tiempo que lo conservamos)."},"400":{"description":"Algún campo no cumple las reglas."}}}},"/company/groups/{group_id}":{"patch":{"tags":["company"],"summary":"Renombrar o archivar un QR","description":"Cambia el nombre de un QR o lo archiva. **Archivar apaga ese QR** (deja de funcionar al escanearlo) pero no saca a nadie: quienes ya se habían suscripto siguen recibiendo tus avisos. `{\"archived\": false}` lo reactiva.","operationId":"edit_group_company_groups__group_id__patch","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupOut"}}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"404":{"description":"Ese QR no existe o no es de tu empresa."},"400":{"description":"Algún campo no cumple las reglas."}}}},"/company/connect-links":{"post":{"tags":["company"],"summary":"Pase para vincular a una persona","description":"Genera un pase de **un solo uso** (vence a los 7 días) para UNA persona de tu sistema, identificada por el `ref` que vos elijas. Se lo mostrás (la página `url` tiene el QR y el botón «Abrir en Noti»); cuando lo abre en su Noti, podés mandarle avisos a ella sola con `ref` en `POST /send`. Noti no te dice quién es en Noti.","operationId":"connect_link_company_connect_links_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectOut"}}}},"400":{"description":"El `ref` está vacío o es demasiado largo."},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"404":{"description":"El QR (`group`) no existe o no es de tu empresa."}}}},"/company/invitations":{"post":{"tags":["company"],"summary":"Invitar a una persona por mail","description":"Le manda un mail a la persona invitándola a recibir tus avisos. Ella instala Noti, entra con ese mail y acepta la invitación en la app; desde ahí le mandás a ella sola con `ref` en `POST /send` (por omisión, el `ref` es su mail). La invitación vive 30 días; volver a invitar a la misma persona la renueva (el mail, como mucho uno por día). **La respuesta es siempre la misma**: Noti no informa si el mail llegó, si la persona ya usa Noti ni si aceptó.","operationId":"invite_by_mail_company_invitations_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteIn"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteOut"}}}},"400":{"description":"El mail o el `ref` no son válidos."},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"404":{"description":"El QR (`group`) no existe o no es de tu empresa."},"429":{"description":"Llegaste al tope de invitaciones del día."},"503":{"description":"Este servidor todavía no manda mails."}}}},"/company/send":{"post":{"tags":["company"],"summary":"Mandar un aviso (igual que POST /send)","description":"Exactamente lo mismo que `POST /send` (mismo cuerpo, misma respuesta). El panel usa esta ruta; tu sistema puede usar cualquiera de las dos con el mismo token. **El 202 no significa «entregado»**.","operationId":"send_company_send_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendIn"}}},"required":true},"responses":{"202":{"description":"Recibimos el pedido. No dice si llegó ni a cuántos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendOut"}}}},"400":{"description":"Algún campo no cumple las reglas."},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."}}}},"/company/logo":{"get":{"tags":["company"],"summary":"Mi logo","description":"El estado del logo de tu empresa: la URL del aprobado (si hay) y si hay uno nuevo en revisión.","operationId":"logo_company_logo_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoOut"}}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."}}},"put":{"tags":["company"],"summary":"Subir el logo de mi empresa","description":"Sube el logo que acompaña a tus avisos en la pantalla de bloqueo. **El cuerpo es la imagen** (PNG o JPEG, hasta 1 MB, de 128 a 4096 píxeles de lado), sin multipart. La recortamos cuadrada, la achicamos a 256 × 256 y la guardamos en Noti: **la app nunca baja nada de tu servidor**, así que no hay forma de poner una URL propia. Una persona de Noti lo revisa antes de usarlo; mientras tanto `pending_review` es `true` y los avisos salen como siempre. Hasta 5 subidas por hora.","operationId":"upload_logo_company_logo_put","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoOut"}}}},"400":{"description":"No es un PNG o JPEG válido, o es muy chico / muy grande de lado."},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"413":{"description":"Pesa más de 1 MB."},"429":{"description":"Demasiadas subidas esta hora."}}},"delete":{"tags":["company"],"summary":"Sacar el logo de mi empresa","description":"Borra tu logo (el aprobado y el que esté en revisión). Tus avisos vuelven a salir con el ícono de Noti.","operationId":"delete_logo_company_logo_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoOut"}}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."}}}},"/company/logo/image":{"get":{"tags":["company"],"summary":"Ver mi logo (el nuevo si está en revisión)","description":"La imagen de tu logo, ya recortada, para mostrarla en el panel: la que esté en revisión y, si no hay, la aprobada. Es privada de tu empresa; la URL pública sólo existe para el logo aprobado.","operationId":"logo_image_company_logo_image_get","responses":{"200":{"description":"El PNG.","content":{"application/json":{"schema":{}},"image/png":{}}},"401":{"description":"Falta la credencial, está mal escrita o fue revocada."},"404":{"description":"Todavía no subiste ningún logo."}}}},"/company/tokens":{"get":{"tags":["company"],"summary":"Mis tokens (panel)","description":"Los tokens de API de tu empresa. Nunca devuelve el valor: sólo el prefijo y las fechas. Sólo para una persona del equipo con sesión iniciada (un token no puede ver ni crear tokens).","operationId":"tokens_company_tokens_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TokenOut"},"type":"array","title":"Response Tokens Company Tokens Get"}}}},"401":{"description":"Sesión inválida."},"404":{"description":"Con un token de empresa no existe: es sólo para el panel."}}},"post":{"tags":["company"],"summary":"Crear un token (panel)","description":"Crea un token de API (`ntf_…`). **El valor se muestra una sola vez**, en esta respuesta. Hasta 10 tokens activos a la vez. Sólo para una persona del equipo con sesión iniciada.","operationId":"new_token_company_tokens_post","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenCreatedOut"}}}},"401":{"description":"Sesión inválida."},"404":{"description":"Con un token de empresa no existe: es sólo para el panel."},"409":{"description":"Ya hay 10 tokens activos: revocá alguno."}}}},"/company/tokens/{token_id}":{"delete":{"tags":["company"],"summary":"Revocar un token (panel)","description":"Revoca un token: deja de funcionar en el acto. Sólo para una persona del equipo con sesión iniciada.","operationId":"revoke_company_tokens__token_id__delete","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Sesión inválida."},"404":{"description":"Ese token no existe o no es de tu empresa."},"400":{"description":"Algún campo no cumple las reglas."}}}}},"components":{"schemas":{"CompanyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"code":{"type":"string","title":"Code"},"status":{"type":"string","title":"Status"},"signups":{"type":"integer","title":"Signups"},"qr_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Qr Text"},"page_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Url"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"}},"type":"object","required":["id","name","code","status","signups","qr_text","page_url"],"title":"CompanyOut","description":"Tu empresa."},"GroupOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"signups":{"type":"integer","title":"Signups"},"archived":{"type":"boolean","title":"Archived"},"qr_text":{"type":"string","title":"Qr Text"},"page_url":{"type":"string","title":"Page Url"}},"type":"object","required":["id","code","name","signups","archived","qr_text","page_url"],"title":"GroupOut","description":"Un QR por grupo (una sucursal, un tema)."},"LogoOut":{"properties":{"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"pending_review":{"type":"boolean","title":"Pending Review"}},"type":"object","required":["logo_url","pending_review"],"title":"LogoOut","description":"El logo que acompaña a tus avisos."},"TokenOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"prefix":{"type":"string","title":"Prefix"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"}},"type":"object","required":["id","prefix","created_at","revoked_at","last_used_at"],"title":"TokenOut","description":"Lo que se ve de un token después de creado: nunca el valor."},"SignupOut":{"properties":{"company":{"$ref":"#/components/schemas/CompanyOut"},"created":{"type":"boolean","title":"Created"},"message":{"type":"string","title":"Message"}},"type":"object","required":["company","created","message"],"title":"SignupOut"},"GroupIn":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"},"code":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Code"}},"type":"object","required":["name"],"title":"GroupIn","description":"Un QR nuevo.","examples":[{"code":"palermo","name":"Sucursal Palermo"}]},"SignupIn":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name","description":"Cómo se llama tu empresa."},"code":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Code","description":"Opcional: el código que la gente escribe en la app (3 a 24 caracteres: minúsculas, números y guiones). Si falta, generamos uno corto."}},"type":"object","required":["name"],"title":"SignupIn"},"StatsOut":{"properties":{"signups_total":{"type":"integer","title":"Signups Total"},"avisos_enviados_30d":{"type":"integer","title":"Avisos Enviados 30D"},"salud":{"additionalProperties":true,"type":"object","title":"Salud"},"qrs":{"items":{"$ref":"#/components/schemas/QrSaludOut"},"type":"array","title":"Qrs"}},"type":"object","required":["signups_total","avisos_enviados_30d","salud","qrs"],"title":"StatsOut","description":"Qué tan bien lo hacés, sin decir nunca quién se fue."},"GroupPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":80,"minLength":1},{"type":"null"}],"title":"Name"},"archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Archived"}},"type":"object","title":"GroupPatch"},"ConnectOut":{"properties":{"url":{"type":"string","title":"Url"},"qr_text":{"type":"string","title":"Qr Text"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["url","qr_text","expires_at"],"title":"ConnectOut","description":"El pase, para mostrárselo a la persona. Sirve una vez y vence a los 7 días."},"CompanyPatch":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"}},"type":"object","required":["name"],"title":"CompanyPatch"},"InviteIn":{"properties":{"email":{"type":"string","maxLength":254,"minLength":3,"title":"Email","description":"El mail de la persona."},"ref":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Ref","description":"Opcional: el id con el que tu empresa la conoce. Si falta, es su mail (en minúsculas)."},"group":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Group","description":"Opcional: el código de un QR al que sumarla."}},"type":"object","required":["email"],"title":"InviteIn","description":"A quién invitar por mail.","examples":[{"email":"ana@tuempresa.com"},{"email":"ana@tuempresa.com","group":"equipo","ref":"ana"}]},"ConnectIn":{"properties":{"ref":{"type":"string","maxLength":128,"minLength":1,"title":"Ref"},"group":{"anyOf":[{"type":"string","maxLength":24},{"type":"null"}],"title":"Group"}},"type":"object","required":["ref"],"title":"ConnectIn","description":"Para quién es el pase.","examples":[{"ref":"cliente-1042"},{"group":"palermo","ref":"cliente-1042"}]},"TokenCreatedOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"prefix":{"type":"string","title":"Prefix"},"token":{"type":"string","title":"Token"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","prefix","token","created_at"],"title":"TokenCreatedOut","description":"La respuesta de crear un token: el único lugar donde el token viaja en\nclaro. Guardalo ahora, no se vuelve a mostrar."},"SendIn":{"properties":{"to":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"To","description":"La dirección de una persona de Noti (`@handle`). Es para personas que se mandan avisos entre sí; una empresa usa `group` o `ref`."},"group":{"anyOf":[{"type":"string","maxLength":24,"minLength":1},{"type":"null"}],"title":"Group","description":"El código de uno de tus QR: el aviso le llega sólo a quienes se sumaron por ese QR.","examples":["palermo"]},"ref":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Ref","description":"El id con el que vos conocés a la persona (el `ref` que pusiste en `POST /company/connect-links`). El aviso le llega sólo a ella.","examples":["cliente-1042"]},"emoji":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Emoji","description":"Exactamente **un** emoji, al lado del título.","examples":["🥬"]},"title":{"type":"string","maxLength":100,"minLength":1,"title":"Title","description":"Lo que se lee en la pantalla bloqueada.","examples":["Llegó la verdura"]},"body":{"type":"string","maxLength":1000,"minLength":1,"title":"Body","description":"El texto del aviso. El push muestra los primeros 140 caracteres; el resto se lee al abrir la app.","examples":["Hoy hay 2x1 en hojas verdes hasta las 20."]},"cta":{"anyOf":[{"$ref":"#/components/schemas/Cta"},{"type":"null"}],"description":"Un botón con un link. `label` y `url` van juntos."}},"type":"object","required":["title","body"],"title":"SendIn","description":"Un aviso. Sin `to`, `group` ni `ref` es una **difusión**: le llega a todos\nlos que se suscribieron a tu empresa. Se puede poner **uno solo** de los tres.","examples":[{"body":"Hoy hay 2x1 en hojas verdes hasta las 20.","cta":{"label":"Ver el local","url":"https://mercadoverde.com"},"emoji":"🥬","title":"Llegó la verdura"},{"body":"Hoy cerramos más tarde.","group":"palermo","title":"Palermo abre hasta las 22"},{"body":"Llega hoy antes de las 18.","emoji":"📦","ref":"cliente-1042","title":"Tu pedido salió"}]},"AvisoPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AvisoOut"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items","next_cursor"],"title":"AvisoPage"},"InviteOut":{"properties":{"ref":{"type":"string","title":"Ref"},"message":{"type":"string","title":"Message"}},"type":"object","required":["ref","message"],"title":"InviteOut"},"AvisoOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"El mismo `id` que te devolvió el 202 de `POST /send`."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"kind":{"type":"string","title":"Kind","description":"`broadcast` (a todos), `group` (a un QR), `ref` (a una persona por tu `ref`) o `to`."},"title":{"type":"string","title":"Title"},"emoji":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Emoji"},"group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group","description":"El código del QR, si fue a un QR."},"ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ref","description":"Tu `ref`, si fue a una persona."},"has_cta":{"type":"boolean","title":"Has Cta"},"seen_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Seen On","description":"Sólo en avisos a UNA persona: el DÍA (sin hora) en que lo abrió. `null` quiere decir «no lo sabemos» y es exactamente lo mismo si no lo abrió, lo silenció, te cortó o nunca se vinculó: nunca es un «no lo vio»."},"seen_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seen Count","description":"Sólo en avisos a muchos: cuántas personas lo abrieron. `null` hasta que llegan a `min_audience`; por debajo no damos número. Sólo sube."},"click_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Click Count","description":"Cuántas tocaron el botón (sólo si el aviso tiene botón). Misma regla que `seen_count`."},"min_audience":{"type":"integer","title":"Min Audience","description":"Desde cuántas personas que lo vieron mostramos números."}},"type":"object","required":["id","created_at","kind","title","emoji","group","ref","has_cta","seen_on","seen_count","click_count","min_audience"],"title":"AvisoOut","description":"Un aviso que mandaste y lo que podés saber de él: sólo lo positivo."},"SendOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"}},"type":"object","required":["id"],"title":"SendOut","description":"Siempre lo mismo, llegue o no. **No significa «entregado»**: es un\nidentificador que no dice nada sobre quién recibió el aviso."},"QrSaludOut":{"properties":{"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"signups":{"type":"integer","title":"Signups"},"salud":{"additionalProperties":true,"type":"object","title":"Salud"}},"type":"object","required":["code","name","signups","salud"],"title":"QrSaludOut"},"Cta":{"properties":{"label":{"type":"string","maxLength":40,"minLength":1,"title":"Label","description":"El texto del botón.","examples":["Ver el local"]},"url":{"type":"string","maxLength":2048,"minLength":1,"title":"Url","description":"A dónde lleva el botón. Empieza con `http://`, `https://`, `mailto:`, `tel:`, `sms:` o `whatsapp:`. **No viaja en el push**: se ve al abrir el aviso en la app.","examples":["https://mercadoverde.com"]}},"type":"object","required":["label","url"],"title":"Cta","description":"Va entero o no va: los dos campos son obligatorios."}},"securitySchemes":{"TokenDeEmpresa":{"type":"http","scheme":"bearer","description":"El token de tu empresa (`ntf_…`). Se crea en el panel y se muestra una sola vez."}}}}