@nasebanal/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,990 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: NASEBANAL Account API
4
+ description: >-
5
+ Centralized user and billing management API for NASEBANAL applications. Manages user profiles, per-service
6
+ subscriptions, Stripe billing, and Personal Access Tokens (PAT).
7
+ version: 1.4.0
8
+ contact:
9
+ name: NASEBANAL
10
+ url: https://www.nasebanal.com
11
+ servers:
12
+ - url: http://localhost:8788
13
+ description: Local dev (wrangler dev)
14
+ - url: https://api.account.nasebanal.com
15
+ description: Production
16
+ security:
17
+ - BearerAuth: []
18
+ paths:
19
+ /health:
20
+ get:
21
+ tags:
22
+ - Health
23
+ summary: Health check
24
+ description: Returns the health status of the API.
25
+ security: []
26
+ responses:
27
+ '200':
28
+ description: API is healthy
29
+ content:
30
+ application/json:
31
+ schema:
32
+ type: object
33
+ properties:
34
+ status:
35
+ type: string
36
+ example: ok
37
+ timestamp:
38
+ type: string
39
+ format: date-time
40
+ /api/v1/me:
41
+ get:
42
+ tags:
43
+ - User
44
+ summary: Get current user profile
45
+ description: Returns the authenticated user's profile information including subscription details.
46
+ responses:
47
+ '200':
48
+ description: User profile
49
+ content:
50
+ application/json:
51
+ schema:
52
+ $ref: '#/components/schemas/UserResponse'
53
+ '401':
54
+ $ref: '#/components/responses/Unauthorized'
55
+ delete:
56
+ tags:
57
+ - User
58
+ summary: Delete account
59
+ description: >-
60
+ Permanently deletes the authenticated user's account and every service's data for it: Recorder records, tags and
61
+ media, Target goals, shares on both sides, organization memberships, and API tokens. An active personal
62
+ subscription is cancelled at Stripe as part of the same call.
63
+
64
+
65
+ Irreversible — there is no soft-delete or reactivation. Callers must confirm intent before invoking, and must
66
+ send the user through logout afterwards: their access token stays cryptographically valid but no longer resolves
67
+ to an account, so a subsequent authenticated request would provision a brand-new empty user.
68
+
69
+
70
+ Refused with `409` while the caller is the only administrator of an organization that still has other members;
71
+ the organization must be handed over or deleted first. `502` means nothing was deleted (a downstream service
72
+ could not be purged) and the request can safely be retried.
73
+ responses:
74
+ '200':
75
+ description: Account deleted
76
+ content:
77
+ application/json:
78
+ schema:
79
+ type: object
80
+ required:
81
+ - success
82
+ - data
83
+ properties:
84
+ success:
85
+ type: boolean
86
+ enum: [true]
87
+ data:
88
+ type: object
89
+ required:
90
+ - deleted
91
+ - user_id
92
+ properties:
93
+ deleted:
94
+ type: boolean
95
+ enum: [true]
96
+ user_id:
97
+ type: integer
98
+ organizations_deleted:
99
+ type: integer
100
+ description: Organizations removed because the deleted user was their only member.
101
+ subscriptions_cancelled:
102
+ type: integer
103
+ auth0_identity_deleted:
104
+ type: boolean
105
+ description: >-
106
+ Whether the Auth0 identity was removed as well. False when the API has no Management API
107
+ credentials configured — the account data is still fully deleted, but signing in again creates
108
+ a new empty account.
109
+ '401':
110
+ $ref: '#/components/responses/Unauthorized'
111
+ '403':
112
+ description: Service accounts cannot be deleted through this endpoint
113
+ content:
114
+ application/json:
115
+ schema:
116
+ $ref: '#/components/schemas/Error'
117
+ '409':
118
+ description: The caller is the last administrator of an organization that still has members
119
+ content:
120
+ application/json:
121
+ schema:
122
+ $ref: '#/components/schemas/Error'
123
+ '502':
124
+ description: A downstream service could not be purged — nothing was deleted
125
+ content:
126
+ application/json:
127
+ schema:
128
+ $ref: '#/components/schemas/Error'
129
+ /api/v1/me/profile:
130
+ put:
131
+ tags:
132
+ - User
133
+ summary: Update profile
134
+ description: Updates the authenticated user's profile information.
135
+ requestBody:
136
+ required: true
137
+ content:
138
+ application/json:
139
+ schema:
140
+ $ref: '#/components/schemas/UpdateProfileInput'
141
+ responses:
142
+ '200':
143
+ description: Profile updated
144
+ content:
145
+ application/json:
146
+ schema:
147
+ $ref: '#/components/schemas/UserResponse'
148
+ '401':
149
+ $ref: '#/components/responses/Unauthorized'
150
+ /api/v1/me/accept-terms:
151
+ post:
152
+ tags:
153
+ - User
154
+ summary: Accept terms of service
155
+ description: Records the user's acceptance of the terms of service.
156
+ responses:
157
+ '200':
158
+ description: Terms accepted
159
+ content:
160
+ application/json:
161
+ schema:
162
+ type: object
163
+ properties:
164
+ data:
165
+ type: object
166
+ properties:
167
+ tos_accepted_at:
168
+ type: string
169
+ format: date-time
170
+ '401':
171
+ $ref: '#/components/responses/Unauthorized'
172
+ /api/v1/me/subscription:
173
+ x-internal: true
174
+ get:
175
+ tags:
176
+ - Subscriptions
177
+ summary: Get subscription info (legacy)
178
+ description: Returns the authenticated user's legacy subscription information.
179
+ responses:
180
+ '200':
181
+ description: Subscription info
182
+ content:
183
+ application/json:
184
+ schema:
185
+ type: object
186
+ properties:
187
+ data:
188
+ type: object
189
+ properties:
190
+ plan:
191
+ type: string
192
+ enum:
193
+ - free
194
+ - pro
195
+ - family
196
+ - enterprise
197
+ subscription_id:
198
+ type: string
199
+ subscription_status:
200
+ type: string
201
+ enum:
202
+ - active
203
+ - canceled
204
+ - past_due
205
+ - trialing
206
+ subscription_expires_at:
207
+ type: string
208
+ format: date-time
209
+ '401':
210
+ $ref: '#/components/responses/Unauthorized'
211
+ /api/v1/me/subscriptions:
212
+ get:
213
+ tags:
214
+ - Subscriptions
215
+ summary: Get all per-service subscriptions
216
+ description: Returns all per-service subscriptions for the authenticated user.
217
+ responses:
218
+ '200':
219
+ description: List of service subscriptions
220
+ content:
221
+ application/json:
222
+ schema:
223
+ type: object
224
+ properties:
225
+ data:
226
+ type: array
227
+ items:
228
+ $ref: '#/components/schemas/ServiceSubscription'
229
+ '401':
230
+ $ref: '#/components/responses/Unauthorized'
231
+ /api/v1/me/subscriptions/{serviceId}:
232
+ get:
233
+ tags:
234
+ - Subscriptions
235
+ summary: Get subscription for a service
236
+ description: Returns the subscription for a specific service.
237
+ parameters:
238
+ - name: serviceId
239
+ in: path
240
+ required: true
241
+ schema:
242
+ type: string
243
+ responses:
244
+ '200':
245
+ description: Service subscription
246
+ content:
247
+ application/json:
248
+ schema:
249
+ type: object
250
+ properties:
251
+ data:
252
+ $ref: '#/components/schemas/ServiceSubscription'
253
+ '401':
254
+ $ref: '#/components/responses/Unauthorized'
255
+ '404':
256
+ $ref: '#/components/responses/NotFound'
257
+ /api/v1/stripe/checkout:
258
+ x-internal: true
259
+ post:
260
+ tags:
261
+ - Stripe
262
+ summary: Create Stripe checkout session
263
+ description: Creates a new Stripe checkout session for subscription purchase.
264
+ requestBody:
265
+ required: true
266
+ content:
267
+ application/json:
268
+ schema:
269
+ type: object
270
+ required:
271
+ - plan
272
+ properties:
273
+ plan:
274
+ type: string
275
+ enum: [pro, family]
276
+ description: Target subscription plan.
277
+ serviceId:
278
+ type: string
279
+ description: Service the subscription applies to (defaults to "default").
280
+ currency:
281
+ type: string
282
+ enum: [jpy, usd]
283
+ default: jpy
284
+ description: >-
285
+ Billing currency for the new subscription; selects the matching
286
+ per-currency Stripe Price. Defaults to jpy when omitted. Stripe
287
+ locks a customer to this currency on first payment, so it only
288
+ applies to the initial checkout.
289
+ returnUrl:
290
+ type: string
291
+ description: >-
292
+ Locale-aware URL Stripe returns the user to; the result query
293
+ string (?upgrade=success&plan=…&serviceId=…) is appended by the API.
294
+ responses:
295
+ '200':
296
+ description: Checkout session created
297
+ content:
298
+ application/json:
299
+ schema:
300
+ type: object
301
+ properties:
302
+ data:
303
+ type: object
304
+ properties:
305
+ url:
306
+ type: string
307
+ description: Stripe checkout session URL
308
+ '401':
309
+ $ref: '#/components/responses/Unauthorized'
310
+ /api/v1/stripe/change-plan:
311
+ x-internal: true
312
+ post:
313
+ tags:
314
+ - Stripe
315
+ summary: Change subscription plan
316
+ description: Changes the authenticated user's subscription plan.
317
+ responses:
318
+ '200':
319
+ description: Plan changed
320
+ content:
321
+ application/json:
322
+ schema:
323
+ type: object
324
+ properties:
325
+ data:
326
+ type: object
327
+ '401':
328
+ $ref: '#/components/responses/Unauthorized'
329
+ /api/v1/stripe/portal:
330
+ x-internal: true
331
+ post:
332
+ tags:
333
+ - Stripe
334
+ summary: Create customer portal session
335
+ description: Creates a Stripe customer portal session for subscription management.
336
+ responses:
337
+ '200':
338
+ description: Portal session created
339
+ content:
340
+ application/json:
341
+ schema:
342
+ type: object
343
+ properties:
344
+ data:
345
+ type: object
346
+ properties:
347
+ url:
348
+ type: string
349
+ description: Stripe customer portal URL
350
+ '401':
351
+ $ref: '#/components/responses/Unauthorized'
352
+ /api/v1/me/tokens:
353
+ get:
354
+ tags:
355
+ - API Tokens
356
+ summary: List API tokens
357
+ description: Returns all Personal Access Tokens for the authenticated user.
358
+ responses:
359
+ '200':
360
+ description: List of API tokens
361
+ content:
362
+ application/json:
363
+ schema:
364
+ type: object
365
+ properties:
366
+ data:
367
+ type: object
368
+ properties:
369
+ data:
370
+ type: array
371
+ items:
372
+ $ref: '#/components/schemas/ApiToken'
373
+ total:
374
+ type: integer
375
+ '401':
376
+ $ref: '#/components/responses/Unauthorized'
377
+ post:
378
+ tags:
379
+ - API Tokens
380
+ summary: Create API token
381
+ description: Creates a new Personal Access Token. The raw token is returned only once in the response.
382
+ requestBody:
383
+ required: true
384
+ content:
385
+ application/json:
386
+ schema:
387
+ $ref: '#/components/schemas/CreateApiTokenInput'
388
+ responses:
389
+ '201':
390
+ description: Token created
391
+ content:
392
+ application/json:
393
+ schema:
394
+ type: object
395
+ properties:
396
+ data:
397
+ allOf:
398
+ - $ref: '#/components/schemas/ApiToken'
399
+ - type: object
400
+ properties:
401
+ token:
402
+ type: string
403
+ description: The raw token (only returned on creation)
404
+ example: nbpat_a1b2c3d4e5f6g7h8i9j0...
405
+ '401':
406
+ $ref: '#/components/responses/Unauthorized'
407
+ /api/v1/me/tokens/{id}:
408
+ delete:
409
+ tags:
410
+ - API Tokens
411
+ summary: Delete API token
412
+ description: Deletes a Personal Access Token by ID.
413
+ parameters:
414
+ - name: id
415
+ in: path
416
+ required: true
417
+ schema:
418
+ type: integer
419
+ responses:
420
+ '200':
421
+ description: Token deleted
422
+ content:
423
+ application/json:
424
+ schema:
425
+ type: object
426
+ properties:
427
+ data:
428
+ type: object
429
+ properties:
430
+ success:
431
+ type: boolean
432
+ example: true
433
+ '401':
434
+ $ref: '#/components/responses/Unauthorized'
435
+ '404':
436
+ $ref: '#/components/responses/NotFound'
437
+ /api/v1/organizations/{orgId}/service-accounts:
438
+ get:
439
+ tags:
440
+ - Service Accounts
441
+ summary: List service accounts
442
+ description: Returns all service accounts owned by the given organization. Requires the caller to be an Org Admin.
443
+ parameters:
444
+ - name: orgId
445
+ in: path
446
+ required: true
447
+ schema:
448
+ type: integer
449
+ responses:
450
+ '200':
451
+ description: List of service accounts
452
+ content:
453
+ application/json:
454
+ schema:
455
+ type: object
456
+ properties:
457
+ data:
458
+ type: object
459
+ properties:
460
+ data:
461
+ type: array
462
+ items:
463
+ $ref: '#/components/schemas/ServiceAccount'
464
+ total:
465
+ type: integer
466
+ '401':
467
+ $ref: '#/components/responses/Unauthorized'
468
+ '403':
469
+ $ref: '#/components/responses/Forbidden'
470
+ '404':
471
+ $ref: '#/components/responses/NotFound'
472
+ post:
473
+ tags:
474
+ - Service Accounts
475
+ summary: Create a service account
476
+ description: >-
477
+ Creates a new service account in the organization. Requires the caller to be an Org Admin AND the organization
478
+ to hold an active Enterprise subscription.
479
+ parameters:
480
+ - name: orgId
481
+ in: path
482
+ required: true
483
+ schema:
484
+ type: integer
485
+ requestBody:
486
+ required: true
487
+ content:
488
+ application/json:
489
+ schema:
490
+ $ref: '#/components/schemas/CreateServiceAccountInput'
491
+ responses:
492
+ '201':
493
+ description: Service account created
494
+ content:
495
+ application/json:
496
+ schema:
497
+ type: object
498
+ properties:
499
+ data:
500
+ $ref: '#/components/schemas/ServiceAccount'
501
+ '401':
502
+ $ref: '#/components/responses/Unauthorized'
503
+ '403':
504
+ $ref: '#/components/responses/Forbidden'
505
+ /api/v1/organizations/{orgId}/service-accounts/{saId}:
506
+ put:
507
+ tags:
508
+ - Service Accounts
509
+ summary: Rename a service account
510
+ parameters:
511
+ - name: orgId
512
+ in: path
513
+ required: true
514
+ schema:
515
+ type: integer
516
+ - name: saId
517
+ in: path
518
+ required: true
519
+ schema:
520
+ type: integer
521
+ requestBody:
522
+ required: true
523
+ content:
524
+ application/json:
525
+ schema:
526
+ $ref: '#/components/schemas/CreateServiceAccountInput'
527
+ responses:
528
+ '200':
529
+ description: Service account updated
530
+ content:
531
+ application/json:
532
+ schema:
533
+ type: object
534
+ properties:
535
+ data:
536
+ $ref: '#/components/schemas/ServiceAccount'
537
+ '401':
538
+ $ref: '#/components/responses/Unauthorized'
539
+ '403':
540
+ $ref: '#/components/responses/Forbidden'
541
+ '404':
542
+ $ref: '#/components/responses/NotFound'
543
+ delete:
544
+ tags:
545
+ - Service Accounts
546
+ summary: Delete a service account
547
+ description: >-
548
+ Deletes the service account and cascades to all of its API tokens. Returns the cascade count for audit
549
+ reference.
550
+ parameters:
551
+ - name: orgId
552
+ in: path
553
+ required: true
554
+ schema:
555
+ type: integer
556
+ - name: saId
557
+ in: path
558
+ required: true
559
+ schema:
560
+ type: integer
561
+ responses:
562
+ '200':
563
+ description: Service account deleted
564
+ content:
565
+ application/json:
566
+ schema:
567
+ type: object
568
+ properties:
569
+ data:
570
+ type: object
571
+ properties:
572
+ success:
573
+ type: boolean
574
+ cascade_token_count:
575
+ type: integer
576
+ '401':
577
+ $ref: '#/components/responses/Unauthorized'
578
+ '403':
579
+ $ref: '#/components/responses/Forbidden'
580
+ '404':
581
+ $ref: '#/components/responses/NotFound'
582
+ /api/v1/organizations/{orgId}/service-accounts/{saId}/tokens:
583
+ get:
584
+ tags:
585
+ - Service Accounts
586
+ summary: List service-account tokens
587
+ parameters:
588
+ - name: orgId
589
+ in: path
590
+ required: true
591
+ schema:
592
+ type: integer
593
+ - name: saId
594
+ in: path
595
+ required: true
596
+ schema:
597
+ type: integer
598
+ responses:
599
+ '200':
600
+ description: List of tokens
601
+ content:
602
+ application/json:
603
+ schema:
604
+ type: object
605
+ properties:
606
+ data:
607
+ type: object
608
+ properties:
609
+ data:
610
+ type: array
611
+ items:
612
+ $ref: '#/components/schemas/ApiToken'
613
+ total:
614
+ type: integer
615
+ '401':
616
+ $ref: '#/components/responses/Unauthorized'
617
+ '403':
618
+ $ref: '#/components/responses/Forbidden'
619
+ '404':
620
+ $ref: '#/components/responses/NotFound'
621
+ post:
622
+ tags:
623
+ - Service Accounts
624
+ summary: Issue a new token for a service account
625
+ description: >-
626
+ Creates a new API token under the service account. The raw `nbpat_...` token is returned exactly once; it is not
627
+ stored in plaintext and cannot be retrieved later.
628
+ parameters:
629
+ - name: orgId
630
+ in: path
631
+ required: true
632
+ schema:
633
+ type: integer
634
+ - name: saId
635
+ in: path
636
+ required: true
637
+ schema:
638
+ type: integer
639
+ requestBody:
640
+ required: true
641
+ content:
642
+ application/json:
643
+ schema:
644
+ $ref: '#/components/schemas/CreateApiTokenInput'
645
+ responses:
646
+ '201':
647
+ description: Token issued
648
+ content:
649
+ application/json:
650
+ schema:
651
+ type: object
652
+ properties:
653
+ data:
654
+ allOf:
655
+ - $ref: '#/components/schemas/ApiToken'
656
+ - type: object
657
+ properties:
658
+ token:
659
+ type: string
660
+ description: The raw token value (returned once on creation)
661
+ example: nbpat_a1b2c3d4...
662
+ '401':
663
+ $ref: '#/components/responses/Unauthorized'
664
+ '403':
665
+ $ref: '#/components/responses/Forbidden'
666
+ '404':
667
+ $ref: '#/components/responses/NotFound'
668
+ /api/v1/organizations/{orgId}/service-accounts/{saId}/tokens/{tokenId}:
669
+ delete:
670
+ tags:
671
+ - Service Accounts
672
+ summary: Revoke a service-account token
673
+ parameters:
674
+ - name: orgId
675
+ in: path
676
+ required: true
677
+ schema:
678
+ type: integer
679
+ - name: saId
680
+ in: path
681
+ required: true
682
+ schema:
683
+ type: integer
684
+ - name: tokenId
685
+ in: path
686
+ required: true
687
+ schema:
688
+ type: integer
689
+ responses:
690
+ '200':
691
+ description: Token revoked
692
+ content:
693
+ application/json:
694
+ schema:
695
+ type: object
696
+ properties:
697
+ data:
698
+ type: object
699
+ properties:
700
+ success:
701
+ type: boolean
702
+ '401':
703
+ $ref: '#/components/responses/Unauthorized'
704
+ '403':
705
+ $ref: '#/components/responses/Forbidden'
706
+ '404':
707
+ $ref: '#/components/responses/NotFound'
708
+ components:
709
+ securitySchemes:
710
+ BearerAuth:
711
+ type: http
712
+ scheme: bearer
713
+ bearerFormat: JWT or PAT
714
+ description: Use either an Auth0 JWT token or a Personal Access Token (PAT) starting with `nbpat_`.
715
+ schemas:
716
+ User:
717
+ type: object
718
+ properties:
719
+ id:
720
+ type: integer
721
+ email:
722
+ type: string
723
+ format: email
724
+ name:
725
+ type: string
726
+ language:
727
+ type: string
728
+ enum:
729
+ - ja
730
+ - en
731
+ timezone:
732
+ type: string
733
+ description: >-
734
+ IANA time zone name (e.g. `Asia/Tokyo`), or `auto` to follow the
735
+ device the client runs on. Defaults to `auto`.
736
+ example: auto
737
+ plan:
738
+ type: string
739
+ enum:
740
+ - free
741
+ - pro
742
+ - family
743
+ - enterprise
744
+ subscription_status:
745
+ type: string
746
+ enum:
747
+ - active
748
+ - canceled
749
+ - past_due
750
+ - trialing
751
+ - null
752
+ nullable: true
753
+ subscription_expires_at:
754
+ type: string
755
+ format: date-time
756
+ nullable: true
757
+ subscriptions:
758
+ type: array
759
+ items:
760
+ $ref: '#/components/schemas/ServiceSubscription'
761
+ tos_accepted_at:
762
+ type: string
763
+ format: date-time
764
+ nullable: true
765
+ created_at:
766
+ type: string
767
+ format: date-time
768
+ UserResponse:
769
+ type: object
770
+ properties:
771
+ data:
772
+ $ref: '#/components/schemas/User'
773
+ UpdateProfileInput:
774
+ type: object
775
+ properties:
776
+ name:
777
+ type: string
778
+ language:
779
+ type: string
780
+ enum:
781
+ - ja
782
+ - en
783
+ timezone:
784
+ type: string
785
+ description: IANA time zone name, or `auto` to follow the device.
786
+ ServiceSubscription:
787
+ type: object
788
+ properties:
789
+ id:
790
+ type: integer
791
+ user_id:
792
+ type: integer
793
+ service_id:
794
+ type: string
795
+ plan:
796
+ type: string
797
+ enum:
798
+ - free
799
+ - pro
800
+ - family
801
+ - enterprise
802
+ subscription_id:
803
+ type: string
804
+ nullable: true
805
+ subscription_status:
806
+ type: string
807
+ enum:
808
+ - active
809
+ - canceled
810
+ - past_due
811
+ - trialing
812
+ - null
813
+ nullable: true
814
+ subscription_expires_at:
815
+ type: string
816
+ format: date-time
817
+ nullable: true
818
+ created_at:
819
+ type: string
820
+ format: date-time
821
+ updated_at:
822
+ type: string
823
+ format: date-time
824
+ ApiToken:
825
+ type: object
826
+ properties:
827
+ id:
828
+ type: integer
829
+ user_id:
830
+ type: integer
831
+ name:
832
+ type: string
833
+ last_used_at:
834
+ type: string
835
+ format: date-time
836
+ nullable: true
837
+ expires_at:
838
+ type: string
839
+ format: date-time
840
+ nullable: true
841
+ created_at:
842
+ type: string
843
+ format: date-time
844
+ updated_at:
845
+ type: string
846
+ format: date-time
847
+ CreateApiTokenInput:
848
+ type: object
849
+ required:
850
+ - name
851
+ properties:
852
+ name:
853
+ type: string
854
+ minLength: 1
855
+ maxLength: 100
856
+ example: My API Token
857
+ expires_at:
858
+ type: string
859
+ format: date-time
860
+ description: Optional expiration date for the token.
861
+ ServiceAccount:
862
+ type: object
863
+ description: >-
864
+ A non-human users row scoped to a single organization. SAs hold API tokens via the existing api_tokens table
865
+ (user_id = SA.id). They never appear in member lists; verifyToken rejects any JWT bearing the synthetic
866
+ 'service-account|' sub.
867
+ properties:
868
+ id:
869
+ type: integer
870
+ name:
871
+ type: string
872
+ organization_id:
873
+ type: integer
874
+ is_service_account:
875
+ type: integer
876
+ enum:
877
+ - 1
878
+ description: Always 1 for SA rows; included for parity with the underlying users schema.
879
+ auth0_sub:
880
+ type: string
881
+ description: Synthetic, prefixed 'service-account|' followed by a UUID. Has no real Auth0 identity behind it.
882
+ email:
883
+ type: string
884
+ nullable: true
885
+ description: Auto-generated 'sa-<uuid>@nasebanal.com'. Not a real mailbox.
886
+ token_count:
887
+ type: integer
888
+ description: Number of api_tokens currently owned by this SA. Returned only on list and create responses.
889
+ created_at:
890
+ type: string
891
+ format: date-time
892
+ updated_at:
893
+ type: string
894
+ format: date-time
895
+ CreateServiceAccountInput:
896
+ type: object
897
+ required:
898
+ - name
899
+ properties:
900
+ name:
901
+ type: string
902
+ minLength: 1
903
+ maxLength: 200
904
+ example: Acme Metrics Bot
905
+ Error:
906
+ type: object
907
+ required:
908
+ - success
909
+ - error
910
+ properties:
911
+ # The implementation has always answered with the same two-field
912
+ # envelope as a success response, `{ success, error }` — see
913
+ # errorResponse() in nb-account-api/src/utils/errors.ts. Only the
914
+ # success half of the envelope was ever described here, so every error
915
+ # response carried an undeclared `success` property. Specmatic reports
916
+ # it as R2003 "Unknown property", which is how this was found: the
917
+ # provider contract test covered only /health until the scope was
918
+ # widened to GET /api/v1/me -> 401.
919
+ #
920
+ # Pinned with `enum` for the same reason the success envelope pins it
921
+ # to [true] — a plain boolean lets a stub answer `success: true`
922
+ # alongside an error body, which is a state the implementation cannot
923
+ # produce.
924
+ success:
925
+ type: boolean
926
+ enum: [false]
927
+ error:
928
+ type: object
929
+ required:
930
+ - code
931
+ - message
932
+ properties:
933
+ code:
934
+ type: string
935
+ message:
936
+ type: string
937
+ # Optional, and omitted entirely when absent: AppError carries
938
+ # `details?: Record<string, unknown>` and JSON.stringify drops the
939
+ # key when it is undefined. Declared so a response that does carry
940
+ # it is not reported as another unknown property.
941
+ details:
942
+ type: object
943
+ additionalProperties: true
944
+ responses:
945
+ Unauthorized:
946
+ description: Authentication required or invalid token
947
+ content:
948
+ application/json:
949
+ schema:
950
+ $ref: '#/components/schemas/Error'
951
+ example:
952
+ success: false
953
+ error:
954
+ code: AUTH_ERROR
955
+ message: No authentication token provided
956
+ NotFound:
957
+ description: Resource not found
958
+ content:
959
+ application/json:
960
+ schema:
961
+ $ref: '#/components/schemas/Error'
962
+ example:
963
+ success: false
964
+ error:
965
+ code: NOT_FOUND
966
+ message: Resource not found
967
+ Forbidden:
968
+ description: Caller is authenticated but lacks the required role or plan
969
+ content:
970
+ application/json:
971
+ schema:
972
+ $ref: '#/components/schemas/Error'
973
+ example:
974
+ success: false
975
+ error:
976
+ code: PLAN_LIMIT_EXCEEDED
977
+ message: Service Accounts require an Enterprise plan for this organization.
978
+ tags:
979
+ - name: Health
980
+ description: Health check endpoints
981
+ - name: User
982
+ description: User profile endpoints
983
+ - name: Subscriptions
984
+ description: Subscription management endpoints
985
+ - name: Stripe
986
+ description: Stripe billing and payment endpoints
987
+ - name: API Tokens
988
+ description: Personal Access Token management
989
+ - name: Service Accounts
990
+ description: Org-scoped service accounts for Enterprise organizations and their API tokens. Org Admin only.