@fleetless/contracts 5.3.0-next.1 → 6.0.0-next.1

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 (89) hide show
  1. package/CHANGELOG.md +96 -33
  2. package/artifacts/openapi.json +2049 -773
  3. package/artifacts/routes.json +1665 -314
  4. package/artifacts/schema/accept-team-invite-request.schema.json +13 -7
  5. package/artifacts/schema/app-auth-config.schema.json +108 -4
  6. package/artifacts/schema/app-hosted-pages.schema.json +33 -0
  7. package/artifacts/schema/app-invitation.schema.json +2 -2
  8. package/artifacts/schema/app-mail-template-list-response.schema.json +4 -3
  9. package/artifacts/schema/app-mail-template.schema.json +3 -2
  10. package/artifacts/schema/app-sign-in-methods.schema.json +19 -0
  11. package/artifacts/schema/app-user-list-response.schema.json +36 -0
  12. package/artifacts/schema/app-user.schema.json +36 -0
  13. package/artifacts/schema/audit-query.schema.json +0 -6
  14. package/artifacts/schema/auth-me-response.schema.json +27 -0
  15. package/artifacts/schema/auth-ok.schema.json +13 -1
  16. package/artifacts/schema/client-accept-invitation-request.schema.json +3 -4
  17. package/artifacts/schema/client-identity.schema.json +13 -1
  18. package/artifacts/schema/client-login-code-request.schema.json +24 -0
  19. package/artifacts/schema/client-login-code-verify-request.schema.json +30 -0
  20. package/artifacts/schema/client-provider-list-response.schema.json +22 -2
  21. package/artifacts/schema/client-register-request.schema.json +3 -4
  22. package/artifacts/schema/client-sign-in-result.schema.json +55 -0
  23. package/artifacts/schema/client-two-factor-disable-request.schema.json +15 -0
  24. package/artifacts/schema/client-two-factor-setup-confirm-request.schema.json +20 -0
  25. package/artifacts/schema/client-two-factor-setup-confirm-response.schema.json +49 -0
  26. package/artifacts/schema/client-two-factor-setup-request.schema.json +12 -0
  27. package/artifacts/schema/client-two-factor-verify-request.schema.json +25 -0
  28. package/artifacts/schema/create-app-invitation-request.schema.json +1 -1
  29. package/artifacts/schema/create-passkey-request.schema.json +25 -0
  30. package/artifacts/schema/create-passkey-response.schema.json +84 -0
  31. package/artifacts/schema/developer-passkey.schema.json +56 -0
  32. package/artifacts/schema/developer-two-factor.schema.json +105 -0
  33. package/artifacts/schema/fleetless-user-list-response.schema.json +22 -0
  34. package/artifacts/schema/fleetless-user.schema.json +22 -0
  35. package/artifacts/schema/invalid-code-details.schema.json +16 -0
  36. package/artifacts/schema/job-actor.schema.json +1 -15
  37. package/artifacts/schema/job-run-list-response.schema.json +1 -15
  38. package/artifacts/schema/job-run.schema.json +1 -15
  39. package/artifacts/schema/org.schema.json +5 -0
  40. package/artifacts/schema/patch-org-request.schema.json +5 -3
  41. package/artifacts/schema/patch-org-response.schema.json +5 -0
  42. package/artifacts/schema/put-app-auth-look-request.schema.json +22 -0
  43. package/artifacts/schema/put-app-auth-mcp-request.schema.json +1 -14
  44. package/artifacts/schema/put-app-auth-sign-in-request.schema.json +39 -0
  45. package/artifacts/schema/put-app-auth-urls-request.schema.json +31 -4
  46. package/artifacts/schema/recovery-codes-response.schema.json +20 -0
  47. package/artifacts/schema/{role-rename-request.schema.json → rename-passkey-request.schema.json} +2 -2
  48. package/artifacts/schema/role-list-response.schema.json +1 -1
  49. package/artifacts/schema/role.schema.json +1 -1
  50. package/artifacts/schema/totp-confirm-request.schema.json +15 -0
  51. package/artifacts/schema/totp-confirm-response.schema.json +27 -0
  52. package/artifacts/schema/two-factor-challenge.schema.json +24 -0
  53. package/artifacts/schema/two-factor-setup-response.schema.json +21 -0
  54. package/artifacts/schema/webauthn-options-response.schema.json +18 -0
  55. package/dist/app-users.d.ts +154 -34
  56. package/dist/app-users.js +177 -45
  57. package/dist/apps.d.ts +2 -38
  58. package/dist/apps.js +3 -46
  59. package/dist/audit.d.ts +0 -1
  60. package/dist/audit.js +0 -13
  61. package/dist/client-auth.d.ts +133 -24
  62. package/dist/client-auth.js +139 -28
  63. package/dist/config.d.ts +2 -2
  64. package/dist/errors.d.ts +10 -1
  65. package/dist/errors.js +34 -34
  66. package/dist/identity.d.ts +172 -123
  67. package/dist/identity.js +189 -101
  68. package/dist/index.d.ts +11 -13
  69. package/dist/index.js +12 -10
  70. package/dist/jobs.d.ts +0 -3
  71. package/dist/jobs.js +0 -16
  72. package/dist/protocol.d.ts +1 -1
  73. package/dist/realtime.d.ts +1 -0
  74. package/dist/rest.d.ts +2 -2
  75. package/dist/rest.js +17 -28
  76. package/dist/routes.d.ts +19 -0
  77. package/dist/routes.js +499 -223
  78. package/package.json +1 -1
  79. package/artifacts/schema/developer-login-request.schema.json +0 -19
  80. package/artifacts/schema/feedback-request.schema.json +0 -34
  81. package/artifacts/schema/feedback-response.schema.json +0 -27
  82. package/artifacts/schema/password-reset-confirm.schema.json +0 -19
  83. package/artifacts/schema/password-reset-request.schema.json +0 -14
  84. package/artifacts/schema/role-delete-query.schema.json +0 -13
  85. package/artifacts/schema/role-in-use-details.schema.json +0 -28
  86. package/artifacts/schema/sign-up-request.schema.json +0 -26
  87. package/artifacts/schema/sign-up-response.schema.json +0 -127
  88. package/dist/feedback.d.ts +0 -42
  89. package/dist/feedback.js +0 -35
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "anyOf": [
4
+ {
5
+ "type": "object",
6
+ "properties": {
7
+ "access_token": {
8
+ "type": "string",
9
+ "minLength": 1,
10
+ "description": "The token to send as `Authorization: Bearer <token>` on every call. Short-lived: read `expires_in` rather than assuming a lifetime."
11
+ },
12
+ "refresh_token": {
13
+ "type": "string",
14
+ "minLength": 1,
15
+ "description": "The token that buys the next access token. It rotates on every use, so a value presented twice is detectable theft and ends the whole family."
16
+ },
17
+ "expires_in": {
18
+ "type": "integer",
19
+ "exclusiveMinimum": 0,
20
+ "maximum": 9007199254740991,
21
+ "description": "How long the access token stays valid, in **seconds** from now. Not a timestamp, and not milliseconds."
22
+ }
23
+ },
24
+ "required": [
25
+ "access_token",
26
+ "refresh_token",
27
+ "expires_in"
28
+ ],
29
+ "additionalProperties": false
30
+ },
31
+ {
32
+ "type": "object",
33
+ "properties": {
34
+ "status": {
35
+ "type": "string",
36
+ "enum": [
37
+ "two_factor_required",
38
+ "two_factor_setup_required"
39
+ ],
40
+ "description": "`two_factor_required`: ask for the authenticator code. `two_factor_setup_required`: the app requires two-factor and the person has none yet, so set one up before any session exists."
41
+ },
42
+ "challenge": {
43
+ "type": "string",
44
+ "minLength": 1,
45
+ "description": "The handle the next step spends. Valid five minutes; afterwards it answers `410 token_spent` and the sign-in starts over."
46
+ }
47
+ },
48
+ "required": [
49
+ "status",
50
+ "challenge"
51
+ ],
52
+ "additionalProperties": false
53
+ }
54
+ ]
55
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "code": {
6
+ "type": "string",
7
+ "pattern": "^\\d{6}$",
8
+ "description": "A code the authenticator shows now."
9
+ }
10
+ },
11
+ "required": [
12
+ "code"
13
+ ],
14
+ "additionalProperties": false
15
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "challenge": {
6
+ "description": "The same challenge as at `setup`, during sign-in; absent with a bearer.",
7
+ "type": "string",
8
+ "minLength": 1
9
+ },
10
+ "code": {
11
+ "type": "string",
12
+ "pattern": "^\\d{6}$",
13
+ "description": "A code the new authenticator shows now. It proves the secret was copied correctly before anything depends on it."
14
+ }
15
+ },
16
+ "required": [
17
+ "code"
18
+ ],
19
+ "additionalProperties": false
20
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "recovery_codes": {
6
+ "minItems": 10,
7
+ "maxItems": 10,
8
+ "type": "array",
9
+ "items": {
10
+ "type": "string",
11
+ "pattern": "^[a-z2-7]{5}-[a-z2-7]{5}$"
12
+ },
13
+ "description": "The ten single-use recovery codes, lowercase, shown once. Any earlier set is void."
14
+ },
15
+ "session": {
16
+ "type": "object",
17
+ "properties": {
18
+ "access_token": {
19
+ "type": "string",
20
+ "minLength": 1,
21
+ "description": "The token to send as `Authorization: Bearer <token>` on every call. Short-lived: read `expires_in` rather than assuming a lifetime."
22
+ },
23
+ "refresh_token": {
24
+ "type": "string",
25
+ "minLength": 1,
26
+ "description": "The token that buys the next access token. It rotates on every use, so a value presented twice is detectable theft and ends the whole family."
27
+ },
28
+ "expires_in": {
29
+ "type": "integer",
30
+ "exclusiveMinimum": 0,
31
+ "maximum": 9007199254740991,
32
+ "description": "How long the access token stays valid, in **seconds** from now. Not a timestamp, and not milliseconds."
33
+ }
34
+ },
35
+ "required": [
36
+ "access_token",
37
+ "refresh_token",
38
+ "expires_in"
39
+ ],
40
+ "additionalProperties": false,
41
+ "description": "The session the sign-in was waiting for, or a fresh one for the account settings."
42
+ }
43
+ },
44
+ "required": [
45
+ "recovery_codes",
46
+ "session"
47
+ ],
48
+ "additionalProperties": false
49
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "challenge": {
6
+ "description": "The `two_factor_setup_required` challenge, during sign-in. Absent when the call carries the app user's bearer instead.",
7
+ "type": "string",
8
+ "minLength": 1
9
+ }
10
+ },
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "challenge": {
6
+ "type": "string",
7
+ "minLength": 1,
8
+ "description": "The challenge the sign-in step answered."
9
+ },
10
+ "code": {
11
+ "description": "The six-digit code the authenticator shows now. A code already accepted once is refused, so a replay of a seen code does not sign anybody in.",
12
+ "type": "string",
13
+ "pattern": "^\\d{6}$"
14
+ },
15
+ "recovery_code": {
16
+ "description": "One of the ten recovery codes, `xxxxx-xxxxx`, in either case. Spent by its use.",
17
+ "type": "string",
18
+ "pattern": "^[a-zA-Z2-7]{5}-[a-zA-Z2-7]{5}$"
19
+ }
20
+ },
21
+ "required": [
22
+ "challenge"
23
+ ],
24
+ "additionalProperties": false
25
+ }
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "send_mail": {
31
31
  "type": "boolean",
32
- "description": "Whether Fleetless mails the invitation. **Refused with `409 target_state_conflict` naming `invite_url` when the app has configured none** — there would be nowhere for the link to point, and a mail carrying a Fleetless-hosted page is a surface this product does not have."
32
+ "description": "Whether Fleetless mails the invitation. The link points at the app's `invite_url`, or at the Fleetless-hosted invitation page when the app has configured none — so the mail always leads somewhere, and nothing is refused for a missing URL."
33
33
  }
