@fleetless/contracts 6.1.0-next.1 → 6.1.0-next.2

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.
@@ -9,7 +9,8 @@
9
9
  "plus",
10
10
  "pro",
11
11
  "enterprise"
12
- ]
12
+ ],
13
+ "description": "The plan."
13
14
  },
14
15
  "name": {
15
16
  "type": "string",
@@ -29,7 +30,8 @@
29
30
  {
30
31
  "type": "null"
31
32
  }
32
- ]
33
+ ],
34
+ "description": "Developers, owners included, plus pending team invitations."
33
35
  },
34
36
  "robots": {
35
37
  "anyOf": [
@@ -41,7 +43,8 @@
41
43
  {
42
44
  "type": "null"
43
45
  }
44
- ]
46
+ ],
47
+ "description": "Robots in the org."
45
48
  },
46
49
  "apps": {
47
50
  "anyOf": [
@@ -53,7 +56,8 @@
53
56
  {
54
57
  "type": "null"
55
58
  }
56
- ]
59
+ ],
60
+ "description": "Apps in the org."
57
61
  },
58
62
  "app_users": {
59
63
  "anyOf": [
@@ -65,7 +69,8 @@
65
69
  {
66
70
  "type": "null"
67
71
  }
68
- ]
72
+ ],
73
+ "description": "App users across every app of the org, plus pending app-user invitations."
69
74
  },
70
75
  "live_video_ms_per_month": {
71
76
  "anyOf": [
@@ -77,7 +82,8 @@
77
82
  {
78
83
  "type": "null"
79
84
  }
80
- ]
85
+ ],
86
+ "description": "Live video watched by app users in one UTC calendar month, in milliseconds, across the org. Console sessions do not count."
81
87
  },
82
88
  "asset_bytes_per_robot": {
83
89
  "anyOf": [
@@ -89,7 +95,8 @@
89
95
  {
90
96
  "type": "null"
91
97
  }
92
- ]
98
+ ],
99
+ "description": "Asset storage per robot, in bytes (decimal: 1 GB = 1,000,000,000). The org stores up to robots × this value in total."
93
100
  },
94
101
  "history_days": {
95
102
  "anyOf": [
@@ -101,7 +108,8 @@
101
108
  {
102
109
  "type": "null"
103
110
  }
104
- ]
111
+ ],
112
+ "description": "Days the org's robot history is kept."
105
113
  },
106
114
  "audit_days": {
107
115
  "anyOf": [
@@ -113,7 +121,8 @@
113
121
  {
114
122
  "type": "null"
115
123
  }
116
- ]
124
+ ],
125
+ "description": "Days the org's audit log is kept."
117
126
  }
118
127
  },
119
128
  "required": [
@@ -126,31 +135,39 @@
126
135
  "history_days",
127
136
  "audit_days"
128
137
  ],
129
- "additionalProperties": false
138
+ "additionalProperties": false,
139
+ "description": "What the plan allows. `null` means by contract."
130
140
  },
131
141
  "features": {
132
142
  "type": "object",
133
143
  "properties": {
134
144
  "app_mcp": {
135
- "type": "boolean"
145
+ "type": "boolean",
146
+ "description": "An app's own MCP endpoint, for its app users."
136
147
  },
137
148
  "two_factor": {
138
- "type": "boolean"
149
+ "type": "boolean",
150
+ "description": "Two-factor sign-in for developers."
139
151
  },
140
152
  "require_two_factor": {
141
- "type": "boolean"
153
+ "type": "boolean",
154
+ "description": "An owner may require two-factor sign-in for every developer of the org."
142
155
  },
143
156
  "app_oidc": {
144
- "type": "boolean"
157
+ "type": "boolean",
158
+ "description": "An app may let its users sign in through an OpenID Connect identity provider."
145
159
  },
146
160
  "hosted_logo": {
147
- "type": "boolean"
161
+ "type": "boolean",
162
+ "description": "An app's hosted pages show its logo and accent colour, not only its name."
148
163
  },
149
164
  "audit_export": {
150
- "type": "boolean"
165
+ "type": "boolean",
166
+ "description": "The audit log can be exported as CSV."
151
167
  },
152
168
  "addons": {
153
- "type": "boolean"
169
+ "type": "boolean",
170
+ "description": "Add-ons can be bought on top of the plan."
154
171
  }
155
172
  },
156
173
  "required": [
@@ -162,7 +179,8 @@
162
179
  "audit_export",
163
180
  "addons"
164
181
  ],
165
- "additionalProperties": false
182
+ "additionalProperties": false,
183
+ "description": "What the plan unlocks."
166
184
  },
