{"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.gualda.ai"}],"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/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/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/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":{"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"},"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)."},"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ó"}]},"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"},"CompanyPatch":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"}},"type":"object","required":["name"],"title":"CompanyPatch"},"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"}},"type":"object","required":["id","name","code","status","signups","qr_text","page_url"],"title":"CompanyOut","description":"Tu empresa."},"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."},"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."},"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"},"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."},"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"}]},"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."}},"securitySchemes":{"TokenDeEmpresa":{"type":"http","scheme":"bearer","description":"El token de tu empresa (`ntf_…`). Se crea en el panel y se muestra una sola vez."}}}}