34
34
  },
35
35
  "required": [
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "name": {
6
+ "type": "string",
7
+ "minLength": 1,
8
+ "maxLength": 80,
9
+ "description": "What to call the passkey, such as the device it lives on."
10
+ },
11
+ "credential": {
12
+ "type": "object",
13
+ "propertyNames": {
14
+ "type": "string"
15
+ },
16
+ "additionalProperties": {},
17
+ "description": "The browser's `RegistrationResponseJSON` for the options `POST /api/auth/passkeys/options` answered."
18
+ }
19
+ },
20
+ "required": [
21
+ "name",
22
+ "credential"
23
+ ],
24
+ "additionalProperties": false
25
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "passkey": {
6
+ "type": "object",
7
+ "properties": {
8
+ "id": {
9
+ "type": "string",
10
+ "format": "uuid",
11
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
12
+ "description": "The passkey in the API, as renamed and removed through `/api/auth/passkeys/:id`."
13
+ },
14
+ "name": {
15
+ "type": "string",
16
+ "minLength": 1,
17
+ "maxLength": 80,
18
+ "description": "What the person called it, such as the device it lives on."
19
+ },
20
+ "created_at": {
21
+ "type": "string",
22
+ "format": "date-time",
23
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
24
+ "description": "When it was registered."
25
+ },
26
+ "last_used_at": {
27
+ "anyOf": [
28
+ {
29
+ "type": "string",
30
+ "format": "date-time",
31
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
32
+ },
33
+ {
34
+ "type": "null"
35
+ }
36
+ ],
37
+ "description": "When it last signed the person in or confirmed a sign-in, or `null` if never."
38
+ },
39
+ "synced": {
40
+ "anyOf": [
41
+ {
42
+ "type": "boolean"
43
+ },
44
+ {
45
+ "type": "null"
46
+ }
47
+ ],
48
+ "description": "Whether the authenticator reported the passkey as syncable across the person's devices (the backup-eligible flag), or `null` when it said nothing."
49
+ }
50
+ },
51
+ "required": [
52
+ "id",
53
+ "name",
54
+ "created_at",
55
+ "last_used_at",
56
+ "synced"
57
+ ],
58
+ "additionalProperties": false,
59
+ "description": "The passkey as it is now stored."
60
+ },
61
+ "recovery_codes": {
62
+ "anyOf": [
63
+ {
64
+ "minItems": 10,
65
+ "maxItems": 10,
66
+ "type": "array",
67
+ "items": {
68
+ "type": "string",
69
+ "pattern": "^[a-z2-7]{5}-[a-z2-7]{5}$"
70
+ }
71
+ },
72
+ {
73
+ "type": "null"
74
+ }
75
+ ],
76
+ "description": "The ten recovery codes, shown once, when this passkey is the account's first second factor; `null` when the account already had one and its codes stay valid."
77
+ }
78
+ },
79
+ "required": [
80
+ "passkey",
81
+ "recovery_codes"
82
+ ],
83
+ "additionalProperties": false
84
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "id": {
6
+ "type": "string",
7
+ "format": "uuid",
8
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
9
+ "description": "The passkey in the API, as renamed and removed through `/api/auth/passkeys/:id`."
10
+ },
11
+ "name": {
12
+ "type": "string",
13
+ "minLength": 1,
14
+ "maxLength": 80,
15
+ "description": "What the person called it, such as the device it lives on."
16
+ },
17
+ "created_at": {
18
+ "type": "string",
19
+ "format": "date-time",
20
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
21
+ "description": "When it was registered."
22
+ },
23
+ "last_used_at": {
24
+ "anyOf": [
25
+ {
26
+ "type": "string",
27
+ "format": "date-time",
28
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
29
+ },
30
+ {
31
+ "type": "null"
32
+ }
33
+ ],
34
+ "description": "When it last signed the person in or confirmed a sign-in, or `null` if never."
35
+ },
36
+ "synced": {
37
+ "anyOf": [
38
+ {
39
+ "type": "boolean"
40
+ },
41
+ {
42
+ "type": "null"
43
+ }
44
+ ],
45
+ "description": "Whether the authenticator reported the passkey as syncable across the person's devices (the backup-eligible flag), or `null` when it said nothing."
46
+ }
47
+ },
48
+ "required": [
49
+ "id",
50
+ "name",
51
+ "created_at",
52
+ "last_used_at",
53
+ "synced"
54
+ ],
55
+ "additionalProperties": false
56
+ }
@@ -0,0 +1,105 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "passkeys": {
6
+ "type": "array",
7
+ "items": {
8
+ "type": "object",
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "format": "uuid",
13
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
14
+ "description": "The passkey in the API, as renamed and removed through `/api/auth/passkeys/:id`."
15
+ },
16
+ "name": {
17
+ "type": "string",
18
+ "minLength": 1,
19
+ "maxLength": 80,
20
+ "description": "What the person called it, such as the device it lives on."
21
+ },
22
+ "created_at": {
23
+ "type": "string",
24
+ "format": "date-time",
25
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
26
+ "description": "When it was registered."
27
+ },
28
+ "last_used_at": {
29
+ "anyOf": [
30
+ {
31
+ "type": "string",
32
+ "format": "date-time",
33
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
34
+ },
35
+ {
36
+ "type": "null"
37
+ }
38
+ ],
39
+ "description": "When it last signed the person in or confirmed a sign-in, or `null` if never."
40
+ },
41
+ "synced": {
42
+ "anyOf": [
43
+ {
44
+ "type": "boolean"
45
+ },
46
+ {
47
+ "type": "null"
48
+ }
49
+ ],
50
+ "description": "Whether the authenticator reported the passkey as syncable across the person's devices (the backup-eligible flag), or `null` when it said nothing."
51
+ }
52
+ },
53
+ "required": [
54
+ "id",
55
+ "name",
56
+ "created_at",
57
+ "last_used_at",
58
+ "synced"
59
+ ],
60
+ "additionalProperties": false
61
+ },
62
+ "description": "Every passkey the caller has registered, oldest first. Empty when none."
63
+ },
64
+ "authenticator": {
65
+ "anyOf": [
66
+ {
67
+ "type": "object",
68
+ "properties": {
69
+ "created_at": {
70
+ "type": "string",
71
+ "format": "date-time",
72
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
73
+ "description": "When the authenticator was confirmed."
74
+ }
75
+ },
76
+ "required": [
77
+ "created_at"
78
+ ],
79
+ "additionalProperties": false
80
+ },
81
+ {
82
+ "type": "null"
83
+ }
84
+ ],
85
+ "description": "The confirmed authenticator app, or `null` when there is none. At most one."
86
+ },
87
+ "recovery_codes_left": {
88
+ "type": "integer",
89
+ "minimum": 0,
90
+ "maximum": 10,
91
+ "description": "How many of the ten recovery codes are unspent. `0` while the caller has no second factor."
92
+ },
93
+ "required_by_org": {
94
+ "type": "boolean",
95
+ "description": "Whether the organisation requires a second factor. While it does, the last one cannot be removed."
96
+ }
97
+ },
98
+ "required": [
99
+ "passkeys",
100
+ "authenticator",
101
+ "recovery_codes_left",
102
+ "required_by_org"
103
+ ],
104
+ "additionalProperties": false
105
+ }
@@ -46,6 +46,27 @@
46
46
  ],