167
185
  "prices": {
168
186
  "anyOf": [
@@ -172,22 +190,26 @@
172
190
  "eur_month": {
173
191
  "type": "integer",
174
192
  "minimum": 0,
175
- "maximum": 9007199254740991
193
+ "maximum": 9007199254740991,
194
+ "description": "Per month in euro cents, excluding VAT."
176
195
  },
177
196
  "usd_month": {
178
197
  "type": "integer",
179
198
  "minimum": 0,
180
- "maximum": 9007199254740991
199
+ "maximum": 9007199254740991,
200
+ "description": "Per month in US dollar cents, excluding VAT: the euro price × 1.15, rounded up to a whole dollar."
181
201
  },
182
202
  "eur_year": {
183
203
  "type": "integer",
184
204
  "minimum": 0,
185
- "maximum": 9007199254740991
205
+ "maximum": 9007199254740991,
206
+ "description": "Per year in euro cents, excluding VAT: twelve months less 15 %."
186
207
  },
187
208
  "usd_year": {
188
209
  "type": "integer",
189
210
  "minimum": 0,
190
- "maximum": 9007199254740991
211
+ "maximum": 9007199254740991,
212
+ "description": "Per year in US dollar cents, excluding VAT: the yearly euro price × 1.15, rounded up to a whole dollar."
191
213
  }
192
214
  },
193
215
  "required": [
@@ -211,7 +233,8 @@
211
233
  "email",
212
234
  "priority",
213
235
  "named_contact"
214
- ]
236
+ ],
237
+ "description": "The support that comes with the plan."
215
238
  }
216
239
  },
217
240
  "required": [
@@ -8,7 +8,8 @@
8
8
  "type": "string",
9
9
  "format": "uuid",
10
10
  "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)$"
11
- }
11
+ },
12
+ "description": "The robots that stay, by id. Every other robot is deleted when the change takes effect."
12
13
  },
13
14
  "apps": {
14
15
  "type": "array",
@@ -16,7 +17,8 @@
16
17
  "type": "string",
17
18
  "format": "uuid",
18
19
  "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)$"
19
- }
20
+ },
21
+ "description": "The apps that stay, by id. Every other app is deleted when the change takes effect."
20
22
  },
21
23
  "app_users": {
22
24
  "type": "array",
@@ -24,7 +26,8 @@
24
26
  "type": "string",
25
27
  "format": "uuid",
26
28
  "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)$"
27
- }
29
+ },
30
+ "description": "The app users that stay, by id, across every app. Every other app user is deleted when the change takes effect."
28
31
  },
29
32
  "developers": {
30
33
  "type": "array",
@@ -32,7 +35,8 @@
32
35
  "type": "string",
33
36
  "format": "uuid",
34
37
  "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)$"
35
- }
38
+ },
39
+ "description": "The developers that stay, by user id, owners never among them: every owner stays. Every other developer is removed from the org when the change takes effect."
36
40
  }
37
41
  },
38
42
  "required": [
@@ -9,7 +9,8 @@
9
9
  "plus",
10
10
  "pro",
11
11
  "enterprise"
12
- ]
12
+ ],
13
+ "description": "The lower plan to move to; `basic` cancels."
13
14
  },
14
15
  "keep": {
15
16
  "anyOf": [
@@ -22,7 +23,8 @@
22
23
  "type": "string",
23
24
  "format": "uuid",
24
25
  "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)$"
25
- }
26
+ },
27
+ "description": "The robots that stay, by id. Every other robot is deleted when the change takes effect."
26
28
  },
27
29
  "apps": {
28
30
  "type": "array",
@@ -30,7 +32,8 @@
30
32
  "type": "string",
31
33
  "format": "uuid",
32
34
  "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)$"
33
- }
35
+ },
36
+ "description": "The apps that stay, by id. Every other app is deleted when the change takes effect."
34
37
  },
