-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinvitation-openapi.yml
More file actions
654 lines (618 loc) · 25.4 KB
/
Copy pathinvitation-openapi.yml
File metadata and controls
654 lines (618 loc) · 25.4 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
openapi: 3.0.4
info:
title: Invitation API
version: 0.1.0
x-products: [vdx]
description: |
Manage in-flight invitations and let recipients redeem them. An invitation is a
tenant-scoped, time-bounded token that grants its holder a single capability:
onboarding a tenant owner, running an identity verification, receiving a
credential offer, or presenting a credential. This API is the one place to
track, inspect, and act on invitations regardless of which feature minted them,
and the public surface a recipient uses to redeem one.
## Where invitations are created
This API does not, by itself, create the bulk of a deployment's invitations.
Invitations are minted by the feature that needs them, so the minting endpoint
can carry that feature's context. The main creation path is the credential
issuance flow: an operator uploads a recipient list (a CSV, optionally promoted
to a reusable source) and the issuance run mints one invitation per row and
sends each recipient an email. That run lives in the issuance batch surface, not
here. Tenant owner onboarding, identity verification, and credential
presentation each mint their invitations the same way, through their own flow.
What every one of those flows shares is the invitation lifecycle, and that is
what this API exposes: a single, action-neutral surface to list and filter
invitations and the batches they came from, read one invitation's detail and
audit timeline, and cancel or resend an in-flight invitation. A small set of
low-level mint, resolve, and revoke endpoints also lives under this base path
for direct integrations; the feature flows above build on them.
## Redeeming an invitation
The redemption endpoints are the recipient-facing half of the API. A holder
lands on an invite page from a tokenised link, the page posts the token to
`POST /redeem`, and the server returns the next step: a credential offer URL to
render as a QR code, a redirect URL to start identity verification, or a safe
error message. When verification is required, the verification engine calls
`POST /redeem/idv-callback` and the holder re-enters the flow. These two
endpoints are public and unauthenticated; every other endpoint requires a bearer
token.
## Authentication and tenant context
Management endpoints require a bearer JWT. Tenant context is resolved from that
token and is never taken from a request parameter. The optional `X-Tenant-ID`
header is informational only and is not used for access control. A client may
send `X-Correlation-ID` to correlate a request with its logs; when absent the
server generates one, and it is echoed on the response. The redemption endpoints
carry no tenant header: the tenant is resolved from the token presented for
redemption.
## Pagination and errors
List endpoints accept `limit`/`offset` (and the OpenAPI-style `page`/`size`);
`limit` is clamped to `[1, 100]` and defaults to `20`. They return a `data`
array alongside a `pagination` block ([PageMeta]). Errors share the stable,
machine-readable [ApiError] body. Timestamps are RFC 3339 / ISO 8601 in UTC.
contact:
name: Sphereon International B.V.
email: support@sphereon.com
url: https://sphereon.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0
servers:
- url: https://api.example.com/api/invitation/v1
description: Production server.
- url: http://localhost:8080/api/invitation/v1
description: Local development server.
security:
- bearer: []
tags:
- name: Invitations
description: >-
Operator surface for in-flight invitations: list and filter invitations and
batches, read one invitation's detail and timeline, and cancel or resend.
- name: Redemption
description: >-
Public, recipient-facing endpoints for redeeming an invitation token and for
the identity-verification callback. These endpoints are unauthenticated.
paths:
/invitations:
get:
tags: [Invitations]
summary: List invitations
operationId: listInvitations
description: |
Returns a page of invitations in the caller's tenant, narrowed by the optional
filters. Default sort is newest first (`createdAt` descending).
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
- name: scope
in: query
description: Filter by binding scope, for example `tabular.batch_row`.
schema:
type: string
- name: targetId
in: query
description: Filter by the target entity identifier the invitation addresses.
schema:
type: string
- name: status
in: query
description: Filter by lifecycle status.
schema:
type: string
enum: [PENDING, DELIVERED, OPENED, IN_PROGRESS, CLAIMED, EXHAUSTED, REVOKED, EXPIRED, FAILED]
- name: action
in: query
description: Filter by the action the invitation drives, for example `credential.issuance`.
schema:
type: string
- name: deliveryState
in: query
description: Filter by delivery state.
schema:
type: string
enum: [PENDING, QUEUED, SENT, DELIVERED, BOUNCED, FAILED, NOT_REQUESTED]
- name: batchId
in: query
description: Restrict to invitations that belong to this batch.
schema:
type: string
- name: since
in: query
description: Return only invitations created at or after this RFC 3339 / ISO 8601 instant.
schema:
type: string
format: date-time
- $ref: './common-components.yml#/components/parameters/Limit'
- $ref: './common-components.yml#/components/parameters/Offset'
- $ref: './common-components.yml#/components/parameters/Page'
- $ref: './common-components.yml#/components/parameters/Size'
responses:
'200':
description: A page of invitations.
content:
application/json:
schema:
$ref: '#/components/schemas/PageOfInvitationSummary'
'400':
$ref: './common-components.yml#/components/responses/ValidationError'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'500':
$ref: './common-components.yml#/components/responses/Error'
/invitations/{invitationId}:
parameters:
- $ref: '#/components/parameters/InvitationId'
get:
tags: [Invitations]
summary: Get an invitation
operationId: getInvitation
description: >-
Returns the full detail of a single invitation, including its mint context,
delivery info, and redemption outcome. The invitation token is never returned.
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
responses:
'200':
description: The invitation detail.
content:
application/json:
schema:
$ref: '#/components/schemas/InvitationDetail'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'404':
$ref: './common-components.yml#/components/responses/NotFound'
'500':
$ref: './common-components.yml#/components/responses/Error'
/invitations/{invitationId}/events:
parameters:
- $ref: '#/components/parameters/InvitationId'
get:
tags: [Invitations]
summary: List an invitation's events
operationId: listInvitationEvents
description: >-
Returns the append-only audit timeline for one invitation: lifecycle
transitions, delivery attempts, and operator actions, in the order they
occurred.
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
responses:
'200':
description: The ordered event timeline.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/InvitationEvent'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'404':
$ref: './common-components.yml#/components/responses/NotFound'
'500':
$ref: './common-components.yml#/components/responses/Error'
/invitations/{invitationId}/actions/cancel:
parameters:
- $ref: '#/components/parameters/InvitationId'
post:
tags: [Invitations]
summary: Cancel an invitation
operationId: cancelInvitation
description: >-
Revokes an invitation so it can no longer be redeemed. The operation is
idempotent: cancelling an already-cancelled invitation returns its current
detail without error.
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/CancelInvitationRequest'
responses:
'200':
description: The invitation detail after cancellation.
content:
application/json:
schema:
$ref: '#/components/schemas/InvitationDetail'
'400':
$ref: './common-components.yml#/components/responses/ValidationError'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'404':
$ref: './common-components.yml#/components/responses/NotFound'
'500':
$ref: './common-components.yml#/components/responses/Error'
/invitations/{invitationId}/actions/resend:
parameters:
- $ref: '#/components/parameters/InvitationId'
post:
tags: [Invitations]
summary: Resend an invitation
operationId: resendInvitation
description: |
Re-delivers an invitation. Because the raw token is never stored, a resend
currently mints a fresh invitation, delivers it, and revokes the original,
linking the two. The response reports the mode that was applied (`REISSUED`
today) and returns the effective invitation. A future `RETRIED` mode will
re-deliver the same token without minting a replacement.
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/ResendInvitationRequest'
responses:
'200':
description: The resend outcome, with the mode applied and the resulting invitation.
content:
application/json:
schema:
$ref: '#/components/schemas/ResendInvitationResponse'
'400':
$ref: './common-components.yml#/components/responses/ValidationError'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'404':
$ref: './common-components.yml#/components/responses/NotFound'
'500':
$ref: './common-components.yml#/components/responses/Error'
/invitations/batches:
get:
tags: [Invitations]
summary: List invitation batches
operationId: listInvitationBatches
description: >-
Returns a page of invitation batches in the caller's tenant. A batch groups
the invitations minted in one operator action, for example one credential
issuance run. Default sort is newest first (`createdAt` descending).
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
- name: state
in: query
description: Filter by batch processing state.
schema:
type: string
enum: [PENDING, RUNNING, COMPLETED, PARTIAL, FAILED]
- name: createdBy
in: query
description: Filter by the operator or system identifier that created the batch.
schema:
type: string
- name: since
in: query
description: Return only batches created at or after this RFC 3339 / ISO 8601 instant.
schema:
type: string
format: date-time
- $ref: './common-components.yml#/components/parameters/Limit'
- $ref: './common-components.yml#/components/parameters/Offset'
- $ref: './common-components.yml#/components/parameters/Page'
- $ref: './common-components.yml#/components/parameters/Size'
responses:
'200':
description: A page of invitation batches.
content:
application/json:
schema:
$ref: '#/components/schemas/PageOfInvitationBatchSummary'
'400':
$ref: './common-components.yml#/components/responses/ValidationError'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'500':
$ref: './common-components.yml#/components/responses/Error'
/invitations/batches/{batchId}:
parameters:
- $ref: '#/components/parameters/BatchId'
get:
tags: [Invitations]
summary: Get an invitation batch
operationId: getInvitationBatch
description: Returns the full detail of a single invitation batch, including its source descriptor and progress counts.
parameters:
- $ref: './common-components.yml#/components/parameters/TenantId'
responses:
'200':
description: The invitation batch detail.
content:
application/json:
schema:
$ref: '#/components/schemas/InvitationBatchDetail'
'401':
$ref: './common-components.yml#/components/responses/Unauthorized'
'404':
$ref: './common-components.yml#/components/responses/NotFound'
'500':
$ref: './common-components.yml#/components/responses/Error'
/redeem:
post:
tags: [Redemption]
summary: Redeem an invitation token
operationId: redeem
description: |
Public, unauthenticated endpoint that the recipient's invite page posts the
token to. The server validates the token, loads the bound batch row, and
returns the next step as a flat result discriminated by `type`:
- `RENDER_QR`: issuance is ready. Render a QR for `credentialOfferUrl` (an
`openid-credential-offer://` URI) for the wallet to scan. `correlationId`
and `credentialType` are included so the page can poll issuance progress
and name the credential without re-parsing the URI.
- `IDV_REDIRECT`: identity verification is required first. Navigate the
user-agent to `dispatchUrl`.
- `ERROR`: the flow halted with a message safe to show the recipient in
`publicMessage`.
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RedeemFlowArgs'
responses:
'200':
description: Redemption proceeded; the result `type` indicates the next step.
content:
application/json:
schema:
$ref: '#/components/schemas/RedeemFlowResult'
'400':
description: The request body was malformed, or the token's target is not a redeemable batch row.
content:
application/json:
schema:
$ref: './common-components.yml#/components/schemas/ApiError'
'403':
description: The invitation is expired or revoked, or identity verification did not match the expected recipient.
content:
application/json:
schema:
$ref: './common-components.yml#/components/schemas/ApiError'
'404':
description: The token is unknown, or its bound batch row no longer exists.
content:
application/json:
schema:
$ref: './common-components.yml#/components/schemas/ApiError'
'500':
$ref: './common-components.yml#/components/responses/Error'
/redeem/idv-callback:
post:
tags: [Redemption]
summary: Complete the identity-verification step
operationId: redeemIdvCallback
description: >-
Public, unauthenticated callback invoked by the identity-verification engine
after its graph completes. The server stores the verification result against
the token and returns a `resumeUrl` so the user-agent can re-enter
`POST /redeem` with the verified attributes available.
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/IdvCallbackArgs'
responses:
'200':
description: The callback was recorded; the client should follow `resumeUrl`.
content:
application/json:
schema:
$ref: '#/components/schemas/IdvCallbackResult'
'400':
description: The request body was malformed.
content:
application/json:
schema:
$ref: './common-components.yml#/components/schemas/ApiError'
'404':
description: The token is unknown or has no in-flight verification.
content:
application/json:
schema:
$ref: './common-components.yml#/components/schemas/ApiError'
'500':
$ref: './common-components.yml#/components/responses/Error'
components:
# Shared pagination parameters (Limit, Offset, Page, Size), error responses,
# ApiError, PageMeta, the X-Tenant-ID/X-Correlation-ID headers, and the bearer
# security scheme are defined in the canonical ./common-components.yml (owned by
# IDK lib-core-api-public, copied in at build time). Only invitation-specific
# components are defined here or re-exported from ./invitation-components.yml.
securitySchemes:
bearer:
$ref: './common-components.yml#/components/securitySchemes/bearer'
parameters:
InvitationId:
$ref: './invitation-components.yml#/components/parameters/InvitationId'
BatchId:
$ref: './invitation-components.yml#/components/parameters/BatchId'
schemas:
# ---- Re-exported entity schemas -----------------------------------------
ChannelAddress:
$ref: './invitation-components.yml#/components/schemas/ChannelAddress'
RecipientRef:
$ref: './invitation-components.yml#/components/schemas/RecipientRef'
OutcomeRef:
$ref: './invitation-components.yml#/components/schemas/OutcomeRef'
ChannelPreference:
$ref: './invitation-components.yml#/components/schemas/ChannelPreference'
InvitationSummary:
$ref: './invitation-components.yml#/components/schemas/InvitationSummary'
InvitationDetail:
$ref: './invitation-components.yml#/components/schemas/InvitationDetail'
InvitationEvent:
$ref: './invitation-components.yml#/components/schemas/InvitationEvent'
InvitationBatchSummary:
$ref: './invitation-components.yml#/components/schemas/InvitationBatchSummary'
InvitationBatchDetail:
$ref: './invitation-components.yml#/components/schemas/InvitationBatchDetail'
# ---- Management action bodies -------------------------------------------
CancelInvitationRequest:
type: object
description: Optional body for the cancel action.
properties:
reason:
type: string
description: Human-readable reason for cancellation, recorded in the audit timeline.
example: Recipient left the organization
ResendInvitationRequest:
type: object
description: Optional body for the resend action. When omitted the tenant and original-invitation defaults are used.
properties:
channelPreferences:
type: array
description: >-
Ordered channel preferences for this resend. When supplied these override
the channel preferences recorded on the original invitation.
items:
$ref: '#/components/schemas/ChannelPreference'
ttlSeconds:
type: integer
format: int64
description: >-
Lifetime in seconds for the reissued invitation, measured from when the
resend is processed. When absent the tenant default is applied.
example: 1209600
ResendInvitationResponse:
type: object
description: Outcome of a resend action.
required:
- mode
- invitation
properties:
mode:
type: string
description: |
REISSUED: a replacement invitation was minted, delivered, and the original
revoked. RETRIED (reserved): the original token was re-delivered without
minting a replacement.
enum: [RETRIED, REISSUED]
example: REISSUED
invitation:
allOf:
- $ref: '#/components/schemas/InvitationDetail'
description: The effective invitation after the resend.
replacedInvitationId:
type: string
nullable: true
description: When `mode` is REISSUED, the id of the original invitation that was revoked. Null for RETRIED.
# ---- Redemption bodies (recipient-facing) -------------------------------
RedeemFlowArgs:
type: object
description: Body of `POST /redeem`.
required:
- token
- redemptionBaseUrl
properties:
token:
type: string
minLength: 1
description: The raw invitation token, taken from the `?t=` query parameter of the invite link.
redemptionBaseUrl:
type: string
format: uri
description: >-
Absolute base URL the identity-verification callback should target. The
flow appends `/redeem/idv-callback?t=<token>`. Typically the same origin
the invite page was loaded from.
example: https://wallet.example.com
RedeemFlowResult:
type: object
description: |
Flat result of `POST /redeem`, discriminated by `type`. Each type populates a
different subset of the optional fields:
- RENDER_QR: `credentialOfferUrl`, `correlationId`, `credentialType`.
- IDV_REDIRECT: `dispatchUrl`.
- ERROR: `publicMessage`.
required:
- type
properties:
type:
type: string
enum: [IDV_REDIRECT, RENDER_QR, ERROR]
description: The next step for the recipient's user-agent.
credentialOfferUrl:
type: string
nullable: true
description: RENDER_QR only. The OID4VCI credential offer URI to render as a QR code.
example: openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fissuer.example.com%2Foffers%2Fabc123
correlationId:
type: string
nullable: true
description: >-
RENDER_QR only. The OID4VCI session correlation id, also carried as the
trailing segment of the offer URI. Use it to poll issuance progress.
example: abc123
credentialType:
type: string
nullable: true
description: RENDER_QR only. The credential type or configuration id being offered, for naming it in the UI.
example: EmployeeID
dispatchUrl:
type: string
format: uri
nullable: true
description: IDV_REDIRECT only. The identity-verification engine URL to navigate the user-agent to.
publicMessage:
type: string
nullable: true
description: ERROR only. A sanitized message safe to display to the recipient.
IdvCallbackArgs:
type: object
description: Body of `POST /redeem/idv-callback`.
required:
- token
- callbackParams
properties:
token:
type: string
minLength: 1
description: The invitation token the verification was started for.
callbackParams:
type: object
additionalProperties:
type: string
description: >-
Method-specific callback parameters forwarded by the verification engine,
for example `code` and `state` for an OAuth-style return.
IdvCallbackResult:
type: object
description: Result of `POST /redeem/idv-callback`.
required:
- resumeUrl
properties:
resumeUrl:
type: string
format: uri
description: The URL the user-agent should follow to re-enter the redemption flow.
# ---- Page envelopes ({ data, pagination }, per ResponseBuilder.paginated) ----
PageOfInvitationSummary:
type: object
description: A page of invitation summaries.
required:
- data
- pagination
properties:
data:
type: array
items:
$ref: '#/components/schemas/InvitationSummary'
pagination:
$ref: './common-components.yml#/components/schemas/PageMeta'
PageOfInvitationBatchSummary:
type: object
description: A page of invitation batch summaries.
required:
- data
- pagination
properties:
data:
type: array
items:
$ref: '#/components/schemas/InvitationBatchSummary'
pagination:
$ref: './common-components.yml#/components/schemas/PageMeta'