47
47
  "description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
48
48
  },
49
+ "two_factor": {
50
+ "type": "object",
51
+ "properties": {
52
+ "passkeys": {
53
+ "type": "integer",
54
+ "minimum": 0,
55
+ "maximum": 9007199254740991,
56
+ "description": "How many passkeys the person has registered."
57
+ },
58
+ "authenticator": {
59
+ "type": "boolean",
60
+ "description": "Whether the person has a confirmed authenticator app."
61
+ }
62
+ },
63
+ "required": [
64
+ "passkeys",
65
+ "authenticator"
66
+ ],
67
+ "additionalProperties": false,
68
+ "description": "The person's second factors, as the team list shows them: none, passkeys, an authenticator, or both. No credential travels here. An owner resets them through `DELETE /api/org/users/:id/two-factor`."
69
+ },
49
70
  "created_at": {
50
71
  "type": "string",
51
72
  "format": "date-time",
@@ -59,6 +80,7 @@
59
80
  "email",
60
81
  "display_name",
61
82
  "tier",
83
+ "two_factor",
62
84
  "created_at"
63
85
  ],
64
86
  "additionalProperties": false
@@ -41,6 +41,27 @@
41
41
  ],
42
42
  "description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
43
43
  },
44
+ "two_factor": {
45
+ "type": "object",
46
+ "properties": {
47
+ "passkeys": {
48
+ "type": "integer",
49
+ "minimum": 0,
50
+ "maximum": 9007199254740991,
51
+ "description": "How many passkeys the person has registered."
52
+ },
53
+ "authenticator": {
54
+ "type": "boolean",
55
+ "description": "Whether the person has a confirmed authenticator app."
56
+ }
57
+ },
58
+ "required": [
59
+ "passkeys",
60
+ "authenticator"
61
+ ],
62
+ "additionalProperties": false,
63
+ "description": "The person's second factors, as the team list shows them: none, passkeys, an authenticator, or both. No credential travels here. An owner resets them through `DELETE /api/org/users/:id/two-factor`."
64
+ },
44
65
  "created_at": {
45
66
  "type": "string",
46
67
  "format": "date-time",
@@ -54,6 +75,7 @@
54
75
  "email",
55
76
  "display_name",
56
77
  "tier",
78
+ "two_factor",
57
79
  "created_at"
58
80
  ],