35
38
  "app_users": {
36
39
  "type": "array",
@@ -38,7 +41,8 @@
38
41
  "type": "string",
39
42
  "format": "uuid",
40
43
  "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)$"
41
- }
44
+ },
45
+ "description": "The app users that stay, by id, across every app. Every other app user is deleted when the change takes effect."
42
46
  },
43
47
  "developers": {
44
48
  "type": "array",
@@ -46,7 +50,8 @@
46
50
  "type": "string",
47
51
  "format": "uuid",
48
52
  "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)$"
49
- }
53
+ },
54
+ "description": "The developers that stay, by user id, owners never among them: every owner stays. Every other developer is removed from the org when the change takes effect."
50
55
  }
51
56
  },
52
57
  "required": [
@@ -60,7 +65,8 @@
60
65
  {
61
66
  "type": "null"
62
67
  }
63
- ]
68
+ ],
69
+ "description": "What stays. `null` when the org already fits the target plan, so nothing is deleted."
64
70
  }
65
71
  },
66
72
  "required": [
@@ -3,25 +3,32 @@
3
3
  "type": "object",
4
4
  "properties": {
5
5
  "app_mcp": {
6
- "type": "boolean"
6
+ "type": "boolean",
7
+ "description": "An app's own MCP endpoint, for its app users."
7
8
  },
8
9
  "two_factor": {
9
- "type": "boolean"
10
+ "type": "boolean",
11
+ "description": "Two-factor sign-in for developers."
10
12
  },
11
13
  "require_two_factor": {
12
- "type": "boolean"
14
+ "type": "boolean",
15
+ "description": "An owner may require two-factor sign-in for every developer of the org."
13
16
  },
14
17
  "app_oidc": {
15
- "type": "boolean"
18
+ "type": "boolean",
19
+ "description": "An app may let its users sign in through an OpenID Connect identity provider."
16
20
  },
17
21
  "hosted_logo": {
18
- "type": "boolean"
22
+ "type": "boolean",
23
+ "description": "An app's hosted pages show its logo and accent colour, not only its name."
19
24
  },
20
25
  "audit_export": {
21
- "type": "boolean"
26
+ "type": "boolean",
27
+ "description": "The audit log can be exported as CSV."
22
28
  },
23
29
  "addons": {
24
- "type": "boolean"
30
+ "type": "boolean",
31
+ "description": "Add-ons can be bought on top of the plan."
25
32
  }
26
33
  },
27
34
  "required": [
@@ -11,17 +11,20 @@
11
11
  "app_users",
12
12
  "live_video_ms_per_month",
13
13
  "asset_bytes_per_robot"
14
- ]
14
+ ],
15
+ "description": "The limit the action would exceed."
15
16
  },
16
17
  "used": {
17
18
  "type": "integer",
18
19
  "minimum": 0,
19
- "maximum": 9007199254740991
20
+ "maximum": 9007199254740991,
21
+ "description": "How much of the limit the org uses now."
20
22
  },
21
23
  "max": {
22
24
  "type": "integer",
23
25
  "minimum": 0,
24
- "maximum": 9007199254740991
26
+ "maximum": 9007199254740991,
27
+ "description": "The limit. For `asset_bytes_per_robot`, the org's whole pool: robots × the plan's bytes per robot."
25
28
  },
26
29
  "plan": {
27
30
  "type": "string",
@@ -30,7 +33,8 @@
30
33
  "plus",
31
34
  "pro",
32
35
  "enterprise"
33
- ]
36
+ ],
37
+ "description": "The plan whose limit refused: the target plan while a move to a lower plan is pending."
34
38
  },
35
39
  "lifted_by": {
36
40
  "type": "object",
@@ -49,7 +53,8 @@
49
53
  {
50
54
  "type": "null"
51
55
  }
52
- ]
56
+ ],
57
+ "description": "The cheapest higher plan that raises this limit, or `null` when none does."
53
58
  },
54
59
  "addon": {
55
60
  "anyOf": [
@@ -66,14 +71,16 @@
66
71
  {
67
72
  "type": "null"
68
73
  }
69
- ]
74
+ ],
75
+ "description": "The add-on that raises this limit, when the org's plan can buy add-ons; otherwise `null`."
70
76
  }
