@onplo/sdk 0.0.0-stage → 0.0.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.
@@ -0,0 +1,2628 @@
1
+ export interface paths {
2
+ "/.well-known/openid-configuration": {
3
+ parameters: {
4
+ query?: never;
5
+ header?: never;
6
+ path?: never;
7
+ cookie?: never;
8
+ };
9
+ /**
10
+ * Read where the sign-in provider is and what it supports
11
+ * @description THE DISCOVERY DOCUMENT (OpenID Connect Discovery 1.0). Every Onplo app — web, desktop, phone,
12
+ * the command line and the agent connector — signs in through identity as an OpenID Connect client
13
+ * (architecture decision record 037), and reads this first: where to send a person to sign in,
14
+ * where to trade the answer for tokens, and where the keys are that check them.
15
+ *
16
+ * IT NAMES ONLY WHAT IS SUPPORTED: the authorization code with PKCE (`S256`), public clients that
17
+ * hold no secret, the `openid` scope, and ID tokens signed ES256. No implicit flow, no password
18
+ * grant and no device code (record 037, rule One).
19
+ *
20
+ * SIGNED OUT, because nobody is signed in yet when it is read, and LIMITED by how often it is
21
+ * called, never by who calls (decision 0732). It may be kept for five minutes.
22
+ */
23
+ get: operations["readProviderDiscovery"];
24
+ put?: never;
25
+ post?: never;
26
+ delete?: never;
27
+ options?: never;
28
+ head?: never;
29
+ patch?: never;
30
+ trace?: never;
31
+ };
32
+ "/.well-known/jwks.json": {
33
+ parameters: {
34
+ query?: never;
35
+ header?: never;
36
+ path?: never;
37
+ cookie?: never;
38
+ };
39
+ /**
40
+ * Read the public keys that check the provider's tokens
41
+ * @description THE KEY SET (RFC 7517): the public half of every key whose ID tokens may still be in a client's
42
+ * hands. A client checks an ID token's signature with the key its header names by `kid`.
43
+ *
44
+ * THE PRIVATE HALF NEVER LEAVES ONPLO'S KEY SERVICE: identity asks it to sign, and reads only the
45
+ * public half to publish here.
46
+ *
47
+ * ACROSS A ROTATION THERE ARE TWO: the key signing now, and the one before it, kept for at least a
48
+ * day after the change, so a token signed just before it is still checked. A `kid` is the key's own
49
+ * thumbprint (RFC 7638), so one id never names two keys.
50
+ *
51
+ * SIGNED OUT AND LIMITED, as the discovery document is. It may be kept for five minutes; a client
52
+ * that meets a `kid` it does not know reads it again.
53
+ */
54
+ get: operations["readProviderKeySet"];
55
+ put?: never;
56
+ post?: never;
57
+ delete?: never;
58
+ options?: never;
59
+ head?: never;
60
+ patch?: never;
61
+ trace?: never;
62
+ };
63
+ "/authorize": {
64
+ parameters: {
65
+ query?: never;
66
+ header?: never;
67
+ path?: never;
68
+ cookie?: never;
69
+ };
70
+ /**
71
+ * Send a person from an app to sign in
72
+ * @description THE AUTHORIZATION ENDPOINT (OpenID Connect Core §3.1.2, architecture decision record 037). An
73
+ * app sends the browser here with its request, and Onplo answers one of three ways:
74
+ *
75
+ * - TO THE SIGN-IN SCREEN, carrying the request, when the app, its return address and every
76
+ * field are ones Onplo takes. The screen asks for the passkey, then
77
+ * `grantAuthorizationCode` sends the browser back to the app with its code.
78
+ * - BACK TO THE APP'S REGISTERED ADDRESS WITH AN OAUTH `error` (RFC 6749 §4.1.2.1), and its
79
+ * `state`, when the app and its address are Onplo's but another field is not.
80
+ * - NOWHERE, AS A 400 `not_registered`, when the app or its return address is not one Onplo
81
+ * lists. A code or an error sent to an address nobody registered is how a sign-in is stolen.
82
+ *
83
+ * THE FIELDS ARE READ BY THE RULES OF `AuthorizationRequest`, not by the parameters' own bounds:
84
+ * each is optional here so that a wrong one is answered as OAuth answers it, never as a 422.
85
+ *
86
+ * SIGNED OUT, because nobody is signed in yet, and LIMITED by how often it is called (decision
87
+ * 0732). It writes nothing.
88
+ */
89
+ get: operations["authorizeSignIn"];
90
+ put?: never;
91
+ post?: never;
92
+ delete?: never;
93
+ options?: never;
94
+ head?: never;
95
+ patch?: never;
96
+ trace?: never;
97
+ };
98
+ "/token": {
99
+ parameters: {
100
+ query?: never;
101
+ header?: never;
102
+ path?: never;
103
+ cookie?: never;
104
+ };
105
+ get?: never;
106
+ put?: never;
107
+ /**
108
+ * Trade an app's one-time code for the person's session
109
+ * @description THE TOKEN ENDPOINT (RFC 6749 §4.1.3, OpenID Connect Core §3.1.3; architecture decision record
110
+ * 037). An app that `grantAuthorizationCode` sent back with a code trades it here, once, for:
111
+ *
112
+ * - THE ACCESS TOKEN, which is the person's session, opaque, read by every Onplo service as every
113
+ * session is. It records the app it was issued to.
114
+ * - THE ID TOKEN, a JWT signed with a key of `readProviderKeySet`, which says who signed in: `iss`,
115
+ * `sub` (the person), `aud` (the app), `exp`, `iat`, `auth_time`, `amr` and the `nonce` the app
116
+ * sent. It is for the app alone and NO ONPLO SERVICE TAKES IT AS A BEARER.
117
+ *
118
+ * THE CODE IS TRADED ONLY BY THE APP IT WAS HANDED TO: the same `client_id`, the same
119
+ * `redirect_uri`, and a `code_verifier` whose SHA-256 is the challenge it was asked with (RFC 7636).
120
+ * A CODE SHOWN A SECOND TIME IS REFUSED, AND THE SESSION ITS FIRST TRADE OPENED IS ENDED, because a
121
+ * code shown twice is a code somebody else holds.
122
+ *
123
+ * REFUSED AS OAUTH REFUSES (RFC 6749 §5.2), with a 400 and an `error`, so any OAuth client reads
124
+ * it: a field missing is `invalid_request`, a grant other than `authorization_code` is
125
+ * `unsupported_grant_type`, and a code unknown, expired, spent or shown by another app is
126
+ * `invalid_grant`, told apart from nothing.
127
+ *
128
+ * SIGNED OUT, because the code is the proof, and LIMITED by how often it is called (decision 0732).
129
+ */
130
+ post: operations["requestTokens"];
131
+ delete?: never;
132
+ options?: never;
133
+ head?: never;
134
+ patch?: never;
135
+ trace?: never;
136
+ };
137
+ "/passkey-proofs": {
138
+ parameters: {
139
+ query?: never;
140
+ header?: never;
141
+ path?: never;
142
+ cookie?: never;
143
+ };
144
+ get?: never;
145
+ put?: never;
146
+ /**
147
+ * Ask for the question a passkey answers
148
+ * @description THE FIRST HALF OF PROVING IT IS YOU. Onplo mints a ceremony and answers
149
+ * `request_options`: hand it to the browser's passkey API untouched, and the browser asks
150
+ * the person for their fingerprint, face or PIN.
151
+ *
152
+ * USER VERIFICATION IS REQUIRED, ALWAYS. A touch with no fingerprint and no PIN proves a
153
+ * device is present, not that the person is.
154
+ *
155
+ * BOUND TO THE SESSION THAT ASKED. The ceremony can be completed only with the same
156
+ * credential, so an answer lifted from one session proves nothing in another.
157
+ *
158
+ * SHORT-LIVED AND SINGLE USE. A person who walks away starts again; nothing has to be
159
+ * cancelled.
160
+ *
161
+ * A `POST` because every call mints a new ceremony. It takes no body: everything it needs is
162
+ * the credential.
163
+ *
164
+ * Not gated: proving it is you is not something a company grants or withholds.
165
+ */
166
+ post: operations["startPasskeyProof"];
167
+ delete?: never;
168
+ options?: never;
169
+ head?: never;
170
+ patch?: never;
171
+ trace?: never;
172
+ };
173
+ "/passkey-proofs/{passkey_proof_id}/completion": {
174
+ parameters: {
175
+ query?: never;
176
+ header?: never;
177
+ path: {
178
+ passkey_proof_id: components["parameters"]["PasskeyProofId"];
179
+ };
180
+ cookie?: never;
181
+ };
182
+ get?: never;
183
+ put?: never;
184
+ /**
185
+ * Bring the passkey's answer back
186
+ * @description THE SECOND HALF. Send what the browser's passkey API returned, untouched. Onplo checks
187
+ * that it answers the question this ceremony asked, on this site, signed by a passkey or
188
+ * security key this person holds, with the person verified.
189
+ *
190
+ * WHAT CHANGES IS ONE MOMENT. The session's `proved_at` becomes now — the moment every
191
+ * service holds against its own freshness window. The session itself is the same session:
192
+ * same credential, same reach, and whatever was on screen is still there.
193
+ *
194
+ * IT WIDENS NOTHING. A fresh proof is what reaching for more demands; it is never the
195
+ * reaching itself.
196
+ *
197
+ * ONE ANSWER PER CEREMONY. Completed, refused or expired, a ceremony is spent, and its id
198
+ * answers `not_found` from then on — as it does from any other session. The cases are not
199
+ * told apart, so nothing is learned by trying ids.
200
+ *
201
+ * A REFUSED ANSWER SAYS ONLY THAT IT WAS REFUSED — `passkey_not_verified`. A wrong
202
+ * signature, another site, no user verification, a passkey that is not this person's: saying
203
+ * which would teach somebody what to fix. Start a new ceremony.
204
+ *
205
+ * Not gated: proving it is you is not something a company grants or withholds.
206
+ */
207
+ post: operations["completePasskeyProof"];
208
+ delete?: never;
209
+ options?: never;
210
+ head?: never;
211
+ patch?: never;
212
+ trace?: never;
213
+ };
214
+ "/device-tokens": {
215
+ parameters: {
216
+ query?: never;
217
+ header?: never;
218
+ path?: never;
219
+ cookie?: never;
220
+ };
221
+ get?: never;
222
+ put?: never;
223
+ /**
224
+ * Trade a statement a device signed for a short-lived token
225
+ * @description HOW AN ENROLLED DEVICE GETS A CREDENTIAL. At enrolment a device was issued a certificate;
226
+ * its private half never leaves the device. Here the device proves it holds that half: it
227
+ * signs a short statement and is answered a token, which it then sends as
228
+ * `Authorization: Bearer` on its own calls like any other credential.
229
+ *
230
+ * THE STATEMENT IS A STANDARD ONE (decision 0647): a signed JSON Web Token as RFC 7523
231
+ * describes a client proving itself with its own key. `iss` and `sub` are both the device
232
+ * record's id; `aud` is the full address of this operation; `exp` is when it stops being
233
+ * good; `jti` is a value used once. Its header names the certificate by its SHA-256
234
+ * thumbprint (`x5t#S256`, RFC 7515). The device never sends the certificate itself: Onplo
235
+ * issued it and finds it by that thumbprint.
236
+ *
237
+ * GOOD FOR AT MOST FIVE MINUTES, AND FOR ONE USE. `exp` may be no more than five minutes
238
+ * ahead of Onplo's clock, and a `jti` is refused the second time. So a device signs a new
239
+ * statement at the moment it sends one — after a week asleep as well — and nothing signed
240
+ * earlier is worth keeping.
241
+ *
242
+ * WHAT THE TOKEN IS. It says which device record is calling, in which account environment,
243
+ * and nothing else. It lives at most an hour. It ends AT ONCE when the device's certificate
244
+ * ends — a retired or un-enrolled device is refused on its next call, not when the hour is
245
+ * up. It signs no person in and carries no permission: what a device may call is marked in
246
+ * each contract as an operation a device makes about itself.
247
+ *
248
+ * ONE KIND OF KEY IS CERTIFIED: an elliptic-curve key on the P-256 curve (decision 0963). So a
249
+ * statement is signed ES256, and one whose header names any other `alg` is not verified. A
250
+ * Mac's Secure Enclave and a TPM both make such a key, and it never leaves them.
251
+ *
252
+ * A CERTIFICATE IS VALID FOR 90 DAYS, AND IT IS RENEWED HERE. Every token answers when the
253
+ * certificate that signed its statement stops being valid. When 30 days or fewer are left,
254
+ * the device makes a NEW key pair and sends a certificate request for it beside its next
255
+ * statement; the answer carries the new certificate as well as the token. The old certificate
256
+ * stays in force until the first statement the new key signs is traded, and ends then. A
257
+ * device that lost that answer sends the same request again and is answered the same
258
+ * certificate. A device whose certificate ran out can no longer prove itself here: it is
259
+ * enrolled again.
260
+ *
261
+ * TWO REFUSALS, AND ONLY ONE SAYS WHY. `statement_not_verified` covers everything about WHO
262
+ * signed it — a certificate Onplo does not know, one that has ended, a signature that does
263
+ * not match, another audience, a `jti` already used. They are never told apart, so nothing
264
+ * is learned by trying, and a device cannot learn here that it was un-enrolled.
265
+ * `statement_outside_its_time` is told apart on purpose: a device with a wrong clock can do
266
+ * nothing with "not verified", and every answer's `Date` header carries Onplo's own time to
267
+ * set a statement by.
268
+ *
269
+ * A RENEWAL'S REFUSALS ARE TOLD APART, because the statement is judged first and has already
270
+ * proved the device by then. `certificate_request_unreadable`: no PKCS #10 request can be
271
+ * read from it. `certificate_request_not_signed_by_its_key`: its signature was not made by
272
+ * the key inside it. `key_not_certified`: the key is not a P-256 one. `key_ended`: a
273
+ * certificate for that key was ended, and such a key is never certified again.
274
+ * `key_already_certified`: the key signed this statement, or another device's certificate
275
+ * holds it.
276
+ *
277
+ * NO 401, NO 403 AND NO 503: it never asks who is calling, because the statement is the
278
+ * whole of the answer to that, so it refuses nobody for a credential and the service that
279
+ * would judge one is never asked. An `Authorization` header sent anyway is not read.
280
+ *
281
+ * A `POST` because every call mints a new token.
282
+ */
283
+ post: operations["tradeDeviceStatement"];
284
+ delete?: never;
285
+ options?: never;
286
+ head?: never;
287
+ patch?: never;
288
+ trace?: never;
289
+ };
290
+ "/environment-switches": {
291
+ parameters: {
292
+ query?: never;
293
+ header?: never;
294
+ path?: never;
295
+ cookie?: never;
296
+ };
297
+ get?: never;
298
+ put?: never;
299
+ /**
300
+ * Open a session in another environment you are a member of
301
+ * @description HOW A SIGNED-IN PERSON STEPS INTO ANOTHER COMPANY. A session acts in one environment, so a
302
+ * switch opens a NEW session in the one named, and answers its token once. The session that
303
+ * asked is not changed and not ended: switching back uses it while it lasts.
304
+ *
305
+ * ONLY A MEMBER, NOW. The person must be a member of that environment at the moment of
306
+ * asking, and not suspended there. An environment that does not exist and one the person is
307
+ * not a member of are refused alike, `not_a_member`, so nothing is learned by trying ids.
308
+ *
309
+ * A PASSKEY TOUCH FIRST, WHEN ONE IS NEEDED — `passkey_proof_needed`. Reaching an environment
310
+ * the sign-in left off needs a touch made in the last five minutes; an environment it chose
311
+ * needs only what the company's entering gate asks. Prove it is you on the session you have,
312
+ * with `startPasskeyProof` and `completePasskeyProof`, and send the switch again. Signing in
313
+ * does not record its chosen environments yet, so today every switch needs the touch.
314
+ *
315
+ * THE NEW SESSION carries the moment of the touch that opened it as its `proved_at`, and it
316
+ * ends no later than the session that asked.
317
+ *
318
+ * `503 authorization_unavailable` also when the company's entering gate could not be read:
319
+ * whether the switch may proceed is unknown, so it is refused, never let through.
320
+ *
321
+ * A `POST` because every call opens a new session.
322
+ *
323
+ * Not gated: stepping into a company you belong to is not something a role grants.
324
+ */
325
+ post: operations["switchEnvironment"];
326
+ delete?: never;
327
+ options?: never;
328
+ head?: never;
329
+ patch?: never;
330
+ trace?: never;
331
+ };
332
+ "/sign-ins": {
333
+ parameters: {
334
+ query?: never;
335
+ header?: never;
336
+ path?: never;
337
+ cookie?: never;
338
+ };
339
+ get?: never;
340
+ put?: never;
341
+ /**
342
+ * Ask for the question a passkey answers to sign in
343
+ * @description THE FIRST OF THREE CALLS THAT SIGN A PERSON IN. Onplo mints a sign-in and answers
344
+ * `request_options`: hand it to the browser's passkey API untouched, and the device shows the
345
+ * Onplo accounts it holds a passkey for. The person picks one and is verified by fingerprint,
346
+ * face or PIN.
347
+ *
348
+ * NOBODY IS NAMED, AND NO KEY IS LISTED. The call takes no body, so it cannot be asked about
349
+ * a username, and `allowCredentials` is always empty: every sign-in is put the same question,
350
+ * whoever is asking and whether or not an account exists (decision 0529). Nothing is learned
351
+ * by trying. A passkey and a security key alike are made to be found this way when they are
352
+ * registered (`startPasskeyRegistration`).
353
+ *
354
+ * A PERSON WHO TYPED A USERNAME sends it with the answer, and an answer from another
355
+ * person's passkey is refused exactly as a wrong passkey is.
356
+ *
357
+ * SHORT-LIVED AND SINGLE USE. A person who walks away starts again; nothing has to be
358
+ * cancelled.
359
+ *
360
+ * A `POST` because every call mints a new sign-in.
361
+ */
362
+ post: operations["startSignIn"];
363
+ delete?: never;
364
+ options?: never;
365
+ head?: never;
366
+ patch?: never;
367
+ trace?: never;
368
+ };
369
+ "/sign-ins/{sign_in_id}/completion": {
370
+ parameters: {
371
+ query?: never;
372
+ header?: never;
373
+ path: {
374
+ sign_in_id: components["parameters"]["SignInId"];
375
+ };
376
+ cookie?: never;
377
+ };
378
+ get?: never;
379
+ put?: never;
380
+ /**
381
+ * Bring the passkey's answer back, and see where you may go
382
+ * @description THE SECOND CALL. Send what the browser's passkey API returned, untouched. Onplo checks
383
+ * that it answers the question this sign-in asked, on this site, signed by a passkey or
384
+ * security key Onplo holds, with the person verified. Which person it is, is read from the
385
+ * answer's `userHandle`.
386
+ *
387
+ * WHAT IT ANSWERS IS A CHOICE, NOT A SESSION. The person has proved who they are and has not
388
+ * chosen where they go (Account → Sign in, step 4): the answer lists the environments they may
389
+ * choose (decision 0562) and a `sign_in_token`, good once and for minutes, that opens the
390
+ * session they choose (`openSignedInSession`). Nothing is signed in until then.
391
+ *
392
+ * WITH THE DEVICE'S OWN TOKEN, WHEN THE CLIENT HAS ONE. An enrolled device shows the token it
393
+ * traded for (`tradeDeviceStatement`) beside the answer, and the session this sign-in opens
394
+ * keeps that device (decision 0886): the passkey says who, the device says where. A token that
395
+ * is not a live device's is refused `device_token_not_verified`; send the answer again in a new
396
+ * sign-in without it, or with a fresh one.
397
+ *
398
+ * A REFUSED ANSWER SAYS ONLY THAT IT WAS REFUSED — `passkey_not_verified`. A wrong signature,
399
+ * another site, no user verification, an answer without a `userHandle`, a passkey Onplo does
400
+ * not hold, and a username that is not the passkey's are never told apart. The person sees
401
+ * "That did not work", whichever it was.
402
+ *
403
+ * ONE ANSWER PER SIGN-IN. Completed, refused or expired, a sign-in is spent, and its id
404
+ * answers `not_found` from then on. The cases are not told apart.
405
+ *
406
+ * `503` when the list of environments could not be read: nothing is answered, and the person
407
+ * starts again.
408
+ */
409
+ post: operations["completeSignIn"];
410
+ delete?: never;
411
+ options?: never;
412
+ head?: never;
413
+ patch?: never;
414
+ trace?: never;
415
+ };
416
+ "/sign-ins/{sign_in_id}/sessions": {
417
+ parameters: {
418
+ query?: never;
419
+ header?: never;
420
+ path: {
421
+ sign_in_id: components["parameters"]["SignInId"];
422
+ };
423
+ cookie?: never;
424
+ };
425
+ get?: never;
426
+ put?: never;
427
+ /**
428
+ * Open the session you chose
429
+ * @description THE THIRD CALL. Send the `sign_in_token` the completion answered, the environments the
430
+ * person chose, and the one they land in. Onplo opens ONE session, in that environment, and
431
+ * answers its token once.
432
+ *
433
+ * ONE SESSION, IN ONE ENVIRONMENT (decision 0798, rule 3). The other environments chosen are
434
+ * recorded on it: switching into one of them later (`switchEnvironment`) asks only the
435
+ * company's entering gate, while one left off asks a passkey touch first (decision 0873). So
436
+ * "Full access" is every choice sent here, and a session is still opened in only one.
437
+ *
438
+ * WHERE TO LAND is the client's to propose, from the sign-in page's rule: the Personal
439
+ * environment, or the most recently used Business one when Personal is off. It must be one of
440
+ * the environments chosen.
441
+ *
442
+ * THE SIGN-IN TOKEN IS GOOD ONCE. Used, expired, or not this sign-in's, it answers
443
+ * `not_found`, and the cases are not told apart.
444
+ *
445
+ * THE SESSION carries the moment of this sign-in's passkey touch as its `proved_at`, and the
446
+ * device shown at completion, if one was.
447
+ *
448
+ * A `POST` because it opens a session.
449
+ */
450
+ post: operations["openSignedInSession"];
451
+ delete?: never;
452
+ options?: never;
453
+ head?: never;
454
+ patch?: never;
455
+ trace?: never;
456
+ };
457
+ "/sign-ins/{sign_in_id}/authorization-codes": {
458
+ parameters: {
459
+ query?: never;
460
+ header?: never;
461
+ path: {
462
+ sign_in_id: components["parameters"]["SignInId"];
463
+ };
464
+ cookie?: never;
465
+ };
466
+ get?: never;
467
+ put?: never;
468
+ /**
469
+ * Send the person back to the app they came from, with its one code
470
+ * @description THE THIRD CALL, FOR AN APP SIGNING IN THROUGH SINGLE SIGN-ON (architecture decision record
471
+ * 037). It takes the place `openSignedInSession` has when the sign-in screen signs itself in:
472
+ * where that opens a session and hands its token to the screen, this keeps a one-time code for
473
+ * the app the person was sent from, and answers the address to send the browser to, back to
474
+ * that app with the code.
475
+ *
476
+ * SEND what `openSignedInSession` takes, the `sign_in_token`, the environments chosen and the
477
+ * one to land in, and beside them the app's request exactly as it arrived at the
478
+ * authorization endpoint. Onplo reads the request again by the same rules, because the screen
479
+ * carried it and nothing it carries is trusted.
480
+ *
481
+ * THE CODE is good once, for one minute, and only for the app it names, at the exact return
482
+ * address it names, with the PKCE verifier that matches its challenge. The app trades it for
483
+ * the session at the token endpoint. Only its hash is kept.
484
+ *
485
+ * THE SIGN-IN TOKEN IS GOOD ONCE. Used, expired, or not this sign-in's, it answers
486
+ * `not_found`, and the cases are not told apart. One sign-in token hands out one code.
487
+ *
488
+ * A `POST` because it keeps a code.
489
+ */
490
+ post: operations["grantAuthorizationCode"];
491
+ delete?: never;
492
+ options?: never;
493
+ head?: never;
494
+ patch?: never;
495
+ trace?: never;
496
+ };
497
+ "/passkey-registrations": {
498
+ parameters: {
499
+ query?: never;
500
+ header?: never;
501
+ path?: never;
502
+ cookie?: never;
503
+ };
504
+ get?: never;
505
+ put?: never;
506
+ /**
507
+ * Ask for the question a new passkey answers
508
+ * @description THE FIRST HALF OF ADDING A PASSKEY OR A SECURITY KEY, for a person already signed in
509
+ * (Account → Settings → Security → Sign-in methods). Onplo mints a registration and answers
510
+ * `creation_options`: hand it to the browser's passkey API untouched, and the device makes a
511
+ * new key for this person on `onplo.com`.
512
+ *
513
+ * A PASSKEY TOUCH FIRST: ONE MADE IN THE LAST FIVE MINUTES — `passkey_proof_needed`. A new way
514
+ * in is the most sensitive thing a session can add, so the person proves it is them on the
515
+ * session they have (`startPasskeyProof`, `completePasskeyProof`) and asks again. Right after
516
+ * signing in, that proof is already fresh.
517
+ *
518
+ * EVERY KEY IS MADE TO BE FOUND WITHOUT A USERNAME (`residentKey: required`), so signing in
519
+ * never has to list anyone's keys (`startSignIn`). The keys this person already holds are
520
+ * listed in `excludeCredentials`, so the same key is not added twice.
521
+ *
522
+ * BOUND TO THE SESSION THAT ASKED, SHORT-LIVED AND SINGLE USE, as a proof is.
523
+ *
524
+ * A `POST` because every call mints a new registration. It takes no body: everything it needs
525
+ * is the credential.
526
+ *
527
+ * GATED BY `account:sign-in-method.create`, the catalogue's act of adding a passkey or a security
528
+ * key (ring 1, a personal environment's): a key or an agent never holds it.
529
+ */
530
+ post: operations["startPasskeyRegistration"];
531
+ delete?: never;
532
+ options?: never;
533
+ head?: never;
534
+ patch?: never;
535
+ trace?: never;
536
+ };
537
+ "/passkey-registrations/{passkey_registration_id}/completion": {
538
+ parameters: {
539
+ query?: never;
540
+ header?: never;
541
+ path: {
542
+ passkey_registration_id: components["parameters"]["PasskeyRegistrationId"];
543
+ };
544
+ cookie?: never;
545
+ };
546
+ get?: never;
547
+ put?: never;
548
+ /**
549
+ * Bring the new key back, and keep it
550
+ * @description THE SECOND HALF. Send what the browser's passkey API returned, untouched, and the name the
551
+ * person gives the key. Onplo checks that it answers the question this registration asked, on
552
+ * this site, from a key that verified the person and can be found without a username, and
553
+ * keeps it as one of the person's sign-in methods.
554
+ *
555
+ * WHAT IS KEPT is the key's public half and how to reach it; its private half never leaves the
556
+ * device. The key is listed in Account → Settings → Security → Sign-in methods from then on,
557
+ * where it can be renamed, made Primary or removed.
558
+ *
559
+ * NO ATTESTATION IS ASKED FOR (`attestation: none`). Onplo does not judge which maker built a
560
+ * key, only that the person holds it and was verified by it.
561
+ *
562
+ * ONE ANSWER PER REGISTRATION. Completed, refused or expired, it is spent, and its id answers
563
+ * `not_found` from then on — as it does from any other session.
564
+ *
565
+ * A REFUSED KEY SAYS ONLY THAT IT WAS REFUSED — `passkey_not_verified`, as a proof's refusal
566
+ * does. `already_registered` is told apart: the person already holds this very key, and a
567
+ * client says so rather than "try again".
568
+ *
569
+ * A `POST` because it adds a sign-in method, which has its own id.
570
+ *
571
+ * GATED BY `account:sign-in-method.create`, the catalogue's act of adding a passkey or a security
572
+ * key (ring 1, a personal environment's): a key or an agent never holds it.
573
+ */
574
+ post: operations["completePasskeyRegistration"];
575
+ delete?: never;
576
+ options?: never;
577
+ head?: never;
578
+ patch?: never;
579
+ trace?: never;
580
+ };
581
+ "/username-checks": {
582
+ parameters: {
583
+ query?: never;
584
+ header?: never;
585
+ path?: never;
586
+ cookie?: never;
587
+ };
588
+ get?: never;
589
+ put?: never;
590
+ /**
591
+ * Ask whether a username may be chosen
592
+ * @description THE CHECK AS YOU TYPE, on the first screen of signing up (Account → Sign up, step 1). Send the
593
+ * username being typed; Onplo answers whether it may be chosen now.
594
+ *
595
+ * ONE ANSWER FOR EVERY NAME THAT MAY NOT BE CHOSEN: somebody holds it, or somebody gave it up
596
+ * less than 90 days ago, or it is reserved. Which of them is never said, because it says
597
+ * something about a person the one signing up has never met (Account's `RegisterPerson`).
598
+ *
599
+ * WHY THIS DOES NOT UNDO SIGN-IN'S RULE. Signing in never says whether a username exists
600
+ * (decision 0529). Signing up must, at the moment a name is chosen, and says only "may be chosen"
601
+ * or "may not", from the same list `RegisterPerson` judges. It is limited per address by the
602
+ * gate, as every signed-out call is.
603
+ *
604
+ * ADVICE, NEVER A HOLD. Nothing is reserved by asking: the name is judged again when the sign-up
605
+ * finishes, and another person may have taken it in between.
606
+ *
607
+ * A `POST` so the name travels in the body and never in a URL a log keeps.
608
+ *
609
+ * `503` when Account, which knows the list, could not be asked: nothing was judged, and the check
610
+ * may be tried again.
611
+ */
612
+ post: operations["checkUsername"];
613
+ delete?: never;
614
+ options?: never;
615
+ head?: never;
616
+ patch?: never;
617
+ trace?: never;
618
+ };
619
+ "/sign-ups": {
620
+ parameters: {
621
+ query?: never;
622
+ header?: never;
623
+ path?: never;
624
+ cookie?: never;
625
+ };
626
+ get?: never;
627
+ put?: never;
628
+ /**
629
+ * Start signing up, and get the question the first passkey answers
630
+ * @description THE FIRST OF TWO CALLS THAT MAKE AN ONPLO ACCOUNT (Account → Sign up). Send the username and
631
+ * the facts Account records about the person; Onplo mints a sign-up and answers
632
+ * `creation_options`: hand it to the browser's passkey API untouched, and the device makes the
633
+ * person's first passkey, or a security key does.
634
+ *
635
+ * THE USERNAME IS JUDGED HERE, as `checkUsername` judges it, and again when the sign-up finishes.
636
+ * `username_not_available` here means pick another.
637
+ *
638
+ * THE EMAIL ADDRESS IS ASKED, NOT REQUIRED, AND IS NEVER A WAY IN (decisions 0198 and 0935). When
639
+ * sent it is recorded as the person's own and mailed its confirmation link; it may be left out.
640
+ * Nothing signs in by it.
641
+ *
642
+ * AN INVITED GUEST SIGNS UP THE SAME WAY. An invitation is accepted afterwards, from the
643
+ * person's own Companies → Requests, by somebody who holds the invited address confirmed
644
+ * (Account's rule, ONPLO-1694). A sign-up carries no invitation, so a forwarded mail lets nobody
645
+ * in.
646
+ *
647
+ * SHORT-LIVED AND SINGLE USE, as a sign-in is. Nothing is recorded anywhere until the sign-up
648
+ * finishes: a person who walks away leaves no account behind.
649
+ *
650
+ * `503` when Account, which judges the username, could not be asked: no sign-up was minted, and
651
+ * the call may be tried again.
652
+ */
653
+ post: operations["startSignUp"];
654
+ delete?: never;
655
+ options?: never;
656
+ head?: never;
657
+ patch?: never;
658
+ trace?: never;
659
+ };
660
+ "/sign-ups/{sign_up_id}/completion": {
661
+ parameters: {
662
+ query?: never;
663
+ header?: never;
664
+ path: {
665
+ sign_up_id: components["parameters"]["SignUpId"];
666
+ };
667
+ cookie?: never;
668
+ };
669
+ get?: never;
670
+ put?: never;
671
+ /**
672
+ * Bring the first passkey back, and get the recovery codes and the first session
673
+ * @description THE SECOND CALL. Send what the browser's passkey API returned, untouched, and the name the
674
+ * person gives the key. Onplo checks it as `completePasskeyRegistration` checks a new key, and
675
+ * then, in one act: keeps the key, has Account record the person, their personal environment
676
+ * and its one membership (`RegisterPerson`), mints the person's recovery codes, and opens their
677
+ * first session there.
678
+ *
679
+ * THE RECOVERY CODES ARE SHOWN ONCE, HERE, AND NEVER AGAIN. They are the only way back for a
680
+ * person who loses every passkey (Account → Sign up, step 3). Onplo keeps none of them in the
681
+ * clear, so nobody can show them a second time. They are not yet counted as a way in: the
682
+ * person says they kept them, on Settings → Security (decision 0614).
683
+ *
684
+ * THE SESSION LANDS IN THE PERSONAL ENVIRONMENT, the only one the person has (step 5). Its
685
+ * `proved_at` is the moment of this passkey touch.
686
+ *
687
+ * ONE ANSWER PER SIGN-UP. Completed, refused or expired, a sign-up is spent, and its id answers
688
+ * `not_found` from then on.
689
+ *
690
+ * `username_not_available` when somebody took the name since the sign-up started: nothing was
691
+ * recorded, and the person starts again with another.
692
+ *
693
+ * `503` when Account could not be asked to record the person: the sign-up is not spent, no codes
694
+ * were minted, and the same answer may be sent again until the sign-up expires.
695
+ */
696
+ post: operations["completeSignUp"];
697
+ delete?: never;
698
+ options?: never;
699
+ head?: never;
700
+ patch?: never;
701
+ trace?: never;
702
+ };
703
+ "/recoveries": {
704
+ parameters: {
705
+ query?: never;
706
+ header?: never;
707
+ path?: never;
708
+ cookie?: never;
709
+ };
710
+ get?: never;
711
+ put?: never;
712
+ /**
713
+ * Use a recovery code, and get the question a new passkey answers
714
+ * @description THE EMERGENCY DOOR, for a person who lost every passkey (Account → Sign in → Choose another
715
+ * sign-in method → Recovery code). Send the username and one recovery code. Onplo spends the code
716
+ * and answers `creation_options` for a new passkey: using a code requires registering a new
717
+ * passkey, which becomes the Primary one (Account → Settings → Security).
718
+ *
719
+ * A REFUSED CODE SAYS ONLY THAT IT WAS REFUSED — `recovery_not_verified`. An unknown username, a
720
+ * wrong code and a code already used are never told apart, so trying says nothing about whether
721
+ * an account exists (decision 0529).
722
+ *
723
+ * A CODE IS SPENT WHEN IT IS ACCEPTED HERE, whether or not the new passkey is then made. A person
724
+ * who walks away uses another code next time. The person's other codes stay good.
725
+ *
726
+ * USING A CODE RAISES A SECURITY ALERT to the person, so somebody who used a stolen code is seen
727
+ * (Account, "Recovery codes & sessions").
728
+ *
729
+ * SHORT-LIVED AND SINGLE USE, as a sign-in is.
730
+ */
731
+ post: operations["startRecovery"];
732
+ delete?: never;
733
+ options?: never;
734
+ head?: never;
735
+ patch?: never;
736
+ trace?: never;
737
+ };
738
+ "/recoveries/{recovery_id}/completion": {
739
+ parameters: {
740
+ query?: never;
741
+ header?: never;
742
+ path: {
743
+ recovery_id: components["parameters"]["RecoveryId"];
744
+ };
745
+ cookie?: never;
746
+ };
747
+ get?: never;
748
+ put?: never;
749
+ /**
750
+ * Bring the new passkey back, and see where you may go
751
+ * @description THE SECOND CALL. Send what the browser's passkey API returned, untouched, and the name the
752
+ * person gives the key. Onplo checks it as `completePasskeyRegistration` checks a new key, keeps
753
+ * it as the person's Primary passkey, and answers as `completeSignIn` does: the environments the
754
+ * person may choose and a `sign_in_token` that opens one session. A RECOVERY IS A SIGN-IN THAT
755
+ * BEGAN WITH A CODE: open the session with `openSignedInSession`, naming this recovery's id as
756
+ * its `sign_in_id`.
757
+ *
758
+ * ONE ANSWER PER RECOVERY. Completed, refused or expired, a recovery is spent, and its id answers
759
+ * `not_found` from then on.
760
+ *
761
+ * `503` when the list of environments could not be read: the key is kept, and the person signs in
762
+ * with it.
763
+ */
764
+ post: operations["completeRecovery"];
765
+ delete?: never;
766
+ options?: never;
767
+ head?: never;
768
+ patch?: never;
769
+ trace?: never;
770
+ };
771
+ }
772
+ export type webhooks = Record<string, never>;
773
+ export interface components {
774
+ schemas: {
775
+ /**
776
+ * Format: uuid
777
+ * @description A UUID version 7 (RFC 9562) — sortable by creation, and never a counter.
778
+ * @example 019250e1-8f3c-7c21-9a4d-3a2f7b6c1d90
779
+ */
780
+ Id: string;
781
+ /**
782
+ * @description ONE CEREMONY: the question to put to a passkey, and until when an answer is taken. Single
783
+ * use, and only the session that asked can complete it.
784
+ */
785
+ PasskeyProof: {
786
+ /** @description What the completion names. */
787
+ id: components["schemas"]["Id"];
788
+ request_options: components["schemas"]["PasskeyRequestOptions"];
789
+ /** @description When the ceremony dies. Seconds to minutes; after it, start again. */
790
+ expires_at: components["schemas"]["Timestamp"];
791
+ };
792
+ /**
793
+ * @description WHAT THE BROWSER HANDS THE PASSKEY — the JSON form of the standard's request options, in the
794
+ * standard's own field names (see the note at the top of this document). Pass it to the
795
+ * browser's passkey API untouched.
796
+ */
797
+ PasskeyRequestOptions: {
798
+ /** @description Random bytes minted for this ceremony. The passkey signs them, so an old answer cannot be replayed. */
799
+ challenge: string;
800
+ /** @description A hint, in milliseconds, for how long the browser waits for the person. `expires_at` is what binds. */
801
+ timeout?: number;
802
+ /**
803
+ * @description The site the passkey belongs to, always `onplo.com` (decision 0728): the registrable
804
+ * domain and never the host the product is served from, so a passkey made once answers on every
805
+ * Onplo site and none outside them. It never changes, because a new one strands every passkey
806
+ * made under the old. A passkey made for another site cannot answer.
807
+ */
808
+ rpId: string;
809
+ /**
810
+ * @description The passkeys and security keys this person holds, so the browser offers theirs and no
811
+ * one else's.
812
+ */
813
+ allowCredentials: {
814
+ id: string;
815
+ /** @enum {string} */
816
+ type: "public-key";
817
+ /** @description How that key was last reached — a hint the browser uses to look in the right place. */
818
+ transports?: string[];
819
+ }[];
820
+ /**
821
+ * @description Always `required`. The person is verified, not merely present.
822
+ * @enum {string}
823
+ */
824
+ userVerification: "required";
825
+ };
826
+ PasskeyProofCompletion: {
827
+ answer: components["schemas"]["PasskeyAnswer"];
828
+ };
829
+ /**
830
+ * @description WHAT THE BROWSER'S PASSKEY API RETURNED — the JSON form of the standard's authentication
831
+ * response, in the standard's own field names. Send it untouched.
832
+ */
833
+ PasskeyAnswer: {
834
+ /** @description Which passkey or security key answered. */
835
+ id: string;
836
+ /** @description The same, as the standard repeats it. */
837
+ rawId: string;
838
+ /** @enum {string} */
839
+ type: "public-key";
840
+ /**
841
+ * @description Whether the key is part of this device or was reached from another.
842
+ * @enum {string}
843
+ */
844
+ authenticatorAttachment?: "platform" | "cross-platform";
845
+ response: {
846
+ /**
847
+ * @description What the browser says it asked — the challenge and the page it asked from. The page's
848
+ * origin must be one identity lists, `https://app.onplo.com` at launch, or the answer is
849
+ * refused, whatever the passkey signed: any page under `onplo.com` may ask a passkey, and the
850
+ * list is what keeps a page that is not ours from completing a sign-in (decision 0728).
851
+ */
852
+ clientDataJSON: string;
853
+ /** @description What the key says about itself — the site it is for, and that the person was verified. */
854
+ authenticatorData: string;
855
+ signature: string;
856
+ /** @description Whose passkey this is, as the key recorded it. Left out by a key that does not keep one. */
857
+ userHandle?: string;
858
+ };
859
+ /**
860
+ * @description Always present in the standard's form, and always empty here: this API asks for no
861
+ * extension, so an answer carrying one is not an answer to its question.
862
+ */
863
+ clientExtensionResults: Record<string, never>;
864
+ };
865
+ /** @description What a completed proof leaves behind. */
866
+ SessionProof: {
867
+ /**
868
+ * @description When this session last proved itself — now. The same moment every service holds
869
+ * against its own freshness window.
870
+ */
871
+ proved_at: components["schemas"]["Timestamp"];
872
+ };
873
+ /**
874
+ * @description WHAT A DEVICE SENDS AS ITS CREDENTIAL, and until when. Opaque: a device stores it, sends
875
+ * it, and never reads it.
876
+ */
877
+ DeviceToken: {
878
+ /**
879
+ * @description The credential. Sent as `Authorization: Bearer` on the device's own calls. Never a
880
+ * token a client can decode: what it means is asked of Onplo on every call.
881
+ */
882
+ token: string;
883
+ /**
884
+ * @description When the token stops being good by itself — at most an hour from now. It may stop
885
+ * sooner: it ends with the device's certificate, at once.
886
+ */
887
+ expires_at: components["schemas"]["Timestamp"];
888
+ /**
889
+ * @description When the certificate that signed this statement stops being valid by itself, 90 days
890
+ * after it was issued. With 30 days or fewer left, the device sends a renewal.
891
+ */
892
+ certificate_expires_at: components["schemas"]["Timestamp"];
893
+ /**
894
+ * @description Only on an answer to a renewal: the certificate for the new key. It comes into force
895
+ * when the first statement the new key signs is traded.
896
+ */
897
+ renewed_certificate?: components["schemas"]["RenewedDeviceCertificate"];
898
+ };
899
+ /**
900
+ * @description A DEVICE'S NEXT CERTIFICATE, and what signed it. Public, every part of it: a certificate
901
+ * authority's key never leaves a managed KMS (architecture decision record 030).
902
+ */
903
+ RenewedDeviceCertificate: {
904
+ /**
905
+ * @description The certificate, X.509 in DER, written base64url with no padding. It names the device
906
+ * record and its environment, as the one it follows did.
907
+ */
908
+ certificate: string;
909
+ /**
910
+ * @description What signed it, the certificate's own issuer first — each X.509 in DER, written
911
+ * base64url with no padding.
912
+ */
913
+ authority_certificates: string[];
914
+ /** @description When the new certificate stops being valid by itself, 90 days after it was issued. */
915
+ expires_at: components["schemas"]["Timestamp"];
916
+ };
917
+ /**
918
+ * @description THE SESSION A SWITCH OPENED. Opaque, like every token here: a client stores it, sends it,
919
+ * and never reads it.
920
+ */
921
+ SwitchedSession: {
922
+ /**
923
+ * @description The new session's credential, shown this once. Sent as `Authorization: Bearer` on every
924
+ * call made in the environment switched into.
925
+ */
926
+ token: string;
927
+ /** @description The account environment this session acts in — the one the switch named. */
928
+ environment_id: components["schemas"]["Id"];
929
+ /**
930
+ * @description When this session stops being good by itself: never later than the session that asked
931
+ * for it.
932
+ */
933
+ expires_at: components["schemas"]["Timestamp"];
934
+ /**
935
+ * @description The moment of the passkey touch that opened this session — the one the asking session
936
+ * carried.
937
+ */
938
+ proved_at: components["schemas"]["Timestamp"];
939
+ };
940
+ /** @description ONE SIGN-IN: the question to put to a passkey, and until when an answer is taken. Single use. */
941
+ SignIn: {
942
+ /** @description What the completion and the session's opening name. */
943
+ id: components["schemas"]["Id"];
944
+ /**
945
+ * @description With `allowCredentials` always empty: the person is not known yet, and the device offers
946
+ * every Onplo key it holds.
947
+ */
948
+ request_options: components["schemas"]["PasskeyRequestOptions"];
949
+ /** @description When the sign-in dies. Seconds to minutes; after it, start again. */
950
+ expires_at: components["schemas"]["Timestamp"];
951
+ };
952
+ SignInCompletion: {
953
+ answer: components["schemas"]["PasskeyAnswer"];
954
+ /**
955
+ * @description The username the person typed, when they typed one — Account's `Username`, lower case
956
+ * (decision 0561). The answer must come from that person's key, or it is refused as a
957
+ * wrong passkey is. Left out when the person picked their account on the device.
958
+ */
959
+ username?: string;
960
+ /**
961
+ * @description The token this device traded for (`DeviceToken.token`), when the client runs on an
962
+ * enrolled device. It proves which device the person is on, never who they are (decisions
963
+ * 0644 and 0886).
964
+ */
965
+ device_token?: string;
966
+ };
967
+ /**
968
+ * @description WHERE A PERSON WHO HAS JUST PROVED THEMSELVES MAY GO, and the token that opens one session.
969
+ * The list is the person's own (decision 0562): which environments they belong to, each one's
970
+ * kind and name, and their state there — never anything inside one.
971
+ */
972
+ SignInChoices: {
973
+ /**
974
+ * @description Good once, and until `expires_at`, for opening this sign-in's session
975
+ * (`openSignedInSession`). It is not a session and opens nothing else.
976
+ */
977
+ sign_in_token: string;
978
+ /** @description When the sign-in token dies. Minutes; after it, sign in again. */
979
+ expires_at: components["schemas"]["Timestamp"];
980
+ /** @description The moment of the passkey touch — the `proved_at` the session will carry. */
981
+ proved_at: components["schemas"]["Timestamp"];
982
+ /**
983
+ * @description EVERY ENVIRONMENT THE PERSON MAY CHOOSE, WHOLE — never a page, because the chooser shows
984
+ * them all on one screen with one "Full access" toggle. Personal first, then Business, then
985
+ * the Onplo team, and oldest first within a kind. A membership the person left is never
986
+ * here; a suspended one is, so the chooser can say access is paused.
987
+ */
988
+ choices: components["schemas"]["SignInChoice"][];
989
+ };
990
+ SignInChoice: {
991
+ /** @description The account environment. */
992
+ environment_id: components["schemas"]["Id"];
993
+ /**
994
+ * @description Which kind of environment it is.
995
+ * @enum {string}
996
+ */
997
+ kind: "personal" | "business" | "onplo";
998
+ /**
999
+ * @description A company's name. Absent exactly when `kind` is `personal`: the client names a personal
1000
+ * environment from its kind, in the reader's language.
1001
+ */
1002
+ name?: string;
1003
+ /**
1004
+ * @description The person's state there. A suspended membership can be seen and not landed in.
1005
+ * @enum {string}
1006
+ */
1007
+ membership_state: "active" | "suspended";
1008
+ };
1009
+ SignedInSessionOpening: {
1010
+ /** @description The token the completion answered. */
1011
+ sign_in_token: string;
1012
+ /**
1013
+ * @description THE ENVIRONMENTS THE PERSON CHOSE for this sign-in, each one of the completion's
1014
+ * choices, each named once. "Full access" sends them all. Switching into one of these later asks no passkey
1015
+ * touch; one left out does.
1016
+ */
1017
+ environment_ids: components["schemas"]["Id"][];
1018
+ /** @description The one of them the session acts in. */
1019
+ land_in: components["schemas"]["Id"];
1020
+ };
1021
+ AuthorizationCodeGrant: {
1022
+ /** @description The token the completion answered. */
1023
+ sign_in_token: string;
1024
+ /** @description The environments the person chose, each one of the completion's choices, each named once. */
1025
+ environment_ids: components["schemas"]["Id"][];
1026
+ /** @description The one of them the app's session will act in. */
1027
+ land_in: components["schemas"]["Id"];
1028
+ authorization_request: components["schemas"]["AuthorizationRequest"];
1029
+ };
1030
+ /**
1031
+ * @description THE APP'S REQUEST, field for field as it arrived at the authorization endpoint (RFC 6749
1032
+ * §4.1.1, RFC 7636 §4.3, OpenID Connect Core §3.1.2.1).
1033
+ */
1034
+ AuthorizationRequest: {
1035
+ /** @description The app, by the id Onplo lists it under. */
1036
+ client_id: string;
1037
+ /** @description Where the app is sent back to, exactly as it registered it. */
1038
+ redirect_uri: string;
1039
+ /**
1040
+ * @description Always `code`, the authorization code.
1041
+ * @enum {string}
1042
+ */
1043
+ response_type: "code";
1044
+ /** @description The scopes asked, space-separated. `openid` is always one of them. */
1045
+ scope: string;
1046
+ /** @description The app's own value, sent back to it beside the code. */
1047
+ state: string;
1048
+ /** @description The PKCE challenge, the SHA-256 of the app's verifier in base64url. */
1049
+ code_challenge: string;
1050
+ /**
1051
+ * @description Always `S256`.
1052
+ * @enum {string}
1053
+ */
1054
+ code_challenge_method: "S256";
1055
+ /** @description The app's value the ID token echoes, when it sent one. */
1056
+ nonce?: string;
1057
+ };
1058
+ AuthorizationCodeGranted: {
1059
+ /**
1060
+ * Format: uri
1061
+ * @description The app's return address with `code` and `state` added. The screen sends the browser
1062
+ * there at once; the code in it is good for one minute.
1063
+ */
1064
+ redirect_to: string;
1065
+ };
1066
+ /**
1067
+ * @description THE SESSION A SIGN-IN OPENED. Opaque, like every token here: a client stores it, sends it,
1068
+ * and never reads it.
1069
+ */
1070
+ SignedInSession: {
1071
+ /**
1072
+ * @description The session's credential, shown this once. Sent as `Authorization: Bearer` on every call
1073
+ * made in the environment it acts in.
1074
+ */
1075
+ token: string;
1076
+ /** @description The account environment this session acts in — `land_in`. */
1077
+ environment_id: components["schemas"]["Id"];
1078
+ /** @description When this session stops being good by itself. */
1079
+ expires_at: components["schemas"]["Timestamp"];
1080
+ /** @description The moment of the passkey touch that signed the person in. */
1081
+ proved_at: components["schemas"]["Timestamp"];
1082
+ };
1083
+ /**
1084
+ * @description ONE REGISTRATION: the question a new key answers, and until when an answer is taken. Single
1085
+ * use, and only the session that asked can complete it.
1086
+ */
1087
+ PasskeyRegistration: {
1088
+ /** @description What the completion names. */
1089
+ id: components["schemas"]["Id"];
1090
+ creation_options: components["schemas"]["PasskeyCreationOptions"];
1091
+ /** @description When the registration dies. Seconds to minutes; after it, start again. */
1092
+ expires_at: components["schemas"]["Timestamp"];
1093
+ };
1094
+ /**
1095
+ * @description WHAT THE BROWSER HANDS THE DEVICE TO MAKE A NEW KEY — the JSON form of the standard's
1096
+ * creation options, in the standard's own field names (see the note at the top of this
1097
+ * document). Pass it to the browser's passkey API untouched.
1098
+ */
1099
+ PasskeyCreationOptions: {
1100
+ rp: {
1101
+ /** @description Always `onplo.com` (decision 0728), as `PasskeyRequestOptions.rpId` is. */
1102
+ id: string;
1103
+ /** @description What the device shows the key as being for — `Onplo`. */
1104
+ name: string;
1105
+ };
1106
+ user: {
1107
+ /**
1108
+ * @description The person, as the key records them and hands back as `userHandle` at every sign-in.
1109
+ * Opaque bytes that name nobody outside Onplo.
1110
+ */
1111
+ id: string;
1112
+ /** @description The person's username, so the device can tell their keys apart. */
1113
+ name: string;
1114
+ /** @description The person's name, as the device shows it. */
1115
+ displayName: string;
1116
+ };
1117
+ /** @description Random bytes minted for this registration. The new key signs them. */
1118
+ challenge: string;
1119
+ /** @description The signature kinds Onplo accepts, most preferred first. */
1120
+ pubKeyCredParams: {
1121
+ /** @enum {string} */
1122
+ type: "public-key";
1123
+ /** @description A COSE algorithm number — `-8` EdDSA, `-7` ES256, `-257` RS256. */
1124
+ alg: number;
1125
+ }[];
1126
+ /** @description A hint, in milliseconds, for how long the browser waits for the person. `expires_at` is what binds. */
1127
+ timeout?: number;
1128
+ /** @description The keys this person already holds, so the device does not add one twice. */
1129
+ excludeCredentials: {
1130
+ id: string;
1131
+ /** @enum {string} */
1132
+ type: "public-key";
1133
+ transports?: string[];
1134
+ }[];
1135
+ authenticatorSelection: {
1136
+ /**
1137
+ * @description Always `required`: the key keeps who it is for, so signing in finds it without a
1138
+ * username and never lists anyone's keys.
1139
+ * @enum {string}
1140
+ */
1141
+ residentKey: "required";
1142
+ /**
1143
+ * @description Always `required`. The person is verified, not merely present.
1144
+ * @enum {string}
1145
+ */
1146
+ userVerification: "required";
1147
+ };
1148
+ /**
1149
+ * @description Always `none`. Onplo does not judge which maker built a key.
1150
+ * @enum {string}
1151
+ */
1152
+ attestation: "none";
1153
+ };
1154
+ PasskeyRegistrationCompletion: {
1155
+ attestation: components["schemas"]["PasskeyAttestation"];
1156
+ /**
1157
+ * @description What the person calls this key in their list of sign-in methods — Account's
1158
+ * `SignInMethod.name`, renamed there later.
1159
+ */
1160
+ name: string;
1161
+ };
1162
+ /**
1163
+ * @description WHAT THE BROWSER'S PASSKEY API RETURNED WHEN IT MADE THE KEY — the JSON form of the
1164
+ * standard's registration response, in the standard's own field names. Send it untouched.
1165
+ */
1166
+ PasskeyAttestation: {
1167
+ /** @description The new key's id. */
1168
+ id: string;
1169
+ /** @description The same, as the standard repeats it. */
1170
+ rawId: string;
1171
+ /** @enum {string} */
1172
+ type: "public-key";
1173
+ /**
1174
+ * @description Whether the key is part of this device or was reached from another.
1175
+ * @enum {string}
1176
+ */
1177
+ authenticatorAttachment?: "platform" | "cross-platform";
1178
+ response: {
1179
+ /**
1180
+ * @description What the browser says it asked — the challenge and the page it asked from, which must
1181
+ * be one identity lists, as for a sign-in (decision 0728).
1182
+ */
1183
+ clientDataJSON: string;
1184
+ /** @description The new key's public half and what the key says about itself. */
1185
+ attestationObject: string;
1186
+ /** @description How the key can be reached, kept so the browser looks in the right place. */
1187
+ transports?: string[];
1188
+ };
1189
+ /** @description Always empty, because this API asks for no extension. */
1190
+ clientExtensionResults: Record<string, never>;
1191
+ };
1192
+ /** @description THE KEY JUST KEPT, by the id Account's list of sign-in methods names it with. */
1193
+ AddedSignInMethod: {
1194
+ /** @description The sign-in method's id — `SignInMethod.id` in Account's list. */
1195
+ id: components["schemas"]["Id"];
1196
+ /** @description The name it was given. */
1197
+ name: string;
1198
+ /** @description When it was kept. */
1199
+ added_at: components["schemas"]["Timestamp"];
1200
+ };
1201
+ /**
1202
+ * @description A person's one identifier, lower case — Account's `Username` (decision 0561). The client lowers
1203
+ * what the person types before sending it.
1204
+ */
1205
+ Username: string;
1206
+ UsernameCheck: {
1207
+ username: components["schemas"]["Username"];
1208
+ };
1209
+ UsernameAvailability: {
1210
+ /**
1211
+ * @description Whether the name may be chosen now. False for a name somebody holds, gave up less than 90
1212
+ * days ago, or that is reserved — never which. Advice, never a hold.
1213
+ */
1214
+ available: boolean;
1215
+ };
1216
+ /**
1217
+ * @description WHAT ACCOUNT RECORDS ABOUT A NEW PERSON (`RegisterPerson`), asked on the sign-up screens and
1218
+ * held by identity until the sign-up finishes.
1219
+ *
1220
+ * A FIRST NAME, AND A LAST NAME IF THEY HAVE ONE (Account → Sign in, "How signing up works",
1221
+ * step 1; ONPLO-4080). There is no middle name, and where one name divides is never guessed.
1222
+ */
1223
+ SignUpStart: {
1224
+ username: components["schemas"]["Username"];
1225
+ /**
1226
+ * @description The person's first name, as typed, by the rule Account's `ProfileUpdate` holds: something a
1227
+ * person can see, on one line.
1228
+ */
1229
+ first_name: string;
1230
+ /** @description The person's last name, as typed, by the same rule. Left out by a person who has one name. */
1231
+ last_name?: string;
1232
+ /**
1233
+ * Format: email
1234
+ * @description The person's own address, OR LEFT OUT: it is asked, and is not required to get in (step 4,
1235
+ * decision 0935). When sent it is recorded as theirs and mailed its confirmation link, and it is
1236
+ * never a way in (decision 0198). Left out, no address is written and no mail is sent.
1237
+ */
1238
+ email_address?: string;
1239
+ /**
1240
+ * @description The language Onplo speaks to them in, a BCP 47 tag, from the browser and confirmed once. The same rule as Account's `Preferences.language`.
1241
+ * @example nl-BE
1242
+ */
1243
+ language: string;
1244
+ /** @description The zone times are shown in for them, an IANA name, from the device and confirmed once. */
1245
+ time_zone: string;
1246
+ /**
1247
+ * @description The country they are in, ISO 3166-1 alpha-2, which their working calendar counts public
1248
+ * holidays by. Left out when the client does not know it.
1249
+ */
1250
+ country?: string;
1251
+ };
1252
+ /**
1253
+ * @description ONE SIGN-UP: the question the first passkey answers, and until when an answer is taken. Single
1254
+ * use.
1255
+ */
1256
+ SignUp: {
1257
+ /** @description What the completion names. */
1258
+ id: components["schemas"]["Id"];
1259
+ /** @description With `excludeCredentials` always empty, since the person holds no key yet. */
1260
+ creation_options: components["schemas"]["PasskeyCreationOptions"];
1261
+ /** @description When the sign-up dies. Minutes; after it, start again. */
1262
+ expires_at: components["schemas"]["Timestamp"];
1263
+ };
1264
+ SignUpCompletion: {
1265
+ attestation: components["schemas"]["PasskeyAttestation"];
1266
+ /** @description What the person calls their first key in their list of sign-in methods. */
1267
+ name: string;
1268
+ };
1269
+ /**
1270
+ * @description AN ACCOUNT MADE: the recovery codes, shown this once, and the first session, in the person's
1271
+ * personal environment.
1272
+ */
1273
+ SignedUp: {
1274
+ /**
1275
+ * @description TEN ONE-TIME CODES, each good once, the only way back for a person who loses every passkey.
1276
+ * Shown here and never again: Onplo keeps none of them in the clear.
1277
+ */
1278
+ recovery_codes: string[];
1279
+ session: components["schemas"]["SignedInSession"];
1280
+ };
1281
+ RecoveryStart: {
1282
+ username: components["schemas"]["Username"];
1283
+ /**
1284
+ * @description One of the person's recovery codes, as it was shown, lower case.
1285
+ * @example abcde-12345
1286
+ */
1287
+ recovery_code: string;
1288
+ };
1289
+ /**
1290
+ * @description ONE RECOVERY: the question the new passkey answers, and until when an answer is taken. Single
1291
+ * use. Its id is the `sign_in_id` that opens the session afterwards.
1292
+ */
1293
+ Recovery: {
1294
+ /** @description What the completion, and then `openSignedInSession`, name. */
1295
+ id: components["schemas"]["Id"];
1296
+ creation_options: components["schemas"]["PasskeyCreationOptions"];
1297
+ /** @description When the recovery dies. Minutes; after it, use another code. */
1298
+ expires_at: components["schemas"]["Timestamp"];
1299
+ };
1300
+ RecoveryCompletion: {
1301
+ attestation: components["schemas"]["PasskeyAttestation"];
1302
+ /** @description What the person calls the new key. It becomes their Primary passkey. */
1303
+ name: string;
1304
+ };
1305
+ /**
1306
+ * Format: date-time
1307
+ * @description RFC 3339, always UTC, always with the offset written out.
1308
+ * @example 2026-09-13T06:06:00Z
1309
+ */
1310
+ Timestamp: string;
1311
+ /**
1312
+ * @description RFC 9457. Served as `application/problem+json`, on every failure, from every operation —
1313
+ * one shape to handle rather than one per endpoint. CLOSED, like every other object here:
1314
+ * the server validates its own answers against this schema, and an open object is the one
1315
+ * shape that check cannot see through.
1316
+ */
1317
+ Problem: {
1318
+ /**
1319
+ * Format: uri
1320
+ * @description A stable URI naming the kind of problem. Dereferenceable; documentation lives there.
1321
+ * @example https://onplo.com/problems/passkey_not_verified
1322
+ */
1323
+ type: string;
1324
+ /** @example The passkey did not verify */
1325
+ title: string;
1326
+ status: number;
1327
+ /** @description What went wrong THIS time, in a sentence a person could be shown. */
1328
+ detail?: string;
1329
+ /**
1330
+ * Format: uri-reference
1331
+ * @description The request path this happened on.
1332
+ */
1333
+ instance?: string;
1334
+ /**
1335
+ * @description The machine name, stable across wording changes. `type` is the URI a human opens; this
1336
+ * is what a client branches on.
1337
+ * @example passkey_not_verified
1338
+ */
1339
+ code: string;
1340
+ /**
1341
+ * Format: uuid
1342
+ * @description Quote it in a support request and the exact call can be found. It is also on the
1343
+ * `Onplo-Request-Id` response header of every response, failed or not.
1344
+ */
1345
+ request_id: string;
1346
+ /** @description One entry per field that is wrong. REQUIRED on a 422. */
1347
+ errors?: {
1348
+ /**
1349
+ * @description A JSON Pointer (RFC 6901) at the wrong value, rooted at the REQUEST: `/body/...`
1350
+ * for a body field, `/path/...` for a path segment.
1351
+ * @example /body/answer/response/signature
1352
+ */
1353
+ pointer: string;
1354
+ code: string;
1355
+ detail: string;
1356
+ }[];
1357
+ };
1358
+ /**
1359
+ * @description The trade, as RFC 6749 §4.1.3 names its fields. NONE IS REQUIRED HERE, so a field missing is
1360
+ * answered as OAuth answers it, `invalid_request`, and never as a 422.
1361
+ */
1362
+ TokenRequest: {
1363
+ /** @description Always `authorization_code`. */
1364
+ grant_type?: string;
1365
+ /** @description The code `grantAuthorizationCode` sent the app back with. */
1366
+ code?: string;
1367
+ /** @description The return address the code was sent to, exactly as the app asked with it. */
1368
+ redirect_uri?: string;
1369
+ /** @description The app, by the id Onplo lists it under. */
1370
+ client_id?: string;
1371
+ /** @description The PKCE verifier whose SHA-256 the app sent as its challenge. */
1372
+ code_verifier?: string;
1373
+ };
1374
+ /** @description The answer, under the names RFC 6749 §5.1 and OpenID Connect Core §3.1.3.3 give it. */
1375
+ Tokens: {
1376
+ /** @description The person's session. Sent as a bearer to every Onplo service. */
1377
+ access_token: string;
1378
+ /**
1379
+ * @description Always `Bearer`.
1380
+ * @enum {string}
1381
+ */
1382
+ token_type: "Bearer";
1383
+ /** @description The seconds until the session ends, twelve hours from the touch at most. */
1384
+ expires_in: number;
1385
+ /** @description Who signed in, signed ES256, for the app alone. Never a bearer. */
1386
+ id_token: string;
1387
+ /** @description The scopes granted, space-separated, as the app asked them. */
1388
+ scope: string;
1389
+ };
1390
+ /** @description OAuth's refusal (RFC 6749 §5.2), so any OAuth client reads it. */
1391
+ TokenRefusal: {
1392
+ /**
1393
+ * @description `invalid_request`: a field is missing. `unsupported_grant_type`: the grant is not
1394
+ * `authorization_code`. `invalid_grant`: the code is unknown, expired, spent or shown by
1395
+ * another app.
1396
+ * @enum {string}
1397
+ */
1398
+ error: "invalid_request" | "unsupported_grant_type" | "invalid_grant";
1399
+ };
1400
+ /**
1401
+ * @description A 400 at the token endpoint: OAuth's refusal, or the server's `malformed_request` for a form it
1402
+ * could not read (a key sent twice). The two never overlap: a refusal carries `error` alone.
1403
+ */
1404
+ TokenRefusedOrUnread: components["schemas"]["TokenRefusal"] | components["schemas"]["TokenFormUnread"];
1405
+ /** @description The server's Problem for a token form it could not read, such as a key sent twice. */
1406
+ TokenFormUnread: components["schemas"]["Problem"] & {
1407
+ /** @enum {unknown} */
1408
+ code?: "malformed_request";
1409
+ };
1410
+ /**
1411
+ * @description The provider's metadata, under the names OpenID Connect Discovery 1.0 gives them, so any
1412
+ * standard client reads it unchanged. Every list holds only what identity supports.
1413
+ */
1414
+ ProviderDiscovery: {
1415
+ /**
1416
+ * Format: uri
1417
+ * @description The provider's address. Every ID token's `iss` is exactly this.
1418
+ */
1419
+ issuer: string;
1420
+ /**
1421
+ * Format: uri
1422
+ * @description Where a person is sent to sign in.
1423
+ */
1424
+ authorization_endpoint: string;
1425
+ /**
1426
+ * Format: uri
1427
+ * @description Where the answer is traded for tokens.
1428
+ */
1429
+ token_endpoint: string;
1430
+ /**
1431
+ * Format: uri
1432
+ * @description Where the key set is, `readProviderKeySet`.
1433
+ */
1434
+ jwks_uri: string;
1435
+ scopes_supported: "openid"[];
1436
+ response_types_supported: "code"[];
1437
+ response_modes_supported: "query"[];
1438
+ grant_types_supported: ("authorization_code" | "refresh_token")[];
1439
+ subject_types_supported: "public"[];
1440
+ id_token_signing_alg_values_supported: "ES256"[];
1441
+ /** @description `none`: every Onplo app is a public client and holds no secret. */
1442
+ token_endpoint_auth_methods_supported: "none"[];
1443
+ code_challenge_methods_supported: "S256"[];
1444
+ claims_supported: ("iss" | "sub" | "aud" | "exp" | "iat" | "auth_time" | "amr")[];
1445
+ };
1446
+ /** @description A JSON Web Key Set (RFC 7517). */
1447
+ ProviderKeySet: {
1448
+ keys: components["schemas"]["ProviderKey"][];
1449
+ };
1450
+ /**
1451
+ * @description One public key, an elliptic-curve key on P-256 as RFC 7518 writes it. The private half is never
1452
+ * anywhere this API reaches.
1453
+ */
1454
+ ProviderKey: {
1455
+ /**
1456
+ * @description The key type.
1457
+ * @enum {string}
1458
+ */
1459
+ kty: "EC";
1460
+ /**
1461
+ * @description The curve.
1462
+ * @enum {string}
1463
+ */
1464
+ crv: "P-256";
1465
+ /** @description The point's x coordinate, 32 bytes in base64url. */
1466
+ x: string;
1467
+ /** @description The point's y coordinate, 32 bytes in base64url. */
1468
+ y: string;
1469
+ /** @description The key's id, its RFC 7638 thumbprint. An ID token's header names its key by it. */
1470
+ kid: string;
1471
+ /**
1472
+ * @description The key signs; it encrypts nothing.
1473
+ * @enum {string}
1474
+ */
1475
+ use: "sig";
1476
+ /**
1477
+ * @description The one algorithm it signs with.
1478
+ * @enum {string}
1479
+ */
1480
+ alg: "ES256";
1481
+ };
1482
+ };
1483
+ responses: {
1484
+ /**
1485
+ * @description The request could not be read, so nothing in it was looked at — `malformed_request`. Its body
1486
+ * is not valid JSON, or its address carries a broken `%`-escape. No field is named, because
1487
+ * none was read. Declared on every operation, the ones that take no body included: a body is
1488
+ * read before the server knows which operation it was sent to.
1489
+ */
1490
+ BadRequest: {
1491
+ headers: {
1492
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1493
+ [name: string]: unknown;
1494
+ };
1495
+ content: {
1496
+ "application/problem+json": components["schemas"]["Problem"] & {
1497
+ /** @enum {unknown} */
1498
+ code?: "malformed_request";
1499
+ };
1500
+ };
1501
+ };
1502
+ /**
1503
+ * @description There is no usable credential on this call — it is missing, malformed, unknown, expired, or
1504
+ * it has been REVOKED. A revoked credential fails HERE and not with 403, because it is no
1505
+ * longer a credential.
1506
+ */
1507
+ Unauthorized: {
1508
+ headers: {
1509
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1510
+ "WWW-Authenticate": components["headers"]["WwwAuthenticate"];
1511
+ [name: string]: unknown;
1512
+ };
1513
+ content: {
1514
+ "application/problem+json": components["schemas"]["Problem"] & {
1515
+ /** @enum {unknown} */
1516
+ code?: "credential_missing" | "credential_malformed" | "credential_unknown" | "credential_expired" | "credential_revoked";
1517
+ };
1518
+ };
1519
+ };
1520
+ /**
1521
+ * @description The credential is good and this call is refused anyway. `permission_missing`: the
1522
+ * credential does not carry the permission this operation needs. `ring_requires_person`: a
1523
+ * ring-1 act attempted by a raw key, refused regardless of scopes. `outside_session_scope`:
1524
+ * allowed to this subject and not within the scope this session declared at sign-in.
1525
+ *
1526
+ * ONE COMPONENT ON EVERY OPERATION, the ungated ones included, because the three reasons are
1527
+ * the same on every contract and a client writes one branch for them.
1528
+ */
1529
+ Forbidden: {
1530
+ headers: {
1531
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1532
+ [name: string]: unknown;
1533
+ };
1534
+ content: {
1535
+ "application/problem+json": components["schemas"]["Problem"] & {
1536
+ /** @enum {unknown} */
1537
+ code?: "permission_missing" | "ring_requires_person" | "outside_session_scope";
1538
+ };
1539
+ };
1540
+ };
1541
+ /**
1542
+ * @description No such ceremony, or not one this session started. The two are never told apart, and
1543
+ * neither is one that expired or was already answered: saying which would let somebody learn
1544
+ * about a ceremony by trying its id.
1545
+ */
1546
+ NotFound: {
1547
+ headers: {
1548
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1549
+ "Cache-Control": components["headers"]["CacheControl"];
1550
+ [name: string]: unknown;
1551
+ };
1552
+ content: {
1553
+ "application/problem+json": components["schemas"]["Problem"] & {
1554
+ /** @enum {unknown} */
1555
+ code?: "not_found";
1556
+ };
1557
+ };
1558
+ };
1559
+ /**
1560
+ * @description The request body is larger than this API reads — `request_too_large`. `detail` names the
1561
+ * limit in bytes. Declared on every operation, for the reason `BadRequest` gives.
1562
+ */
1563
+ ContentTooLarge: {
1564
+ headers: {
1565
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1566
+ [name: string]: unknown;
1567
+ };
1568
+ content: {
1569
+ "application/problem+json": components["schemas"]["Problem"] & {
1570
+ /** @enum {unknown} */
1571
+ code?: "request_too_large";
1572
+ };
1573
+ };
1574
+ };
1575
+ /**
1576
+ * @description The request body is in a form this API does not read — `unsupported_media_type`. This API
1577
+ * reads JSON, sent uncompressed as `application/json`, and nothing else.
1578
+ */
1579
+ UnsupportedMediaType: {
1580
+ headers: {
1581
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1582
+ [name: string]: unknown;
1583
+ };
1584
+ content: {
1585
+ "application/problem+json": components["schemas"]["Problem"] & {
1586
+ /** @enum {unknown} */
1587
+ code?: "unsupported_media_type";
1588
+ };
1589
+ };
1590
+ };
1591
+ /**
1592
+ * @description The request parsed and something in it is wrong. `errors` says what, one entry per field,
1593
+ * each with a JSON Pointer at it. ONE `code`, because the detail is in `errors`. This is
1594
+ * about the SHAPE of what was sent; an answer of the right shape that does not verify is the
1595
+ * 409 on that operation.
1596
+ */
1597
+ Unprocessable: {
1598
+ headers: {
1599
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1600
+ [name: string]: unknown;
1601
+ };
1602
+ content: {
1603
+ "application/problem+json": components["schemas"]["Problem"] & {
1604
+ /** @enum {unknown} */
1605
+ code?: "unprocessable_request";
1606
+ };
1607
+ };
1608
+ };
1609
+ /**
1610
+ * @description This door was called too often in the last minute, from this address or from everyone
1611
+ * together — `too_many_requests`. Nothing was read and no token was issued. `Retry-After`
1612
+ * says how many whole seconds to wait; the same call is then answered as usual.
1613
+ *
1614
+ * Only a door that answers with no credential is limited this way, because nobody is charged
1615
+ * for the call (decision 0732). How many calls a minute it takes is not part of this contract.
1616
+ */
1617
+ TooManyCalls: {
1618
+ headers: {
1619
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1620
+ "Retry-After": components["headers"]["RetryAfter"];
1621
+ [name: string]: unknown;
1622
+ };
1623
+ content: {
1624
+ "application/problem+json": components["schemas"]["Problem"] & {
1625
+ /** @enum {unknown} */
1626
+ code?: "too_many_requests";
1627
+ };
1628
+ };
1629
+ };
1630
+ /**
1631
+ * @description Something failed that this contract cannot describe — including an answer this API produced
1632
+ * and then refused to send, because the contract does not describe that answer either
1633
+ * (`response_not_in_contract`). Response validation stays on in production (decision 0551).
1634
+ * `request_id` is what finds the call.
1635
+ */
1636
+ ServerError: {
1637
+ headers: {
1638
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1639
+ [name: string]: unknown;
1640
+ };
1641
+ content: {
1642
+ "application/problem+json": components["schemas"]["Problem"] & {
1643
+ /** @enum {unknown} */
1644
+ code?: "internal_error" | "response_not_in_contract";
1645
+ };
1646
+ };
1647
+ };
1648
+ /**
1649
+ * @description The authorization service could not be asked, so whether this call may proceed is UNKNOWN —
1650
+ * `authorization_unavailable`. Never a 403, which would claim a refusal when all that is
1651
+ * known is that we could not ask.
1652
+ */
1653
+ ServiceUnavailable: {
1654
+ headers: {
1655
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1656
+ [name: string]: unknown;
1657
+ };
1658
+ content: {
1659
+ "application/problem+json": components["schemas"]["Problem"] & {
1660
+ /** @enum {unknown} */
1661
+ code?: "authorization_unavailable";
1662
+ };
1663
+ };
1664
+ };
1665
+ /**
1666
+ * @description **Onplo has not built this operation yet** — `not_implemented` (decision 0580). It is
1667
+ * declared here because the contract is written before the code; it is not a 404, because the
1668
+ * address is right, and not a 503, because there is nothing to retry. Treat the operation as
1669
+ * absent for now. Nothing about your credential or your request caused this.
1670
+ */
1671
+ NotImplemented: {
1672
+ headers: {
1673
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1674
+ "Cache-Control": components["headers"]["CacheControl"];
1675
+ [name: string]: unknown;
1676
+ };
1677
+ content: {
1678
+ "application/problem+json": components["schemas"]["Problem"] & {
1679
+ /** @enum {unknown} */
1680
+ code?: "not_implemented";
1681
+ };
1682
+ };
1683
+ };
1684
+ };
1685
+ parameters: {
1686
+ PasskeyProofId: components["schemas"]["Id"];
1687
+ SignInId: components["schemas"]["Id"];
1688
+ PasskeyRegistrationId: components["schemas"]["Id"];
1689
+ SignUpId: components["schemas"]["Id"];
1690
+ RecoveryId: components["schemas"]["Id"];
1691
+ };
1692
+ requestBodies: never;
1693
+ headers: {
1694
+ /**
1695
+ * @description The id of this call. Minted by the server as a UUID version 7 and **never taken from a
1696
+ * request header** (decision 0553). It rides EVERY response, and it is the same value as the
1697
+ * problem document's `request_id` when there is one. Quote it in a support request and the
1698
+ * exact call can be found.
1699
+ */
1700
+ OnploRequestId: string;
1701
+ /**
1702
+ * @description The challenge RFC 9110 §11.6.1 and RFC 6750 §3 require on a 401 under a bearer scheme
1703
+ * (decision 0569). `Bearer realm="onplo"` alone when no credential was sent; otherwise
1704
+ * `Bearer realm="onplo", error="invalid_token"`. It never carries `error_description` and never
1705
+ * `scope`. A 403 carries no challenge at all.
1706
+ */
1707
+ WwwAuthenticate: string;
1708
+ /**
1709
+ * @description `no-store`, in two places. On the statuses RFC 9110 §15.1 makes cacheable by default — a
1710
+ * 404 and a 501 (decision 0580): a cached "not built yet" would outlive the day the operation
1711
+ * is built. And on every answer that carries a credential — `tradeDeviceStatement`'s,
1712
+ * `switchEnvironment`'s, `completeSignIn`'s, `openSignedInSession`'s, `completeSignUp`'s and
1713
+ * `completeRecovery`'s — each marked `x-carries-secret` so the server sends it.
1714
+ */
1715
+ CacheControl: "no-store";
1716
+ /**
1717
+ * @description Whole seconds until a limited caller may call this door again (decision 0732). It rides a
1718
+ * 429 and no other status.
1719
+ */
1720
+ RetryAfter: number;
1721
+ /**
1722
+ * @description Where the browser is sent (decision 1027): identity's sign-in screen, or the app's registered
1723
+ * return address with an OAuth `error`. It rides a redirect and no other answer.
1724
+ */
1725
+ Location: string;
1726
+ };
1727
+ pathItems: never;
1728
+ }
1729
+ export type $defs = Record<string, never>;
1730
+ export interface operations {
1731
+ readProviderDiscovery: {
1732
+ parameters: {
1733
+ query?: never;
1734
+ header?: never;
1735
+ path?: never;
1736
+ cookie?: never;
1737
+ };
1738
+ requestBody?: never;
1739
+ responses: {
1740
+ /** @description The discovery document. */
1741
+ 200: {
1742
+ headers: {
1743
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1744
+ [name: string]: unknown;
1745
+ };
1746
+ content: {
1747
+ "application/json": components["schemas"]["ProviderDiscovery"];
1748
+ };
1749
+ };
1750
+ 400: components["responses"]["BadRequest"];
1751
+ 413: components["responses"]["ContentTooLarge"];
1752
+ 415: components["responses"]["UnsupportedMediaType"];
1753
+ 429: components["responses"]["TooManyCalls"];
1754
+ 500: components["responses"]["ServerError"];
1755
+ 501: components["responses"]["NotImplemented"];
1756
+ };
1757
+ };
1758
+ readProviderKeySet: {
1759
+ parameters: {
1760
+ query?: never;
1761
+ header?: never;
1762
+ path?: never;
1763
+ cookie?: never;
1764
+ };
1765
+ requestBody?: never;
1766
+ responses: {
1767
+ /** @description The key set. */
1768
+ 200: {
1769
+ headers: {
1770
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1771
+ [name: string]: unknown;
1772
+ };
1773
+ content: {
1774
+ "application/json": components["schemas"]["ProviderKeySet"];
1775
+ };
1776
+ };
1777
+ 400: components["responses"]["BadRequest"];
1778
+ 413: components["responses"]["ContentTooLarge"];
1779
+ 415: components["responses"]["UnsupportedMediaType"];
1780
+ 429: components["responses"]["TooManyCalls"];
1781
+ 500: components["responses"]["ServerError"];
1782
+ 501: components["responses"]["NotImplemented"];
1783
+ };
1784
+ };
1785
+ authorizeSignIn: {
1786
+ parameters: {
1787
+ query?: {
1788
+ /** @description The app, by the id Onplo lists it under. */
1789
+ client_id?: string;
1790
+ /** @description Where the app is sent back to, exactly as it registered it. */
1791
+ redirect_uri?: string;
1792
+ /** @description Always `code`. */
1793
+ response_type?: string;
1794
+ /** @description The scopes asked, space-separated. `openid` is always one of them. */
1795
+ scope?: string;
1796
+ /** @description The app's own value, sent back to it. */
1797
+ state?: string;
1798
+ /** @description The PKCE challenge, the SHA-256 of the app's verifier in base64url. */
1799
+ code_challenge?: string;
1800
+ /** @description Always `S256`. */
1801
+ code_challenge_method?: string;
1802
+ /** @description The app's value the ID token echoes. */
1803
+ nonce?: string;
1804
+ };
1805
+ header?: never;
1806
+ path?: never;
1807
+ cookie?: never;
1808
+ };
1809
+ requestBody?: never;
1810
+ responses: {
1811
+ /** @description The browser is sent to the sign-in screen, or back to the app with an error. */
1812
+ 302: {
1813
+ headers: {
1814
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1815
+ Location: components["headers"]["Location"];
1816
+ [name: string]: unknown;
1817
+ };
1818
+ content?: never;
1819
+ };
1820
+ /**
1821
+ * @description Nothing is redirected. `not_registered`: the app, or its return address, is not one Onplo
1822
+ * lists. `malformed_request`: the address could not be read.
1823
+ */
1824
+ 400: {
1825
+ headers: {
1826
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1827
+ [name: string]: unknown;
1828
+ };
1829
+ content: {
1830
+ "application/problem+json": components["schemas"]["Problem"] & {
1831
+ /** @enum {unknown} */
1832
+ code?: "malformed_request" | "not_registered";
1833
+ };
1834
+ };
1835
+ };
1836
+ 413: components["responses"]["ContentTooLarge"];
1837
+ 415: components["responses"]["UnsupportedMediaType"];
1838
+ 422: components["responses"]["Unprocessable"];
1839
+ 429: components["responses"]["TooManyCalls"];
1840
+ 500: components["responses"]["ServerError"];
1841
+ };
1842
+ };
1843
+ requestTokens: {
1844
+ parameters: {
1845
+ query?: never;
1846
+ header?: never;
1847
+ path?: never;
1848
+ cookie?: never;
1849
+ };
1850
+ requestBody: {
1851
+ content: {
1852
+ "application/x-www-form-urlencoded": components["schemas"]["TokenRequest"];
1853
+ };
1854
+ };
1855
+ responses: {
1856
+ /** @description The session and the ID token. */
1857
+ 200: {
1858
+ headers: {
1859
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1860
+ [name: string]: unknown;
1861
+ };
1862
+ content: {
1863
+ "application/json": components["schemas"]["Tokens"];
1864
+ };
1865
+ };
1866
+ /**
1867
+ * @description OAuth's refusal, its `error` saying which. Or a body the server could not read, before the
1868
+ * endpoint saw it: the `malformed_request` problem every operation declares on 400, sent as
1869
+ * `application/problem+json`. One media type is declared because a status carries one.
1870
+ */
1871
+ 400: {
1872
+ headers: {
1873
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1874
+ [name: string]: unknown;
1875
+ };
1876
+ content: {
1877
+ "application/json": components["schemas"]["TokenRefusedOrUnread"];
1878
+ };
1879
+ };
1880
+ 413: components["responses"]["ContentTooLarge"];
1881
+ 415: components["responses"]["UnsupportedMediaType"];
1882
+ 422: components["responses"]["Unprocessable"];
1883
+ 429: components["responses"]["TooManyCalls"];
1884
+ 500: components["responses"]["ServerError"];
1885
+ 501: components["responses"]["NotImplemented"];
1886
+ };
1887
+ };
1888
+ startPasskeyProof: {
1889
+ parameters: {
1890
+ query?: never;
1891
+ header?: never;
1892
+ path?: never;
1893
+ cookie?: never;
1894
+ };
1895
+ requestBody?: never;
1896
+ responses: {
1897
+ /** @description The ceremony, and the question to put to the passkey. */
1898
+ 201: {
1899
+ headers: {
1900
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1901
+ [name: string]: unknown;
1902
+ };
1903
+ content: {
1904
+ "application/json": components["schemas"]["PasskeyProof"];
1905
+ };
1906
+ };
1907
+ 400: components["responses"]["BadRequest"];
1908
+ 401: components["responses"]["Unauthorized"];
1909
+ 403: components["responses"]["Forbidden"];
1910
+ /**
1911
+ * @description Refused: the credential is not a person's signed-in session (`not_a_person_session`).
1912
+ * A key, a grant and an AI agent have no passkey to ask.
1913
+ */
1914
+ 409: {
1915
+ headers: {
1916
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1917
+ [name: string]: unknown;
1918
+ };
1919
+ content: {
1920
+ "application/problem+json": components["schemas"]["Problem"] & {
1921
+ /** @enum {unknown} */
1922
+ code?: "not_a_person_session";
1923
+ };
1924
+ };
1925
+ };
1926
+ 413: components["responses"]["ContentTooLarge"];
1927
+ 415: components["responses"]["UnsupportedMediaType"];
1928
+ 500: components["responses"]["ServerError"];
1929
+ 503: components["responses"]["ServiceUnavailable"];
1930
+ };
1931
+ };
1932
+ completePasskeyProof: {
1933
+ parameters: {
1934
+ query?: never;
1935
+ header?: never;
1936
+ path: {
1937
+ passkey_proof_id: components["parameters"]["PasskeyProofId"];
1938
+ };
1939
+ cookie?: never;
1940
+ };
1941
+ requestBody: {
1942
+ content: {
1943
+ "application/json": components["schemas"]["PasskeyProofCompletion"];
1944
+ };
1945
+ };
1946
+ responses: {
1947
+ /** @description Proved. The moment this session now carries. */
1948
+ 200: {
1949
+ headers: {
1950
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1951
+ [name: string]: unknown;
1952
+ };
1953
+ content: {
1954
+ "application/json": components["schemas"]["SessionProof"];
1955
+ };
1956
+ };
1957
+ 400: components["responses"]["BadRequest"];
1958
+ 401: components["responses"]["Unauthorized"];
1959
+ 403: components["responses"]["Forbidden"];
1960
+ 404: components["responses"]["NotFound"];
1961
+ /**
1962
+ * @description Refused: the passkey's answer did not verify (`passkey_not_verified`). The ceremony is
1963
+ * spent.
1964
+ */
1965
+ 409: {
1966
+ headers: {
1967
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
1968
+ [name: string]: unknown;
1969
+ };
1970
+ content: {
1971
+ "application/problem+json": components["schemas"]["Problem"] & {
1972
+ /** @enum {unknown} */
1973
+ code?: "passkey_not_verified";
1974
+ };
1975
+ };
1976
+ };
1977
+ 413: components["responses"]["ContentTooLarge"];
1978
+ 415: components["responses"]["UnsupportedMediaType"];
1979
+ 422: components["responses"]["Unprocessable"];
1980
+ 500: components["responses"]["ServerError"];
1981
+ 503: components["responses"]["ServiceUnavailable"];
1982
+ };
1983
+ };
1984
+ tradeDeviceStatement: {
1985
+ parameters: {
1986
+ query?: never;
1987
+ header?: never;
1988
+ path?: never;
1989
+ cookie?: never;
1990
+ };
1991
+ requestBody: {
1992
+ content: {
1993
+ "application/json": {
1994
+ /**
1995
+ * @description The signed statement, in the compact form: three base64url parts joined by
1996
+ * dots. Bounded, because an unbounded string in a body makes the body limit
1997
+ * unprovable. The example shows the SHAPE and is no statement at all: its three
1998
+ * parts are the words header, claims and signature.
1999
+ * @example aGVhZGVy.Y2xhaW1z.c2lnbmF0dXJl
2000
+ */
2001
+ statement: string;
2002
+ /**
2003
+ * @description A RENEWAL, sent only when the device wants its next certificate: a PKCS #10
2004
+ * certificate request (RFC 2986) for a NEW P-256 key, signed by that key, in DER
2005
+ * and written base64url with no padding. One for P-256 is a few hundred bytes;
2006
+ * the ceiling leaves room for the names it carries.
2007
+ */
2008
+ certificate_request?: string;
2009
+ };
2010
+ };
2011
+ };
2012
+ responses: {
2013
+ /**
2014
+ * @description The token, and when it stops being good, and when the certificate behind it does. With
2015
+ * a renewal, the new certificate too. Sent with `no-store`.
2016
+ */
2017
+ 201: {
2018
+ headers: {
2019
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2020
+ "Cache-Control": components["headers"]["CacheControl"];
2021
+ [name: string]: unknown;
2022
+ };
2023
+ content: {
2024
+ "application/json": components["schemas"]["DeviceToken"];
2025
+ };
2026
+ };
2027
+ 400: components["responses"]["BadRequest"];
2028
+ /**
2029
+ * @description Refused. `statement_not_verified`: the statement does not prove a device Onplo will
2030
+ * answer — which check failed is never said. `statement_outside_its_time`: its `exp` is
2031
+ * past, or more than five minutes ahead; set the clock by this answer's `Date` header
2032
+ * and sign a new one. The five others refuse a renewal's certificate request, and the
2033
+ * operation's description says each. No token is issued, and no certificate, either way.
2034
+ */
2035
+ 409: {
2036
+ headers: {
2037
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2038
+ [name: string]: unknown;
2039
+ };
2040
+ content: {
2041
+ "application/problem+json": components["schemas"]["Problem"] & {
2042
+ /** @enum {unknown} */
2043
+ code?: "statement_not_verified" | "statement_outside_its_time" | "certificate_request_unreadable" | "certificate_request_not_signed_by_its_key" | "key_not_certified" | "key_ended" | "key_already_certified";
2044
+ };
2045
+ };
2046
+ };
2047
+ 413: components["responses"]["ContentTooLarge"];
2048
+ 415: components["responses"]["UnsupportedMediaType"];
2049
+ 422: components["responses"]["Unprocessable"];
2050
+ 429: components["responses"]["TooManyCalls"];
2051
+ 500: components["responses"]["ServerError"];
2052
+ 501: components["responses"]["NotImplemented"];
2053
+ };
2054
+ };
2055
+ switchEnvironment: {
2056
+ parameters: {
2057
+ query?: never;
2058
+ header?: never;
2059
+ path?: never;
2060
+ cookie?: never;
2061
+ };
2062
+ requestBody: {
2063
+ content: {
2064
+ "application/json": {
2065
+ /** @description The account environment to open a session in. */
2066
+ environment_id: components["schemas"]["Id"];
2067
+ };
2068
+ };
2069
+ };
2070
+ responses: {
2071
+ /** @description The new session's token, where it acts, and until when. Sent with `no-store`. */
2072
+ 201: {
2073
+ headers: {
2074
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2075
+ "Cache-Control": components["headers"]["CacheControl"];
2076
+ [name: string]: unknown;
2077
+ };
2078
+ content: {
2079
+ "application/json": components["schemas"]["SwitchedSession"];
2080
+ };
2081
+ };
2082
+ 400: components["responses"]["BadRequest"];
2083
+ 401: components["responses"]["Unauthorized"];
2084
+ 403: components["responses"]["Forbidden"];
2085
+ /**
2086
+ * @description Refused, and no session is opened. `not_a_person_session`: the credential is not a
2087
+ * person's signed-in session. `not_a_member`: the person is not a member of that
2088
+ * environment now, or it does not exist. `membership_suspended`: they are a member and
2089
+ * suspended there. `passkey_proof_needed`: prove it is you on this session, then send the
2090
+ * switch again. `already_in_that_environment`: the session that asked already acts there.
2091
+ */
2092
+ 409: {
2093
+ headers: {
2094
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2095
+ [name: string]: unknown;
2096
+ };
2097
+ content: {
2098
+ "application/problem+json": components["schemas"]["Problem"] & {
2099
+ /** @enum {unknown} */
2100
+ code?: "not_a_person_session" | "not_a_member" | "membership_suspended" | "passkey_proof_needed" | "already_in_that_environment";
2101
+ };
2102
+ };
2103
+ };
2104
+ 413: components["responses"]["ContentTooLarge"];
2105
+ 415: components["responses"]["UnsupportedMediaType"];
2106
+ 422: components["responses"]["Unprocessable"];
2107
+ 500: components["responses"]["ServerError"];
2108
+ 503: components["responses"]["ServiceUnavailable"];
2109
+ };
2110
+ };
2111
+ startSignIn: {
2112
+ parameters: {
2113
+ query?: never;
2114
+ header?: never;
2115
+ path?: never;
2116
+ cookie?: never;
2117
+ };
2118
+ requestBody?: never;
2119
+ responses: {
2120
+ /** @description The sign-in, and the question to put to the passkey. */
2121
+ 201: {
2122
+ headers: {
2123
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2124
+ [name: string]: unknown;
2125
+ };
2126
+ content: {
2127
+ "application/json": components["schemas"]["SignIn"];
2128
+ };
2129
+ };
2130
+ 400: components["responses"]["BadRequest"];
2131
+ 413: components["responses"]["ContentTooLarge"];
2132
+ 415: components["responses"]["UnsupportedMediaType"];
2133
+ 429: components["responses"]["TooManyCalls"];
2134
+ 500: components["responses"]["ServerError"];
2135
+ };
2136
+ };
2137
+ completeSignIn: {
2138
+ parameters: {
2139
+ query?: never;
2140
+ header?: never;
2141
+ path: {
2142
+ sign_in_id: components["parameters"]["SignInId"];
2143
+ };
2144
+ cookie?: never;
2145
+ };
2146
+ requestBody: {
2147
+ content: {
2148
+ "application/json": components["schemas"]["SignInCompletion"];
2149
+ };
2150
+ };
2151
+ responses: {
2152
+ /** @description Proved. Where the person may go, and the token that opens one session. Sent with `no-store`. */
2153
+ 200: {
2154
+ headers: {
2155
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2156
+ "Cache-Control": components["headers"]["CacheControl"];
2157
+ [name: string]: unknown;
2158
+ };
2159
+ content: {
2160
+ "application/json": components["schemas"]["SignInChoices"];
2161
+ };
2162
+ };
2163
+ 400: components["responses"]["BadRequest"];
2164
+ 404: components["responses"]["NotFound"];
2165
+ /**
2166
+ * @description Refused, and the sign-in is spent. `passkey_not_verified`: the answer did not prove a
2167
+ * person Onplo will sign in, and which check failed is never said.
2168
+ * `device_token_not_verified`: the device token shown beside it is not a live device's.
2169
+ */
2170
+ 409: {
2171
+ headers: {
2172
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2173
+ [name: string]: unknown;
2174
+ };
2175
+ content: {
2176
+ "application/problem+json": components["schemas"]["Problem"] & {
2177
+ /** @enum {unknown} */
2178
+ code?: "passkey_not_verified" | "device_token_not_verified";
2179
+ };
2180
+ };
2181
+ };
2182
+ 413: components["responses"]["ContentTooLarge"];
2183
+ 415: components["responses"]["UnsupportedMediaType"];
2184
+ 422: components["responses"]["Unprocessable"];
2185
+ 429: components["responses"]["TooManyCalls"];
2186
+ 500: components["responses"]["ServerError"];
2187
+ 503: components["responses"]["ServiceUnavailable"];
2188
+ };
2189
+ };
2190
+ openSignedInSession: {
2191
+ parameters: {
2192
+ query?: never;
2193
+ header?: never;
2194
+ path: {
2195
+ sign_in_id: components["parameters"]["SignInId"];
2196
+ };
2197
+ cookie?: never;
2198
+ };
2199
+ requestBody: {
2200
+ content: {
2201
+ "application/json": components["schemas"]["SignedInSessionOpening"];
2202
+ };
2203
+ };
2204
+ responses: {
2205
+ /** @description The session's token, where it acts, and until when. Sent with `no-store`. */
2206
+ 201: {
2207
+ headers: {
2208
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2209
+ "Cache-Control": components["headers"]["CacheControl"];
2210
+ [name: string]: unknown;
2211
+ };
2212
+ content: {
2213
+ "application/json": components["schemas"]["SignedInSession"];
2214
+ };
2215
+ };
2216
+ 400: components["responses"]["BadRequest"];
2217
+ 404: components["responses"]["NotFound"];
2218
+ /**
2219
+ * @description Refused, and no session is opened; the sign-in token is still good until it expires.
2220
+ * `not_a_choice`: an environment sent is not one the completion listed, or the one to land
2221
+ * in is not among those chosen. `membership_suspended`: the person is suspended in the
2222
+ * environment they would land in; choose another.
2223
+ */
2224
+ 409: {
2225
+ headers: {
2226
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2227
+ [name: string]: unknown;
2228
+ };
2229
+ content: {
2230
+ "application/problem+json": components["schemas"]["Problem"] & {
2231
+ /** @enum {unknown} */
2232
+ code?: "not_a_choice" | "membership_suspended";
2233
+ };
2234
+ };
2235
+ };
2236
+ 413: components["responses"]["ContentTooLarge"];
2237
+ 415: components["responses"]["UnsupportedMediaType"];
2238
+ 422: components["responses"]["Unprocessable"];
2239
+ 429: components["responses"]["TooManyCalls"];
2240
+ 500: components["responses"]["ServerError"];
2241
+ };
2242
+ };
2243
+ grantAuthorizationCode: {
2244
+ parameters: {
2245
+ query?: never;
2246
+ header?: never;
2247
+ path: {
2248
+ sign_in_id: components["parameters"]["SignInId"];
2249
+ };
2250
+ cookie?: never;
2251
+ };
2252
+ requestBody: {
2253
+ content: {
2254
+ "application/json": components["schemas"]["AuthorizationCodeGrant"];
2255
+ };
2256
+ };
2257
+ responses: {
2258
+ /** @description Where to send the browser, back to the app with its code. Sent with `no-store`. */
2259
+ 201: {
2260
+ headers: {
2261
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2262
+ "Cache-Control": components["headers"]["CacheControl"];
2263
+ [name: string]: unknown;
2264
+ };
2265
+ content: {
2266
+ "application/json": components["schemas"]["AuthorizationCodeGranted"];
2267
+ };
2268
+ };
2269
+ 400: components["responses"]["BadRequest"];
2270
+ 404: components["responses"]["NotFound"];
2271
+ /**
2272
+ * @description Refused, and no code is kept; the sign-in token is still good until it expires.
2273
+ * `not_a_choice` and `membership_suspended` are what `openSignedInSession` answers.
2274
+ * `not_registered`: the app, or its return address, is not one Onplo lists. The browser is
2275
+ * never sent there. `authorization_request_refused`: the request is not one the
2276
+ * authorization endpoint takes.
2277
+ */
2278
+ 409: {
2279
+ headers: {
2280
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2281
+ [name: string]: unknown;
2282
+ };
2283
+ content: {
2284
+ "application/problem+json": components["schemas"]["Problem"] & {
2285
+ /** @enum {unknown} */
2286
+ code?: "not_a_choice" | "membership_suspended" | "not_registered" | "authorization_request_refused";
2287
+ };
2288
+ };
2289
+ };
2290
+ 413: components["responses"]["ContentTooLarge"];
2291
+ 415: components["responses"]["UnsupportedMediaType"];
2292
+ 422: components["responses"]["Unprocessable"];
2293
+ 429: components["responses"]["TooManyCalls"];
2294
+ 500: components["responses"]["ServerError"];
2295
+ };
2296
+ };
2297
+ startPasskeyRegistration: {
2298
+ parameters: {
2299
+ query?: never;
2300
+ header?: never;
2301
+ path?: never;
2302
+ cookie?: never;
2303
+ };
2304
+ requestBody?: never;
2305
+ responses: {
2306
+ /** @description The registration, and the question to put to the new key. */
2307
+ 201: {
2308
+ headers: {
2309
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2310
+ [name: string]: unknown;
2311
+ };
2312
+ content: {
2313
+ "application/json": components["schemas"]["PasskeyRegistration"];
2314
+ };
2315
+ };
2316
+ 400: components["responses"]["BadRequest"];
2317
+ 401: components["responses"]["Unauthorized"];
2318
+ 403: components["responses"]["Forbidden"];
2319
+ /**
2320
+ * @description Refused. `not_a_person_session`: the credential is not a person's signed-in session.
2321
+ * `passkey_proof_needed`: prove it is you on this session, then ask again.
2322
+ */
2323
+ 409: {
2324
+ headers: {
2325
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2326
+ [name: string]: unknown;
2327
+ };
2328
+ content: {
2329
+ "application/problem+json": components["schemas"]["Problem"] & {
2330
+ /** @enum {unknown} */
2331
+ code?: "not_a_person_session" | "passkey_proof_needed";
2332
+ };
2333
+ };
2334
+ };
2335
+ 413: components["responses"]["ContentTooLarge"];
2336
+ 415: components["responses"]["UnsupportedMediaType"];
2337
+ 500: components["responses"]["ServerError"];
2338
+ 503: components["responses"]["ServiceUnavailable"];
2339
+ };
2340
+ };
2341
+ completePasskeyRegistration: {
2342
+ parameters: {
2343
+ query?: never;
2344
+ header?: never;
2345
+ path: {
2346
+ passkey_registration_id: components["parameters"]["PasskeyRegistrationId"];
2347
+ };
2348
+ cookie?: never;
2349
+ };
2350
+ requestBody: {
2351
+ content: {
2352
+ "application/json": components["schemas"]["PasskeyRegistrationCompletion"];
2353
+ };
2354
+ };
2355
+ responses: {
2356
+ /** @description Kept. The new sign-in method, as Account lists it. */
2357
+ 201: {
2358
+ headers: {
2359
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2360
+ [name: string]: unknown;
2361
+ };
2362
+ content: {
2363
+ "application/json": components["schemas"]["AddedSignInMethod"];
2364
+ };
2365
+ };
2366
+ 400: components["responses"]["BadRequest"];
2367
+ 401: components["responses"]["Unauthorized"];
2368
+ 403: components["responses"]["Forbidden"];
2369
+ 404: components["responses"]["NotFound"];
2370
+ /**
2371
+ * @description Refused, and nothing is kept. `passkey_not_verified`: the new key did not prove what it
2372
+ * must, and which check failed is never said. `already_registered`: this person already
2373
+ * holds this key.
2374
+ */
2375
+ 409: {
2376
+ headers: {
2377
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2378
+ [name: string]: unknown;
2379
+ };
2380
+ content: {
2381
+ "application/problem+json": components["schemas"]["Problem"] & {
2382
+ /** @enum {unknown} */
2383
+ code?: "passkey_not_verified" | "already_registered";
2384
+ };
2385
+ };
2386
+ };
2387
+ 413: components["responses"]["ContentTooLarge"];
2388
+ 415: components["responses"]["UnsupportedMediaType"];
2389
+ 422: components["responses"]["Unprocessable"];
2390
+ 500: components["responses"]["ServerError"];
2391
+ 503: components["responses"]["ServiceUnavailable"];
2392
+ };
2393
+ };
2394
+ checkUsername: {
2395
+ parameters: {
2396
+ query?: never;
2397
+ header?: never;
2398
+ path?: never;
2399
+ cookie?: never;
2400
+ };
2401
+ requestBody: {
2402
+ content: {
2403
+ "application/json": components["schemas"]["UsernameCheck"];
2404
+ };
2405
+ };
2406
+ responses: {
2407
+ /** @description Whether the name may be chosen now. */
2408
+ 200: {
2409
+ headers: {
2410
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2411
+ [name: string]: unknown;
2412
+ };
2413
+ content: {
2414
+ "application/json": components["schemas"]["UsernameAvailability"];
2415
+ };
2416
+ };
2417
+ 400: components["responses"]["BadRequest"];
2418
+ 413: components["responses"]["ContentTooLarge"];
2419
+ 415: components["responses"]["UnsupportedMediaType"];
2420
+ 422: components["responses"]["Unprocessable"];
2421
+ 429: components["responses"]["TooManyCalls"];
2422
+ 500: components["responses"]["ServerError"];
2423
+ 503: components["responses"]["ServiceUnavailable"];
2424
+ };
2425
+ };
2426
+ startSignUp: {
2427
+ parameters: {
2428
+ query?: never;
2429
+ header?: never;
2430
+ path?: never;
2431
+ cookie?: never;
2432
+ };
2433
+ requestBody: {
2434
+ content: {
2435
+ "application/json": components["schemas"]["SignUpStart"];
2436
+ };
2437
+ };
2438
+ responses: {
2439
+ /** @description The sign-up, and the question to put to the first passkey. */
2440
+ 201: {
2441
+ headers: {
2442
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2443
+ [name: string]: unknown;
2444
+ };
2445
+ content: {
2446
+ "application/json": components["schemas"]["SignUp"];
2447
+ };
2448
+ };
2449
+ 400: components["responses"]["BadRequest"];
2450
+ /**
2451
+ * @description Refused. `username_not_available`: somebody holds the name, gave it up recently, or it is
2452
+ * reserved — never which.
2453
+ */
2454
+ 409: {
2455
+ headers: {
2456
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2457
+ [name: string]: unknown;
2458
+ };
2459
+ content: {
2460
+ "application/problem+json": components["schemas"]["Problem"] & {
2461
+ /** @enum {unknown} */
2462
+ code?: "username_not_available";
2463
+ };
2464
+ };
2465
+ };
2466
+ 413: components["responses"]["ContentTooLarge"];
2467
+ 415: components["responses"]["UnsupportedMediaType"];
2468
+ 422: components["responses"]["Unprocessable"];
2469
+ 429: components["responses"]["TooManyCalls"];
2470
+ 500: components["responses"]["ServerError"];
2471
+ 503: components["responses"]["ServiceUnavailable"];
2472
+ };
2473
+ };
2474
+ completeSignUp: {
2475
+ parameters: {
2476
+ query?: never;
2477
+ header?: never;
2478
+ path: {
2479
+ sign_up_id: components["parameters"]["SignUpId"];
2480
+ };
2481
+ cookie?: never;
2482
+ };
2483
+ requestBody: {
2484
+ content: {
2485
+ "application/json": components["schemas"]["SignUpCompletion"];
2486
+ };
2487
+ };
2488
+ responses: {
2489
+ /** @description Signed up. The recovery codes, shown this once, and the first session. Sent with `no-store`. */
2490
+ 201: {
2491
+ headers: {
2492
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2493
+ "Cache-Control": components["headers"]["CacheControl"];
2494
+ [name: string]: unknown;
2495
+ };
2496
+ content: {
2497
+ "application/json": components["schemas"]["SignedUp"];
2498
+ };
2499
+ };
2500
+ 400: components["responses"]["BadRequest"];
2501
+ 404: components["responses"]["NotFound"];
2502
+ /**
2503
+ * @description Refused, and the sign-up is spent. `passkey_not_verified`: the answer did not prove a new
2504
+ * key Onplo will keep, and which check failed is never said. `username_not_available`:
2505
+ * somebody took the name since the sign-up started.
2506
+ */
2507
+ 409: {
2508
+ headers: {
2509
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2510
+ [name: string]: unknown;
2511
+ };
2512
+ content: {
2513
+ "application/problem+json": components["schemas"]["Problem"] & {
2514
+ /** @enum {unknown} */
2515
+ code?: "passkey_not_verified" | "username_not_available";
2516
+ };
2517
+ };
2518
+ };
2519
+ 413: components["responses"]["ContentTooLarge"];
2520
+ 415: components["responses"]["UnsupportedMediaType"];
2521
+ 422: components["responses"]["Unprocessable"];
2522
+ 429: components["responses"]["TooManyCalls"];
2523
+ 500: components["responses"]["ServerError"];
2524
+ 503: components["responses"]["ServiceUnavailable"];
2525
+ };
2526
+ };
2527
+ startRecovery: {
2528
+ parameters: {
2529
+ query?: never;
2530
+ header?: never;
2531
+ path?: never;
2532
+ cookie?: never;
2533
+ };
2534
+ requestBody: {
2535
+ content: {
2536
+ "application/json": components["schemas"]["RecoveryStart"];
2537
+ };
2538
+ };
2539
+ responses: {
2540
+ /** @description The recovery, and the question to put to the new passkey. */
2541
+ 201: {
2542
+ headers: {
2543
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2544
+ [name: string]: unknown;
2545
+ };
2546
+ content: {
2547
+ "application/json": components["schemas"]["Recovery"];
2548
+ };
2549
+ };
2550
+ 400: components["responses"]["BadRequest"];
2551
+ /**
2552
+ * @description Refused. `recovery_not_verified`: the username and code did not prove a person, and why is
2553
+ * never said.
2554
+ */
2555
+ 409: {
2556
+ headers: {
2557
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2558
+ [name: string]: unknown;
2559
+ };
2560
+ content: {
2561
+ "application/problem+json": components["schemas"]["Problem"] & {
2562
+ /** @enum {unknown} */
2563
+ code?: "recovery_not_verified";
2564
+ };
2565
+ };
2566
+ };
2567
+ 413: components["responses"]["ContentTooLarge"];
2568
+ 415: components["responses"]["UnsupportedMediaType"];
2569
+ 422: components["responses"]["Unprocessable"];
2570
+ 429: components["responses"]["TooManyCalls"];
2571
+ 500: components["responses"]["ServerError"];
2572
+ 501: components["responses"]["NotImplemented"];
2573
+ };
2574
+ };
2575
+ completeRecovery: {
2576
+ parameters: {
2577
+ query?: never;
2578
+ header?: never;
2579
+ path: {
2580
+ recovery_id: components["parameters"]["RecoveryId"];
2581
+ };
2582
+ cookie?: never;
2583
+ };
2584
+ requestBody: {
2585
+ content: {
2586
+ "application/json": components["schemas"]["RecoveryCompletion"];
2587
+ };
2588
+ };
2589
+ responses: {
2590
+ /** @description Back in. Where the person may go, and the token that opens one session. Sent with `no-store`. */
2591
+ 200: {
2592
+ headers: {
2593
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2594
+ "Cache-Control": components["headers"]["CacheControl"];
2595
+ [name: string]: unknown;
2596
+ };
2597
+ content: {
2598
+ "application/json": components["schemas"]["SignInChoices"];
2599
+ };
2600
+ };
2601
+ 400: components["responses"]["BadRequest"];
2602
+ 404: components["responses"]["NotFound"];
2603
+ /**
2604
+ * @description Refused, and the recovery is spent. `passkey_not_verified`: the answer did not prove a new
2605
+ * key Onplo will keep. `already_registered`: that key is kept already.
2606
+ */
2607
+ 409: {
2608
+ headers: {
2609
+ "Onplo-Request-Id": components["headers"]["OnploRequestId"];
2610
+ [name: string]: unknown;
2611
+ };
2612
+ content: {
2613
+ "application/problem+json": components["schemas"]["Problem"] & {
2614
+ /** @enum {unknown} */
2615
+ code?: "passkey_not_verified" | "already_registered";
2616
+ };
2617
+ };
2618
+ };
2619
+ 413: components["responses"]["ContentTooLarge"];
2620
+ 415: components["responses"]["UnsupportedMediaType"];
2621
+ 422: components["responses"]["Unprocessable"];
2622
+ 429: components["responses"]["TooManyCalls"];
2623
+ 500: components["responses"]["ServerError"];
2624
+ 501: components["responses"]["NotImplemented"];
2625
+ 503: components["responses"]["ServiceUnavailable"];
2626
+ };
2627
+ };
2628
+ }