59
81
  "additionalProperties": false
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "attempts_left": {
6
+ "type": "integer",
7
+ "minimum": 0,
8
+ "maximum": 9007199254740991,
9
+ "description": "How many more wrong codes this code or challenge takes before it is spent. `0` means the next attempt answers `410 token_spent`."
10
+ }
11
+ },
12
+ "required": [
13
+ "attempts_left"
14
+ ],
15
+ "additionalProperties": false
16
+ }
@@ -23,26 +23,12 @@
23
23
  "minLength": 1,
24
24
  "maxLength": 200,
25
25
  "description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
26
- },
27
- "name": {
28
- "anyOf": [
29
- {
30
- "type": "string",
31
- "minLength": 1,
32
- "maxLength": 200
33
- },
34
- {
35
- "type": "null"
36
- }
37
- ],
38
- "description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
39
26
  }
40
27
  },
41
28
  "required": [
42
29
  "kind",
43
30
  "id",
44
- "label",
45
- "name"
31
+ "label"
46
32
  ],
47
33
  "additionalProperties": false
48
34
  }
@@ -142,26 +142,12 @@
142
142
  "minLength": 1,
143
143
  "maxLength": 200,
144
144
  "description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
145
- },
146
- "name": {
147
- "anyOf": [
148
- {
149
- "type": "string",
150
- "minLength": 1,
151
- "maxLength": 200
152
- },
153
- {
154
- "type": "null"
155
- }
156
- ],
157
- "description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
158
145
  }
