-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplatform-admin-components.yml
More file actions
1037 lines (1033 loc) · 36.7 KB
/
Copy pathplatform-admin-components.yml
File metadata and controls
1037 lines (1033 loc) · 36.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
openapi: 3.0.4
info:
title: Sphereon Platform Admin API Components
version: 0.1.0
description: >
Entity schemas, enums, value objects, and path parameters for the Sphereon
Platform Admin API. These components back the application-tenant control
surface, tenant lifecycle, self-service signup, tenant federation, and owner
onboarding operations. Secret values are write-only on the way in and are
represented on the way out only by opaque reference fields (for example
`clientSecretRef`); plaintext secrets never appear in any response schema.
paths: {}
components:
parameters:
TenantId:
name: tenantId
in: path
required: true
description: Identifier of the tenant the operation targets.
schema:
type: string
example: 7b3c2d9e-1f4a-4c8b-9e2d-5a6f7c8b9d01
Domain:
name: domain
in: path
required: true
description: Fully qualified host attached to the tenant, lowercased and without scheme or port.
schema:
type: string
example: wallet.acme.example
PublicEndpointServiceType:
name: serviceType
in: path
required: true
description: Protocol surface the public-endpoint binding describes.
schema:
$ref: '#/components/schemas/TenantPublicEndpointServiceType'
example: OID4VCI_ISSUER
SignupRequestId:
name: signupRequestId
in: path
required: true
description: Identifier of the self-service signup request.
schema:
type: string
example: signup-9f12a7c4
IdpId:
name: idpId
in: path
required: true
description: Identifier of the tenant identity provider.
schema:
type: string
example: idp-acme-corp
schemas:
# =====================================================================
# Application tenant: bootstrap/status, license, secret backend,
# onboarding policy + evaluated availability
# =====================================================================
ApplicationTenantStatus:
type: object
description: >
Runtime state of the application tenant. The application tenant is always
reachable through the platform's own hosted identity provider, so operators
can sign in even before any customer tenant exists.
required: [tenantId, status, hostedAs, canRegisterFirstRealTenant]
properties:
tenantId:
type: string
description: Identifier of the application tenant.
status:
$ref: '#/components/schemas/ApplicationTenantStatus_Status'
hostedAs:
$ref: '#/components/schemas/HostedAsStatus'
canRegisterFirstRealTenant:
type: boolean
description: True when the application tenant is ready to register the first customer tenant.
example:
tenantId: application
status: ACTIVE
hostedAs:
required: true
available: true
issuerUrl: https://auth.platform.example
canRegisterFirstRealTenant: true
ApplicationTenantStatus_Status:
type: string
description: Lifecycle state of the application tenant.
enum: [ACTIVE, SUSPENDED, PENDING_VERIFICATION]
example: ACTIVE
HostedAsStatus:
type: object
description: >
Readiness of the platform-hosted authorization server that fronts the
application tenant. `required` is a system invariant and is always `true`.
required: [required, available]
properties:
required:
type: boolean
description: Always `true`. The hosted authorization server is a system requirement.
enum: [true]
available:
type: boolean
description: True when the hosted authorization server is reachable and serving metadata.
issuerUrl:
type: string
format: uri
nullable: true
description: Issuer URL of the hosted authorization server when available.
example:
required: true
available: true
issuerUrl: https://auth.platform.example
LicenseStatusProjection:
type: object
description: >
Sanitized projection of the effective license. It exposes lifecycle status,
per-product summaries, and expiry signals; it never carries the raw license
token, decoded claims, entitlement keys, quota values, certificate chains,
or key material.
required: [status]
properties:
status:
type: string
description: Effective lifecycle status of the license.
enum: [ACTIVE, GRACE, RECOVERY, MISSING, INVALID, EXPIRED, BLOCKED]
productSummaries:
type: array
items:
$ref: '#/components/schemas/ProductLicenseSummary'
default: []
description: One summary per licensed product.
customerId:
type: string
nullable: true
description: Customer identifier from the license claims, when present.
deploymentId:
type: string
nullable: true
description: Deployment identifier the license is bound to, when present.
issuer:
type: string
nullable: true
description: Display name or identifier of the license issuer, when present.
signingCertificateFingerprint:
type: string
nullable: true
description: Hex-encoded SHA-256 fingerprint of the leaf certificate that signed the license.
expiresAt:
type: string
format: date-time
nullable: true
description: Expiry instant of the license claims, when present.
daysToExpiry:
type: integer
format: int64
nullable: true
description: Whole days until expiry, rounded up, when an expiry is present.
recoveryMode:
type: boolean
default: false
description: True when the deployment runs in recovery mode after repeated verification failures.
graceMode:
type: boolean
default: false
description: True when the license is expired but still inside the configured grace window.
warnings:
type: array
items:
type: string
default: []
description: Human-readable warnings about the license state.
example:
status: ACTIVE
productSummaries:
- product: vdx
edition: enterprise
modules: [tenant, party, oid4vci]
quotaKeys: [max-root-tenants, max-total-tenants]
customerId: cust-acme-001
deploymentId: dep-eu-west-1
issuer: Sphereon Licensing
signingCertificateFingerprint: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
expiresAt: '2027-06-30T23:59:59Z'
daysToExpiry: 386
recoveryMode: false
graceMode: false
warnings: []
ProductLicenseSummary:
type: object
description: Per-product license summary - product, edition, licensed module names, and quota key names.
required: [product, edition]
properties:
product:
type: string
description: Licensed product identifier.
edition:
type: string
description: Licensed edition of the product.
modules:
type: array
items:
type: string
default: []
description: Names of the licensed modules. Module entitlement details are not exposed.
quotaKeys:
type: array
items:
type: string
default: []
description: Names of the quota keys the license defines. Quota values are not exposed.
example:
product: vdx
edition: enterprise
modules: [tenant, party, oid4vci]
quotaKeys: [max-root-tenants, max-total-tenants]
LicenseVerificationProjection:
type: object
description: >
Outcome of verifying a license token without installing it. `status` is
present when the token verifies; `error` carries a short reason when it does not.
required: [valid]
properties:
valid:
type: boolean
description: True when the token is a well-formed, currently valid license.
status:
type: object
allOf:
- $ref: '#/components/schemas/LicenseStatusProjection'
nullable: true
description: Sanitized projection of the verified license, when the token verifies.
error:
type: string
nullable: true
description: Short reason string when the token is not valid.
example:
valid: true
status:
status: ACTIVE
productSummaries:
- product: vdx
edition: enterprise
modules: [tenant, party, oid4vci]
quotaKeys: [max-root-tenants, max-total-tenants]
customerId: cust-acme-001
deploymentId: dep-eu-west-1
issuer: Sphereon Licensing
signingCertificateFingerprint: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
expiresAt: '2027-06-30T23:59:59Z'
daysToExpiry: 386
recoveryMode: false
graceMode: false
warnings: []
error: null
SecretBackendType:
type: string
description: Secret backend that stores tenant and federation secrets.
enum:
- azure-key-vault
- hashicorp-vault
- kubernetes-secret-mount
- config-system-dev-only
example: hashicorp-vault
SecretBackendStatus:
type: object
description: >
Configured secret backend. The opaque `configRef` points at the resolved
backend instance (vault name, key path prefix, mount path); plaintext
secrets never appear here.
required: [configured]
properties:
configured:
type: boolean
description: True when a secret backend has been selected.
backend:
type: string
allOf:
- $ref: '#/components/schemas/SecretBackendType'
nullable: true
description: Selected secret backend, when one is configured.
configRef:
type: string
nullable: true
description: Opaque reference to the resolved backend instance.
example:
configured: true
backend: hashicorp-vault
configRef: secret://platform/kv/tenant-secrets
OnboardingPolicy:
type: object
description: >
Onboarding policy toggles. Each toggle constrains, within what the license
allows, which onboarding flows are accepted.
required:
- rootTenantCreationEnabled
- adminInviteEnabled
- selfSignupEnabled
- subtenantSignupEnabled
- requireApprovalForSelfSignup
properties:
rootTenantCreationEnabled:
type: boolean
description: True when operators may register root tenants.
adminInviteEnabled:
type: boolean
description: True when operator-driven tenant invitations are accepted.
selfSignupEnabled:
type: boolean
description: True when public self-service signup is accepted.
subtenantSignupEnabled:
type: boolean
description: True when self-service signup under a parent tenant is accepted.
requireApprovalForSelfSignup:
type: boolean
description: True when an operator must approve a self-service signup after email verification.
updatedAt:
type: string
format: date-time
nullable: true
description: Timestamp of the last policy change, when the policy has been set.
example:
rootTenantCreationEnabled: true
adminInviteEnabled: true
selfSignupEnabled: true
subtenantSignupEnabled: false
requireApprovalForSelfSignup: true
updatedAt: '2026-06-08T09:15:00Z'
OnboardingAvailability:
type: object
description: >
Evaluated availability matrix. It composes license features (deny authority),
config toggles (deny authority within what the license allows), and runtime
readiness (email transport, secret backend, application tenant). Reason
strings follow a stable `<source>_<reason-id>` convention, for example
`license_missing_self_signup` or `config_disabled_self_signup`.
required: [applicationTenant, rootTenantCreation, adminInvite, selfSignup, subtenants, email, secretBackend]
properties:
applicationTenant:
$ref: '#/components/schemas/AvailabilityApplicationTenant'
rootTenantCreation:
$ref: '#/components/schemas/AvailabilityDecision'
adminInvite:
$ref: '#/components/schemas/AvailabilityDecision'
selfSignup:
$ref: '#/components/schemas/AvailabilityDecision'
subtenants:
$ref: '#/components/schemas/AvailabilityDecision'
email:
$ref: '#/components/schemas/AvailabilityEmail'
secretBackend:
$ref: '#/components/schemas/AvailabilitySecretBackend'
example:
applicationTenant:
enabled: true
hostedAsAvailable: true
rootTenantCreation:
enabled: true
reason: null
adminInvite:
enabled: true
reason: null
selfSignup:
enabled: false
reason: config_disabled_self_signup
subtenants:
enabled: true
reason: null
email:
configured: true
healthy: true
secretBackend:
configured: true
AvailabilityApplicationTenant:
type: object
description: Application-tenant readiness inputs to the availability evaluation.
required: [enabled, hostedAsAvailable]
properties:
enabled:
type: boolean
description: True when the application tenant is active.
hostedAsAvailable:
type: boolean
description: True when the hosted authorization server is reachable.
example:
enabled: true
hostedAsAvailable: true
AvailabilityDecision:
type: object
description: Evaluated decision for a single onboarding flow, with an optional stable reason string.
required: [enabled]
properties:
enabled:
type: boolean
description: True when the flow is currently available.
reason:
type: string
nullable: true
description: Stable `<source>_<reason-id>` reason when the flow is unavailable.
example:
enabled: false
reason: license_missing_self_signup
AvailabilityEmail:
type: object
description: Email transport readiness inputs to the availability evaluation.
required: [configured, healthy]
properties:
configured:
type: boolean
description: True when an email transport is configured.
healthy:
type: boolean
description: True when the configured email transport passes its readiness probe.
example:
configured: true
healthy: true
AvailabilitySecretBackend:
type: object
description: Secret-backend readiness input to the availability evaluation.
required: [configured]
properties:
configured:
type: boolean
description: True when a secret backend is configured.
example:
configured: true
# =====================================================================
# Tenants and self-service signup
# =====================================================================
Tenant:
type: object
description: A tenant in the platform control surface, including hierarchy and audit fields.
required: [id, tenantType, name, slug, status, system, createdAt, updatedAt]
properties:
id:
type: string
description: Tenant identifier.
tenantType:
type: string
description: Open tenant type label.
name:
type: string
description: Human-readable tenant name.
description:
type: string
nullable: true
description: Optional tenant description.
slug:
type: string
description: Globally unique URL-safe slug, used as the platform subdomain label and resolver path segment.
parentTenantId:
type: string
nullable: true
description: Parent tenant identifier, or null for root tenants.
status:
$ref: '#/components/schemas/TenantStatus'
system:
type: boolean
description: True for system tenants, which are excluded from default listings and slug-based resolution.
ownerPartyId:
type: string
format: uuid
nullable: true
description: Party identifier of the tenant owner, when assigned.
ownerEmail:
type: string
format: email
nullable: true
description: Email of the tenant owner captured at registration, when assigned. Human-readable owner contact for management surfaces.
ownerDisplayName:
type: string
nullable: true
description: Display name of the tenant owner captured at registration, when assigned.
createdAt:
type: string
format: date-time
description: Creation timestamp.
createdById:
type: string
format: uuid
nullable: true
description: Identifier of the principal that created the tenant.
updatedAt:
type: string
format: date-time
description: Last update timestamp.
updatedById:
type: string
format: uuid
nullable: true
description: Identifier of the principal that last updated the tenant.
deletedAt:
type: string
format: date-time
nullable: true
description: Soft-delete timestamp, when deleted.
deletedById:
type: string
format: uuid
nullable: true
description: Identifier of the principal that deleted the tenant.
example:
id: 7b3c2d9e-1f4a-4c8b-9e2d-5a6f7c8b9d01
tenantType: organization
name: Acme Corporation
description: Acme issuing and verification tenant
slug: acme
parentTenantId: null
status: ACTIVE
system: false
ownerPartyId: 2c4e6a80-3b5d-4f70-9a12-8c0d1e2f3a4b
createdAt: '2026-06-08T09:00:00Z'
createdById: null
updatedAt: '2026-06-08T09:00:00Z'
updatedById: null
deletedAt: null
deletedById: null
TenantStatus:
type: string
description: Lifecycle status of a tenant.
enum: [ACTIVE, SUSPENDED, PENDING_VERIFICATION]
example: ACTIVE
TenantDomain:
type: object
description: Association between a tenant and a fully qualified host that resolves to it.
required: [id, tenantId, domain, kind, isPrimary, createdAt, updatedAt]
properties:
id:
type: string
description: Domain row identifier.
tenantId:
type: string
description: Tenant the domain belongs to.
domain:
type: string
description: Lowercased host, stored without scheme or port.
kind:
$ref: '#/components/schemas/TenantDomainKind'
isPrimary:
type: boolean
description: True when this is the canonical issuer host the authorization-server metadata renders.
verifiedAt:
type: string
format: date-time
nullable: true
description: Verification timestamp for custom domains, when verified.
verificationToken:
type: string
nullable: true
writeOnly: true
description: One-time DNS challenge token. Never returned in responses.
createdAt:
type: string
format: date-time
description: Creation timestamp.
updatedAt:
type: string
format: date-time
description: Last update timestamp.
example:
id: dom-acme-platform
tenantId: 7b3c2d9e-1f4a-4c8b-9e2d-5a6f7c8b9d01
domain: acme.wallet.platform.example
kind: PLATFORM_SUBDOMAIN
isPrimary: true
verifiedAt: '2026-06-08T09:00:00Z'
createdAt: '2026-06-08T09:00:00Z'
updatedAt: '2026-06-08T09:00:00Z'
TenantDomainKind:
type: string
description: >
Domain flavour. `PLATFORM_SUBDOMAIN` is the platform-owned `<slug>.<base>`
host, auto-created and verified at registration. `CUSTOM_DOMAIN` is a host
the customer brings; it is inserted unverified.
enum: [PLATFORM_SUBDOMAIN, CUSTOM_DOMAIN]
example: PLATFORM_SUBDOMAIN
TenantPublicEndpoint:
type: object
description: >
Public endpoint binding for a tenant-owned protocol surface. Rows describe
the externally reachable host and path that protocol metadata and dispatch
use for OID4VCI, OID4VP, and OAuth2/OIDC surfaces.
required: [id, tenantId, serviceType, enabled, primaryEndpoint, createdAt, updatedAt]
properties:
id:
type: string
description: Public-endpoint row identifier.
tenantId:
type: string
description: Tenant the binding belongs to.
serviceType:
$ref: '#/components/schemas/TenantPublicEndpointServiceType'
instanceId:
type: string
nullable: true
description: >
Software instance this binding routes to. Null is the single default
binding for the tenant and service type; a non-null value lets a tenant
own several concurrent public endpoints for the same service type, one
per instance.
host:
type: string
nullable: true
description: Lowercased host without scheme or port. Null uses the runtime host or default base.
pathPrefix:
type: string
nullable: true
description: Protocol route prefix, for example `/acme/oid4vci`.
wellKnownPath:
type: string
nullable: true
description: Well-known route, for example `/.well-known/openid-credential-issuer/acme`.
enabled:
type: boolean
description: True when the binding is active.
primaryEndpoint:
type: boolean
description: True when this is the primary binding for the tenant and service type.
createdAt:
type: string
format: date-time
description: Creation timestamp.
updatedAt:
type: string
format: date-time
description: Last update timestamp.
example:
id: pep-acme-issuer
tenantId: 7b3c2d9e-1f4a-4c8b-9e2d-5a6f7c8b9d01
serviceType: OID4VCI_ISSUER
instanceId: null
host: acme.wallet.platform.example
pathPrefix: /acme/oid4vci
wellKnownPath: /.well-known/openid-credential-issuer/acme
enabled: true
primaryEndpoint: true
createdAt: '2026-06-08T09:00:00Z'
updatedAt: '2026-06-08T09:00:00Z'
TenantPublicEndpointServiceType:
type: string
description: Protocol surface a public-endpoint binding describes.
enum: [OID4VCI_ISSUER, OID4VP_VERIFIER, OAUTH2_AUTHORIZATION_SERVER]
example: OID4VCI_ISSUER
TenantOnboardingStatus:
type: object
description: >
Public-safe view of a tenant registration attempt. The step timeline lets a
UI render progress; internal recovery handles are never exposed.
required: [correlationId, tenantId, status, startedAt, updatedAt, steps]
properties:
correlationId:
type: string
description: Durable registration-log identifier for polling onboarding status.
tenantId:
type: string
description: Tenant the registration attempt targets.
status:
$ref: '#/components/schemas/TenantRegistrationStatus'
startedAt:
type: string
format: date-time
description: Timestamp the registration attempt started.
updatedAt:
type: string
format: date-time
description: Timestamp of the last registration-log update.
completedAt:
type: string
format: date-time
nullable: true
description: Completion timestamp, when the attempt has finished.
lastError:
type: string
nullable: true
description: Last error captured during registration or compensation.
steps:
type: array
description: Per-step timeline of the registration attempt.
items:
$ref: '#/components/schemas/TenantRegistrationStepRecord'
example:
correlationId: reg-acme-7f12a7c4
tenantId: 7b3c2d9e-1f4a-4c8b-9e2d-5a6f7c8b9d01
status: COMPLETED
startedAt: '2026-06-08T09:00:00Z'
updatedAt: '2026-06-08T09:00:05Z'
completedAt: '2026-06-08T09:00:05Z'
lastError: null
steps:
- step:
id: routing-inserted
startedAt: '2026-06-08T09:00:00Z'
completedAt: '2026-06-08T09:00:01Z'
error: null
- step:
id: owner-invitation-minted
startedAt: '2026-06-08T09:00:04Z'
completedAt: '2026-06-08T09:00:05Z'
error: null
TenantRegistrationStatus:
type: string
description: >
Lifecycle of a registration attempt. `IN_FLIGHT` may still complete or fail;
`COMPLETED` finished successfully; `COMPENSATED` failed and was cleanly rolled
back; `ORPHANED` failed and at least one rollback step also failed.
enum: [IN_FLIGHT, COMPLETED, COMPENSATED, ORPHANED]
example: COMPLETED
TenantRegistrationStepRecord:
type: object
description: >
One step entry in a registration attempt. `completedAt` is set when the step
side effect succeeds; `error` is set when the step fails and `completedAt`
stays null.
required: [step, startedAt]
properties:
step:
$ref: '#/components/schemas/TenantRegistrationStep'
startedAt:
type: string
format: date-time
description: Timestamp the step started.
completedAt:
type: string
format: date-time
nullable: true
description: Timestamp the step side effect succeeded, when it did.
error:
type: string
nullable: true
description: Error captured when the step failed.
example:
step:
id: routing-inserted
startedAt: '2026-06-08T09:00:00Z'
completedAt: '2026-06-08T09:00:01Z'
error: null
TenantRegistrationStep:
type: object
description: >
Identifier for a single registration step. The set is open - the platform
defines standard side-effect steps (`routing-inserted`,
`isolation-provisioned`, `tenant-schemas-ensured`, `user-schema-ensured`,
`as-provisioned`, `issuer-provisioned`, `owner-provisioned`,
`owner-invitation-minted`) and higher layers mint additional stable
kebab-case identifiers.
required: [id]
properties:
id:
type: string
description: Stable kebab-case step identifier.
example:
id: routing-inserted
LocalOwnerInput:
type: object
description: >
Initial tenant owner contact details. Tenant registration captures the owner
for platform-managed onboarding; the owner configures federation later
through tenant-owned admin operations.
required: [type, email, displayName]
properties:
type:
type: string
enum: [local]
description: Owner kind. Only `local` is accepted.
email:
type: string
format: email
description: Owner email address.
displayName:
type: string
description: Owner display name.
example:
type: local
email: admin@acme.example
displayName: Acme Administrator
OwnerDeliveryMode:
type: string
description: >
How the owner invitation is delivered. `none` mints the invitation without
sending it. `email` sends the invitation through the configured email
transport. `manual` is not accepted through this API.
enum: [none, email, manual]
example: email
OwnerDeliveryStatus:
type: string
description: Result of the owner invitation delivery attempt.
enum: [NOT_REQUESTED, SENT, MANUAL_READY, SKIPPED]
example: SENT
SignupEmailDeliveryStatus:
type: string
description: Result of the signup verification email delivery.
enum: [SENT]
example: SENT
TenantSignupStatus:
type: string
description: >
Lifecycle status of a self-service signup request, from email verification
through registration or a terminal rejected/expired/failed state.
enum: [PENDING_EMAIL, PENDING_APPROVAL, CONFIRMED, REGISTERED, REJECTED, EXPIRED, FAILED]
example: PENDING_APPROVAL
TenantSignupRequestView:
type: object
description: Operator-facing view of a self-service signup request. Token and IP hashes are never exposed.
required: [id, email, slug, displayName, expiresAt, status, createdAt, resendCount]
properties:
id:
type: string
description: Signup request identifier.
email:
type: string
format: email
description: Requester email address.
slug:
type: string
description: Requested tenant slug.
parentTenantId:
type: string
nullable: true
description: Parent tenant for a subtenant signup, or null for a root signup.
displayName:
type: string
description: Requested tenant display name.
expiresAt:
type: string
format: date-time
description: Expiry of the signup request.
status:
$ref: '#/components/schemas/TenantSignupStatus'
createdAt:
type: string
format: date-time
description: Creation timestamp.
emailVerifiedAt:
type: string
format: date-time
nullable: true
description: Email verification timestamp, when verified.
approvedAt:
type: string
format: date-time
nullable: true
description: Operator approval timestamp, when approved.
approvedById:
type: string
nullable: true
description: Identifier of the operator who approved the request.
confirmedAt:
type: string
format: date-time
nullable: true
description: Email confirmation timestamp, when confirmed.
rejectedAt:
type: string
format: date-time
nullable: true
description: Rejection timestamp, when rejected.
rejectedReason:
type: string
nullable: true
description: Operator-supplied rejection reason.
registeredTenantId:
type: string
nullable: true
description: Identifier of the tenant created from this request, when registered.
registeredAt:
type: string
format: date-time
nullable: true
description: Registration timestamp, when registered.
failureReason:
type: string
nullable: true
description: Failure reason when registration failed.
lastResendAt:
type: string
format: date-time
nullable: true
description: Timestamp of the last verification resend.
resendCount:
type: integer
format: int32
minimum: 0
description: Number of verification resends so far.
example:
id: signup-9f12a7c4
email: admin@acme.example
slug: acme
parentTenantId: null
displayName: Acme Corporation
expiresAt: '2026-06-09T09:00:00Z'
status: PENDING_APPROVAL
createdAt: '2026-06-08T09:00:00Z'
emailVerifiedAt: '2026-06-08T09:05:00Z'
approvedAt: null
approvedById: null
confirmedAt: null
rejectedAt: null
rejectedReason: null
registeredTenantId: null
registeredAt: null
failureReason: null
lastResendAt: null
resendCount: 0
# =====================================================================
# Tenant federation
# =====================================================================
TenantIdp:
type: object
description: >
Tenant identity provider. Carries the opaque `clientSecretRef` only; the
plaintext client secret is never echoed.
required: [idpId, displayName, issuer, clientId, scopes, claimsMapping, enabled]
properties:
idpId:
type: string
description: Identity provider identifier.
displayName:
type: string
description: Human-readable identity provider name.
issuer:
type: string
format: uri
description: OpenID Connect issuer URL.
clientId:
type: string
description: OAuth2 client identifier registered with the provider.
clientSecretRef:
type: string
nullable: true
description: Opaque reference to the stored client secret.
scopes:
type: array
description: OAuth2 scopes requested during federation.
items:
type: string
claimsMapping:
$ref: '#/components/schemas/ClaimsMapping'
enabled:
type: boolean
description: True when the identity provider is enabled for sign-in.
createdAt:
type: string
format: date-time
nullable: true
description: Creation timestamp.
updatedAt:
type: string
format: date-time
nullable: true
description: Last update timestamp.
example:
idpId: idp-acme-corp
displayName: Acme Corporate IdP
issuer: https://idp.acme.example
clientId: acme-client
clientSecretRef: secret://tenant-acme/idp/acme-client-secret
scopes: [openid, profile, email]
claimsMapping:
subject: sub
email: email
displayName: name
enabled: true
createdAt: '2026-06-08T09:10:00Z'
updatedAt: '2026-06-08T09:10:00Z'
ClaimsMapping:
type: object
description: Mapping from local claim names to the identity provider's claim names.
required: [subject, email, displayName]
properties:
subject:
type: string
default: sub
description: Claim carrying the subject identifier.
email:
type: string
default: email
description: Claim carrying the email address.
displayName:
type: string
default: name
description: Claim carrying the display name.
example:
subject: sub
email: email
displayName: name
TenantIdpTestResult:
type: object
description: Result of probing an identity provider's connectivity and metadata.
required: [success]
properties:
success:
type: boolean
description: True when the provider is reachable and its metadata is valid.
issuerReachable:
type: boolean
nullable: true
description: True when the issuer URL responded.