@faable/auth-sdk 2.6.14 → 2.6.16

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.
package/spec/openapi.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "info": {
4
4
  "title": "@faablecloud/auth",
5
5
  "description": "Auth Platform made by Faable. Manage Users and Roles",
6
- "version": "2.15.0",
6
+ "version": "2.17.0",
7
7
  "license": {
8
8
  "name": "private",
9
9
  "url": "https://faable.com/docs/platform/privacy-policy"
@@ -378,6 +378,117 @@
378
378
  "additionalProperties": false,
379
379
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
380
380
  },
381
+ "recovery_channels": {
382
+ "type": "object",
383
+ "properties": {
384
+ "enabled": {
385
+ "type": "array",
386
+ "items": {
387
+ "anyOf": [
388
+ {
389
+ "type": "string",
390
+ "enum": [
391
+ "email"
392
+ ]
393
+ },
394
+ {
395
+ "type": "string",
396
+ "enum": [
397
+ "sms"
398
+ ]
399
+ },
400
+ {
401
+ "type": "string",
402
+ "enum": [
403
+ "whatsapp"
404
+ ]
405
+ },
406
+ {
407
+ "type": "string",
408
+ "enum": [
409
+ "factor"
410
+ ]
411
+ }
412
+ ]
413
+ },
414
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
415
+ },
416
+ "default": {
417
+ "anyOf": [
418
+ {
419
+ "anyOf": [
420
+ {
421
+ "type": "string",
422
+ "enum": [
423
+ "email"
424
+ ]
425
+ },
426
+ {
427
+ "type": "string",
428
+ "enum": [
429
+ "sms"
430
+ ]
431
+ },
432
+ {
433
+ "type": "string",
434
+ "enum": [
435
+ "whatsapp"
436
+ ]
437
+ },
438
+ {
439
+ "type": "string",
440
+ "enum": [
441
+ "factor"
442
+ ]
443
+ }
444
+ ]
445
+ }
446
+ ],
447
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
448
+ },
449
+ "visible": {
450
+ "type": "array",
451
+ "items": {
452
+ "anyOf": [
453
+ {
454
+ "type": "string",
455
+ "enum": [
456
+ "email"
457
+ ]
458
+ },
459
+ {
460
+ "type": "string",
461
+ "enum": [
462
+ "sms"
463
+ ]
464
+ },
465
+ {
466
+ "type": "string",
467
+ "enum": [
468
+ "whatsapp"
469
+ ]
470
+ },
471
+ {
472
+ "type": "string",
473
+ "enum": [
474
+ "factor"
475
+ ]
476
+ }
477
+ ]
478
+ },
479
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
480
+ }
481
+ },
482
+ "additionalProperties": false,
483
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
484
+ },
485
+ "default_country_iso": {
486
+ "type": "string",
487
+ "minLength": 2,
488
+ "maxLength": 2,
489
+ "description": "ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.",
490
+ "nullable": true
491
+ },
381
492
  "webauthn_rp_id": {
382
493
  "type": "string",
383
494
  "description": "WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.",
@@ -1431,6 +1542,110 @@
1431
1542
  "additionalProperties": false,
1432
1543
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
1433
1544
  },
1545
+ "recovery_channels": {
1546
+ "type": "object",
1547
+ "properties": {
1548
+ "enabled": {
1549
+ "type": "array",
1550
+ "items": {
1551
+ "anyOf": [
1552
+ {
1553
+ "type": "string",
1554
+ "enum": [
1555
+ "email"
1556
+ ]
1557
+ },
1558
+ {
1559
+ "type": "string",
1560
+ "enum": [
1561
+ "sms"
1562
+ ]
1563
+ },
1564
+ {
1565
+ "type": "string",
1566
+ "enum": [
1567
+ "whatsapp"
1568
+ ]
1569
+ },
1570
+ {
1571
+ "type": "string",
1572
+ "enum": [
1573
+ "factor"
1574
+ ]
1575
+ }
1576
+ ]
1577
+ },
1578
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
1579
+ },
1580
+ "default": {
1581
+ "anyOf": [
1582
+ {
1583
+ "anyOf": [
1584
+ {
1585
+ "type": "string",
1586
+ "enum": [
1587
+ "email"
1588
+ ]
1589
+ },
1590
+ {
1591
+ "type": "string",
1592
+ "enum": [
1593
+ "sms"
1594
+ ]
1595
+ },
1596
+ {
1597
+ "type": "string",
1598
+ "enum": [
1599
+ "whatsapp"
1600
+ ]
1601
+ },
1602
+ {
1603
+ "type": "string",
1604
+ "enum": [
1605
+ "factor"
1606
+ ]
1607
+ }
1608
+ ]
1609
+ }
1610
+ ],
1611
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
1612
+ },
1613
+ "visible": {
1614
+ "type": "array",
1615
+ "items": {
1616
+ "anyOf": [
1617
+ {
1618
+ "type": "string",
1619
+ "enum": [
1620
+ "email"
1621
+ ]
1622
+ },
1623
+ {
1624
+ "type": "string",
1625
+ "enum": [
1626
+ "sms"
1627
+ ]
1628
+ },
1629
+ {
1630
+ "type": "string",
1631
+ "enum": [
1632
+ "whatsapp"
1633
+ ]
1634
+ },
1635
+ {
1636
+ "type": "string",
1637
+ "enum": [
1638
+ "factor"
1639
+ ]
1640
+ }
1641
+ ]
1642
+ },
1643
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
1644
+ }
1645
+ },
1646
+ "additionalProperties": false,
1647
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
1648
+ },
1434
1649
  "login_flow": {
1435
1650
  "anyOf": [
1436
1651
  {
@@ -1795,91 +2010,202 @@
1795
2010
  }
1796
2011
  ]
1797
2012
  },
1798
- "login_flow": {
2013
+ "recovery_channels": {
1799
2014
  "anyOf": [
1800
2015
  {
1801
- "type": "string"
1802
- },
1803
- {
1804
- "type": "null"
1805
- }
1806
- ]
1807
- },
1808
- "metadata": {
1809
- "type": "object",
1810
- "properties": {},
1811
- "additionalProperties": true,
1812
- "description": "Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta.",
1813
- "default": {}
1814
- }
1815
- },
1816
- "description": "Partial update for a Client. Only the supplied fields are modified. `client_id` and `client_secret` are not editable through this endpoint to prevent accidental rotation; use a dedicated endpoint when secret rotation is added.",
1817
- "additionalProperties": false
1818
- },
1819
- "ClientUpdateMetadata": {
1820
- "type": "object",
1821
- "properties": {},
1822
- "additionalProperties": true,
1823
- "description": "Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta.",
1824
- "default": {}
1825
- },
1826
- "User": {
1827
- "type": "object",
1828
- "required": [
1829
- "id",
1830
- "email_verified",
1831
- "phone_verified",
1832
- "logins_count",
1833
- "user_metadata",
1834
- "app_metadata",
1835
- "account",
1836
- "createdAt"
1837
- ],
1838
- "properties": {
1839
- "id": {
1840
- "type": "string",
1841
- "description": "User ID"
1842
- },
1843
- "name": {
1844
- "type": "string",
1845
- "description": "User name",
1846
- "nullable": true
1847
- },
1848
- "username": {
1849
- "type": "string",
1850
- "description": "unique username for this user",
1851
- "nullable": true
1852
- },
1853
- "given_name": {
1854
- "type": "string",
1855
- "description": "Given name",
1856
- "nullable": true
1857
- },
1858
- "family_name": {
1859
- "type": "string",
1860
- "description": "Family name",
1861
- "nullable": true
1862
- },
1863
- "middle_name": {
1864
- "type": "string",
1865
- "description": "Middle name (OIDC §5.1)",
1866
- "nullable": true
1867
- },
1868
- "nickname": {
1869
- "type": "string",
1870
- "description": "Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1",
1871
- "nullable": true
1872
- },
1873
- "email": {
1874
- "type": "string",
1875
- "description": "User email",
1876
- "nullable": true
1877
- },
1878
- "email_verified": {
1879
- "type": "boolean",
1880
- "description": "true if email is verified",
1881
- "default": false
1882
- },
2016
+ "type": "object",
2017
+ "properties": {
2018
+ "enabled": {
2019
+ "type": "array",
2020
+ "items": {
2021
+ "anyOf": [
2022
+ {
2023
+ "type": "string",
2024
+ "enum": [
2025
+ "email"
2026
+ ]
2027
+ },
2028
+ {
2029
+ "type": "string",
2030
+ "enum": [
2031
+ "sms"
2032
+ ]
2033
+ },
2034
+ {
2035
+ "type": "string",
2036
+ "enum": [
2037
+ "whatsapp"
2038
+ ]
2039
+ },
2040
+ {
2041
+ "type": "string",
2042
+ "enum": [
2043
+ "factor"
2044
+ ]
2045
+ }
2046
+ ]
2047
+ },
2048
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
2049
+ },
2050
+ "default": {
2051
+ "anyOf": [
2052
+ {
2053
+ "anyOf": [
2054
+ {
2055
+ "type": "string",
2056
+ "enum": [
2057
+ "email"
2058
+ ]
2059
+ },
2060
+ {
2061
+ "type": "string",
2062
+ "enum": [
2063
+ "sms"
2064
+ ]
2065
+ },
2066
+ {
2067
+ "type": "string",
2068
+ "enum": [
2069
+ "whatsapp"
2070
+ ]
2071
+ },
2072
+ {
2073
+ "type": "string",
2074
+ "enum": [
2075
+ "factor"
2076
+ ]
2077
+ }
2078
+ ]
2079
+ }
2080
+ ],
2081
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
2082
+ },
2083
+ "visible": {
2084
+ "type": "array",
2085
+ "items": {
2086
+ "anyOf": [
2087
+ {
2088
+ "type": "string",
2089
+ "enum": [
2090
+ "email"
2091
+ ]
2092
+ },
2093
+ {
2094
+ "type": "string",
2095
+ "enum": [
2096
+ "sms"
2097
+ ]
2098
+ },
2099
+ {
2100
+ "type": "string",
2101
+ "enum": [
2102
+ "whatsapp"
2103
+ ]
2104
+ },
2105
+ {
2106
+ "type": "string",
2107
+ "enum": [
2108
+ "factor"
2109
+ ]
2110
+ }
2111
+ ]
2112
+ },
2113
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
2114
+ }
2115
+ },
2116
+ "additionalProperties": false,
2117
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
2118
+ },
2119
+ {
2120
+ "type": "null"
2121
+ }
2122
+ ]
2123
+ },
2124
+ "login_flow": {
2125
+ "anyOf": [
2126
+ {
2127
+ "type": "string"
2128
+ },
2129
+ {
2130
+ "type": "null"
2131
+ }
2132
+ ]
2133
+ },
2134
+ "metadata": {
2135
+ "type": "object",
2136
+ "properties": {},
2137
+ "additionalProperties": true,
2138
+ "description": "Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta.",
2139
+ "default": {}
2140
+ }
2141
+ },
2142
+ "description": "Partial update for a Client. Only the supplied fields are modified. `client_id` and `client_secret` are not editable through this endpoint to prevent accidental rotation; use a dedicated endpoint when secret rotation is added.",
2143
+ "additionalProperties": false
2144
+ },
2145
+ "ClientUpdateMetadata": {
2146
+ "type": "object",
2147
+ "properties": {},
2148
+ "additionalProperties": true,
2149
+ "description": "Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta.",
2150
+ "default": {}
2151
+ },
2152
+ "User": {
2153
+ "type": "object",
2154
+ "required": [
2155
+ "id",
2156
+ "email_verified",
2157
+ "phone_verified",
2158
+ "logins_count",
2159
+ "user_metadata",
2160
+ "app_metadata",
2161
+ "account",
2162
+ "createdAt"
2163
+ ],
2164
+ "properties": {
2165
+ "id": {
2166
+ "type": "string",
2167
+ "description": "User ID"
2168
+ },
2169
+ "name": {
2170
+ "type": "string",
2171
+ "description": "User name",
2172
+ "nullable": true
2173
+ },
2174
+ "username": {
2175
+ "type": "string",
2176
+ "description": "unique username for this user",
2177
+ "nullable": true
2178
+ },
2179
+ "given_name": {
2180
+ "type": "string",
2181
+ "description": "Given name",
2182
+ "nullable": true
2183
+ },
2184
+ "family_name": {
2185
+ "type": "string",
2186
+ "description": "Family name",
2187
+ "nullable": true
2188
+ },
2189
+ "middle_name": {
2190
+ "type": "string",
2191
+ "description": "Middle name (OIDC §5.1)",
2192
+ "nullable": true
2193
+ },
2194
+ "nickname": {
2195
+ "type": "string",
2196
+ "description": "Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1",
2197
+ "nullable": true
2198
+ },
2199
+ "email": {
2200
+ "type": "string",
2201
+ "description": "User email",
2202
+ "nullable": true
2203
+ },
2204
+ "email_verified": {
2205
+ "type": "boolean",
2206
+ "description": "true if email is verified",
2207
+ "default": false
2208
+ },
1883
2209
  "email_verified_method": {
1884
2210
  "anyOf": [
1885
2211
  {
@@ -1918,6 +2244,12 @@
1918
2244
  "federated"
1919
2245
  ]
1920
2246
  },
2247
+ {
2248
+ "type": "string",
2249
+ "enum": [
2250
+ "sms_otp"
2251
+ ]
2252
+ },
1921
2253
  {
1922
2254
  "type": "null"
1923
2255
  }
@@ -2005,11 +2337,17 @@
2005
2337
  "federated"
2006
2338
  ]
2007
2339
  },
2340
+ {
2341
+ "type": "string",
2342
+ "enum": [
2343
+ "sms_otp"
2344
+ ]
2345
+ },
2008
2346
  {
2009
2347
  "type": "null"
2010
2348
  }
2011
2349
  ],