159
146
  },
160
147
  "required": [
161
148
  "kind",
162
149
  "id",
163
- "label",
164
- "name"
150
+ "label"
165
151
  ],
166
152
  "additionalProperties": false,
167
153
  "description": "Who invoked the run, and what they were acting as at the time."
@@ -137,26 +137,12 @@
137
137
  "minLength": 1,
138
138
  "maxLength": 200,
139
139
  "description": "A display name taken at invoke time — the email for a Fleetless user or an app user, the key's own name for a server key. Storing it rather than joining is the point: renaming a key afterwards does not rewrite history."
140
- },
141
- "name": {
142
- "anyOf": [
143
- {
144
- "type": "string",
145
- "minLength": 1,
146
- "maxLength": 200
147
- },
148
- {
149
- "type": "null"
150
- }
151
- ],
152
- "description": "The person's display name when the job started: the Fleetless user's `display_name` for a developer, the app user's `display_name` for an app user. `null` for a server key, when the person had no name set, and for runs recorded before contracts 5.3.0. Show `label` when it is null."
153
140
  }
154
141
  },
155
142
  "required": [
156
143
  "kind",
157
144
  "id",
158
- "label",
159
- "name"
145
+ "label"
160
146
  ],
161
147
  "additionalProperties": false,
162
148
  "description": "Who invoked the run, and what they were acting as at the time."
@@ -14,6 +14,10 @@
14
14
  "maxLength": 120,
15
15
  "description": "The organisation's display name. Free text, changed through `PATCH /api/org`."
16
16
  },
17
+ "require_two_factor": {
18
+ "type": "boolean",
19
+ "description": "Whether every member must have a second factor — a passkey or an authenticator app. A member without one sets it up at their next sign-in, before any session exists; nobody is signed out when it is switched on. It covers the console and the central MCP endpoint; server keys and robot bridges are not people and are not affected. Owners change it through `PATCH /api/org`."
20
+ },
17
21
  "created_at": {
18
22
  "type": "string",
19
23
  "format": "date-time",
@@ -24,6 +28,7 @@
24
28
  "required": [
25
29
  "id",
26
30
  "name",
31
+ "require_two_factor",
27
32
  "created_at"
28
33
  ],
29
34
  "additionalProperties": false