@hablala/api-contract 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +906 -11
  2. package/openapi.json +1261 -56
  3. package/package.json +3 -3
package/openapi.json CHANGED
@@ -730,6 +730,48 @@
730
730
  "AccountManifestSections": {
731
731
  "description": "Las secciones declarables de una cuenta (ADR-027). El `kind` fija la SUPERFICIE: una `organization` declara el diccionario (objects…), la mensajería (spaces/channels) y el acceso (roles, sso, domains, invitations); un `individual` declara su `profile` y su `privacy` (singletons). Cada sección es opcional (ausente = no gestionada). Gobernanza: la mayoría es AUTORITATIVA (lo no declarado se archiva — policies/roles/sso se BORRAN); son ADITIVAS (siembra, no expulsa) los `initialMembers` de un canal, los `domains` y las `invitations` — lifecycle, no estado deseado. Los datos (records/mensajes) no son configuración.",
732
732
  "properties": {
733
+ "actions": {
734
+ "oneOf": [
735
+ {
736
+ "items": {
737
+ "$ref": "#/components/schemas/ManifestAction"
738
+ },
739
+ "type": "array",
740
+ "x-struct": null,
741
+ "x-validate": null
742
+ },
743
+ {
744
+ "properties": {
745
+ "items": {
746
+ "items": {
747
+ "$ref": "#/components/schemas/ManifestAction"
748
+ },
749
+ "type": "array",
750
+ "x-struct": null,
751
+ "x-validate": null
752
+ },
753
+ "mode": {
754
+ "description": "`authoritative` (default): lo no declarado se archiva/borra. `additive`: solo crea/actualiza lo declarado, sin expulsar lo demás (least astonishment).",
755
+ "enum": [
756
+ "authoritative",
757
+ "additive"
758
+ ],
759
+ "type": "string",
760
+ "x-struct": null,
761
+ "x-validate": null
762
+ }
763
+ },
764
+ "required": [
765
+ "items"
766
+ ],
767
+ "type": "object",
768
+ "x-struct": null,
769
+ "x-validate": null
770
+ }
771
+ ],
772
+ "x-struct": null,
773
+ "x-validate": null
774
+ },
733
775
  "agents": {
734
776
  "oneOf": [
735
777
  {
@@ -1277,6 +1319,54 @@
1277
1319
  "x-struct": "Elixir.HablalaApiWeb.Schemas.AccountManifestSections",
1278
1320
  "x-validate": null
1279
1321
  },
1322
+ "ActionRunRequest": {
1323
+ "description": "Input de una action transaccional. `input` es el mapa que interpolan los steps (`{{input.…}}`, y `{{item.…}}` en los steps con for_each).",
1324
+ "properties": {
1325
+ "idempotency_key": {
1326
+ "description": "Clave natural para reintentos seguros (se propaga a cada step creado).",
1327
+ "type": "string",
1328
+ "x-struct": null,
1329
+ "x-validate": null
1330
+ },
1331
+ "input": {
1332
+ "description": "Los valores de entrada de la action (forma libre, definida por sus steps).",
1333
+ "type": "object",
1334
+ "x-struct": null,
1335
+ "x-validate": null
1336
+ }
1337
+ },
1338
+ "title": "ActionRunRequest",
1339
+ "type": "object",
1340
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.ActionRunRequest",
1341
+ "x-validate": null
1342
+ },
1343
+ "ActionRunResponse": {
1344
+ "properties": {
1345
+ "data": {
1346
+ "properties": {
1347
+ "results": {
1348
+ "description": "Por step_id, el/los records creados (`{id, properties}`). Un step con for_each devuelve una lista; uno simple, un objeto.",
1349
+ "type": "object",
1350
+ "x-struct": null,
1351
+ "x-validate": null
1352
+ }
1353
+ },
1354
+ "required": [
1355
+ "results"
1356
+ ],
1357
+ "type": "object",
1358
+ "x-struct": null,
1359
+ "x-validate": null
1360
+ }
1361
+ },
1362
+ "required": [
1363
+ "data"
1364
+ ],
1365
+ "title": "ActionRunResponse",
1366
+ "type": "object",
1367
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.ActionRunResponse",
1368
+ "x-validate": null
1369
+ },
1280
1370
  "AddMemberRequest": {
1281
1371
  "description": "Añade un usuario del tenant a una conversación (invitar a un canal).",
1282
1372
  "properties": {
@@ -1698,6 +1788,134 @@
1698
1788
  "x-struct": "Elixir.HablalaApiWeb.Schemas.AgentsResponse",
1699
1789
  "x-validate": null
1700
1790
  },
1791
+ "AggregateQueryRequest": {
1792
+ "description": "Aggregation query: agrupa records por `dimensions` y calcula `measures` (count/sum/avg/min/max), bajo la misma gobernanza (RBAC + ABAC default-deny) que la lectura puntual. El par canónico measures + dimensions del semantic layer (Cube.dev).",
1793
+ "properties": {
1794
+ "dimensions": {
1795
+ "description": "Los attributes por los que agrupar (GROUP BY).",
1796
+ "items": {
1797
+ "properties": {
1798
+ "attr": {
1799
+ "description": "Slug del attribute a agrupar.",
1800
+ "type": "string",
1801
+ "x-struct": null,
1802
+ "x-validate": null
1803
+ },
1804
+ "granularity": {
1805
+ "description": "Solo para date/timestamp (time dimension): trunca el valor a ese bucket.",
1806
+ "enum": [
1807
+ "day",
1808
+ "week",
1809
+ "month",
1810
+ "quarter",
1811
+ "year"
1812
+ ],
1813
+ "type": "string",
1814
+ "x-struct": null,
1815
+ "x-validate": null
1816
+ }
1817
+ },
1818
+ "required": [
1819
+ "attr"
1820
+ ],
1821
+ "type": "object",
1822
+ "x-struct": null,
1823
+ "x-validate": null
1824
+ },
1825
+ "type": "array",
1826
+ "x-struct": null,
1827
+ "x-validate": null
1828
+ },
1829
+ "measures": {
1830
+ "description": "Las agregaciones a calcular (al menos una).",
1831
+ "items": {
1832
+ "properties": {
1833
+ "as": {
1834
+ "description": "Alias de salida (default: `count` o `<fn>_<attr>`).",
1835
+ "type": "string",
1836
+ "x-struct": null,
1837
+ "x-validate": null
1838
+ },
1839
+ "attr": {
1840
+ "description": "Slug del attribute a agregar (number para sum/avg; number/date/timestamp para min/max). Omitido solo en `count`.",
1841
+ "type": "string",
1842
+ "x-struct": null,
1843
+ "x-validate": null
1844
+ },
1845
+ "fn": {
1846
+ "description": "La función de agregación. `count` no lleva `attr`.",
1847
+ "enum": [
1848
+ "count",
1849
+ "sum",
1850
+ "avg",
1851
+ "min",
1852
+ "max"
1853
+ ],
1854
+ "type": "string",
1855
+ "x-struct": null,
1856
+ "x-validate": null
1857
+ }
1858
+ },
1859
+ "required": [
1860
+ "fn"
1861
+ ],
1862
+ "type": "object",
1863
+ "x-struct": null,
1864
+ "x-validate": null
1865
+ },
1866
+ "type": "array",
1867
+ "x-struct": null,
1868
+ "x-validate": null
1869
+ },
1870
+ "object": {
1871
+ "description": "Slug del objeto raíz (requerido).",
1872
+ "type": "string",
1873
+ "x-struct": null,
1874
+ "x-validate": null
1875
+ },
1876
+ "where": {
1877
+ "description": "Mismo formato que en la consulta puntual: filtra ANTES de agregar.",
1878
+ "items": {
1879
+ "type": "object",
1880
+ "x-struct": null,
1881
+ "x-validate": null
1882
+ },
1883
+ "type": "array",
1884
+ "x-struct": null,
1885
+ "x-validate": null
1886
+ }
1887
+ },
1888
+ "required": [
1889
+ "object",
1890
+ "measures"
1891
+ ],
1892
+ "title": "AggregateQueryRequest",
1893
+ "type": "object",
1894
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.AggregateQueryRequest",
1895
+ "x-validate": null
1896
+ },
1897
+ "AggregateResultResponse": {
1898
+ "properties": {
1899
+ "data": {
1900
+ "description": "Un grupo por combinación de dimensions (o un solo grupo con los totales si no hay dimensions). Cada grupo es un mapa: cada dimension por su slug y cada measure por su alias de salida.",
1901
+ "items": {
1902
+ "type": "object",
1903
+ "x-struct": null,
1904
+ "x-validate": null
1905
+ },
1906
+ "type": "array",
1907
+ "x-struct": null,
1908
+ "x-validate": null
1909
+ }
1910
+ },
1911
+ "required": [
1912
+ "data"
1913
+ ],
1914
+ "title": "AggregateResultResponse",
1915
+ "type": "object",
1916
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.AggregateResultResponse",
1917
+ "x-validate": null
1918
+ },
1701
1919
  "ApiKey": {
1702
1920
  "description": "Metadatos de una API key de la Public API. El secreto en claro NO se incluye aquí — solo se devuelve una vez, al crearla.",
1703
1921
  "properties": {
@@ -2030,6 +2248,13 @@
2030
2248
  "x-struct": null,
2031
2249
  "x-validate": null
2032
2250
  },
2251
+ "seatsPerSlot": {
2252
+ "description": "Capacidad del slot: cuántas citas solapadas del mismo organizador caben a la vez (Cal.com seats per time slot). 1 = no-solape estricto; N = hasta N concurrentes.",
2253
+ "minimum": 1,
2254
+ "type": "integer",
2255
+ "x-struct": null,
2256
+ "x-validate": null
2257
+ },
2033
2258
  "startsAt": {
2034
2259
  "format": "date-time",
2035
2260
  "type": "string",
@@ -2100,6 +2325,13 @@
2100
2325
  "x-struct": null,
2101
2326
  "x-validate": null
2102
2327
  },
2328
+ "seatsPerSlot": {
2329
+ "description": "Capacidad del slot (default 1 = no-solape estricto). N permite hasta N citas solapadas del mismo organizador (aforo, clases, atención en paralelo).",
2330
+ "minimum": 1,
2331
+ "type": "integer",
2332
+ "x-struct": null,
2333
+ "x-validate": null
2334
+ },
2103
2335
  "startsAt": {
2104
2336
  "format": "date-time",
2105
2337
  "type": "string",
@@ -6734,6 +6966,60 @@
6734
6966
  "x-struct": "Elixir.HablalaApiWeb.Schemas.MachineOrganizationResponse",
6735
6967
  "x-validate": null
6736
6968
  },
6969
+ "ManifestAction": {
6970
+ "description": "Una action transaccional del tenant declarada (sección `actions/`), identificada por `slug`. Ejecuta sus `steps` en UNA transacción (todo-o-nada) al invocar `POST /v1/actions/:slug`: crear una cabecera + N líneas de forma atómica (el caso `create_rental` de una app interna). Cada step declara un `object` del diccionario y las `properties` a escribir, con interpolación `{{input.…}}`, `{{steps.<id>.id}}` (ref entre steps) y `{{item.…}}` en un step con `for_each`. Sección AUTORITATIVA: retirarla del manifiesto la archiva. `description`/`synonyms` son el contexto con el que los agentes LLM la descubren vía MCP (obligatoria la descripción, como el resto del diccionario).",
6971
+ "properties": {
6972
+ "description": {
6973
+ "description": "Qué hace la action (contexto para humanos y agentes LLM). Obligatoria al crear.",
6974
+ "type": "string",
6975
+ "x-struct": null,
6976
+ "x-validate": null
6977
+ },
6978
+ "slug": {
6979
+ "description": "Identidad estable snake_case, única por organización (match key).",
6980
+ "type": "string",
6981
+ "x-struct": null,
6982
+ "x-validate": null
6983
+ },
6984
+ "steps": {
6985
+ "description": "Lista NO vacía de pasos. Cada paso: `id` (opcional, para refs `{{steps.<id>.id}}`), `object` (slug del diccionario), `properties` (mapa slug→valor con interpolación) y opcionalmente `for_each` (una ruta del input que expande el step en N records).",
6986
+ "items": {
6987
+ "additionalProperties": true,
6988
+ "type": "object",
6989
+ "x-struct": null,
6990
+ "x-validate": null
6991
+ },
6992
+ "type": "array",
6993
+ "x-struct": null,
6994
+ "x-validate": null
6995
+ },
6996
+ "synonyms": {
6997
+ "description": "Sinónimos para el descubrimiento por búsqueda/LLM.",
6998
+ "items": {
6999
+ "type": "string",
7000
+ "x-struct": null,
7001
+ "x-validate": null
7002
+ },
7003
+ "type": "array",
7004
+ "x-struct": null,
7005
+ "x-validate": null
7006
+ },
7007
+ "title": {
7008
+ "description": "Nombre visible; si se omite, se deriva del slug.",
7009
+ "type": "string",
7010
+ "x-struct": null,
7011
+ "x-validate": null
7012
+ }
7013
+ },
7014
+ "required": [
7015
+ "slug",
7016
+ "steps"
7017
+ ],
7018
+ "title": "ManifestAction",
7019
+ "type": "object",
7020
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.ManifestAction",
7021
+ "x-validate": null
7022
+ },
6737
7023
  "ManifestAgent": {
6738
7024
  "description": "Un agente de IA declarado, identificado por `handle`. Archivar (no declararlo) lo revoca. `ownerUserId` (id público `user_…`) es obligatorio al crear con un access token de máquina; con un usuario, el creador es el dueño por defecto.",
6739
7025
  "properties": {
@@ -7845,49 +8131,178 @@
7845
8131
  "x-struct": "Elixir.HablalaApiWeb.Schemas.MediaResponse",
7846
8132
  "x-validate": null
7847
8133
  },
7848
- "Membership": {
8134
+ "Member": {
8135
+ "description": "Un miembro de la organización: identidad legible + sus roles (sin secretos).",
7849
8136
  "properties": {
7850
- "avatarUrl": {
7851
- "description": "Avatar override para esta org; `null` usa el global del usuario.",
7852
- "nullable": true,
7853
- "type": "string",
7854
- "x-struct": null,
7855
- "x-validate": null
7856
- },
7857
8137
  "displayName": {
7858
- "description": "Nombre visible en esta org (override del `name` global).",
7859
8138
  "nullable": true,
7860
8139
  "type": "string",
7861
8140
  "x-struct": null,
7862
8141
  "x-validate": null
7863
8142
  },
7864
8143
  "email": {
7865
- "description": "Email corporativo del miembro (dato de org entre colegas, acotado al tenant). `null` si la cuenta no tiene email. No confundir con el perfil público `user`, que no lleva PII.",
7866
- "format": "email",
7867
8144
  "nullable": true,
7868
8145
  "type": "string",
7869
8146
  "x-struct": null,
7870
8147
  "x-validate": null
7871
8148
  },
7872
8149
  "id": {
8150
+ "description": "Id de la membresía (`mbr_…`).",
7873
8151
  "type": "string",
7874
8152
  "x-struct": null,
7875
8153
  "x-validate": null
7876
8154
  },
7877
- "insertedAt": {
7878
- "format": "date-time",
8155
+ "roles": {
8156
+ "description": "Los roles del miembro por nombre (owner/admin/member o custom).",
8157
+ "items": {
8158
+ "type": "string",
8159
+ "x-struct": null,
8160
+ "x-validate": null
8161
+ },
8162
+ "type": "array",
8163
+ "x-struct": null,
8164
+ "x-validate": null
8165
+ }
8166
+ },
8167
+ "required": [
8168
+ "id",
8169
+ "roles"
8170
+ ],
8171
+ "title": "Member",
8172
+ "type": "object",
8173
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.Member",
8174
+ "x-validate": null
8175
+ },
8176
+ "MemberLoginRequest": {
8177
+ "description": "Credenciales de un miembro (empleado que opera la org). La organización sale de la API key.",
8178
+ "properties": {
8179
+ "email": {
8180
+ "format": "email",
7879
8181
  "type": "string",
7880
8182
  "x-struct": null,
7881
8183
  "x-validate": null
7882
8184
  },
7883
- "organizationId": {
8185
+ "password": {
8186
+ "format": "password",
7884
8187
  "type": "string",
7885
8188
  "x-struct": null,
7886
8189
  "x-validate": null
7887
- },
7888
- "roles": {
7889
- "items": {
7890
- "$ref": "#/components/schemas/RoleRef"
8190
+ }
8191
+ },
8192
+ "required": [
8193
+ "email",
8194
+ "password"
8195
+ ],
8196
+ "title": "MemberLoginRequest",
8197
+ "type": "object",
8198
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.MemberLoginRequest",
8199
+ "x-validate": null
8200
+ },
8201
+ "MemberResponse": {
8202
+ "properties": {
8203
+ "data": {
8204
+ "$ref": "#/components/schemas/Member"
8205
+ }
8206
+ },
8207
+ "required": [
8208
+ "data"
8209
+ ],
8210
+ "title": "MemberResponse",
8211
+ "type": "object",
8212
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.MemberResponse",
8213
+ "x-validate": null
8214
+ },
8215
+ "MemberSession": {
8216
+ "properties": {
8217
+ "expiresAt": {
8218
+ "format": "date-time",
8219
+ "nullable": true,
8220
+ "type": "string",
8221
+ "x-struct": null,
8222
+ "x-validate": null
8223
+ },
8224
+ "member": {
8225
+ "$ref": "#/components/schemas/Member"
8226
+ },
8227
+ "prefix": {
8228
+ "nullable": true,
8229
+ "type": "string",
8230
+ "x-struct": null,
8231
+ "x-validate": null
8232
+ },
8233
+ "token": {
8234
+ "description": "El member session token en claro (`mem_…`), legible SOLO en esta respuesta.",
8235
+ "type": "string",
8236
+ "x-struct": null,
8237
+ "x-validate": null
8238
+ }
8239
+ },
8240
+ "required": [
8241
+ "token",
8242
+ "member"
8243
+ ],
8244
+ "title": "MemberSession",
8245
+ "type": "object",
8246
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.MemberSession",
8247
+ "x-validate": null
8248
+ },
8249
+ "MemberSessionResponse": {
8250
+ "properties": {
8251
+ "data": {
8252
+ "$ref": "#/components/schemas/MemberSession"
8253
+ }
8254
+ },
8255
+ "required": [
8256
+ "data"
8257
+ ],
8258
+ "title": "MemberSessionResponse",
8259
+ "type": "object",
8260
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.MemberSessionResponse",
8261
+ "x-validate": null
8262
+ },
8263
+ "Membership": {
8264
+ "properties": {
8265
+ "avatarUrl": {
8266
+ "description": "Avatar override para esta org; `null` usa el global del usuario.",
8267
+ "nullable": true,
8268
+ "type": "string",
8269
+ "x-struct": null,
8270
+ "x-validate": null
8271
+ },
8272
+ "displayName": {
8273
+ "description": "Nombre visible en esta org (override del `name` global).",
8274
+ "nullable": true,
8275
+ "type": "string",
8276
+ "x-struct": null,
8277
+ "x-validate": null
8278
+ },
8279
+ "email": {
8280
+ "description": "Email corporativo del miembro (dato de org entre colegas, acotado al tenant). `null` si la cuenta no tiene email. No confundir con el perfil público `user`, que no lleva PII.",
8281
+ "format": "email",
8282
+ "nullable": true,
8283
+ "type": "string",
8284
+ "x-struct": null,
8285
+ "x-validate": null
8286
+ },
8287
+ "id": {
8288
+ "type": "string",
8289
+ "x-struct": null,
8290
+ "x-validate": null
8291
+ },
8292
+ "insertedAt": {
8293
+ "format": "date-time",
8294
+ "type": "string",
8295
+ "x-struct": null,
8296
+ "x-validate": null
8297
+ },
8298
+ "organizationId": {
8299
+ "type": "string",
8300
+ "x-struct": null,
8301
+ "x-validate": null
8302
+ },
8303
+ "roles": {
8304
+ "items": {
8305
+ "$ref": "#/components/schemas/RoleRef"
7891
8306
  },
7892
8307
  "type": "array",
7893
8308
  "x-struct": null,
@@ -10114,6 +10529,111 @@
10114
10529
  "x-struct": "Elixir.HablalaApiWeb.Schemas.PresignResult",
10115
10530
  "x-validate": null
10116
10531
  },
10532
+ "ProvisionMemberInput": {
10533
+ "description": "Datos para dar de alta un operador DIRECTO (sin invitación por email): queda miembro activo al instante y establece su contraseña por el link de set-password de la respuesta.",
10534
+ "properties": {
10535
+ "email": {
10536
+ "description": "Email del operador. Si ya existe un usuario con ese email, se reutiliza.",
10537
+ "format": "email",
10538
+ "type": "string",
10539
+ "x-struct": null,
10540
+ "x-validate": null
10541
+ },
10542
+ "role": {
10543
+ "description": "Nombre del rol a asignar (p. ej. `operator`). Opcional.",
10544
+ "nullable": true,
10545
+ "type": "string",
10546
+ "x-struct": null,
10547
+ "x-validate": null
10548
+ }
10549
+ },
10550
+ "required": [
10551
+ "email"
10552
+ ],
10553
+ "title": "ProvisionMemberInput",
10554
+ "type": "object",
10555
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.ProvisionMemberInput",
10556
+ "x-validate": null
10557
+ },
10558
+ "ProvisionMemberRequest": {
10559
+ "properties": {
10560
+ "member": {
10561
+ "$ref": "#/components/schemas/ProvisionMemberInput"
10562
+ }
10563
+ },
10564
+ "required": [
10565
+ "member"
10566
+ ],
10567
+ "title": "ProvisionMemberRequest",
10568
+ "type": "object",
10569
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.ProvisionMemberRequest",
10570
+ "x-validate": null
10571
+ },
10572
+ "ProvisionMemberResponse": {
10573
+ "description": "El operador dado de alta. `passwordSetupToken` es el token de un solo uso con el que la persona establece SU contraseña (nunca se le comparte una contraseña generada). `userCreated` indica si el usuario nació con esta alta o ya existía (en cuyo caso su contraseña no se toca).",
10574
+ "properties": {
10575
+ "data": {
10576
+ "properties": {
10577
+ "displayName": {
10578
+ "nullable": true,
10579
+ "type": "string",
10580
+ "x-struct": null,
10581
+ "x-validate": null
10582
+ },
10583
+ "email": {
10584
+ "nullable": true,
10585
+ "type": "string",
10586
+ "x-struct": null,
10587
+ "x-validate": null
10588
+ },
10589
+ "id": {
10590
+ "description": "Id de la membresía (`mbr_…`).",
10591
+ "type": "string",
10592
+ "x-struct": null,
10593
+ "x-validate": null
10594
+ },
10595
+ "passwordSetupToken": {
10596
+ "description": "Token de set-password (un solo uso). Entrégaselo a la persona para que fije su contraseña vía el flujo de reset. NO es una contraseña.",
10597
+ "type": "string",
10598
+ "x-struct": null,
10599
+ "x-validate": null
10600
+ },
10601
+ "roles": {
10602
+ "items": {
10603
+ "type": "string",
10604
+ "x-struct": null,
10605
+ "x-validate": null
10606
+ },
10607
+ "type": "array",
10608
+ "x-struct": null,
10609
+ "x-validate": null
10610
+ },
10611
+ "userCreated": {
10612
+ "description": "`true` si el usuario nació con esta alta; `false` si ya existía.",
10613
+ "type": "boolean",
10614
+ "x-struct": null,
10615
+ "x-validate": null
10616
+ }
10617
+ },
10618
+ "required": [
10619
+ "id",
10620
+ "roles",
10621
+ "passwordSetupToken",
10622
+ "userCreated"
10623
+ ],
10624
+ "type": "object",
10625
+ "x-struct": null,
10626
+ "x-validate": null
10627
+ }
10628
+ },
10629
+ "required": [
10630
+ "data"
10631
+ ],
10632
+ "title": "ProvisionMemberResponse",
10633
+ "type": "object",
10634
+ "x-struct": "Elixir.HablalaApiWeb.Schemas.ProvisionMemberResponse",
10635
+ "x-validate": null
10636
+ },
10117
10637
  "PublicOrganization": {
10118
10638
  "description": "Identidad PÚBLICA del tenant (la org del token) — el paralelo horizontal del `shop`/`brand` de Shopify. Deja al cliente pintar su chrome (nombre, logo) y conocer su `endUserObjectSlug` sin hardcodear la marca. NUNCA incluye la PII legal del negocio.",
10119
10639
  "properties": {
@@ -13361,6 +13881,11 @@
13361
13881
  "description": "End-user session token: `Authorization: Bearer <sess_…>`.",
13362
13882
  "scheme": "bearer",
13363
13883
  "type": "http"
13884
+ },
13885
+ "memberToken": {
13886
+ "description": "Member session token: `Authorization: Bearer <mem_…>` (empleado que opera la org).",
13887
+ "scheme": "bearer",
13888
+ "type": "http"
13364
13889
  }
13365
13890
  }
13366
13891
  },
@@ -13582,33 +14107,65 @@
13582
14107
  ]
13583
14108
  }
13584
14109
  },
13585
- "/v1/device-links": {
14110
+ "/v1/actions/{slug}": {
13586
14111
  "post": {
13587
14112
  "callbacks": {},
13588
- "description": "Inicia la vinculación de la web: devuelve un `linkCode` para pintar el QR que el teléfono escaneará. Público (es un flujo de login). El teléfono es la raíz de identidad; la web hereda la sesión al aprobarse (modelo WhatsApp *linked devices*).",
13589
- "operationId": "post /v1/device-links",
13590
- "parameters": [],
14113
+ "description": "Corre los steps de la action `slug` en UNA transacción (todo-o-nada), interpolando el input, y devuelve los records creados por step. `create_rental`/`register_return` y similares. Gate: records:create.",
14114
+ "operationId": "post /v1/actions/{slug}",
14115
+ "parameters": [
14116
+ {
14117
+ "description": "Slug de la action a ejecutar.",
14118
+ "in": "path",
14119
+ "name": "slug",
14120
+ "required": true,
14121
+ "schema": {
14122
+ "type": "string",
14123
+ "x-struct": null,
14124
+ "x-validate": null
14125
+ }
14126
+ }
14127
+ ],
13591
14128
  "requestBody": {
13592
14129
  "content": {
13593
14130
  "application/json": {
13594
14131
  "schema": {
13595
- "$ref": "#/components/schemas/DeviceLinkCreateRequest"
14132
+ "$ref": "#/components/schemas/ActionRunRequest"
13596
14133
  }
13597
14134
  }
13598
14135
  },
13599
- "description": "Contexto del navegador",
14136
+ "description": "Input de la action",
13600
14137
  "required": false
13601
14138
  },
13602
14139
  "responses": {
13603
- "201": {
14140
+ "200": {
13604
14141
  "content": {
13605
14142
  "application/json": {
13606
14143
  "schema": {
13607
- "$ref": "#/components/schemas/DeviceLinkCreateResponse"
14144
+ "$ref": "#/components/schemas/ActionRunResponse"
13608
14145
  }
13609
14146
  }
13610
14147
  },
13611
- "description": "linkCode para el QR"
14148
+ "description": "Resultado por step"
14149
+ },
14150
+ "403": {
14151
+ "content": {
14152
+ "application/json": {
14153
+ "schema": {
14154
+ "$ref": "#/components/schemas/Error"
14155
+ }
14156
+ }
14157
+ },
14158
+ "description": "Sin permiso"
14159
+ },
14160
+ "404": {
14161
+ "content": {
14162
+ "application/json": {
14163
+ "schema": {
14164
+ "$ref": "#/components/schemas/Error"
14165
+ }
14166
+ }
14167
+ },
14168
+ "description": "Action desconocida"
13612
14169
  },
13613
14170
  "422": {
13614
14171
  "content": {
@@ -13618,46 +14175,44 @@
13618
14175
  }
13619
14176
  }
13620
14177
  },
13621
- "description": "Datos inválidos"
14178
+ "description": "Fallo de un step (rollback total)"
13622
14179
  }
13623
14180
  },
13624
- "security": [],
13625
- "summary": "Cross-device — la web pide un QR",
14181
+ "summary": "Ejecutar una action transaccional",
13626
14182
  "tags": [
13627
- "auth"
14183
+ "actions"
13628
14184
  ]
13629
14185
  }
13630
14186
  },
13631
- "/v1/device-links/{link_code}": {
13632
- "get": {
14187
+ "/v1/aggregate": {
14188
+ "post": {
13633
14189
  "callbacks": {},
13634
- "description": "Devuelve `pending` (sigue esperando), `linked` (aprobado: incluye `user` y el `token` de sesión web, SOLO en el primer poll) o `denied`. Público: el `linkCode` es la credencial.",
13635
- "operationId": "get /v1/device-links/{link_code}",
13636
- "parameters": [
13637
- {
13638
- "description": "El `linkCode` del QR (`dlnk_…`).",
13639
- "in": "path",
13640
- "name": "link_code",
13641
- "required": true,
13642
- "schema": {
13643
- "type": "string",
13644
- "x-struct": null,
13645
- "x-validate": null
14190
+ "description": "Agrupa records por `dimensions` y calcula `measures` (count/sum/avg/min/max), bajo la misma gobernanza (RBAC + ABAC default-deny) que la lectura puntual. Reemplaza los reportes que serían RPCs de SQL por-tenant.",
14191
+ "operationId": "post /v1/aggregate",
14192
+ "parameters": [],
14193
+ "requestBody": {
14194
+ "content": {
14195
+ "application/json": {
14196
+ "schema": {
14197
+ "$ref": "#/components/schemas/AggregateQueryRequest"
14198
+ }
13646
14199
  }
13647
- }
13648
- ],
14200
+ },
14201
+ "description": "Aggregation query",
14202
+ "required": false
14203
+ },
13649
14204
  "responses": {
13650
14205
  "200": {
13651
14206
  "content": {
13652
14207
  "application/json": {
13653
14208
  "schema": {
13654
- "$ref": "#/components/schemas/DeviceLinkStatusResponse"
14209
+ "$ref": "#/components/schemas/AggregateResultResponse"
13655
14210
  }
13656
14211
  }
13657
14212
  },
13658
- "description": "Estado del flujo"
14213
+ "description": "Grupos agregados"
13659
14214
  },
13660
- "404": {
14215
+ "403": {
13661
14216
  "content": {
13662
14217
  "application/json": {
13663
14218
  "schema": {
@@ -13665,9 +14220,121 @@
13665
14220
  }
13666
14221
  }
13667
14222
  },
13668
- "description": "Código inexistente o expirado"
14223
+ "description": "Sin permiso"
13669
14224
  },
13670
- "409": {
14225
+ "404": {
14226
+ "content": {
14227
+ "application/json": {
14228
+ "schema": {
14229
+ "$ref": "#/components/schemas/Error"
14230
+ }
14231
+ }
14232
+ },
14233
+ "description": "Objeto desconocido"
14234
+ },
14235
+ "422": {
14236
+ "content": {
14237
+ "application/json": {
14238
+ "schema": {
14239
+ "$ref": "#/components/schemas/Error"
14240
+ }
14241
+ }
14242
+ },
14243
+ "description": "Consulta inválida"
14244
+ }
14245
+ },
14246
+ "summary": "Ejecutar una aggregation query",
14247
+ "tags": [
14248
+ "query"
14249
+ ]
14250
+ }
14251
+ },
14252
+ "/v1/device-links": {
14253
+ "post": {
14254
+ "callbacks": {},
14255
+ "description": "Inicia la vinculación de la web: devuelve un `linkCode` para pintar el QR que el teléfono escaneará. Público (es un flujo de login). El teléfono es la raíz de identidad; la web hereda la sesión al aprobarse (modelo WhatsApp *linked devices*).",
14256
+ "operationId": "post /v1/device-links",
14257
+ "parameters": [],
14258
+ "requestBody": {
14259
+ "content": {
14260
+ "application/json": {
14261
+ "schema": {
14262
+ "$ref": "#/components/schemas/DeviceLinkCreateRequest"
14263
+ }
14264
+ }
14265
+ },
14266
+ "description": "Contexto del navegador",
14267
+ "required": false
14268
+ },
14269
+ "responses": {
14270
+ "201": {
14271
+ "content": {
14272
+ "application/json": {
14273
+ "schema": {
14274
+ "$ref": "#/components/schemas/DeviceLinkCreateResponse"
14275
+ }
14276
+ }
14277
+ },
14278
+ "description": "linkCode para el QR"
14279
+ },
14280
+ "422": {
14281
+ "content": {
14282
+ "application/json": {
14283
+ "schema": {
14284
+ "$ref": "#/components/schemas/Error"
14285
+ }
14286
+ }
14287
+ },
14288
+ "description": "Datos inválidos"
14289
+ }
14290
+ },
14291
+ "security": [],
14292
+ "summary": "Cross-device — la web pide un QR",
14293
+ "tags": [
14294
+ "auth"
14295
+ ]
14296
+ }
14297
+ },
14298
+ "/v1/device-links/{link_code}": {
14299
+ "get": {
14300
+ "callbacks": {},
14301
+ "description": "Devuelve `pending` (sigue esperando), `linked` (aprobado: incluye `user` y el `token` de sesión web, SOLO en el primer poll) o `denied`. Público: el `linkCode` es la credencial.",
14302
+ "operationId": "get /v1/device-links/{link_code}",
14303
+ "parameters": [
14304
+ {
14305
+ "description": "El `linkCode` del QR (`dlnk_…`).",
14306
+ "in": "path",
14307
+ "name": "link_code",
14308
+ "required": true,
14309
+ "schema": {
14310
+ "type": "string",
14311
+ "x-struct": null,
14312
+ "x-validate": null
14313
+ }
14314
+ }
14315
+ ],
14316
+ "responses": {
14317
+ "200": {
14318
+ "content": {
14319
+ "application/json": {
14320
+ "schema": {
14321
+ "$ref": "#/components/schemas/DeviceLinkStatusResponse"
14322
+ }
14323
+ }
14324
+ },
14325
+ "description": "Estado del flujo"
14326
+ },
14327
+ "404": {
14328
+ "content": {
14329
+ "application/json": {
14330
+ "schema": {
14331
+ "$ref": "#/components/schemas/Error"
14332
+ }
14333
+ }
14334
+ },
14335
+ "description": "Código inexistente o expirado"
14336
+ },
14337
+ "409": {
13671
14338
  "content": {
13672
14339
  "application/json": {
13673
14340
  "schema": {
@@ -13966,6 +14633,61 @@
13966
14633
  ]
13967
14634
  }
13968
14635
  },
14636
+ "/v1/invitations": {
14637
+ "post": {
14638
+ "callbacks": {},
14639
+ "description": "Crea una invitación por **email** con un rol opcional (`{\"invitation\": {\"email\": \"…\", \"role_id\": \"role_…\"}}`). Al aceptarla, la persona queda como miembro de la organización y podrá iniciar sesión como operador (member session `mem_…`). La org sale del token (org-less), como el resto de la superficie de máquina. Requiere el permiso `invitations:create` (preset `team_admin`).",
14640
+ "operationId": "post /v1/invitations",
14641
+ "parameters": [],
14642
+ "requestBody": {
14643
+ "content": {
14644
+ "application/json": {
14645
+ "schema": {
14646
+ "$ref": "#/components/schemas/InvitationRequest"
14647
+ }
14648
+ }
14649
+ },
14650
+ "description": "Datos de la invitación",
14651
+ "required": false
14652
+ },
14653
+ "responses": {
14654
+ "201": {
14655
+ "content": {
14656
+ "application/json": {
14657
+ "schema": {
14658
+ "$ref": "#/components/schemas/InvitationResponse"
14659
+ }
14660
+ }
14661
+ },
14662
+ "description": "Invitación creada"
14663
+ },
14664
+ "403": {
14665
+ "content": {
14666
+ "application/json": {
14667
+ "schema": {
14668
+ "$ref": "#/components/schemas/Error"
14669
+ }
14670
+ }
14671
+ },
14672
+ "description": "Sin permiso"
14673
+ },
14674
+ "422": {
14675
+ "content": {
14676
+ "application/json": {
14677
+ "schema": {
14678
+ "$ref": "#/components/schemas/Error"
14679
+ }
14680
+ }
14681
+ },
14682
+ "description": "Error de validación"
14683
+ }
14684
+ },
14685
+ "summary": "Invitar a un operador por email (bootstrap del equipo, credencial de máquina)",
14686
+ "tags": [
14687
+ "invitations"
14688
+ ]
14689
+ }
14690
+ },
13969
14691
  "/v1/invitations/accept": {
13970
14692
  "post": {
13971
14693
  "callbacks": {},
@@ -16390,16 +17112,229 @@
16390
17112
  "content": {
16391
17113
  "application/json": {
16392
17114
  "schema": {
16393
- "$ref": "#/components/schemas/UsernameAvailableResponse"
17115
+ "$ref": "#/components/schemas/UsernameAvailableResponse"
17116
+ }
17117
+ }
17118
+ },
17119
+ "description": "Disponibilidad"
17120
+ }
17121
+ },
17122
+ "summary": "¿Está disponible este username? (check en vivo del alta/cambio)",
17123
+ "tags": [
17124
+ "people"
17125
+ ]
17126
+ }
17127
+ },
17128
+ "/v1/members": {
17129
+ "post": {
17130
+ "callbacks": {},
17131
+ "description": "Crea al operador y su membresía ACTIVA al instante. Devuelve un `passwordSetupToken` para que la persona fije su contraseña (no se comparte ninguna contraseña generada). Requiere `organization_memberships:create` (preset `team_admin`).",
17132
+ "operationId": "post /v1/members",
17133
+ "parameters": [],
17134
+ "requestBody": {
17135
+ "content": {
17136
+ "application/json": {
17137
+ "schema": {
17138
+ "$ref": "#/components/schemas/ProvisionMemberRequest"
17139
+ }
17140
+ }
17141
+ },
17142
+ "description": "Datos del operador",
17143
+ "required": false
17144
+ },
17145
+ "responses": {
17146
+ "201": {
17147
+ "content": {
17148
+ "application/json": {
17149
+ "schema": {
17150
+ "$ref": "#/components/schemas/ProvisionMemberResponse"
17151
+ }
17152
+ }
17153
+ },
17154
+ "description": "Operador dado de alta"
17155
+ },
17156
+ "403": {
17157
+ "content": {
17158
+ "application/json": {
17159
+ "schema": {
17160
+ "$ref": "#/components/schemas/Error"
17161
+ }
17162
+ }
17163
+ },
17164
+ "description": "Sin permiso"
17165
+ },
17166
+ "422": {
17167
+ "content": {
17168
+ "application/json": {
17169
+ "schema": {
17170
+ "$ref": "#/components/schemas/Error"
17171
+ }
17172
+ }
17173
+ },
17174
+ "description": "Error de validación"
17175
+ }
17176
+ },
17177
+ "summary": "Dar de alta un operador directo (sin invitación)",
17178
+ "tags": [
17179
+ "members"
17180
+ ]
17181
+ }
17182
+ },
17183
+ "/v1/members/me": {
17184
+ "get": {
17185
+ "callbacks": {},
17186
+ "description": "Devuelve el miembro de la member session actual: id, email, nombre visible y sus roles. El equivalente a `getCurrentUser` de una app interna.",
17187
+ "operationId": "get /v1/members/me",
17188
+ "parameters": [],
17189
+ "responses": {
17190
+ "200": {
17191
+ "content": {
17192
+ "application/json": {
17193
+ "schema": {
17194
+ "$ref": "#/components/schemas/MemberResponse"
17195
+ }
17196
+ }
17197
+ },
17198
+ "description": "El miembro actual"
17199
+ },
17200
+ "401": {
17201
+ "content": {
17202
+ "application/json": {
17203
+ "schema": {
17204
+ "$ref": "#/components/schemas/Error"
17205
+ }
17206
+ }
17207
+ },
17208
+ "description": "Sesión inválida"
17209
+ }
17210
+ },
17211
+ "security": [
17212
+ {
17213
+ "memberToken": []
17214
+ }
17215
+ ],
17216
+ "summary": "El miembro autenticado (quién soy)",
17217
+ "tags": [
17218
+ "members"
17219
+ ]
17220
+ }
17221
+ },
17222
+ "/v1/members/session": {
17223
+ "delete": {
17224
+ "callbacks": {},
17225
+ "description": "Revoca el member session token. Idempotente.",
17226
+ "operationId": "delete /v1/members/session",
17227
+ "parameters": [],
17228
+ "responses": {
17229
+ "204": {
17230
+ "description": "Sesión cerrada"
17231
+ }
17232
+ },
17233
+ "security": [
17234
+ {
17235
+ "memberToken": []
17236
+ }
17237
+ ],
17238
+ "summary": "Cerrar la sesión de miembro (logout)",
17239
+ "tags": [
17240
+ "members"
17241
+ ]
17242
+ },
17243
+ "post": {
17244
+ "callbacks": {},
17245
+ "description": "Autentica a un miembro humano (empleado que opera la org) por email+password y devuelve un member session token (`mem_…`) que porta su rol. La organización sale de la API key de la app. Es el login de operador de una app interna (patrón Organizations+Members).",
17246
+ "operationId": "post /v1/members/session",
17247
+ "parameters": [],
17248
+ "requestBody": {
17249
+ "content": {
17250
+ "application/json": {
17251
+ "schema": {
17252
+ "$ref": "#/components/schemas/MemberLoginRequest"
17253
+ }
17254
+ }
17255
+ },
17256
+ "description": "Credenciales del miembro",
17257
+ "required": false
17258
+ },
17259
+ "responses": {
17260
+ "201": {
17261
+ "content": {
17262
+ "application/json": {
17263
+ "schema": {
17264
+ "$ref": "#/components/schemas/MemberSessionResponse"
17265
+ }
17266
+ }
17267
+ },
17268
+ "description": "Sesión de miembro"
17269
+ },
17270
+ "401": {
17271
+ "content": {
17272
+ "application/json": {
17273
+ "schema": {
17274
+ "$ref": "#/components/schemas/Error"
17275
+ }
17276
+ }
17277
+ },
17278
+ "description": "API key o credenciales inválidas"
17279
+ },
17280
+ "422": {
17281
+ "content": {
17282
+ "application/json": {
17283
+ "schema": {
17284
+ "$ref": "#/components/schemas/Error"
17285
+ }
17286
+ }
17287
+ },
17288
+ "description": "Datos inválidos"
17289
+ }
17290
+ },
17291
+ "security": [
17292
+ {
17293
+ "apiKey": []
17294
+ }
17295
+ ],
17296
+ "summary": "Iniciar sesión de un miembro de la organización (login)",
17297
+ "tags": [
17298
+ "members"
17299
+ ]
17300
+ }
17301
+ },
17302
+ "/v1/members/session/refresh": {
17303
+ "post": {
17304
+ "callbacks": {},
17305
+ "description": "Rota el member session token: revoca el actual y emite uno nuevo para la misma membresía (patrón WorkOS refresh). Autenticado por el `mem_` actual.",
17306
+ "operationId": "post /v1/members/session/refresh",
17307
+ "parameters": [],
17308
+ "responses": {
17309
+ "201": {
17310
+ "content": {
17311
+ "application/json": {
17312
+ "schema": {
17313
+ "$ref": "#/components/schemas/MemberSessionResponse"
17314
+ }
17315
+ }
17316
+ },
17317
+ "description": "Nueva sesión"
17318
+ },
17319
+ "401": {
17320
+ "content": {
17321
+ "application/json": {
17322
+ "schema": {
17323
+ "$ref": "#/components/schemas/Error"
16394
17324
  }
16395
17325
  }
16396
17326
  },
16397
- "description": "Disponibilidad"
17327
+ "description": "Sesión inválida"
16398
17328
  }
16399
17329
  },
16400
- "summary": "¿Está disponible este username? (check en vivo del alta/cambio)",
17330
+ "security": [
17331
+ {
17332
+ "memberToken": []
17333
+ }
17334
+ ],
17335
+ "summary": "Renovar la sesión de miembro",
16401
17336
  "tags": [
16402
- "people"
17337
+ "members"
16403
17338
  ]
16404
17339
  }
16405
17340
  },
@@ -18966,6 +19901,83 @@
18966
19901
  ]
18967
19902
  }
18968
19903
  },
19904
+ "/v1/organizations/{organization_id}/aggregate": {
19905
+ "post": {
19906
+ "callbacks": {},
19907
+ "description": "Agrupa records por `dimensions` y calcula `measures` (count/sum/avg/min/max), bajo la misma gobernanza (RBAC + ABAC default-deny) que la lectura puntual. Reemplaza los reportes que serían RPCs de SQL por-tenant.",
19908
+ "operationId": "post /v1/organizations/{organization_id}/aggregate",
19909
+ "parameters": [
19910
+ {
19911
+ "description": "",
19912
+ "in": "path",
19913
+ "name": "organization_id",
19914
+ "required": true,
19915
+ "schema": {
19916
+ "type": "string",
19917
+ "x-struct": null,
19918
+ "x-validate": null
19919
+ }
19920
+ }
19921
+ ],
19922
+ "requestBody": {
19923
+ "content": {
19924
+ "application/json": {
19925
+ "schema": {
19926
+ "$ref": "#/components/schemas/AggregateQueryRequest"
19927
+ }
19928
+ }
19929
+ },
19930
+ "description": "Aggregation query",
19931
+ "required": false
19932
+ },
19933
+ "responses": {
19934
+ "200": {
19935
+ "content": {
19936
+ "application/json": {
19937
+ "schema": {
19938
+ "$ref": "#/components/schemas/AggregateResultResponse"
19939
+ }
19940
+ }
19941
+ },
19942
+ "description": "Grupos agregados"
19943
+ },
19944
+ "403": {
19945
+ "content": {
19946
+ "application/json": {
19947
+ "schema": {
19948
+ "$ref": "#/components/schemas/Error"
19949
+ }
19950
+ }
19951
+ },
19952
+ "description": "Sin permiso"
19953
+ },
19954
+ "404": {
19955
+ "content": {
19956
+ "application/json": {
19957
+ "schema": {
19958
+ "$ref": "#/components/schemas/Error"
19959
+ }
19960
+ }
19961
+ },
19962
+ "description": "Objeto desconocido"
19963
+ },
19964
+ "422": {
19965
+ "content": {
19966
+ "application/json": {
19967
+ "schema": {
19968
+ "$ref": "#/components/schemas/Error"
19969
+ }
19970
+ }
19971
+ },
19972
+ "description": "Consulta inválida"
19973
+ }
19974
+ },
19975
+ "summary": "Ejecutar una aggregation query",
19976
+ "tags": [
19977
+ "query"
19978
+ ]
19979
+ }
19980
+ },
18969
19981
  "/v1/organizations/{organization_id}/api-keys": {
18970
19982
  "get": {
18971
19983
  "callbacks": {},
@@ -29338,6 +30350,86 @@
29338
30350
  ]
29339
30351
  }
29340
30352
  },
30353
+ "/v1/public/aggregate": {
30354
+ "post": {
30355
+ "callbacks": {},
30356
+ "description": "Agrupa records por `dimensions` y calcula `measures` (count/sum/avg/min/max), autenticado por una API key. Bajo el MISMO default-deny que la lectura: solo agrega lo que las policies exponen al principal `public`/`end_user`.",
30357
+ "operationId": "post /v1/public/aggregate",
30358
+ "parameters": [],
30359
+ "requestBody": {
30360
+ "content": {
30361
+ "application/json": {
30362
+ "schema": {
30363
+ "$ref": "#/components/schemas/AggregateQueryRequest"
30364
+ }
30365
+ }
30366
+ },
30367
+ "description": "Aggregation query",
30368
+ "required": false
30369
+ },
30370
+ "responses": {
30371
+ "200": {
30372
+ "content": {
30373
+ "application/json": {
30374
+ "schema": {
30375
+ "$ref": "#/components/schemas/AggregateResultResponse"
30376
+ }
30377
+ }
30378
+ },
30379
+ "description": "Grupos agregados"
30380
+ },
30381
+ "401": {
30382
+ "content": {
30383
+ "application/json": {
30384
+ "schema": {
30385
+ "$ref": "#/components/schemas/Error"
30386
+ }
30387
+ }
30388
+ },
30389
+ "description": "Token inválido"
30390
+ },
30391
+ "403": {
30392
+ "content": {
30393
+ "application/json": {
30394
+ "schema": {
30395
+ "$ref": "#/components/schemas/Error"
30396
+ }
30397
+ }
30398
+ },
30399
+ "description": "Sin permiso"
30400
+ },
30401
+ "404": {
30402
+ "content": {
30403
+ "application/json": {
30404
+ "schema": {
30405
+ "$ref": "#/components/schemas/Error"
30406
+ }
30407
+ }
30408
+ },
30409
+ "description": "Objeto o tenant desconocido"
30410
+ },
30411
+ "422": {
30412
+ "content": {
30413
+ "application/json": {
30414
+ "schema": {
30415
+ "$ref": "#/components/schemas/Error"
30416
+ }
30417
+ }
30418
+ },
30419
+ "description": "Consulta inválida"
30420
+ }
30421
+ },
30422
+ "security": [
30423
+ {
30424
+ "apiKey": []
30425
+ }
30426
+ ],
30427
+ "summary": "Agregar por la Data API pública (reportes)",
30428
+ "tags": [
30429
+ "public"
30430
+ ]
30431
+ }
30432
+ },
29341
30433
  "/v1/public/end-users": {
29342
30434
  "post": {
29343
30435
  "callbacks": {},
@@ -29478,6 +30570,86 @@
29478
30570
  ]
29479
30571
  }
29480
30572
  },
30573
+ "/v1/public/me/aggregate": {
30574
+ "post": {
30575
+ "callbacks": {},
30576
+ "description": "Agrupa records por `dimensions` y calcula `measures` (count/sum/avg/min/max), autenticado por una API key. Bajo el MISMO default-deny que la lectura: solo agrega lo que las policies exponen al principal `public`/`end_user`.",
30577
+ "operationId": "post /v1/public/me/aggregate",
30578
+ "parameters": [],
30579
+ "requestBody": {
30580
+ "content": {
30581
+ "application/json": {
30582
+ "schema": {
30583
+ "$ref": "#/components/schemas/AggregateQueryRequest"
30584
+ }
30585
+ }
30586
+ },
30587
+ "description": "Aggregation query",
30588
+ "required": false
30589
+ },
30590
+ "responses": {
30591
+ "200": {
30592
+ "content": {
30593
+ "application/json": {
30594
+ "schema": {
30595
+ "$ref": "#/components/schemas/AggregateResultResponse"
30596
+ }
30597
+ }
30598
+ },
30599
+ "description": "Grupos agregados"
30600
+ },
30601
+ "401": {
30602
+ "content": {
30603
+ "application/json": {
30604
+ "schema": {
30605
+ "$ref": "#/components/schemas/Error"
30606
+ }
30607
+ }
30608
+ },
30609
+ "description": "Token inválido"
30610
+ },
30611
+ "403": {
30612
+ "content": {
30613
+ "application/json": {
30614
+ "schema": {
30615
+ "$ref": "#/components/schemas/Error"
30616
+ }
30617
+ }
30618
+ },
30619
+ "description": "Sin permiso"
30620
+ },
30621
+ "404": {
30622
+ "content": {
30623
+ "application/json": {
30624
+ "schema": {
30625
+ "$ref": "#/components/schemas/Error"
30626
+ }
30627
+ }
30628
+ },
30629
+ "description": "Objeto o tenant desconocido"
30630
+ },
30631
+ "422": {
30632
+ "content": {
30633
+ "application/json": {
30634
+ "schema": {
30635
+ "$ref": "#/components/schemas/Error"
30636
+ }
30637
+ }
30638
+ },
30639
+ "description": "Consulta inválida"
30640
+ }
30641
+ },
30642
+ "security": [
30643
+ {
30644
+ "apiKey": []
30645
+ }
30646
+ ],
30647
+ "summary": "Agregar por la Data API pública (reportes)",
30648
+ "tags": [
30649
+ "public"
30650
+ ]
30651
+ }
30652
+ },
29481
30653
  "/v1/public/me/checkout": {
29482
30654
  "post": {
29483
30655
  "callbacks": {},
@@ -30861,6 +32033,39 @@
30861
32033
  ]
30862
32034
  }
30863
32035
  },
32036
+ "/v1/roles": {
32037
+ "get": {
32038
+ "callbacks": {},
32039
+ "operationId": "get /v1/roles",
32040
+ "parameters": [],
32041
+ "responses": {
32042
+ "200": {
32043
+ "content": {
32044
+ "application/json": {
32045
+ "schema": {
32046
+ "$ref": "#/components/schemas/RolesResponse"
32047
+ }
32048
+ }
32049
+ },
32050
+ "description": "Roles"
32051
+ },
32052
+ "403": {
32053
+ "content": {
32054
+ "application/json": {
32055
+ "schema": {
32056
+ "$ref": "#/components/schemas/Error"
32057
+ }
32058
+ }
32059
+ },
32060
+ "description": "Sin permiso"
32061
+ }
32062
+ },
32063
+ "summary": "Listar los roles de la organización (para asignar al invitar)",
32064
+ "tags": [
32065
+ "roles"
32066
+ ]
32067
+ }
32068
+ },
30864
32069
  "/v1/sso/callback": {
30865
32070
  "post": {
30866
32071
  "callbacks": {},