2012
- "description": "How `phone_verified` was last set. Same enum as `email_verified_method`."
2350
+ "description": "How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."
2013
2351
  },
2014
2352
  "phone_verified_at": {
2015
2353
  "type": "string",
@@ -6035,77 +6373,200 @@
6035
6373
  }
6036
6374
  ]
6037
6375
  },
6038
- "webauthn_rp_id": {
6039
- "type": "string",
6040
- "nullable": true
6041
- },
6042
- "login_flow": {
6043
- "type": "string",
6044
- "nullable": true
6045
- }
6046
- },
6047
- "description": "AuthAccountUpdate",
6048
- "additionalProperties": false
6049
- },
6050
- "FactorSummary": {
6051
- "type": "object",
6052
- "required": [
6053
- "id",
6054
- "type"
6055
- ],
6056
- "properties": {
6057
- "id": {
6058
- "type": "string"
6059
- },
6060
- "type": {
6061
- "type": "string"
6062
- },
6063
- "name": {
6064
- "type": "string"
6065
- },
6066
- "confirmed_at": {
6067
- "type": "string"
6068
- },
6069
- "last_used_at": {
6070
- "type": "string"
6071
- },
6072
- "remaining": {
6073
- "type": "integer"
6074
- }
6075
- }
6076
- },
6077
- "OAuthTokenParams": {
6078
- "type": "object",
6079
- "properties": {
6080
- "grant_type": {
6376
+ "recovery_channels": {
6081
6377
  "anyOf": [
6082
6378
  {
6083
- "type": "string",
6084
- "enum": [
6085
- "authorization_code"
6086
- ]
6087
- },
6088
- {
6089
- "type": "string",
6090
- "enum": [
6091
- "client_credentials"
6092
- ]
6093
- },
6094
- {
6095
- "type": "string",
6096
- "enum": [
6097
- "refresh_token"
6098
- ]
6099
- },
6100
- {
6101
- "type": "string",
6102
- "enum": [
6103
- "password"
6104
- ]
6105
- },
6106
- {
6107
- "type": "string",
6108
- "enum": [
6379
+ "type": "object",
6380
+ "properties": {
6381
+ "enabled": {
6382
+ "type": "array",
6383
+ "items": {
6384
+ "anyOf": [
6385
+ {
6386
+ "type": "string",
6387
+ "enum": [
6388
+ "email"
6389
+ ]
6390
+ },
6391
+ {
6392
+ "type": "string",
6393
+ "enum": [
6394
+ "sms"
6395
+ ]
6396
+ },
6397
+ {
6398
+ "type": "string",
6399
+ "enum": [
6400
+ "whatsapp"
6401
+ ]
6402
+ },
6403
+ {
6404
+ "type": "string",
6405
+ "enum": [
6406
+ "factor"
6407
+ ]
6408
+ }
6409
+ ]
6410
+ },
6411
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
6412
+ },
6413
+ "default": {
6414
+ "anyOf": [
6415
+ {
6416
+ "anyOf": [
6417
+ {
6418
+ "type": "string",
6419
+ "enum": [
6420
+ "email"
6421
+ ]
6422
+ },
6423
+ {
6424
+ "type": "string",
6425
+ "enum": [
6426
+ "sms"
6427
+ ]
6428
+ },
6429
+ {
6430
+ "type": "string",
6431
+ "enum": [
6432
+ "whatsapp"
6433
+ ]
6434
+ },
6435
+ {
6436
+ "type": "string",
6437
+ "enum": [
6438
+ "factor"
6439
+ ]
6440
+ }
6441
+ ]
6442
+ }
6443
+ ],
6444
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
6445
+ },
6446
+ "visible": {
6447
+ "type": "array",
6448
+ "items": {
6449
+ "anyOf": [
6450
+ {
6451
+ "type": "string",
6452
+ "enum": [
6453
+ "email"
6454
+ ]
6455
+ },
6456
+ {
6457
+ "type": "string",
6458
+ "enum": [
6459
+ "sms"
6460
+ ]
6461
+ },
6462
+ {
6463
+ "type": "string",
6464
+ "enum": [
6465
+ "whatsapp"
6466
+ ]
6467
+ },
6468
+ {
6469
+ "type": "string",
6470
+ "enum": [
6471
+ "factor"
6472
+ ]
6473
+ }
6474
+ ]
6475
+ },
6476
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
6477
+ }
6478
+ },
6479
+ "additionalProperties": false,
6480
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
6481
+ },
6482
+ {
6483
+ "type": "null"
6484
+ }
6485
+ ]
6486
+ },
6487
+ "default_country_iso": {
6488
+ "anyOf": [
6489
+ {
6490
+ "type": "string",
6491
+ "minLength": 2,
6492
+ "maxLength": 2
6493
+ },
6494
+ {
6495
+ "type": "null"
6496
+ }
6497
+ ]
6498
+ },
6499
+ "webauthn_rp_id": {
6500
+ "type": "string",
6501
+ "nullable": true
6502
+ },
6503
+ "login_flow": {
6504
+ "type": "string",
6505
+ "nullable": true
6506
+ }
6507
+ },
6508
+ "description": "AuthAccountUpdate",
6509
+ "additionalProperties": false
6510
+ },
6511
+ "FactorSummary": {
6512
+ "type": "object",
6513
+ "required": [
6514
+ "id",
6515
+ "type"
6516
+ ],
6517
+ "properties": {
6518
+ "id": {
6519
+ "type": "string"
6520
+ },
6521
+ "type": {
6522
+ "type": "string"
6523
+ },
6524
+ "name": {
6525
+ "type": "string"
6526
+ },
6527
+ "confirmed_at": {
6528
+ "type": "string"
6529
+ },
6530
+ "last_used_at": {
6531
+ "type": "string"
6532
+ },
6533
+ "remaining": {
6534
+ "type": "integer"
6535
+ }
6536
+ }
6537
+ },
6538
+ "OAuthTokenParams": {
6539
+ "type": "object",
6540
+ "properties": {
6541
+ "grant_type": {
6542
+ "anyOf": [
6543
+ {
6544
+ "type": "string",
6545
+ "enum": [
6546
+ "authorization_code"
6547
+ ]
6548
+ },
6549
+ {
6550
+ "type": "string",
6551
+ "enum": [
6552
+ "client_credentials"
6553
+ ]
6554
+ },
6555
+ {
6556
+ "type": "string",
6557
+ "enum": [
6558
+ "refresh_token"
6559
+ ]
6560
+ },
6561
+ {
6562
+ "type": "string",
6563
+ "enum": [
6564
+ "password"
6565
+ ]
6566
+ },
6567
+ {
6568
+ "type": "string",
6569
+ "enum": [
6109
6570
  "http://auth0.com/oauth/grant-type/mfa-otp"
6110
6571
  ]
6111
6572
  },
@@ -6728,90 +7189,201 @@
6728
7189
  "additionalProperties": false,
6729
7190
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
6730
7191
  },
6731
- "webauthn_rp_id": {
6732
- "type": "string",
6733
- "description": "WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.",
6734
- "nullable": true
6735
- },
6736
- "createdAt": {
6737
- "type": "string",
6738
- "description": "AuthAccount creation date"
6739
- },
6740
- "updatedAt": {
6741
- "type": "string",
6742
- "description": "AuthAccount updated date"
6743
- }
6744
- },
6745
- "description": "AuthAccount"
6746
- }
6747
- }
6748
- }
6749
- }
6750
- }
6751
- }
6752
- },
6753
- "/account": {
6754
- "post": {
6755
- "operationId": "account/create",
6756
- "summary": "Create an Account",
6757
- "tags": [
6758
- "account"
6759
- ],
6760
- "description": "Creates a new Auth Account. The slug is derived from the name and used to build the default `*.auth.faable.link` domain.",
6761
- "requestBody": {
6762
- "required": true,
6763
- "content": {
6764
- "application/json": {
6765
- "schema": {
6766
- "type": "object",
6767
- "required": [
6768
- "name",
6769
- "team"
6770
- ],
6771
- "properties": {
6772
- "name": {
6773
- "type": "string",
6774
- "minLength": 1,
6775
- "maxLength": 100
6776
- },
6777
- "team": {
6778
- "type": "string"
6779
- },
6780
- "logo_src": {
6781
- "type": "string"
6782
- },
6783
- "icon_src": {
6784
- "type": "string"
6785
- },
6786
- "callback_hostnames": {
6787
- "type": "array",
6788
- "items": {
6789
- "type": "string"
6790
- }
6791
- },
6792
- "default_connection": {
6793
- "type": "string"
6794
- },
6795
- "tags": {
6796
- "type": "array",
6797
- "items": {
6798
- "type": "string",
6799
- "maxLength": 50
6800
- },
6801
- "maxItems": 20
6802
- },
6803
- "metadata": {
6804
- "type": "object",
6805
- "properties": {},
6806
- "additionalProperties": true,
6807
- "description": "Add Metadata"
6808
- }
6809
- },
6810
- "description": "AuthAccountCreate",
6811
- "additionalProperties": false
6812
- }
6813
- }
6814
- },
7192
+ "recovery_channels": {
7193
+ "type": "object",
7194
+ "properties": {
7195
+ "enabled": {
7196
+ "type": "array",
7197
+ "items": {
7198
+ "anyOf": [
7199
+ {
7200
+ "type": "string",
7201
+ "enum": [
7202
+ "email"
7203
+ ]
7204
+ },
7205
+ {
7206
+ "type": "string",
7207
+ "enum": [
7208
+ "sms"
7209
+ ]
7210
+ },
7211
+ {
7212
+ "type": "string",
7213
+ "enum": [
7214
+ "whatsapp"
7215
+ ]
7216
+ },
7217
+ {
7218
+ "type": "string",
7219
+ "enum": [
7220
+ "factor"
7221
+ ]
7222
+ }
7223
+ ]
7224
+ },
7225
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
7226
+ },
7227
+ "default": {
7228
+ "anyOf": [
7229
+ {
7230
+ "anyOf": [
7231
+ {
7232
+ "type": "string",
7233
+ "enum": [
7234
+ "email"
7235
+ ]
7236
+ },
7237
+ {
7238
+ "type": "string",
7239
+ "enum": [
7240
+ "sms"
7241
+ ]
7242
+ },
7243
+ {
7244
+ "type": "string",
7245
+ "enum": [
7246
+ "whatsapp"
7247
+ ]
7248
+ },
7249
+ {
7250
+ "type": "string",
7251
+ "enum": [
7252
+ "factor"
7253
+ ]
7254
+ }
7255
+ ]
7256
+ }
7257
+ ],
7258
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
7259
+ },
7260
+ "visible": {
7261
+ "type": "array",
7262
+ "items": {
7263
+ "anyOf": [
7264
+ {
7265
+ "type": "string",
7266
+ "enum": [
7267
+ "email"
7268
+ ]
7269
+ },
7270
+ {
7271
+ "type": "string",
7272
+ "enum": [
7273
+ "sms"
7274
+ ]
7275
+ },
7276
+ {
7277
+ "type": "string",
7278
+ "enum": [
7279
+ "whatsapp"
7280
+ ]
7281
+ },
7282
+ {
7283
+ "type": "string",
7284
+ "enum": [
7285
+ "factor"
7286
+ ]
7287
+ }
7288
+ ]
7289
+ },
7290
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
7291
+ }
7292
+ },
7293
+ "additionalProperties": false,
7294
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
7295
+ },
7296
+ "default_country_iso": {
7297
+ "type": "string",
7298
+ "minLength": 2,
7299
+ "maxLength": 2,
7300
+ "description": "ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.",
7301
+ "nullable": true
7302
+ },
7303
+ "webauthn_rp_id": {
7304
+ "type": "string",
7305
+ "description": "WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.",
7306
+ "nullable": true
7307
+ },
7308
+ "createdAt": {
7309
+ "type": "string",
7310
+ "description": "AuthAccount creation date"
7311
+ },
7312
+ "updatedAt": {
7313
+ "type": "string",
7314
+ "description": "AuthAccount updated date"
7315
+ }
7316
+ },
7317
+ "description": "AuthAccount"
7318
+ }
7319
+ }
7320
+ }
7321
+ }
7322
+ }
7323
+ }
7324
+ },
7325
+ "/account": {
7326
+ "post": {
7327
+ "operationId": "account/create",
7328
+ "summary": "Create an Account",
7329
+ "tags": [
7330
+ "account"
7331
+ ],
7332
+ "description": "Creates a new Auth Account. The slug is derived from the name and used to build the default `*.auth.faable.link` domain.",
7333
+ "requestBody": {
7334
+ "required": true,
7335
+ "content": {
7336
+ "application/json": {
7337
+ "schema": {
7338
+ "type": "object",
7339
+ "required": [
7340
+ "name",
7341
+ "team"
7342
+ ],
7343
+ "properties": {
7344
+ "name": {
7345
+ "type": "string",
7346
+ "minLength": 1,
7347
+ "maxLength": 100
7348
+ },
7349
+ "team": {
7350
+ "type": "string"
7351
+ },
7352
+ "logo_src": {
7353
+ "type": "string"
7354
+ },
7355
+ "icon_src": {
7356
+ "type": "string"
7357
+ },
7358
+ "callback_hostnames": {
7359
+ "type": "array",
7360
+ "items": {
7361
+ "type": "string"
7362
+ }
7363
+ },
7364
+ "default_connection": {
7365
+ "type": "string"
7366
+ },
7367
+ "tags": {
7368
+ "type": "array",
7369
+ "items": {
7370
+ "type": "string",
7371
+ "maxLength": 50
7372
+ },
7373
+ "maxItems": 20
7374
+ },
7375
+ "metadata": {
7376
+ "type": "object",
7377
+ "properties": {},
7378
+ "additionalProperties": true,
7379
+ "description": "Add Metadata"
7380
+ }
7381
+ },
7382
+ "description": "AuthAccountCreate",
7383
+ "additionalProperties": false
7384
+ }
7385
+ }
7386
+ },
6815
7387
  "description": "AuthAccountCreate"
6816
7388
  },
6817
7389
  "security": [
@@ -7166,6 +7738,117 @@
7166
7738
  "additionalProperties": false,
7167
7739
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
7168
7740
  },
7741
+ "recovery_channels": {
7742
+ "type": "object",
7743
+ "properties": {
7744
+ "enabled": {
7745
+ "type": "array",
7746
+ "items": {
7747
+ "anyOf": [
7748
+ {
7749
+ "type": "string",
7750
+ "enum": [
7751
+ "email"
7752
+ ]
7753
+ },
7754
+ {
7755
+ "type": "string",
7756
+ "enum": [
7757
+ "sms"
7758
+ ]
7759
+ },
7760
+ {
7761
+ "type": "string",
7762
+ "enum": [
7763
+ "whatsapp"
7764
+ ]
7765
+ },
7766
+ {
7767
+ "type": "string",
7768
+ "enum": [
7769
+ "factor"
7770
+ ]
7771
+ }
7772
+ ]
7773
+ },
7774
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
7775
+ },
7776
+ "default": {
7777
+ "anyOf": [
7778
+ {
7779
+ "anyOf": [
7780
+ {
7781
+ "type": "string",
7782
+ "enum": [
7783
+ "email"
7784
+ ]
7785
+ },
7786
+ {
7787
+ "type": "string",
7788
+ "enum": [
7789
+ "sms"
7790
+ ]
7791
+ },
7792
+ {
7793
+ "type": "string",
7794
+ "enum": [
7795
+ "whatsapp"
7796
+ ]
7797
+ },
7798
+ {
7799
+ "type": "string",
7800
+ "enum": [
7801
+ "factor"
7802
+ ]
7803
+ }
7804
+ ]
7805
+ }
7806
+ ],
7807
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
7808
+ },
7809
+ "visible": {
7810
+ "type": "array",
7811
+ "items": {
7812
+ "anyOf": [
7813
+ {
7814
+ "type": "string",
7815
+ "enum": [
7816
+ "email"
7817
+ ]
7818
+ },
7819
+ {
7820
+ "type": "string",
7821
+ "enum": [
7822
+ "sms"
7823
+ ]
7824
+ },
7825
+ {
7826
+ "type": "string",
7827
+ "enum": [
7828
+ "whatsapp"
7829
+ ]
7830
+ },
7831
+ {
7832
+ "type": "string",
7833
+ "enum": [
7834
+ "factor"
7835
+ ]
7836
+ }
7837
+ ]
7838
+ },
7839
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
7840
+ }
7841
+ },
7842
+ "additionalProperties": false,
7843
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
7844
+ },
7845
+ "default_country_iso": {
7846
+ "type": "string",
7847
+ "minLength": 2,
7848
+ "maxLength": 2,
7849
+ "description": "ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.",
7850
+ "nullable": true
7851
+ },
7169
7852
  "webauthn_rp_id": {
7170
7853
  "type": "string",
7171
7854
  "description": "WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.",
@@ -7598,65 +8281,176 @@
7598
8281
  "description": "The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.",
7599
8282
  "nullable": true
7600
8283
  },
7601
- "mfa_policy": {
8284
+ "mfa_policy": {
8285
+ "type": "object",
8286
+ "properties": {
8287
+ "mode": {
8288
+ "anyOf": [
8289
+ {
8290
+ "anyOf": [
8291
+ {
8292
+ "type": "string",
8293
+ "enum": [
8294
+ "off"
8295
+ ]
8296
+ },
8297
+ {
8298
+ "type": "string",
8299
+ "enum": [
8300
+ "optional"
8301
+ ]
8302
+ },
8303
+ {
8304
+ "type": "string",
8305
+ "enum": [
8306
+ "required"
8307
+ ]
8308
+ }
8309
+ ]
8310
+ }
8311
+ ],
8312
+ "description": "`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."
8313
+ },
8314
+ "allowed_factors": {
8315
+ "type": "array",
8316
+ "items": {
8317
+ "anyOf": [
8318
+ {
8319
+ "type": "string",
8320
+ "enum": [
8321
+ "totp"
8322
+ ]
8323
+ },
8324
+ {
8325
+ "type": "string",
8326
+ "enum": [
8327
+ "webauthn"
8328
+ ]
8329
+ }
8330
+ ]
8331
+ },
8332
+ "description": "Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."
8333
+ },
8334
+ "remember_device_days": {
8335
+ "type": "integer",
8336
+ "minimum": 0,
8337
+ "maximum": 365,
8338
+ "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
8339
+ }
8340
+ },
8341
+ "additionalProperties": false,
8342
+ "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
8343
+ },
8344
+ "recovery_channels": {
7602
8345
  "type": "object",
7603
8346
  "properties": {
7604
- "mode": {
8347
+ "enabled": {
8348
+ "type": "array",
8349
+ "items": {
8350
+ "anyOf": [
8351
+ {
8352
+ "type": "string",
8353
+ "enum": [
8354
+ "email"
8355
+ ]
8356
+ },
8357
+ {
8358
+ "type": "string",
8359
+ "enum": [
8360
+ "sms"
8361
+ ]
8362
+ },
8363
+ {
8364
+ "type": "string",
8365
+ "enum": [
8366
+ "whatsapp"
8367
+ ]
8368
+ },
8369
+ {
8370
+ "type": "string",
8371
+ "enum": [
8372
+ "factor"
8373
+ ]
8374
+ }
8375
+ ]
8376
+ },
8377
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
8378
+ },
8379
+ "default": {
7605
8380
  "anyOf": [
7606
8381
  {
7607
8382
  "anyOf": [
7608
8383
  {
7609
8384
  "type": "string",
7610
8385
  "enum": [
7611
- "off"
8386
+ "email"
7612
8387
  ]
7613
8388
  },
7614
8389
  {
7615
8390
  "type": "string",
7616
8391
  "enum": [
7617
- "optional"
8392
+ "sms"
7618
8393
  ]
7619
8394
  },
7620
8395
  {
7621
8396
  "type": "string",
7622
8397
  "enum": [
7623
- "required"
8398
+ "whatsapp"
8399
+ ]
8400
+ },
8401
+ {
8402
+ "type": "string",
8403
+ "enum": [
8404
+ "factor"
7624
8405
  ]
7625
8406
  }
7626
8407
  ]
7627
8408
  }
7628
8409
  ],
7629
- "description": "`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."
8410
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
7630
8411
  },
7631
- "allowed_factors": {
8412
+ "visible": {
7632
8413
  "type": "array",
7633
8414
  "items": {
7634
8415
  "anyOf": [
7635
8416
  {
7636
8417
  "type": "string",
7637
8418
  "enum": [
7638
- "totp"
8419
+ "email"
7639
8420
  ]
7640
8421
  },
7641
8422
  {
7642
8423
  "type": "string",
7643
8424
  "enum": [
7644
- "webauthn"
8425
+ "sms"
8426
+ ]
8427
+ },
8428
+ {
8429
+ "type": "string",
8430
+ "enum": [
8431
+ "whatsapp"
8432
+ ]
8433
+ },
8434
+ {
8435
+ "type": "string",
8436
+ "enum": [
8437
+ "factor"
7645
8438
  ]
7646
8439
  }
7647
8440
  ]
7648
8441
  },
7649
- "description": "Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."
7650
- },
7651
- "remember_device_days": {
7652
- "type": "integer",
7653
- "minimum": 0,
7654
- "maximum": 365,
7655
- "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
8442
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
7656
8443
  }
7657
8444
  },
7658
8445
  "additionalProperties": false,
7659
- "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
8446
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
8447
+ },
8448
+ "default_country_iso": {
8449
+ "type": "string",
8450
+ "minLength": 2,
8451
+ "maxLength": 2,
8452
+ "description": "ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.",
8453
+ "nullable": true
7660
8454
  },
7661
8455
  "webauthn_rp_id": {
7662
8456
  "type": "string",
@@ -7962,6 +8756,129 @@
7962
8756
  }
7963
8757
  ]
7964
8758
  },
8759
+ "recovery_channels": {
8760
+ "anyOf": [
8761
+ {
8762
+ "type": "object",
8763
+ "properties": {
8764
+ "enabled": {
8765
+ "type": "array",
8766
+ "items": {
8767
+ "anyOf": [
8768
+ {
8769
+ "type": "string",
8770
+ "enum": [
8771
+ "email"
8772
+ ]
8773
+ },
8774
+ {
8775
+ "type": "string",
8776
+ "enum": [
8777
+ "sms"
8778
+ ]
8779
+ },
8780
+ {
8781
+ "type": "string",
8782
+ "enum": [
8783
+ "whatsapp"
8784
+ ]
8785
+ },
8786
+ {
8787
+ "type": "string",
8788
+ "enum": [
8789
+ "factor"
8790
+ ]
8791
+ }
8792
+ ]
8793
+ },
8794
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
8795
+ },
8796
+ "default": {
8797
+ "anyOf": [
8798
+ {
8799
+ "anyOf": [
8800
+ {
8801
+ "type": "string",
8802
+ "enum": [
8803
+ "email"
8804
+ ]
8805
+ },
8806
+ {
8807
+ "type": "string",
8808
+ "enum": [
8809
+ "sms"
8810
+ ]
8811
+ },
8812
+ {
8813
+ "type": "string",
8814
+ "enum": [
8815
+ "whatsapp"
8816
+ ]
8817
+ },
8818
+ {
8819
+ "type": "string",
8820
+ "enum": [
8821
+ "factor"
8822
+ ]
8823
+ }
8824
+ ]
8825
+ }
8826
+ ],
8827
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
8828
+ },
8829
+ "visible": {
8830
+ "type": "array",
8831
+ "items": {
8832
+ "anyOf": [
8833
+ {
8834
+ "type": "string",
8835
+ "enum": [
8836
+ "email"
8837
+ ]
8838
+ },
8839
+ {
8840
+ "type": "string",
8841
+ "enum": [
8842
+ "sms"
8843
+ ]
8844
+ },
8845
+ {
8846
+ "type": "string",
8847
+ "enum": [
8848
+ "whatsapp"
8849
+ ]
8850
+ },
8851
+ {
8852
+ "type": "string",
8853
+ "enum": [
8854
+ "factor"
8855
+ ]
8856
+ }
8857
+ ]
8858
+ },
8859
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
8860
+ }
8861
+ },
8862
+ "additionalProperties": false,
8863
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
8864
+ },
8865
+ {
8866
+ "type": "null"
8867
+ }
8868
+ ]
8869
+ },
8870
+ "default_country_iso": {
8871
+ "anyOf": [
8872
+ {
8873
+ "type": "string",
8874
+ "minLength": 2,
8875
+ "maxLength": 2
8876
+ },
8877
+ {
8878
+ "type": "null"
8879
+ }
8880
+ ]
8881
+ },
7965
8882
  "webauthn_rp_id": {
7966
8883
  "type": "string",
7967
8884
  "nullable": true
@@ -8317,28 +9234,139 @@
8317
9234
  {
8318
9235
  "type": "string",
8319
9236
  "enum": [
8320
- "totp"
9237
+ "totp"
9238
+ ]
9239
+ },
9240
+ {
9241
+ "type": "string",
9242
+ "enum": [
9243
+ "webauthn"
9244
+ ]
9245
+ }
9246
+ ]
9247
+ },
9248
+ "description": "Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."
9249
+ },
9250
+ "remember_device_days": {
9251
+ "type": "integer",
9252
+ "minimum": 0,
9253
+ "maximum": 365,
9254
+ "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
9255
+ }
9256
+ },
9257
+ "additionalProperties": false,
9258
+ "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
9259
+ },
9260
+ "recovery_channels": {
9261
+ "type": "object",
9262
+ "properties": {
9263
+ "enabled": {
9264
+ "type": "array",
9265
+ "items": {
9266
+ "anyOf": [
9267
+ {
9268
+ "type": "string",
9269
+ "enum": [
9270
+ "email"
9271
+ ]
9272
+ },
9273
+ {
9274
+ "type": "string",
9275
+ "enum": [
9276
+ "sms"
9277
+ ]
9278
+ },
9279
+ {
9280
+ "type": "string",
9281
+ "enum": [
9282
+ "whatsapp"
9283
+ ]
9284
+ },
9285
+ {
9286
+ "type": "string",
9287
+ "enum": [
9288
+ "factor"
9289
+ ]
9290
+ }
9291
+ ]
9292
+ },
9293
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
9294
+ },
9295
+ "default": {
9296
+ "anyOf": [
9297
+ {
9298
+ "anyOf": [
9299
+ {
9300
+ "type": "string",
9301
+ "enum": [
9302
+ "email"
9303
+ ]
9304
+ },
9305
+ {
9306
+ "type": "string",
9307
+ "enum": [
9308
+ "sms"
9309
+ ]
9310
+ },
9311
+ {
9312
+ "type": "string",
9313
+ "enum": [
9314
+ "whatsapp"
9315
+ ]
9316
+ },
9317
+ {
9318
+ "type": "string",
9319
+ "enum": [
9320
+ "factor"
9321
+ ]
9322
+ }
9323
+ ]
9324
+ }
9325
+ ],
9326
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
9327
+ },
9328
+ "visible": {
9329
+ "type": "array",
9330
+ "items": {
9331
+ "anyOf": [
9332
+ {
9333
+ "type": "string",
9334
+ "enum": [
9335
+ "email"
9336
+ ]
9337
+ },
9338
+ {
9339
+ "type": "string",
9340
+ "enum": [
9341
+ "sms"
9342
+ ]
9343
+ },
9344
+ {
9345
+ "type": "string",
9346
+ "enum": [
9347
+ "whatsapp"
8321
9348
  ]
8322
9349
  },
8323
9350
  {
8324
9351
  "type": "string",
8325
9352
  "enum": [
8326
- "webauthn"
9353
+ "factor"
8327
9354
  ]
8328
9355
  }
8329
9356
  ]
8330
9357
  },
8331
- "description": "Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."
8332
- },
8333
- "remember_device_days": {
8334
- "type": "integer",
8335
- "minimum": 0,
8336
- "maximum": 365,
8337
- "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
9358
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
8338
9359
  }
8339
9360
  },
8340
9361
  "additionalProperties": false,
8341
- "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
9362
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
9363
+ },
9364
+ "default_country_iso": {
9365
+ "type": "string",
9366
+ "minLength": 2,
9367
+ "maxLength": 2,
9368
+ "description": "ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.",
9369
+ "nullable": true
8342
9370
  },
