@openresidency/sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,12 @@
1
1
  # @openresidency/sdk
2
2
 
3
3
  Typed client for the OpenResidency API. Dependency-free, uses the global `fetch`
4
- (Node 18+ or any browser). Every method maps to an endpoint in `docs/openapi.yaml`.
4
+ (Node 18+ or any browser).
5
+
6
+ The client reaches the whole API. Its types are generated from `docs/openapi.yaml`
7
+ (`sdk/src/openapi.ts`, regenerated with `npm run sdk:generate` at the repository root), and
8
+ CI fails if the spec misses a route the server declares or if the generated file is stale.
9
+ So what the client can call is what the server serves, by construction.
5
10
 
6
11
  ## Install
7
12
 
@@ -9,12 +14,18 @@ Typed client for the OpenResidency API. Dependency-free, uses the global `fetch`
9
14
  npm install @openresidency/sdk
10
15
  ```
11
16
 
12
- ## Use
17
+ Pin the version to the server release you integrate against; `@openresidency/sdk@0.1.0` is
18
+ the client for `v0.1.0`.
19
+
20
+ ## Two ways in
21
+
22
+ **Named methods** cover what a sector service, a wallet backend or a registry console calls,
23
+ and pick the right credential for each:
13
24
 
14
25
  ```ts
15
26
  import { OpenResidencyClient } from '@openresidency/sdk';
16
27
 