71
77
  },
72
78
  "required": [
73
79
  "plan",
74
80
  "addon"
75
81
  ],
76
- "additionalProperties": false
82
+ "additionalProperties": false,
83
+ "description": "What would lift the limit."
77
84
  }
78
85
  },
79
86
  "required": [
@@ -12,7 +12,8 @@
12
12
  {
13
13
  "type": "null"
14
14
  }
15
- ]
15
+ ],
16
+ "description": "Developers, owners included, plus pending team invitations."
16
17
  },
17
18
  "robots": {
18
19
  "anyOf": [
@@ -24,7 +25,8 @@
24
25
  {
25
26
  "type": "null"
26
27
  }
27
- ]
28
+ ],
29
+ "description": "Robots in the org."
28
30
  },
29
31
  "apps": {
30
32
  "anyOf": [
@@ -36,7 +38,8 @@
36
38
  {
37
39
  "type": "null"
38
40
  }
39
- ]
41
+ ],
42
+ "description": "Apps in the org."
40
43
  },
41
44
  "app_users": {
42
45
  "anyOf": [
@@ -48,7 +51,8 @@
48
51
  {
49
52
  "type": "null"
50
53
  }
51
- ]
54
+ ],
55
+ "description": "App users across every app of the org, plus pending app-user invitations."
52
56
  },
53
57
  "live_video_ms_per_month": {
54
58
  "anyOf": [
@@ -60,7 +64,8 @@
60
64
  {
61
65
  "type": "null"
62
66
  }
63
- ]
67
+ ],
68
+ "description": "Live video watched by app users in one UTC calendar month, in milliseconds, across the org. Console sessions do not count."
64
69
  },
65
70
  "asset_bytes_per_robot": {
66
71
  "anyOf": [
@@ -72,7 +77,8 @@
72
77
  {
73
78
  "type": "null"
74
79
  }
75
- ]
80
+ ],
81
+ "description": "Asset storage per robot, in bytes (decimal: 1 GB = 1,000,000,000). The org stores up to robots × this value in total."
76
82
  },
77
83
  "history_days": {
78
84
  "anyOf": [
@@ -84,7 +90,8 @@
84
90
  {
85
91
  "type": "null"
86
92
  }
87
- ]
93
+ ],
94
+ "description": "Days the org's robot history is kept."
88
95
  },
89
96
  "audit_days": {
90
97
  "anyOf": [
@@ -96,7 +103,8 @@
96
103
  {
97
104
  "type": "null"
98
105
  }
99
- ]
106
+ ],
107
+ "description": "Days the org's audit log is kept."
100
108
  }
101
109
  },