8343
9371
  "webauthn_rp_id": {
8344
9372
  "type": "string",
@@ -8718,54 +9746,411 @@
8718
9746
  }
8719
9747
  ]
8720
9748
  }
8721
- ],
8722
- "description": "`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."
9749
+ ],
9750
+ "description": "`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."
9751
+ },
9752
+ "allowed_factors": {
9753
+ "type": "array",
9754
+ "items": {
9755
+ "anyOf": [
9756
+ {
9757
+ "type": "string",
9758
+ "enum": [
9759
+ "totp"
9760
+ ]
9761
+ },
9762
+ {
9763
+ "type": "string",
9764
+ "enum": [
9765
+ "webauthn"
9766
+ ]
9767
+ }
9768
+ ]
9769
+ },
9770
+ "description": "Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."
9771
+ },
9772
+ "remember_device_days": {
9773
+ "type": "integer",
9774
+ "minimum": 0,
9775
+ "maximum": 365,
9776
+ "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
9777
+ }
9778
+ },
9779
+ "additionalProperties": false,
9780
+ "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
9781
+ },
9782
+ "recovery_channels": {
9783
+ "type": "object",
9784
+ "properties": {
9785
+ "enabled": {
9786
+ "type": "array",
9787
+ "items": {
9788
+ "anyOf": [
9789
+ {
9790
+ "type": "string",
9791
+ "enum": [
9792
+ "email"
9793
+ ]
9794
+ },
9795
+ {
9796
+ "type": "string",
9797
+ "enum": [
9798
+ "sms"
9799
+ ]
9800
+ },
9801
+ {
9802
+ "type": "string",
9803
+ "enum": [
9804
+ "whatsapp"
9805
+ ]
9806
+ },
9807
+ {
9808
+ "type": "string",
9809
+ "enum": [
9810
+ "factor"
9811
+ ]
9812
+ }
9813
+ ]
9814
+ },
9815
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
9816
+ },
9817
+ "default": {
9818
+ "anyOf": [
9819
+ {
9820
+ "anyOf": [
9821
+ {
9822
+ "type": "string",
9823
+ "enum": [
9824
+ "email"
9825
+ ]
9826
+ },
9827
+ {
9828
+ "type": "string",
9829
+ "enum": [
9830
+ "sms"
9831
+ ]
9832
+ },
9833
+ {
9834
+ "type": "string",
9835
+ "enum": [
9836
+ "whatsapp"
9837
+ ]
9838
+ },
9839
+ {
9840
+ "type": "string",
9841
+ "enum": [
9842
+ "factor"
9843
+ ]
9844
+ }
9845
+ ]
9846
+ }
9847
+ ],
9848
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
9849
+ },
9850
+ "visible": {
9851
+ "type": "array",
9852
+ "items": {
9853
+ "anyOf": [
9854
+ {
9855
+ "type": "string",
9856
+ "enum": [
9857
+ "email"
9858
+ ]
9859
+ },
9860
+ {
9861
+ "type": "string",
9862
+ "enum": [
9863
+ "sms"
9864
+ ]
9865
+ },
9866
+ {
9867
+ "type": "string",
9868
+ "enum": [
9869
+ "whatsapp"
9870
+ ]
9871
+ },
9872
+ {
9873
+ "type": "string",
9874
+ "enum": [
9875
+ "factor"
9876
+ ]
9877
+ }
9878
+ ]
9879
+ },
9880
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
9881
+ }
9882
+ },
9883
+ "additionalProperties": false,
9884
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
9885
+ },
9886
+ "default_country_iso": {
9887
+ "type": "string",
9888
+ "minLength": 2,
9889
+ "maxLength": 2,
9890
+ "description": "ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.",
9891
+ "nullable": true
9892
+ },
9893
+ "webauthn_rp_id": {
9894
+ "type": "string",
9895
+ "description": "WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.",
9896
+ "nullable": true
9897
+ },
9898
+ "createdAt": {
9899
+ "type": "string",
9900
+ "description": "AuthAccount creation date"
9901
+ },
9902
+ "updatedAt": {
9903
+ "type": "string",
9904
+ "description": "AuthAccount updated date"
9905
+ }
9906
+ },
9907
+ "description": "AuthAccount"
9908
+ }
9909
+ }
9910
+ }
9911
+ }
9912
+ }
9913
+ }
9914
+ },
9915
+ "/recovery-channels/capabilities": {
9916
+ "get": {
9917
+ "operationId": "account/recoveryChannelsCapabilities",
9918
+ "summary": "Recovery channel capabilities for this tenant",
9919
+ "tags": [
9920
+ "account"
9921
+ ],
9922
+ "description": "Which recovery channels the platform can send, whether the plan allows the paid ones, this month's SMS usage against the included amount, and the tenant configuration as resolved. Drives the \"Recovery channels\" editor in the dashboard.",
9923
+ "security": [
9924
+ {
9925
+ "bearerAuth": []
9926
+ }
9927
+ ],
9928
+ "responses": {
9929
+ "200": {
9930
+ "description": "Default Response",
9931
+ "content": {
9932
+ "application/json": {
9933
+ "schema": {
9934
+ "type": "object",
9935
+ "required": [
9936
+ "provider",
9937
+ "plan",
9938
+ "usage",
9939
+ "resolved",
9940
+ "channels"
9941
+ ],
9942
+ "properties": {
9943
+ "provider": {
9944
+ "type": "object",
9945
+ "required": [
9946
+ "sms",
9947
+ "whatsapp"
9948
+ ],
9949
+ "properties": {
9950
+ "sms": {
9951
+ "type": "boolean"
9952
+ },
9953
+ "whatsapp": {
9954
+ "type": "boolean"
9955
+ }
9956
+ },
9957
+ "description": "Whether the platform can send on each channel right now."
9958
+ },
9959
+ "plan": {
9960
+ "type": "object",
9961
+ "required": [
9962
+ "known",
9963
+ "allows_messaging"
9964
+ ],
9965
+ "properties": {
9966
+ "known": {
9967
+ "type": "boolean",
9968
+ "description": "False when billing did not answer. Messaging channels are then treated as not allowed — the failure mode for a paid-per-message channel is \"do not send\", not \"allow\"."
9969
+ },
9970
+ "tier": {
9971
+ "type": "string"
9972
+ },
9973
+ "allows_messaging": {
9974
+ "type": "boolean"
9975
+ }
9976
+ }
9977
+ },
9978
+ "usage": {
9979
+ "type": "object",
9980
+ "required": [
9981
+ "month",
9982
+ "sent",
9983
+ "included",
9984
+ "metered",
9985
+ "hard_ceiling"
9986
+ ],
9987
+ "properties": {
9988
+ "month": {
9989
+ "type": "string",
9990
+ "description": "YYYY-MM, UTC."
9991
+ },
9992
+ "sent": {
9993
+ "type": "integer"
9994
+ },
9995
+ "included": {
9996
+ "type": "integer"
9997
+ },
9998
+ "metered": {
9999
+ "type": "boolean"
10000
+ },
10001
+ "hard_ceiling": {
10002
+ "type": "integer"
10003
+ }
10004
+ }
10005
+ },
10006
+ "resolved": {
10007
+ "type": "object",
10008
+ "required": [
10009
+ "enabled",
10010
+ "default",
10011
+ "visible"
10012
+ ],
10013
+ "properties": {
10014
+ "enabled": {
10015
+ "type": "array",
10016
+ "items": {
10017
+ "anyOf": [
10018
+ {
10019
+ "type": "string",
10020
+ "enum": [
10021
+ "email"
10022
+ ]
10023
+ },
10024
+ {
10025
+ "type": "string",
10026
+ "enum": [
10027
+ "sms"
10028
+ ]
10029
+ },
10030
+ {
10031
+ "type": "string",
10032
+ "enum": [
10033
+ "whatsapp"
10034
+ ]
10035
+ },
10036
+ {
10037
+ "type": "string",
10038
+ "enum": [
10039
+ "factor"
10040
+ ]
10041
+ }
10042
+ ]
10043
+ }
10044
+ },
10045
+ "default": {
10046
+ "anyOf": [
10047
+ {
10048
+ "type": "string",
10049
+ "enum": [
10050
+ "email"
10051
+ ]
10052
+ },
10053
+ {
10054
+ "type": "string",
10055
+ "enum": [
10056
+ "sms"
10057
+ ]
10058
+ },
10059
+ {
10060
+ "type": "string",
10061
+ "enum": [
10062
+ "whatsapp"
10063
+ ]
10064
+ },
10065
+ {
10066
+ "type": "string",
10067
+ "enum": [
10068
+ "factor"
10069
+ ]
10070
+ }
10071
+ ]
8723
10072
  },
8724
- "allowed_factors": {
10073
+ "visible": {
8725
10074
  "type": "array",
8726
10075
  "items": {
8727
10076
  "anyOf": [
8728
10077
  {
8729
10078
  "type": "string",
8730
10079
  "enum": [
8731
- "totp"
10080
+ "email"
8732
10081
  ]
8733
10082
  },
8734
10083
  {
8735
10084
  "type": "string",
8736
10085
  "enum": [
8737
- "webauthn"
10086
+ "sms"
10087
+ ]
10088
+ },
10089
+ {
10090
+ "type": "string",
10091
+ "enum": [
10092
+ "whatsapp"
10093
+ ]
10094
+ },
10095
+ {
10096
+ "type": "string",
10097
+ "enum": [
10098
+ "factor"
10099
+ ]
10100
+ }
10101
+ ]
10102
+ }
10103
+ }
10104
+ }
10105
+ },
10106
+ "channels": {
10107
+ "type": "array",
10108
+ "items": {
10109
+ "type": "object",
10110
+ "required": [
10111
+ "channel",
10112
+ "available"
10113
+ ],
10114
+ "properties": {
10115
+ "channel": {
10116
+ "anyOf": [
10117
+ {
10118
+ "type": "string",
10119
+ "enum": [
10120
+ "email"
10121
+ ]
10122
+ },
10123
+ {
10124
+ "type": "string",
10125
+ "enum": [
10126
+ "sms"
10127
+ ]
10128
+ },
10129
+ {
10130
+ "type": "string",
10131
+ "enum": [
10132
+ "whatsapp"
10133
+ ]
10134
+ },
10135
+ {
10136
+ "type": "string",
10137
+ "enum": [
10138
+ "factor"
8738
10139
  ]
8739
10140
  }
8740
10141
  ]
8741
10142
  },
8742
- "description": "Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."
8743
- },
8744
- "remember_device_days": {
8745
- "type": "integer",
8746
- "minimum": 0,
8747
- "maximum": 365,
8748
- "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
10143
+ "available": {
10144
+ "type": "boolean"
10145
+ },
10146
+ "reason": {
10147
+ "type": "string"
10148
+ }
8749
10149
  }
8750
10150
  },
8751
- "additionalProperties": false,
8752
- "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
8753
- },
8754
- "webauthn_rp_id": {
8755
- "type": "string",
8756
- "description": "WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.",
8757
- "nullable": true
8758
- },
8759
- "createdAt": {
8760
- "type": "string",
8761
- "description": "AuthAccount creation date"
8762
- },
8763
- "updatedAt": {
8764
- "type": "string",
8765
- "description": "AuthAccount updated date"
10151
+ "description": "Tenant-level availability (configuration × platform × plan). The per-user step — verified phone, enrolled factor — is not applied here."
8766
10152
  }
8767
- },
8768
- "description": "AuthAccount"
10153
+ }
8769
10154
  }
8770
10155
  }
8771
10156
  }
@@ -10872,6 +12257,110 @@
10872
12257
  "additionalProperties": false,
10873
12258
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
10874
12259
  },
12260
+ "recovery_channels": {
12261
+ "type": "object",
12262
+ "properties": {
12263
+ "enabled": {
12264
+ "type": "array",
12265
+ "items": {
12266
+ "anyOf": [
12267
+ {
12268
+ "type": "string",
12269
+ "enum": [
12270
+ "email"
12271
+ ]
12272
+ },
12273
+ {
12274
+ "type": "string",
12275
+ "enum": [
12276
+ "sms"
12277
+ ]
12278
+ },
12279
+ {
12280
+ "type": "string",
12281
+ "enum": [
12282
+ "whatsapp"
12283
+ ]
12284
+ },
12285
+ {
12286
+ "type": "string",
12287
+ "enum": [
12288
+ "factor"
12289
+ ]
12290
+ }
12291
+ ]
12292
+ },
12293
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
12294
+ },
12295
+ "default": {
12296
+ "anyOf": [
12297
+ {
12298
+ "anyOf": [
12299
+ {
12300
+ "type": "string",
12301
+ "enum": [
12302
+ "email"
12303
+ ]
12304
+ },
12305
+ {
12306
+ "type": "string",
12307
+ "enum": [
12308
+ "sms"
12309
+ ]
12310
+ },
12311
+ {
12312
+ "type": "string",
12313
+ "enum": [
12314
+ "whatsapp"
12315
+ ]
12316
+ },
12317
+ {
12318
+ "type": "string",
12319
+ "enum": [
12320
+ "factor"
12321
+ ]
12322
+ }
12323
+ ]
12324
+ }
12325
+ ],
12326
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
12327
+ },
12328
+ "visible": {
12329
+ "type": "array",
12330
+ "items": {
12331
+ "anyOf": [
12332
+ {
12333
+ "type": "string",
12334
+ "enum": [
12335
+ "email"
12336
+ ]
12337
+ },
12338
+ {
12339
+ "type": "string",
12340
+ "enum": [
12341
+ "sms"
12342
+ ]
12343
+ },
12344
+ {
12345
+ "type": "string",
12346
+ "enum": [
12347
+ "whatsapp"
12348
+ ]
12349
+ },
12350
+ {
12351
+ "type": "string",
12352
+ "enum": [
12353
+ "factor"
12354
+ ]
12355
+ }
12356
+ ]
12357
+ },
12358
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
12359
+ }
12360
+ },
12361
+ "additionalProperties": false,
12362
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
12363
+ },
10875
12364
  "login_flow": {
10876
12365
  "anyOf": [
10877
12366
  {
@@ -11220,7 +12709,111 @@
11220
12709
  }
11221
12710
  },
11222
12711
  "additionalProperties": false,
11223
- "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
12712
+ "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
12713
+ },
12714
+ "recovery_channels": {
12715
+ "type": "object",
12716
+ "properties": {
12717
+ "enabled": {
12718
+ "type": "array",
12719
+ "items": {
12720
+ "anyOf": [
12721
+ {
12722
+ "type": "string",
12723
+ "enum": [
12724
+ "email"
12725
+ ]
12726
+ },
12727
+ {
12728
+ "type": "string",
12729
+ "enum": [
12730
+ "sms"
12731
+ ]
12732
+ },
12733
+ {
12734
+ "type": "string",
12735
+ "enum": [
12736
+ "whatsapp"
12737
+ ]
12738
+ },
12739
+ {
12740
+ "type": "string",
12741
+ "enum": [
12742
+ "factor"
12743
+ ]
12744
+ }
12745
+ ]
12746
+ },
12747
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
12748
+ },
12749
+ "default": {
12750
+ "anyOf": [
12751
+ {
12752
+ "anyOf": [
12753
+ {
12754
+ "type": "string",
12755
+ "enum": [
12756
+ "email"
12757
+ ]
12758
+ },
12759
+ {
12760
+ "type": "string",
12761
+ "enum": [
12762
+ "sms"
12763
+ ]
12764
+ },
12765
+ {
12766
+ "type": "string",
12767
+ "enum": [
12768
+ "whatsapp"
12769
+ ]
12770
+ },
12771
+ {
12772
+ "type": "string",
12773
+ "enum": [
12774
+ "factor"
12775
+ ]
12776
+ }
12777
+ ]
12778
+ }
12779
+ ],
12780
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
12781
+ },
12782
+ "visible": {
12783
+ "type": "array",
12784
+ "items": {
12785
+ "anyOf": [
12786
+ {
12787
+ "type": "string",
12788
+ "enum": [
12789
+ "email"
12790
+ ]
12791
+ },
12792
+ {
12793
+ "type": "string",
12794
+ "enum": [
12795
+ "sms"
12796
+ ]
12797
+ },
12798
+ {
12799
+ "type": "string",
12800
+ "enum": [
12801
+ "whatsapp"
12802
+ ]
12803
+ },
12804
+ {
12805
+ "type": "string",
12806
+ "enum": [
12807
+ "factor"
12808
+ ]
12809
+ }
12810
+ ]
12811
+ },
12812
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
12813
+ }
12814
+ },
12815
+ "additionalProperties": false,
12816
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
11224
12817
  },
11225
12818
  "login_flow": {
11226
12819
  "anyOf": [
@@ -11549,6 +13142,117 @@
11549
13142
  }
11550
13143
  ]
11551
13144
  },
13145
+ "recovery_channels": {
13146
+ "anyOf": [
13147
+ {
13148
+ "type": "object",
13149
+ "properties": {
13150
+ "enabled": {
13151
+ "type": "array",
13152
+ "items": {
13153
+ "anyOf": [
13154
+ {
13155
+ "type": "string",
13156
+ "enum": [
13157
+ "email"
13158
+ ]
13159
+ },
13160
+ {
13161
+ "type": "string",
13162
+ "enum": [
13163
+ "sms"
13164
+ ]
13165
+ },
13166
+ {
13167
+ "type": "string",
13168
+ "enum": [
13169
+ "whatsapp"
13170
+ ]
13171
+ },
13172
+ {
13173
+ "type": "string",
13174
+ "enum": [
13175
+ "factor"
13176
+ ]
13177
+ }
13178
+ ]
13179
+ },
13180
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
13181
+ },
13182
+ "default": {
13183
+ "anyOf": [
13184
+ {
13185
+ "anyOf": [
13186
+ {
13187
+ "type": "string",
13188
+ "enum": [
13189
+ "email"
13190
+ ]
13191
+ },
13192
+ {
13193
+ "type": "string",
13194
+ "enum": [
13195
+ "sms"
13196
+ ]
13197
+ },
13198
+ {
13199
+ "type": "string",
13200
+ "enum": [
13201
+ "whatsapp"
13202
+ ]
13203
+ },
13204
+ {
13205
+ "type": "string",
13206
+ "enum": [
13207
+ "factor"
13208
+ ]
13209
+ }
13210
+ ]
13211
+ }
13212
+ ],
13213
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
13214
+ },
13215
+ "visible": {
13216
+ "type": "array",
13217
+ "items": {
13218
+ "anyOf": [
13219
+ {
13220
+ "type": "string",
13221
+ "enum": [
13222
+ "email"
13223
+ ]
13224
+ },
13225
+ {
13226
+ "type": "string",
13227
+ "enum": [
13228
+ "sms"
13229
+ ]
13230
+ },
13231
+ {
13232
+ "type": "string",
13233
+ "enum": [
13234
+ "whatsapp"
13235
+ ]
13236
+ },
13237
+ {
13238
+ "type": "string",
13239
+ "enum": [
13240
+ "factor"
13241
+ ]
13242
+ }
13243
+ ]
13244
+ },
13245
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
13246
+ }
13247
+ },
13248
+ "additionalProperties": false,
13249
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
13250
+ },
13251
+ {
13252
+ "type": "null"
13253
+ }
13254
+ ]
13255
+ },
11552
13256
  "login_flow": {
11553
13257
  "anyOf": [
11554
13258
  {
@@ -11877,6 +13581,110 @@
11877
13581
  "additionalProperties": false,
11878
13582
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
11879
13583
  },
13584
+ "recovery_channels": {
13585
+ "type": "object",
13586
+ "properties": {
13587
+ "enabled": {
13588
+ "type": "array",
13589
+ "items": {
13590
+ "anyOf": [
13591
+ {
13592
+ "type": "string",
13593
+ "enum": [
13594
+ "email"
13595
+ ]
13596
+ },
13597
+ {
13598
+ "type": "string",
13599
+ "enum": [
13600
+ "sms"
13601
+ ]
13602
+ },
13603
+ {
13604
+ "type": "string",
13605
+ "enum": [
13606
+ "whatsapp"
13607
+ ]
13608
+ },
13609
+ {
13610
+ "type": "string",
13611
+ "enum": [
13612
+ "factor"
13613
+ ]
13614
+ }
13615
+ ]
13616
+ },
13617
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
13618
+ },
13619
+ "default": {
13620
+ "anyOf": [
13621
+ {
13622
+ "anyOf": [
13623
+ {
13624
+ "type": "string",
13625
+ "enum": [
13626
+ "email"
13627
+ ]
13628
+ },
13629
+ {
13630
+ "type": "string",
13631
+ "enum": [
13632
+ "sms"
13633
+ ]
13634
+ },
13635
+ {
13636
+ "type": "string",
13637
+ "enum": [
13638
+ "whatsapp"
13639
+ ]
13640
+ },
13641
+ {
13642
+ "type": "string",
13643
+ "enum": [
13644
+ "factor"
13645
+ ]
13646
+ }
13647
+ ]
13648
+ }
13649
+ ],
13650
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
13651
+ },
13652
+ "visible": {
13653
+ "type": "array",
13654
+ "items": {
13655
+ "anyOf": [
13656
+ {
13657
+ "type": "string",
13658
+ "enum": [
13659
+ "email"
13660
+ ]
13661
+ },
13662
+ {
13663
+ "type": "string",
13664
+ "enum": [
13665
+ "sms"
13666
+ ]
13667
+ },
13668
+ {
13669
+ "type": "string",
13670
+ "enum": [
13671
+ "whatsapp"
13672
+ ]
13673
+ },
13674
+ {
13675
+ "type": "string",
13676
+ "enum": [
13677
+ "factor"
13678
+ ]
13679
+ }
13680
+ ]
13681
+ },
13682
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
13683
+ }
13684
+ },
13685
+ "additionalProperties": false,
13686
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
13687
+ },
11880
13688
  "login_flow": {
11881
13689
  "anyOf": [
11882
13690
  {
@@ -12223,7 +14031,111 @@
12223
14031
  }
12224
14032
  },
12225
14033
  "additionalProperties": false,
12226
- "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
14034
+ "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
14035
+ },
14036
+ "recovery_channels": {
14037
+ "type": "object",
14038
+ "properties": {
14039
+ "enabled": {
14040
+ "type": "array",
14041
+ "items": {
14042
+ "anyOf": [
14043
+ {
14044
+ "type": "string",
14045
+ "enum": [
14046
+ "email"
14047
+ ]
14048
+ },
14049
+ {
14050
+ "type": "string",
14051
+ "enum": [
14052
+ "sms"
14053
+ ]
14054
+ },
14055
+ {
14056
+ "type": "string",
14057
+ "enum": [
14058
+ "whatsapp"
14059
+ ]
14060
+ },
14061
+ {
14062
+ "type": "string",
14063
+ "enum": [
14064
+ "factor"
14065
+ ]
14066
+ }
14067
+ ]
14068
+ },
14069
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
14070
+ },
14071
+ "default": {
14072
+ "anyOf": [
14073
+ {
14074
+ "anyOf": [
14075
+ {
14076
+ "type": "string",
14077
+ "enum": [
14078
+ "email"
14079
+ ]
14080
+ },
14081
+ {
14082
+ "type": "string",
14083
+ "enum": [
14084
+ "sms"
14085
+ ]
14086
+ },
14087
+ {
14088
+ "type": "string",
14089
+ "enum": [
14090
+ "whatsapp"
14091
+ ]
14092
+ },
14093
+ {
14094
+ "type": "string",
14095
+ "enum": [
14096
+ "factor"
14097
+ ]
14098
+ }
14099
+ ]
14100
+ }
14101
+ ],
14102
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
14103
+ },
14104
+ "visible": {
14105
+ "type": "array",
14106
+ "items": {
14107
+ "anyOf": [
14108
+ {
14109
+ "type": "string",
14110
+ "enum": [
14111
+ "email"
14112
+ ]
14113
+ },
14114
+ {
14115
+ "type": "string",
14116
+ "enum": [
14117
+ "sms"
14118
+ ]
14119
+ },
14120
+ {
14121
+ "type": "string",
14122
+ "enum": [
14123
+ "whatsapp"
14124
+ ]
14125
+ },
14126
+ {
14127
+ "type": "string",
14128
+ "enum": [
14129
+ "factor"
14130
+ ]
14131
+ }
14132
+ ]
14133
+ },
14134
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
14135
+ }
14136
+ },
14137
+ "additionalProperties": false,
14138
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
12227
14139
  },
12228
14140
  "login_flow": {
12229
14141
  "anyOf": [
@@ -12575,6 +14487,110 @@
12575
14487
  "additionalProperties": false,
12576
14488
  "description": "Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."
12577
14489
  },
14490
+ "recovery_channels": {
14491
+ "type": "object",
14492
+ "properties": {
14493
+ "enabled": {
14494
+ "type": "array",
14495
+ "items": {
14496
+ "anyOf": [
14497
+ {
14498
+ "type": "string",
14499
+ "enum": [
14500
+ "email"
14501
+ ]
14502
+ },
14503
+ {
14504
+ "type": "string",
14505
+ "enum": [
14506
+ "sms"
14507
+ ]
14508
+ },
14509
+ {
14510
+ "type": "string",
14511
+ "enum": [
14512
+ "whatsapp"
14513
+ ]
14514
+ },
14515
+ {
14516
+ "type": "string",
14517
+ "enum": [
14518
+ "factor"
14519
+ ]
14520
+ }
14521
+ ]
14522
+ },
14523
+ "description": "Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."
14524
+ },
14525
+ "default": {
14526
+ "anyOf": [
14527
+ {
14528
+ "anyOf": [
14529
+ {
14530
+ "type": "string",
14531
+ "enum": [
14532
+ "email"
14533
+ ]
14534
+ },
14535
+ {
14536
+ "type": "string",
14537
+ "enum": [
14538
+ "sms"
14539
+ ]
14540
+ },
14541
+ {
14542
+ "type": "string",
14543
+ "enum": [
14544
+ "whatsapp"
14545
+ ]
14546
+ },
14547
+ {
14548
+ "type": "string",
14549
+ "enum": [
14550
+ "factor"
14551
+ ]
14552
+ }
14553
+ ]
14554
+ }
14555
+ ],
14556
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
14557
+ },
14558
+ "visible": {
14559
+ "type": "array",
14560
+ "items": {
14561
+ "anyOf": [
14562
+ {
14563
+ "type": "string",
14564
+ "enum": [
14565
+ "email"
14566
+ ]
14567
+ },
14568
+ {
14569
+ "type": "string",
14570
+ "enum": [
14571
+ "sms"
14572
+ ]
14573
+ },
14574
+ {
14575
+ "type": "string",
14576
+ "enum": [
14577
+ "whatsapp"
14578
+ ]
14579
+ },
14580
+ {
14581
+ "type": "string",
14582
+ "enum": [
14583
+ "factor"
14584
+ ]
14585
+ }
14586
+ ]
14587
+ },
14588
+ "description": "Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."
14589
+ }
14590
+ },
14591
+ "additionalProperties": false,
14592
+ "description": "Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."
14593
+ },
12578
14594
  "login_flow": {
12579
14595
  "anyOf": [
12580
14596
  {
@@ -12957,6 +14973,12 @@
12957
14973
  "federated"
12958
14974
  ]
12959
14975
  },
14976
+ {
14977
+ "type": "string",
14978
+ "enum": [
14979
+ "sms_otp"
14980
+ ]
14981
+ },
12960
14982
  {
12961
14983
  "type": "null"
12962
14984
  }
@@ -13044,11 +15066,17 @@
13044
15066
  "federated"
13045
15067
  ]
13046
15068
  },
15069
+ {
15070
+ "type": "string",
15071
+ "enum": [
15072
+ "sms_otp"
15073
+ ]
15074
+ },
13047
15075
  {
13048
15076
  "type": "null"
13049
15077
  }
13050
15078
  ],
13051
- "description": "How `phone_verified` was last set. Same enum as `email_verified_method`."
15079
+ "description": "How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."
13052
15080
  },
13053
15081
  "phone_verified_at": {
13054
15082
  "type": "string",
@@ -13329,6 +15357,12 @@
13329
15357
  "federated"
13330
15358
  ]
13331
15359
  },
15360
+ {
15361
+ "type": "string",
15362
+ "enum": [
15363
+ "sms_otp"
15364
+ ]
15365
+ },
13332
15366
  {
13333
15367
  "type": "null"
13334
15368
  }
@@ -13416,11 +15450,17 @@
13416
15450
  "federated"
13417
15451
  ]