17
- // Identity verification is an operator action, so the client needs a credential.
28
+ // Identity verification and issuance are operator actions, so the client needs a credential.
18
29
  const client = new OpenResidencyClient({
19
30
  baseUrl: 'https://id.katsina.gov.ng',
20
31
  operatorKey: process.env.OPERATOR_KEY, // ork_..., minted at POST /operator/keys
@@ -39,6 +50,13 @@ console.log(issued.residentId, issued.credentialJwt);
39
50
  const check = await client.verifyCredential(issued.credentialJwt!);
40
51
  console.log(check.valid, check.subject);
41
52
 
53
+ // The relationship's ORCS state, and moving it
54
+ const rel = await client.relationship(issued.residentId!);
55
+ await client.transitionRelationship(issued.residentId!, {
56
+ status: 'SUSPENDED',
57
+ reason: 'Address under review',
58
+ });
59
+
42
60
  // Consent
43
61
  await client.grantConsent({
44
62
  residentId: issued.residentId!,
@@ -49,19 +67,58 @@ await client.grantConsent({
49
67
  const consents = await client.listConsents(issued.residentId!);
50
68
  ```
51
69
 
52
- ## Admin endpoints
53
-
54
- Pass `adminKey` to reach the registry and audit endpoints:
70
+ **`request`** reaches every operation in the spec, including the ones with no named method.
71
+ The path is a string literal from the spec; its parameters, body and response are typed:
55
72
 
56
73
  ```ts
57
- const admin = new OpenResidencyClient({
58
- baseUrl: 'https://id.katsina.gov.ng',
59
- adminKey: process.env.ADMIN_API_KEY,
74
+ const credential = await client.request('get', '/residency/{residentId}/credential', {
75
+ path: { residentId: 'KT-GT1F-75WJ-6' },
76
+ });
77
+
78
+ const page = await client.request('get', '/admin/residents', {
79
+ query: { countryCode: 'NG', limit: 50 },
80
+ auth: 'operator',
60
81
  });
61
- const chain = await admin.verifyAuditChain(); // { ok: true, length: N }
62
- const page = await admin.listResidents({ countryCode: 'NG', limit: 50 });
63
82
  ```
64
83
 
84
+ `auth` is `'auto'` by default (the configured operator credential, if any), `'operator'` to
85
+ require one, `'none'` to send nothing, `{ bearer }` for a one-off token such as the
86
+ OpenID4VCI access token, or `{ headers }` for a one-off header such as `x-ussd-secret`.
87
+
88
+ ## What the named methods cover
89
+
90
+ | Area | Methods |
91
+ | --- | --- |
92
+ | Health | `live`, `ready` |
93
+ | Identity | `identityChallenge`, `verifyIdentity` |
94
+ | Residency | `countries`, `issueResidency`, `residencyStatus`, `verifyCredential`, `revokeResidency`, `eraseResidency`, `retentionSweep`, `provisionalSweep`, `reconcile` |
95
+ | Relationship and credential lifecycle (ORCS §6, §10) | `relationship`, `transitionRelationship`, `credential`, `transitionCredential`, `refusal`, `reviewRefusal` |
96
+ | Assurance (ORCS §7) | `assuranceProfiles`, `assuranceMappings`, `resolveAssurance`, `residentAssurance` |
97
+ | Consent and legal bases (ORCS §9) | `listConsents`, `grantConsent`, `revokeConsent`, `legalBases`, `legalBasis`, `deactivateLegalBasis` |
98
+ | Operator identity | `operatorLogin`, `me`, `listOperators`, `createOperator`, `disableOperator`, `listKeys`, `createKey`, `rotateKey`, `revokeKey` |
99
+ | Audit and admin | `auditLog`, `verifyAuditChain`, `listResidents`, `stats`, `statistics`, `statisticsCsv` |
100
+ | Offline | `qr`, `ussd` |
101
+ | OpenID4VCI (issuing into a wallet) | `credentialIssuerMetadata`, `oauthAuthorizationServerMetadata`, `createCredentialOffer`, `walletToken`, `walletNonce`, `walletCredential` |
102
+ | OpenID4VP (asking a wallet to present) | `createPresentationRequest`, `presentationRequest`, `submitPresentation`, `presentationResult` |
103
+ | W3C VC-API | `vcIssue`, `vcVerify`, `vpVerify` |
104
+ | Discovery and trust | `didDocument`, `didDocumentFor`, `statusList`, `oidcDiscovery` |
105
+
106
+ The OIDC login interaction (`/interaction/{uid}/...`), WebAuthn registration and the upstream
107
+ enrolment callback are browser-driven and have no named method. `request` reaches them.
108
+
109
+ ## Types
110
+
111
+ `paths` and `components` are exported from the generated contract, so a caller can name
112
+ any request or response type:
113
+
114
+ ```ts
115
+ import type { components } from '@openresidency/sdk';
116
+ type Relationship = components['schemas']['RelationshipStatus'];
117
+ ```
118
+
119
+ The hand-written interfaces the 0.1.0 methods return (`IssueResult`, `ResidencyStatus`,
120
+ `ConsentRecord`, ...) are unchanged.
121
+
65
122
  ## Errors
66
123
 
67
124
  Non-2xx responses throw `OpenResidencyError` with `status` and parsed `body`.
package/dist/index.d.ts CHANGED
@@ -2,10 +2,112 @@
2
2
  * OpenResidency Interoperability SDK.
3
3
  *
4
4
  * A small, dependency-free typed client for the OpenResidency API. Uses the global
5
- * fetch (Node 18+ or any browser). Every method maps one-to-one to an endpoint in
6
- * docs/openapi.yaml, so a sector service (Health, Tax, ...) or a partner system can
7
- * integrate without hand-writing HTTP calls.
5
+ * fetch (Node 18+ or any browser).
6
+ *
7
+ * Two layers:
8
+ *
9
+ * - `client.request(method, path, opts)` reaches EVERY operation in docs/openapi.yaml. The
10
+ * path, its parameters, the request body and the response are typed from `openapi.ts`,
11
+ * which is generated from that file (`npm run sdk:generate` at the repository root) and
12
+ * checked in CI against the controllers, so the client cannot fall behind the server.
13
+ * - Named methods (`issueResidency`, `transitionRelationship`, ...) wrap the operations a
14
+ * sector service, a wallet backend or a registry console calls, and choose the right
15
+ * credentials for each. They are thin: each is one `request` call.
16
+ *
17
+ * Browser-driven flows (the OIDC login interaction under /interaction, WebAuthn
18
+ * registration, the upstream enrolment callback) have no named method; a server-side
19
+ * client does not drive them, but `request` reaches them if one has to.
20
+ */
21
+ import type { components, paths } from './openapi.js';
22
+ export type { components, paths } from './openapi.js';
23
+ export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
24
+ type OperationLike = {
25
+ responses: unknown;
26
+ };
27
+ /** The paths that serve `M` (`PathsFor<'post'>` is every POST route). */
28
+ export type PathsFor<M extends HttpMethod> = {
29
+ [P in keyof paths]: paths[P][M] extends OperationLike ? P : never;
30
+ }[keyof paths];
31
+ /** The operation object for `M path`. */
32
+ export type Operation<M extends HttpMethod, P extends PathsFor<M>> = paths[P][M] extends OperationLike ? paths[P][M] : never;
33
+ export type PathParams<O> = O extends {
34
+ parameters: {
35
+ path: infer X;
36
+ };
37
+ } ? X : never;
38
+ export type QueryParams<O> = O extends {
39
+ parameters: {
40
+ query?: infer Q;
41
+ };
42
+ } ? Exclude<Q, undefined> : never;
43
+ export type RequestBody<O> = O extends {
44
+ requestBody: {
45
+ content: {
46
+ 'application/json': infer B;
47
+ };
48
+ };
49
+ } ? B : O extends {
50
+ requestBody?: {
51
+ content: {
52
+ 'application/json': infer B;
53
+ };
54
+ };
55
+ } ? B | undefined : never;
56
+ type SuccessCode = 200 | 201 | 202 | 204;
57
+ type ContentOf<R> = R extends {
58
+ content: infer C;
59
+ } ? C[keyof C] : R extends {
60
+ content?: never;
61
+ } ? undefined : unknown;
62
+ /** The body of a successful response, whatever its media type. `undefined` for 204. */
63
+ export type ResponseBody<O> = O extends {
64
+ responses: infer R;
65
+ } ? {
66
+ [K in keyof R & SuccessCode]: ContentOf<R[K]>;
67
+ }[keyof R & SuccessCode] : never;
68
+ /**
69
+ * Which credential a call carries.
70
+ *
71
+ * - `'auto'` (default): the operator credential from `ClientOptions` if one is set,
72
+ * otherwise nothing. Right for the named methods, wrong for nothing.
73
+ * - `'operator'`: an operator credential is required; throws before the request if none
74
+ * is configured, so a misconfigured service fails at the call site and not with a 401.
75
+ * - `'none'`: send no credential even if one is configured (a public endpoint).
76
+ * - `{ bearer }`: a bearer token for this call only, such as the OpenID4VCI access token
77
+ * at `/openid4vci/credential`.
78
+ * - `{ headers }`: arbitrary headers for this call only, such as `x-ussd-secret`.
8
79
  */
80
+ export type Auth = 'auto' | 'operator' | 'none' | {
81
+ bearer: string;
82
+ } | {
83
+ headers: Record<string, string>;
84
+ };
85
+ type PathOpt<O> = [PathParams<O>] extends [never] ? {
86
+ path?: undefined;
87
+ } : {
88
+ path: PathParams<O>;
89
+ };
90
+ type QueryOpt<O> = [QueryParams<O>] extends [never] ? {
91
+ query?: undefined;
92
+ } : {
93
+ query?: QueryParams<O>;
94
+ };
95
+ type BodyOpt<O> = [RequestBody<O>] extends [never] ? {
96
+ body?: undefined;
97
+ } : undefined extends RequestBody<O> ? {
98
+ body?: RequestBody<O>;
99
+ } : {
100
+ body: RequestBody<O>;
101
+ };
102
+ /** Options for `request`, derived from the operation: what it needs is required. */
103
+ export type RequestOptions<O> = PathOpt<O> & QueryOpt<O> & BodyOpt<O> & {
104
+ auth?: Auth;
105
+ /** Extra headers, merged after the ones the client sets. */
106
+ headers?: Record<string, string>;
107
+ /** Override the `accept` header (default `application/json`). */
108
+ accept?: string;
109
+ };
110
+ type RequestArgs<O> = {} extends RequestOptions<O> ? [opts?: RequestOptions<O>] : [opts: RequestOptions<O>];
9
111
  export type AssuranceLevel = 'none' | 'basic' | 'verified' | 'high';
10
112
  export interface ClientOptions {
11
113
  baseUrl: string;
@@ -124,6 +226,26 @@ export declare class OpenResidencyClient {
124
226
  private operatorToken?;
125
227
  private doFetch;
126
228
  constructor(opts: ClientOptions);
229
+ /**
230
+ * Call any operation in docs/openapi.yaml. Path parameters, query, body and the
231
+ * response are typed from the generated contract:
232
+ *
233
+ * ```ts
234
+ * const rel = await client.request('get', '/residency/{residentId}/relationship', {
235
+ * path: { residentId },
236
+ * });
237
+ * ```
238
+ */
239
+ request<M extends HttpMethod, P extends PathsFor<M>>(method: M, path: P, ...args: RequestArgs<Operation<M, P>>): Promise<ResponseBody<Operation<M, P>>>;
240
+ live(): Promise<{
241
+ status?: "ok";
242
+ }>;
243
+ ready(): Promise<{
244
+ status: "ok" | "unavailable";
245
+ checks: {
246
+ database?: "ok" | "failed";
247
+ };
248
+ }>;
127
249
  /** Operator action: needs the `registrar` role. */
128
250
  identityChallenge(countryCode: string, identifiers: Record<string, string>): Promise<{
129
251
  challengeRequired: boolean;
@@ -132,12 +254,12 @@ export declare class OpenResidencyClient {
132
254
  }>;
133
255
  /** Operator action: needs the `registrar` role. */
134
256
  verifyIdentity(req: IdentityVerifyRequest): Promise<IdentityVerifyResponse>;
135
- countries(): Promise<{
257
+ countries(): Promise<Array<{
136
258
  countryCode: string;
137
259
  countryName: string;
138
260
  provider: string;
139
261
  inputs: unknown[];
140
- }[]>;
262
+ }>>;
141
263
  /** Operator action: needs the `registrar` role. */
142
264
  issueResidency(req: IssueRequest): Promise<IssueResult>;
143
265
  residencyStatus(residentId: string): Promise<ResidencyStatus>;
@@ -146,6 +268,130 @@ export declare class OpenResidencyClient {
146
268
  revokeResidency(residentId: string): Promise<{
147
269
  revoked: boolean;
148
270
  }>;
271
+ /** Operator action: needs the `admin` role. Revokes first, then destroys personal data. */
272
+ eraseResidency(residentId: string, body?: RequestBody<Operation<'post', '/residency/{residentId}/erase'>>): Promise<{
273
+ status: "erased" | "already-erased" | "unknown";
274
+ residentId: string;
275
+ erasedAt?: string;
276
+ auditEventsRedacted: number;
277
+ auditChainIntact: boolean;
278
+ }>;
279
+ /** Operator action. Erases records whose retention period has ended. */
280
+ retentionSweep(body?: RequestBody<Operation<'post', '/residency/retention/sweep'>>): Promise<{
281
+ dryRun: boolean;
282
+ auditChainIntact: boolean;
283
+ results: {
284
+ countryCode: string;
285
+ dueCount: number;
286
+ due: string[];
287
+ erased: number;
288
+ skipped?: "legal-hold" | "no-policy";
289
+ }[];
290
+ }>;
291
+ /** Operator action. Expires provisional registrations that were never completed. */
292
+ provisionalSweep(body?: RequestBody<Operation<'post', '/residency/provisional/sweep'>>): Promise<{
293
+ report?: components["schemas"]["ProvisionalSweepEntry"][];
294
+ }>;
295
+ /** Operator action. Re-verifies a resident against the foundational source. */
296
+ reconcile(residentId: string, body: RequestBody<Operation<'post', '/residency/{residentId}/reconcile'>>): Promise<{
297
+ status: "confirmed" | "already-confirmed" | "unknown" | "unconfirmed" | "mismatch";
298
+ reason?: string;
299
+ provisional?: boolean;
300
+ }>;
301
+ /** The relationship's ORCS state and how it got there. */
302
+ relationship(residentId: string): Promise<{
303
+ residentId?: string;
304
+ relationship?: components["schemas"]["RelationshipAttributes"];
305
+ }>;
306
+ /** Operator action. Move the relationship to a new ORCS state. */
307
+ transitionRelationship(residentId: string, body: RequestBody<Operation<'post', '/residency/{residentId}/relationship/transition'>>): Promise<{
308
+ residentId?: string;
309
+ from?: components["schemas"]["RelationshipStatus"];
310
+ to?: components["schemas"]["RelationshipStatus"];
311
+ relationship?: components["schemas"]["RelationshipAttributes"];
312
+ }>;
313
+ /** The credential's ORCS status. */
314
+ credential(residentId: string): Promise<{
315
+ residentId?: string;
316
+ credentialStatus?: components["schemas"]["CredentialStatusRecord"];
317
+ }>;
318
+ /** Operator action. Move the credential to a new ORCS status. */
319
+ transitionCredential(residentId: string, body: RequestBody<Operation<'post', '/residency/{residentId}/credential/transition'>>): Promise<{
320
+ residentId?: string;
321
+ from?: components["schemas"]["CredentialStatus"];
322
+ to?: components["schemas"]["CredentialStatus"];
323
+ credentialStatus?: components["schemas"]["CredentialStatusRecord"];
324
+ }>;
325
+ /** Why an application was refused, and how to appeal. */
326
+ refusal(reference: string): Promise<{
327
+ reference: string;
328
+ countryCode: string;
329
+ subnationalUnit: string;
330
+ subjectRef?: string;
331
+ reason: string;
332
+ decidedBy: string;
333
+ submittedBy?: string;
334
+ appealPath: string;
335
+ humanReviewPath?: string;
336
+ reviewStatus: "none" | "requested" | "upheld" | "overturned";
337
+ reviewedBy?: string;
338
+ reviewedAt?: string;
339
+ reviewNote?: string;
340
+ refusedAt: string;
341
+ }>;
342
+ /** Operator action. Record the outcome of reviewing a refusal. */
343
+ reviewRefusal(reference: string, body: RequestBody<Operation<'post', '/residency/refusals/{reference}/review'>>): Promise<{
344
+ reference: string;
345
+ countryCode: string;
346
+ subnationalUnit: string;
347
+ subjectRef?: string;
348
+ reason: string;
349
+ decidedBy: string;
350
+ submittedBy?: string;
351
+ appealPath: string;
352
+ humanReviewPath?: string;
353
+ reviewStatus: "none" | "requested" | "upheld" | "overturned";
354
+ reviewedBy?: string;
355
+ reviewedAt?: string;
356
+ reviewNote?: string;
357
+ refusedAt: string;
358
+ }>;
359
+ assuranceProfiles(): Promise<{
360
+ id: string;
361
+ name: string;
362
+ version: string;
363
+ issuer: string;
364
+ dimensions: components["schemas"]["AssuranceDimensions"];
365
+ limitations: string[];
366
+ }[]>;
367
+ assuranceMappings(): Promise<{
368
+ providerCode: string;
369
+ assuranceValue: string;
370
+ profileId: string;
371
+ version: string;
372
+ issuer: string;
373
+ verificationMethod: string;
374
+ limitations: string[];
375
+ }[]>;
376
+ /** Resolve a source-specific assurance value to the ORCS assurance profile. */
377
+ resolveAssurance(value: string): Promise<{
378
+ id: string;
379
+ name: string;
380
+ version: string;
381
+ issuer: string;
382
+ dimensions: components["schemas"]["AssuranceDimensions"];
383
+ limitations: string[];
384
+ }>;
385
+ residentAssurance(residentId: string): Promise<{
386
+ residentId: string;
387
+ declaredValue: string;
388
+ providerCode: string;
389
+ profile: components["schemas"]["AssuranceProfile"];
390
+ mapping?: components["schemas"]["ProviderAssuranceMapping"];
391
+ dimensions: components["schemas"]["AssuranceDimensions"];
392
+ limitations: string[];
393
+ authenticationAssurance: string;
394
+ }>;
149
395
  listConsents(residentId: string): Promise<{
150
396
  residentId: string;
151
397
  consents: ConsentRecord[];
@@ -164,6 +410,69 @@ export declare class OpenResidencyClient {
164
410
  revokeConsent(id: string): Promise<{
165
411
  consent: ConsentRecord;
166
412
  }>;
413
+ legalBases(): Promise<{
414
+ legalBases: components["schemas"]["LegalBasis"][];
415
+ }>;
416
+ legalBasis(id: string): Promise<{
417
+ legalBasis: components["schemas"]["LegalBasis"];
418
+ inForce: boolean;
419
+ }>;
420
+ /** Operator action. Withdraw a legal basis; consents resting on it stop being valid. */
421
+ deactivateLegalBasis(id: string, body: RequestBody<Operation<'post', '/consent/legal-bases/{id}/deactivate'>>): Promise<{
422
+ legalBasis: components["schemas"]["LegalBasis"];
423
+ }>;
424
+ /** Local sign-in (`operatorAuth.mode: local`). The token goes in `ClientOptions.operatorToken`. */
425
+ operatorLogin(body: RequestBody<Operation<'post', '/operator/login'>>): Promise<{
426
+ accessToken?: string;
427
+ tokenType?: string;
428
+ expiresIn?: number;
429
+ operator?: {
430
+ id?: string;
431
+ displayName?: string;
432
+ roles?: components["schemas"]["OperatorRole"][];
433
+ };
434
+ } | {
435
+ mfaRequired?: true;
436
+ }>;
437
+ /** The calling operator's identity and roles. */
438
+ me(): Promise<{
439
+ id?: string;
440
+ displayName?: string;
441
+ roles?: components["schemas"]["OperatorRole"][];
442
+ via?: "oidc" | "local" | "apiKey" | "sharedKey";
443
+ }>;
444
+ listOperators(): Promise<{
445
+ operators?: components["schemas"]["OperatorSummary"][];
446
+ }>;
447
+ createOperator(body: RequestBody<Operation<'post', '/operator/operators'>>): Promise<{
448
+ id?: string;
449
+ email?: string;
450
+ roles?: components["schemas"]["OperatorRole"][];
451
+ totpSecret?: string;
452
+ totpUri?: string;
453
+ }>;
454
+ /** Disable (or with `disabled: false`, re-enable) an operator account. Needs the `admin` role. */
455
+ disableOperator(operatorId: string, disabled?: boolean): Promise<{
456
+ ok?: boolean;
457
+ }>;
458
+ listKeys(): Promise<{
459
+ keys?: components["schemas"]["OperatorKeySummary"][];
460
+ }>;
461
+ /** Mint a per-operator API key. The secret is returned once. */
462
+ createKey(body: RequestBody<Operation<'post', '/operator/keys'>>): Promise<{
463
+ id?: string;
464
+ key?: string;
465
+ expiresAt?: string;
466
+ }>;
467
+ /** Mint a replacement key; the old one keeps working for the overlap window. */
468
+ rotateKey(body: RequestBody<Operation<'post', '/operator/keys/rotate'>>): Promise<{
469
+ id?: string;
470
+ key?: string;
471
+ oldKeyRetiresAt?: string;
472
+ }>;
473
+ revokeKey(body: RequestBody<Operation<'post', '/operator/keys/revoke'>>): Promise<{
474
+ revoked?: boolean;
475
+ }>;
167
476
  listResidents(params?: {
168
477
  countryCode?: string;
169
478
  limit?: number;
@@ -185,8 +494,193 @@ export declare class OpenResidencyClient {
185
494
  length: number;
186
495
  brokenAtSeq?: number;
187
496
  }>;
188
- oidcDiscovery(): Promise<Record<string, unknown>>;
189
- private get;
190
- private post;
191
- private request;
497
+ /** Counts by country. */
498
+ stats(): Promise<{
499
+ countries: number;
500
+ residentsByCountry: {
501
+ [key: string]: number;
502
+ };
503
+ }>;
504
+ /** Aggregate, non-PII statistics (the open-data surface), as JSON. */
505
+ statistics(query?: QueryParams<Operation<'get', '/admin/statistics'>>): Promise<{
506
+ generatedAt: string;
507
+ suppressionThreshold: number;
508
+ totalResidents: number | null;
509
+ countries: number;
510
+ cells: {
511
+ countryCode: string;
512
+ subnationalUnit: string;
513
+ providerCode: string;
514
+ assuranceLevel: string;
515
+ provisional: boolean;
516
+ count: number | null;
517
+ suppressed: boolean;
518
+ }[];
519
+ }>;
520
+ /** The same report as RFC 4180 CSV. */
521
+ statisticsCsv(query?: QueryParams<Operation<'get', '/admin/statistics.csv'>>): Promise<string>;
522
+ /** Render a credential as an SVG QR for paper or low-connectivity carriage. */
523
+ qr(body: RequestBody<Operation<'post', '/offline/qr'>>): Promise<{
524
+ mode: "full" | "pointer";
525
+ svg: string;
526
+ }>;
527
+ /** The USSD aggregator webhook. `secret` is the shared USSD_GATEWAY_SECRET. */
528
+ ussd(body: RequestBody<Operation<'post', '/offline/ussd'>>, secret: string): Promise<string>;
529
+ credentialIssuerMetadata(): Promise<Record<string, never>>;
530
+ oauthAuthorizationServerMetadata(): Promise<{
531
+ issuer?: string;
532
+ token_endpoint?: string;
533
+ grant_types_supported?: "urn:ietf:params:oauth:grant-type:pre-authorized_code"[];
534
+ response_types_supported?: string[];
535
+ "pre-authorized_grant_anonymous_access_supported"?: boolean;
536
+ token_endpoint_auth_methods_supported?: "none"[];
537
+ }>;
538
+ /** Operator action. Create a credential offer for a resident's wallet to redeem. */
539
+ createCredentialOffer(body: RequestBody<Operation<'post', '/openid4vci/offer'>>): Promise<{
540
+ offerId?: string;
541
+ offerUri?: string;
542
+ offer?: {
543
+ credential_issuer?: string;
544
+ credential_configuration_ids?: string[];
545
+ grants?: {
546
+ "urn:ietf:params:oauth:grant-type:pre-authorized_code"?: {
547
+ "pre-authorized_code"?: string;
548
+ tx_code?: {
549
+ length?: number;
550
+ input_mode?: "numeric";
551
+ description?: string;
552
+ };
553
+ };
554
+ };
555
+ };
556
+ txCode?: string;
557
+ expiresAt?: string;
558
+ credentialConfigurationIds?: string[];
559
+ }>;
560
+ /** The wallet side: exchange the pre-authorized code for an access token. */
561
+ walletToken(body: RequestBody<Operation<'post', '/openid4vci/token'>>): Promise<{
562
+ access_token?: string;
563
+ token_type?: string;
564
+ expires_in?: number;
565
+ authorization_details?: {
566
+ type?: "openid_credential";
567
+ credential_configuration_id?: string;
568
+ }[];
569
+ c_nonce?: string;
570
+ c_nonce_expires_in?: number;
571
+ }>;
572
+ walletNonce(): Promise<{
573
+ c_nonce?: string;
574
+ }>;
575
+ /** The wallet side: obtain the credential with the access token from `walletToken`. */
576
+ walletCredential(accessToken: string, body: RequestBody<Operation<'post', '/openid4vci/credential'>>): Promise<{
577
+ credentials?: {
578
+ credential?: unknown;
579
+ }[];
580
+ format?: "ldp_vc" | "jwt_vc_json";
581
+ credential?: unknown;
582
+ c_nonce?: string;
583
+ c_nonce_expires_in?: number;
584
+ }>;
585
+ /** Create a presentation request for a wallet to answer. */
586
+ createPresentationRequest(body?: RequestBody<Operation<'post', '/openid4vp/request'>>): Promise<{
587
+ requestId?: string;
588
+ requestUri?: string;
589
+ expiresAt?: string;
590
+ }>;
591
+ /** The request object a wallet fetches (the `request_uri`). */
592
+ presentationRequest(id: string): Promise<string>;
593
+ /** The wallet side: submit the presentation. */
594
+ submitPresentation(id: string, body: RequestBody<Operation<'post', '/openid4vp/response/{id}'>>): Promise<{
595
+ status?: "accepted";
596
+ }>;
597
+ /** What the wallet presented, once it has. */
598
+ presentationResult(id: string): Promise<{
599
+ requestId?: string;
600
+ status?: "pending" | "fulfilled" | "failed";
601
+ reference?: string;
602
+ purpose?: string;
603
+ expiresAt?: string;
604
+ outcome?: components["schemas"]["PresentationOutcome"];
605
+ }>;
606
+ /** Operator action. Issue a credential through the VC-API issuer interface. */
607
+ vcIssue(body: RequestBody<Operation<'post', '/credentials/issue'>>): Promise<{
608
+ verifiableCredential?: components["schemas"]["DataIntegrityCredential"];
609
+ }>;
610
+ /** Verify a Verifiable Credential (JWT or Data Integrity) through the VC-API verifier interface. */
611
+ vcVerify(body: RequestBody<Operation<'post', '/credentials/verify'>>): Promise<{
612
+ checks: ("proof" | "expiration" | "credentialStatus")[];
613
+ warnings: string[];
614
+ errors: string[];
615
+ }>;
616
+ /** Verify a Verifiable Presentation through the VC-API verifier interface. */
617
+ vpVerify(body: RequestBody<Operation<'post', '/presentations/verify'>>): Promise<{
618
+ checks: ("proof" | "expiration" | "credentialStatus")[];
619
+ warnings: string[];
620
+ errors: string[];
621
+ }>;
622
+ /** This deployment's DID document (`did:web`). */
623
+ didDocument(): Promise<Record<string, never>>;
624
+ /** The DID document for one country's issuer key. */
625
+ didDocumentFor(countryCode: string): Promise<{
626
+ "@context": string[];
627
+ id: string;
628
+ verificationMethod: {
629
+ id: string;
630
+ type: "JsonWebKey2020" | "Multikey";
631
+ controller: string;
632
+ publicKeyJwk?: {
633
+ [key: string]: unknown;
634
+ };
635
+ publicKeyMultibase?: string;
636
+ }[];
637
+ assertionMethod: string[];
638
+ authentication: string[];
639
+ }>;
640
+ /** A Bitstring Status List credential, for offline revocation checks. */
641
+ statusList(file: string): Promise<{
642
+ "@context": string[];
643
+ id: string;
644
+ type: string[];
645
+ issuer: string;
646
+ validFrom: string;
647
+ credentialSubject: {
648
+ id: string;
649
+ type: "BitstringStatusList";
650
+ statusPurpose: "revocation" | "suspension";
651
+ encodedList: string;
652
+ };
653
+ proof: components["schemas"]["DataIntegrityProof"];
654
+ }>;
655
+ /** OpenID Connect discovery for the SSO provider mounted under /oidc. */
656
+ oidcDiscovery(): Promise<{
657
+ issuer?: string;
658
+ authorization_endpoint?: string;
659
+ token_endpoint?: string;
660
+ userinfo_endpoint?: string;
661
+ jwks_uri?: string;
662
+ revocation_endpoint?: string;
663
+ introspection_endpoint?: string;
664
+ end_session_endpoint?: string;
665
+ pushed_authorization_request_endpoint?: string;
666
+ scopes_supported?: string[];
667
+ claims_supported?: string[];
668
+ response_types_supported?: string[];
669
+ grant_types_supported?: string[];
670
+ response_modes_supported?: string[];
671
+ subject_types_supported?: ("public" | "pairwise")[];
672
+ id_token_signing_alg_values_supported?: string[];
673
+ token_endpoint_auth_methods_supported?: string[];
674
+ token_endpoint_auth_signing_alg_values_supported?: string[];
675
+ code_challenge_methods_supported?: "S256"[];
676
+ dpop_signing_alg_values_supported?: string[];
677
+ claims_parameter_supported?: boolean;
678
+ request_uri_parameter_supported?: boolean;
679
+ authorization_response_iss_parameter_supported?: boolean;
680
+ claim_types_supported?: "normal"[];
681
+ } & {
682
+ [key: string]: unknown;
683
+ }>;
684
+ private send;
685
+ private applyAuth;
192
686
  }