102
110
  "required": [
@@ -3,6 +3,7 @@
3
3
  "type": "object",
4
4
  "properties": {
5
5
  "seats": {
6
+ "description": "Replaces the plan's `seats`. `null` clears the override.",
6
7
  "anyOf": [
7
8
  {
8
9
  "type": "integer",
@@ -15,6 +16,7 @@
15
16
  ]
16
17
  },
17
18
  "robots": {
19
+ "description": "Replaces the plan's `robots`. `null` clears the override.",
18
20
  "anyOf": [
19
21
  {
20
22
  "type": "integer",
@@ -27,6 +29,7 @@
27
29
  ]
28
30
  },
29
31
  "apps": {
32
+ "description": "Replaces the plan's `apps`. `null` clears the override.",
30
33
  "anyOf": [
31
34
  {
32
35
  "type": "integer",
@@ -39,6 +42,7 @@
39
42
  ]
40
43
  },
41
44
  "app_users": {
45
+ "description": "Replaces the plan's `app_users`. `null` clears the override.",
42
46
  "anyOf": [
43
47
  {
44
48
  "type": "integer",
@@ -51,6 +55,7 @@
51
55
  ]
52
56
  },
53
57
  "live_video_ms_per_month": {
58
+ "description": "Replaces the plan's `live_video_ms_per_month`. `null` clears the override.",
54
59
  "anyOf": [
55
60
  {
56
61
  "type": "integer",
@@ -63,6 +68,7 @@
63
68
  ]
64
69
  },
65
70
  "asset_bytes_per_robot": {
71
+ "description": "Replaces the plan's `asset_bytes_per_robot`. `null` clears the override.",
66
72
  "anyOf": [
67
73
  {
68
74
  "type": "integer",
@@ -75,6 +81,7 @@
75
81
  ]
76
82
  },
77
83
  "history_days": {
84
+ "description": "Replaces the plan's `history_days`. `null` clears the override.",
78
85
  "anyOf": [
79
86
  {
80
87
  "type": "integer",
@@ -87,6 +94,7 @@
87
94
  ]
88
95
  },
89
96
  "audit_days": {
97
+ "description": "Replaces the plan's `audit_days`. `null` clears the override.",
90
98
  "anyOf": [
91
99
  {
92
100
  "type": "integer",
@@ -5,22 +5,26 @@
5
5
  "eur_month": {
6
6
  "type": "integer",
7
7
  "minimum": 0,
8
- "maximum": 9007199254740991
8
+ "maximum": 9007199254740991,
9
+ "description": "Per month in euro cents, excluding VAT."
9
10
  },
10
11
  "usd_month": {
11
12
  "type": "integer",
12
13
  "minimum": 0,
13
- "maximum": 9007199254740991
14
+ "maximum": 9007199254740991,
15
+ "description": "Per month in US dollar cents, excluding VAT: the euro price × 1.15, rounded up to a whole dollar."
14
16
  },
15
17
  "eur_year": {
16
18
  "type": "integer",
17
19
  "minimum": 0,
18
- "maximum": 9007199254740991
20
+ "maximum": 9007199254740991,
21
+ "description": "Per year in euro cents, excluding VAT: twelve months less 15 %."
19
22
  },
20
23
  "usd_year": {
21
24
  "type": "integer",
22
25
  "minimum": 0,
23
- "maximum": 9007199254740991
26
+ "maximum": 9007199254740991,
27
+ "description": "Per year in US dollar cents, excluding VAT: the yearly euro price × 1.15, rounded up to a whole dollar."
24
28
  }
25
29
  },
26
30
  "required": [
@@ -12,7 +12,8 @@
12
12
  "hosted_logo",
13
13
  "audit_export",
14
14
  "addons"
15
- ]
15
+ ],
16
+ "description": "The feature the action needs."
16
17
  },
17
18
  "plan": {
18
19
  "type": "string",
@@ -21,7 +22,8 @@
21
22
  "plus",
22
23
  "pro",
23
24
  "enterprise"
24
- ]
25
+ ],
26
+ "description": "The org's current plan."
25
27
  },
26
28
  "required_plan": {
27
29
  "type": "string",
@@ -30,7 +32,8 @@
30
32
  "plus",
31
33
  "pro",
32
34
  "enterprise"
33
- ]
35
+ ],
36
+ "description": "The cheapest plan that has the feature."
34
37
  }
35
38
  },
36
39
  "required": [
package/dist/errors.js CHANGED
@@ -79,14 +79,20 @@ export const invalidCodeDetails = z.object({
79
79
  * that cannot buy add-ons has nothing in that field.
80
80
  */
81
81
  export const planLimitDetails = z.object({
82
- limit: planLimitKey.exclude(['history_days', 'audit_days']),
83
- used: z.number().int().nonnegative(),
84
- max: z.number().int().nonnegative(),
85
- plan: planId,
86
- lifted_by: z.object({
87
- plan: planId.nullable(),
88
- addon: addonKey.nullable(),
82
+ limit: planLimitKey.exclude(['history_days', 'audit_days']).meta({ description: 'The limit the action would exceed.' }),
83
+ used: z.number().int().nonnegative().meta({ description: 'How much of the limit the org uses now.' }),
84
+ max: z.number().int().nonnegative().meta({
85
+ description: 'The limit. For `asset_bytes_per_robot`, the org\'s whole pool: robots × the plan\'s bytes per robot.',
89
86
  }),
87
+ plan: planId.meta({ description: 'The plan whose limit refused: the target plan while a move to a lower plan is pending.' }),
88
+ lifted_by: z
89
+ .object({
90
+ plan: planId.nullable().meta({ description: 'The cheapest higher plan that raises this limit, or `null` when none does.' }),
91
+ addon: addonKey.nullable().meta({
92
+ description: 'The add-on that raises this limit, when the org\'s plan can buy add-ons; otherwise `null`.',
93
+ }),
94
+ })
95
+ .meta({ description: 'What would lift the limit.' }),
90
96
  });
91
97
  /**
92
98
  * `planLimitDetails` plus the three numbers `assetStoreRefusedDetails`
@@ -119,13 +125,15 @@ export const assetPlanLimitDetails = planLimitDetails.extend({
119
125
  });
120
126
  /** The `details` of a `403 plan_required` refusal: the feature, the org's own plan, and the cheapest plan that has it (`requiredPlanFor`). */
121
127
  export const planRequiredDetails = z.object({
122
- feature: planFeature,
123
- plan: planId,
124
- required_plan: planId,
128
+ feature: planFeature.meta({ description: 'The feature the action needs.' }),
129
+ plan: planId.meta({ description: 'The org\'s current plan.' }),
130
+ required_plan: planId.meta({ description: 'The cheapest plan that has the feature.' }),
125
131
  });
126
132
  /** The `details` of a `403 org_locked` refusal: why the org is locked, matching `orgLock.reason`. */
127
133
  export const orgLockedDetails = z.object({
128
- reason: z.enum(['payment', 'migration']),
134
+ reason: z.enum(['payment', 'migration']).meta({
135
+ description: '`payment`: a payment is missing. `migration`: the org did not choose what stays when the beta ended.',
136
+ }),
129
137
  });
130
138
  /**
131
139
  * The codes in use today. The wire deliberately allows any string — this
@@ -958,8 +966,8 @@ export const ERROR_CODES = [
958
966
  // 2026-10-02 — plans (#103).
959
967
  /**
960
968
  * `409`: the action would push the org past one of its plan's limits —
961
- * a seat, a robot, an app, an app user, this month's live video, or a
962
- * robot's asset storage. Answered **before anything is written**, so a
969
+ * a seat, a robot, an app, an app user, this month's live video, or the
970
+ * org's asset storage pool (its robots × the plan's bytes per robot). Answered **before anything is written**, so a
963
971
  * refused create or invite never leaves a half-made row behind. `details`
964
972
  * is `planLimitDetails` (or `assetPlanLimitDetails` for an asset-storage
965
973
  * refusal): which limit, how much is used, the ceiling, the plan that
@@ -970,19 +978,21 @@ export const ERROR_CODES = [
970
978
  */
971
979
  'plan_limit',
972
980
  /**
973
- * `403`: the feature the caller reached for is not on the org's plan — a
974
- * two-factor requirement, an app's OIDC federation, a hosted logo, the
975
- * audit export, or add-ons themselves. `details` is `planRequiredDetails`:
981
+ * `403`: the feature the caller reached for is not on the org's plan — an
982
+ * app's MCP endpoint, two-factor for a developer, a two-factor requirement
983
+ * for the org, an app's OIDC federation, a hosted logo, the audit export,
984
+ * or add-ons themselves. `details` is `planRequiredDetails`:
976
985
  * the feature, the org's own plan, and the cheapest plan that has it, so
977
986
  * the console can offer the upgrade in the same breath as the refusal.
978
987
  */
979
988
  'plan_required',
980
989
  /**
981
- * `403`: the org is locked and may only move to Basic. `details` is
982
- * `orgLockedDetails`, naming why — a failed payment, or the platform's own
983
- * move off the beta. Distinct from `plan_required`, which is about a
984
- * feature the plan never had; this is about an org that is not allowed to
985
- * spend on any plan right now.
990
+ * `403`: the org is locked. Every sign-in and every token refresh is
991
+ * refused, except an owner's, so an owner can still sign in and settle
992
+ * it; the sign-in pages render this refusal. A bridge's `hello` is
993
+ * refused with this code as well. Nothing is deleted while an org is
994
+ * locked. `details` is `orgLockedDetails`, naming why — a missing
995
+ * payment, or the platform's own move off the beta.
986
996
  */
987
997
  'org_locked',
988
998
  ];