13418
15452
  },
15453
+ {
15454
+ "type": "string",
15455
+ "enum": [
15456
+ "sms_otp"
15457
+ ]
15458
+ },
13419
15459
  {
13420
15460
  "type": "null"
13421
15461
  }
13422
15462
  ],
13423
- "description": "How `phone_verified` was last set. Same enum as `email_verified_method`."
15463
+ "description": "How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."
13424
15464
  },
13425
15465
  "phone_verified_at": {
13426
15466
  "type": "string",
@@ -13780,6 +15820,12 @@
13780
15820
  "federated"
13781
15821
  ]
13782
15822
  },
15823
+ {
15824
+ "type": "string",
15825
+ "enum": [
15826
+ "sms_otp"
15827
+ ]
15828
+ },
13783
15829
  {
13784
15830
  "type": "null"
13785
15831
  }
@@ -13867,11 +15913,17 @@
13867
15913
  "federated"
13868
15914
  ]
13869
15915
  },
15916
+ {
15917
+ "type": "string",
15918
+ "enum": [
15919
+ "sms_otp"
15920
+ ]
15921
+ },
13870
15922
  {
13871
15923
  "type": "null"
13872
15924
  }
13873
15925
  ],
13874
- "description": "How `phone_verified` was last set. Same enum as `email_verified_method`."
15926
+ "description": "How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."
13875
15927
  },
13876
15928
  "phone_verified_at": {
13877
15929
  "type": "string",
@@ -14150,6 +16202,12 @@
14150
16202
  "federated"
14151
16203
  ]
14152
16204
  },
16205
+ {
16206
+ "type": "string",
16207
+ "enum": [
16208
+ "sms_otp"
16209
+ ]
16210
+ },
14153
16211
  {
14154
16212
  "type": "null"
14155
16213
  }
@@ -14237,11 +16295,17 @@
14237
16295
  "federated"
14238
16296
  ]
14239
16297
  },
16298
+ {
16299
+ "type": "string",
16300
+ "enum": [
16301
+ "sms_otp"
16302
+ ]
16303
+ },
14240
16304
  {
14241
16305
  "type": "null"
14242
16306
  }
14243
16307
  ],
14244
- "description": "How `phone_verified` was last set. Same enum as `email_verified_method`."
16308
+ "description": "How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."
14245
16309
  },
14246
16310
  "phone_verified_at": {
14247
16311
  "type": "string",
@@ -14877,6 +16941,141 @@
14877
16941
  }
14878
16942
  }
14879
16943
  },
16944
+ "/user/{user_id}/verify-phone/start": {
16945
+ "post": {
16946
+ "operationId": "user/verifyPhoneStart",
16947
+ "summary": "Send a verification code to the user's phone",
16948
+ "tags": [
16949
+ "user"
16950
+ ],
16951
+ "description": "Sends a 6-digit code by SMS to the user's phone (optionally setting a new phone first) and returns an opaque `state` to confirm it with. Same authorization as `verify-email/start`: the user themself (session or bearer), an admin of another tenant, or a machine token. Counts against the tenant's monthly SMS allowance. Fails with 409 `sms_unavailable` when no SMS can go out (no provider, plan does not allow it, allowance exhausted).",
16952
+ "requestBody": {
16953
+ "required": true,
16954
+ "content": {
16955
+ "application/json": {
16956
+ "schema": {
16957
+ "type": "object",
16958
+ "properties": {
16959
+ "phone": {
16960
+ "type": "string",
16961
+ "description": "Phone to verify. When given, it replaces the user's phone (normalised to E.164 with the account's `default_country_iso` as fallback) before the code is sent. When omitted, the code goes to the phone already on the user."
16962
+ }
16963
+ },
16964
+ "additionalProperties": false
16965
+ }
16966
+ }
16967
+ }
16968
+ },
16969
+ "parameters": [
16970
+ {
16971
+ "schema": {
16972
+ "type": "string"
16973
+ },
16974
+ "in": "path",
16975
+ "name": "user_id",
16976
+ "required": true
16977
+ }
16978
+ ],
16979
+ "responses": {
16980
+ "200": {
16981
+ "description": "Default Response",
16982
+ "content": {
16983
+ "application/json": {
16984
+ "schema": {
16985
+ "type": "object",
16986
+ "required": [
16987
+ "status",
16988
+ "destination_masked",
16989
+ "state",
16990
+ "expires_in"
16991
+ ],
16992
+ "properties": {
16993
+ "status": {
16994
+ "type": "string",
16995
+ "enum": [
16996
+ "sent"
16997
+ ]
16998
+ },
16999
+ "destination_masked": {
17000
+ "type": "string"
17001
+ },
17002
+ "state": {
17003
+ "type": "string",
17004
+ "description": "Opaque handle to confirm with. It is what lets a tenant backend start the verification (M2M) and send the person to `/flow/verify-phone?state=…` without a session."
17005
+ },
17006
+ "expires_in": {
17007
+ "type": "integer"
17008
+ }
17009
+ }
17010
+ }
17011
+ }
17012
+ }
17013
+ }
17014
+ }
17015
+ }
17016
+ },
17017
+ "/verify-phone/confirm": {
17018
+ "post": {
17019
+ "operationId": "user/verifyPhoneConfirm",
17020
+ "summary": "Confirm a phone verification code",
17021
+ "tags": [
17022
+ "user"
17023
+ ],
17024
+ "description": "Marks the phone verified (`phone_verified_method: sms_otp`) when the code matches the pending verification identified by `state`. Five wrong codes burn the `state`; start again. No session needed: the `state` plus the code are the proof.",
17025
+ "requestBody": {
17026
+ "required": true,
17027
+ "content": {
17028
+ "application/json": {
17029
+ "schema": {
17030
+ "type": "object",
17031
+ "required": [
17032
+ "state",
17033
+ "code"
17034
+ ],
17035
+ "properties": {
17036
+ "state": {
17037
+ "type": "string"
17038
+ },
17039
+ "code": {
17040
+ "type": "string",
17041
+ "minLength": 4,
17042
+ "maxLength": 12
17043
+ }
17044
+ },
17045
+ "additionalProperties": false
17046
+ }
17047
+ }
17048
+ }
17049
+ },
17050
+ "responses": {
17051
+ "200": {
17052
+ "description": "Default Response",
17053
+ "content": {
17054
+ "application/json": {
17055
+ "schema": {
17056
+ "type": "object",
17057
+ "required": [
17058
+ "status",
17059
+ "user_id"
17060
+ ],
17061
+ "properties": {
17062
+ "status": {
17063
+ "type": "string",
17064
+ "enum": [
17065
+ "verified"
17066
+ ]
17067
+ },
17068
+ "user_id": {
17069
+ "type": "string"
17070
+ }
17071
+ }
17072
+ }
17073
+ }
17074
+ }
17075
+ }
17076
+ }
17077
+ }
17078
+ },
14880
17079
  "/user/{user_id}/factors": {
14881
17080
  "get": {
14882
17081
  "operationId": "user/factors",
@@ -29588,6 +31787,12 @@
29588
31787
  "federated"
29589
31788
  ]
29590
31789
  },
31790
+ {
31791
+ "type": "string",
31792
+ "enum": [
31793
+ "sms_otp"
31794
+ ]
31795
+ },
29591
31796
  {
29592
31797
  "type": "null"
29593
31798
  }
@@ -29675,11 +31880,17 @@
29675
31880
  "federated"
29676
31881
  ]
29677
31882
  },
31883
+ {
31884
+ "type": "string",
31885
+ "enum": [
31886
+ "sms_otp"
31887
+ ]
31888
+ },
29678
31889
  {
29679
31890
  "type": "null"
29680
31891
  }
29681
31892
  ],
29682
- "description": "How `phone_verified` was last set. Same enum as `email_verified_method`."
31893
+ "description": "How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."
29683
31894
  },
29684
31895
  "phone_verified_at": {
29685
31896
  "type": "string",