@verne-software/sdk 1.3.0 → 1.4.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/dist/index.d.mts CHANGED
@@ -1,10 +1,20 @@
1
1
  /**
2
2
  * Paginated response wrapper returned by list endpoints.
3
+ *
4
+ * Lists are cursor-paged: pass `next_cursor` back as `cursor` while `has_more` is
5
+ * `true`. `next_cursor` is `null` (or absent) on the last page.
3
6
  */
4
7
  interface Paginated<T> {
5
8
  data: T[];
6
9
  has_more: boolean;
7
- next_cursor?: string;
10
+ next_cursor?: string | null;
11
+ }
12
+ /** Query parameters shared by every cursor-paged list. */
13
+ interface ListParams {
14
+ /** Items per page, 1 to 100. Defaults to 20; the server clamps values outside the range. */
15
+ limit?: number;
16
+ /** The `next_cursor` of the previous page. Opaque: pass it back, do not parse it. */
17
+ cursor?: string;
8
18
  }
9
19
  /**
10
20
  * Per-request options that can override client defaults.
@@ -19,16 +29,24 @@ interface RequestOptions {
19
29
  }
20
30
  /**
21
31
  * Configuration for the unified Verne client.
22
- * Each key corresponds to a service; only provide keys for the services you use.
32
+ *
33
+ * A Nautilus API key looks like `vrn_…` and carries a set of scopes — the APIs it may
34
+ * call (`relay`, `gate`, `clockwork`, `passepartout`). Pass it once as `apiKey`. The
35
+ * per-service fields are for using a *different* key for one service; they override
36
+ * `apiKey` for that service. Keys created before scopes existed (`vrn_gate_…`,
37
+ * `vrn_relay_…`, …) keep working for the one API they name. There is no test or live
38
+ * mode: every key acts on your real data.
23
39
  */
24
40
  interface VerneConfig {
25
- /** API key for the Relay (Webhooks-as-a-Service) service. Format: vrn_relay_<env>_<secret> */
41
+ /** The API key used for every service that has no key of its own below. */
42
+ apiKey?: string;
43
+ /** Key for Relay (Webhooks-as-a-Service), when it differs from `apiKey`. */
26
44
  relay?: string;
27
- /** API key for the Gate (Auth-as-a-Service) service. Format: vrn_gate_<env>_<secret> */
45
+ /** Key for Gate (sign-in and users), when it differs from `apiKey`. */
28
46
  gate?: string;
29
- /** API key for the Passepartout (Telegram Auth) service. Format: vrn_passepartout_<env>_<secret> */
47
+ /** Key for Passepartout (Telegram sign-in), when it differs from `apiKey`. */
30
48
  passepartout?: string;
31
- /** API key for the Clockwork (Cron-as-a-Service) service. Format: vrn_clockwork_<env>_<secret> */
49
+ /** Key for Clockwork (Cron-as-a-Service), when it differs from `apiKey`. */
32
50
  clockwork?: string;
33
51
  /** Base URL override. Defaults to https://api.vernesoft.com */
34
52
  baseUrl?: string;
@@ -36,10 +54,10 @@ interface VerneConfig {
36
54
  timeoutMs?: number;
37
55
  }
38
56
  /**
39
- * Configuration for a single-service client (Relay or Gate).
57
+ * Configuration for a single-service client (Relay, Gate, Passepartout or Clockwork).
40
58
  */
41
59
  interface ServiceConfig {
42
- /** API key for the service. */
60
+ /** API key (`vrn_…`) with the scope for the service. */
43
61
  apiKey: string;
44
62
  /** Base URL override. Defaults to https://api.vernesoft.com */
45
63
  baseUrl?: string;
@@ -53,14 +71,19 @@ interface HttpClientConfig {
53
71
  timeoutMs?: number;
54
72
  }
55
73
  interface HttpRequestOptions extends RequestOptions {
56
- /** When true, omit the Authorization header (used by gate.tokens.create). */
74
+ /** When true, omit the Authorization header (calls authenticated by something else). */
57
75
  skipAuth?: boolean;
58
76
  /** Body to send as JSON. */
59
77
  body?: unknown;
78
+ /** Query parameters. `undefined` and `null` values are dropped. */
79
+ query?: QueryParams;
80
+ /** An end user's session token, sent as `X-Session-Token`. */
81
+ sessionToken?: string;
60
82
  }
83
+ type QueryParams = Record<string, string | number | boolean | Date | null | undefined>;
61
84
  declare class HttpClient {
62
85
  private readonly apiKey;
63
- private readonly baseUrl;
86
+ readonly baseUrl: string;
64
87
  private readonly timeoutMs;
65
88
  constructor(config: HttpClientConfig);
66
89
  get<T>(path: string, options?: HttpRequestOptions): Promise<T>;
@@ -150,6 +173,21 @@ interface Execution {
150
173
  response_status: number | null;
151
174
  response_body: string | null;
152
175
  error_message: string | null;
176
+ /** Which attempt of the run this row is, from 1. A retried run has one row per attempt. */
177
+ attempt?: number;
178
+ }
179
+ /** Where the tenant stands against its plan's job limits. `limit` is `null` for no limit. */
180
+ interface ClockworkUsage {
181
+ plan: string;
182
+ tier?: string;
183
+ cron: {
184
+ active: number;
185
+ limit: number | null;
186
+ };
187
+ delayed: {
188
+ pending: number;
189
+ limit: number | null;
190
+ };
153
191
  }
154
192
 
155
193
  declare class ClockworkDelayedResource {
@@ -217,31 +255,65 @@ declare class Clockwork {
217
255
  readonly delayed: ClockworkDelayedResource;
218
256
  private readonly http;
219
257
  constructor(config: ServiceConfig);
258
+ /**
259
+ * How many jobs exist against the plan's limits. Creating a job over a limit, or
260
+ * resuming a paused one, is refused with `402` (`UPGRADE_REQUIRED` or
261
+ * `JOB_LIMIT_REACHED`) — never retried.
262
+ */
263
+ usage(options?: RequestOptions): Promise<ClockworkUsage>;
220
264
  }
221
265
 
222
- /** A Kratos identity managed by Gate. */
266
+ /**
267
+ * An end user (a Kratos identity) managed by Gate.
268
+ *
269
+ * The object is the identity service's own; the stable fields are typed and the rest
270
+ * stays reachable through the index signature.
271
+ */
223
272
  interface Identity {
224
273
  id: string;
225
274
  schema_id: string;
226
275
  state: 'active' | 'inactive';
227
276
  traits: IdentityTraits;
277
+ created_at?: string;
278
+ updated_at?: string;
279
+ state_changed_at?: string;
280
+ /** Your custom metadata. Read and change it with `identities.getMetadata()` / `updateMetadata()`. */
281
+ metadata_public?: Record<string, unknown> | null;
282
+ verifiable_addresses?: Array<{
283
+ value: string;
284
+ verified: boolean;
285
+ via: string;
286
+ status?: string;
287
+ }>;
288
+ recovery_addresses?: Array<{
289
+ value: string;
290
+ via: string;
291
+ }>;
292
+ [key: string]: unknown;
228
293
  }
294
+ /**
295
+ * An end user's traits. The `user` schema has exactly two: `email` (required) and
296
+ * `locale`. Application data does not go here — keep it in custom metadata
297
+ * (`identities.updateMetadata()`).
298
+ */
229
299
  interface IdentityTraits {
230
300
  email: string;
231
- tenant_id: string;
232
- custom_data?: Record<string, unknown>;
301
+ locale?: string;
302
+ [key: string]: unknown;
233
303
  }
234
304
  /**
235
- * Parameters for creating a new identity (end-user) bound to your tenant.
236
- * The `traits.tenant_id` is injected automatically by the Edge Gateway.
305
+ * Parameters for creating a new identity (end user) bound to your tenant.
237
306
  */
238
307
  interface CreateIdentityParams {
239
308
  /** Must be 'user'. */
240
309
  schema_id: 'user';
310
+ /**
311
+ * The schema allows only these two traits; anything else is refused with
312
+ * `400 invalid_payload`. Use custom metadata for your own fields.
313
+ */
241
314
  traits: {
242
315
  email: string;
243
- /** Optional arbitrary fields for your application. */
244
- custom_data?: Record<string, unknown>;
316
+ locale?: string;
245
317
  };
246
318
  credentials?: {
247
319
  password?: {
@@ -264,9 +336,9 @@ interface JsonPatchOp {
264
336
  }
265
337
  /** Security-related Gate Identity settings for your tenant. */
266
338
  interface SecuritySettings {
267
- /** Whether email-OTP (passwordless) login is enabled. */
339
+ /** Whether sign-in and sign-up by an emailed one-time code is enabled. */
268
340
  passwordless_enabled: boolean;
269
- /** Whether TOTP two-factor authentication is enabled. */
341
+ /** Whether a second factor (TOTP) is required — enforced by `sessions.check()`. */
270
342
  mfa_enabled: boolean;
271
343
  }
272
344
  /**
@@ -286,6 +358,37 @@ interface OidcProvider {
286
358
  * …); pass it straight to your browser-side code to render social buttons.
287
359
  */
288
360
  type LoginFlow = Record<string, unknown>;
361
+ /** Optional parameters of `gate.createLoginFlow()`. */
362
+ interface LoginFlowParams {
363
+ /**
364
+ * One of the project's registered return URLs, exactly as registered (see
365
+ * `settings.updateReturnUrls()`). The first one is used when absent.
366
+ */
367
+ return_to?: string;
368
+ /**
369
+ * An opaque value of yours, 1–256 characters of letters, digits, `-`, `.`, `_`, `~`,
370
+ * returned unchanged on the redirect to your return URL. Always send one and check it.
371
+ */
372
+ state?: string;
373
+ /** `'aal2'` starts a step-up (second factor) flow; needs `session_token`. */
374
+ aal?: 'aal1' | 'aal2';
375
+ /** The end user's session token, for a step-up flow. Sent as `X-Session-Token`. */
376
+ session_token?: string;
377
+ }
378
+ /** Optional parameters of `gate.createRegistrationFlow()`. */
379
+ interface RegistrationFlowParams {
380
+ /** As for the login flow. */
381
+ return_to?: string;
382
+ /** As for the login flow. */
383
+ state?: string;
384
+ }
385
+ /** What `exchangeOidcCode()` returns: the same shape a password login returns. */
386
+ interface OidcExchangeResult {
387
+ /** The end user's session token. Keep it on your server. */
388
+ session_token: string;
389
+ session: GateSession;
390
+ [key: string]: unknown;
391
+ }
289
392
  /** Short-lived access token returned by tokens.create(). */
290
393
  interface AccessToken {
291
394
  access_token: string;
@@ -302,43 +405,355 @@ interface CreateTokenParams {
302
405
  /** Custom lifetime in seconds. Default 3600, max 86400. */
303
406
  ttl_seconds?: number;
304
407
  }
305
- /** Result of token introspection. */
408
+ /**
409
+ * One organization a user belongs to: the unit Gate uses everywhere it says who a user
410
+ * is — in introspection, the session check, the signed token's `orgs` claim and
411
+ * `identities.listOrganizations()`.
412
+ */
413
+ interface OrgClaim {
414
+ /** The organization's id. */
415
+ id: string;
416
+ /** The name of the role the user holds there. */
417
+ role: string;
418
+ /** That role's permissions, e.g. `'invoices:write'`. */
419
+ permissions: string[];
420
+ }
421
+ /**
422
+ * Result of token introspection.
423
+ *
424
+ * An inactive token (unknown, expired, another tenant's, or its user deactivated or
425
+ * deleted) is `{ active: false }` and nothing else.
426
+ */
306
427
  interface IntrospectResult {
307
428
  active: boolean;
308
- subject: string;
309
- tenant_id: string;
310
- scopes: string[];
311
- expires_at: string;
429
+ subject?: string;
430
+ tenant_id?: string;
431
+ scopes?: string[];
432
+ expires_at?: string;
433
+ /**
434
+ * When `subject` is an end user's id: their organizations in the key's project, read
435
+ * live, the first 100 in the order they joined. Absent for any other subject.
436
+ */
437
+ orgs?: OrgClaim[];
438
+ /** `true`, and only then, when there are more than `orgs` shows. */
439
+ orgs_truncated?: boolean;
312
440
  }
313
441
  /** Parameters for checking whether a subject can perform an action. */
314
442
  interface AuthorizeParams {
315
- /** ID of the user or service account (e.g. 'usr_123'). */
443
+ /** The end user's identity id. */
316
444
  subject: string;
317
- /** Action being performed (e.g. 'relay.messages.read'). */
445
+ /**
446
+ * For an organization resource: the permission to check, e.g. `'invoices:write'`.
447
+ * Otherwise a label for your own records.
448
+ */
318
449
  action: string;
319
- /** Resource identifier (e.g. 'tenant:ten_001' or 'relay:key:key_123'). */
450
+ /**
451
+ * `'organization:<uuid>'` to check a permission in an organization (prefer
452
+ * `organizations.authorize()` / `can()`, which build it); anything else asks only
453
+ * whether `subject` is one of your active end users.
454
+ */
320
455
  resource: string;
321
- /** Optional extra attributes for policy evaluation. */
456
+ /** Accepted and ignored by the API today. */
322
457
  context?: Record<string, unknown>;
323
458
  }
459
+ /** Why an organization permission check answered as it did. */
460
+ type OrganizationAuthorizeReason = 'member_with_permission' | 'member_without_permission' | 'not_a_member' | 'subject_inactive' | 'no_such_organization';
461
+ /**
462
+ * Why a check on any resource that is not an organization answered as it did — the only
463
+ * question it asks is *is `subject` one of your active end users?* `owned_by_tenant` is
464
+ * the one `allowed: true` answer.
465
+ */
466
+ type OwnershipAuthorizeReason = 'owned_by_tenant' | 'subject_inactive' | 'not_owned_by_tenant' | 'not_a_known_identity';
324
467
  /** Authorization decision returned by gate.authorize(). */
325
468
  interface AuthorizeResult {
326
469
  allowed: boolean;
470
+ /** Identifies the decision in Gate's logs, for support. */
327
471
  decision_id: string;
328
- reason: string;
472
+ /**
473
+ * A code to branch on: one of {@link OrganizationAuthorizeReason} for an organization
474
+ * resource, one of {@link OwnershipAuthorizeReason} for any other. The set is open —
475
+ * treat an unknown value as a denial.
476
+ */
477
+ reason: OrganizationAuthorizeReason | OwnershipAuthorizeReason | (string & {});
478
+ /**
479
+ * For a resource that is not an organization: the decision as a sentence, for your
480
+ * logs. Its wording can change; only `reason` is a contract. Absent for an
481
+ * organization decision.
482
+ */
483
+ message?: string;
484
+ /**
485
+ * The role the user holds in the organization — present for
486
+ * `member_with_permission`, `member_without_permission` and `subject_inactive`.
487
+ */
488
+ role?: string;
489
+ }
490
+ /** What `tokens.createSigned()` returns. */
491
+ interface SignedToken {
492
+ /** The compact ES256 JWT. Valid for 15 minutes; it cannot be revoked. */
493
+ token: string;
494
+ /** The session the token was made from, as `whoami` returns it. */
495
+ session: GateSession;
496
+ }
497
+ /** The verified payload of a Gate signed token. */
498
+ interface SignedTokenClaims {
499
+ /** `<base URL>/auth/<tenant id>` — checked by the verifier. */
500
+ iss: string;
501
+ /** The end user's identity id. */
502
+ sub: string;
503
+ /** The session the token was made from (revocable with `sessions.revoke()`). */
504
+ sid: string;
505
+ tenant_id: string;
506
+ schema_id: string;
507
+ email: string | null;
508
+ /** Always `'authenticated'` (the database role PostgREST-style consumers use). */
509
+ role: string;
510
+ /** The user's organizations across all your projects, as of when the token was made. */
511
+ orgs: OrgClaim[];
512
+ /** `true` when `orgs` is incomplete; list them with `identities.listOrganizations()`. */
513
+ orgs_truncated?: boolean;
514
+ iat: number;
515
+ nbf: number;
516
+ exp: number;
517
+ jti: string;
518
+ [claim: string]: unknown;
519
+ }
520
+ /** Options of `tokens.verifySigned()`. */
521
+ interface VerifySignedOptions {
522
+ /** Your tenant id: the issuer is built from it. */
523
+ tenantId: string;
524
+ /** Allowed clock skew in seconds for `exp` and `nbf`. Default 10. */
525
+ clockToleranceSeconds?: number;
526
+ }
527
+ /** An end user's session, as the identity service returns it. */
528
+ interface GateSession {
529
+ id: string;
530
+ active?: boolean;
531
+ authenticated_at?: string;
532
+ expires_at?: string;
533
+ issued_at?: string;
534
+ authenticator_assurance_level?: string;
535
+ authentication_methods?: Array<{
536
+ method: string;
537
+ completed_at?: string;
538
+ [key: string]: unknown;
539
+ }>;
540
+ devices?: Array<{
541
+ id?: string;
542
+ ip_address?: string;
543
+ user_agent?: string;
544
+ location?: string;
545
+ }>;
546
+ identity?: Record<string, unknown>;
547
+ [key: string]: unknown;
548
+ }
549
+ /** The answer of `sessions.check()` — whether a session is good enough to act on. */
550
+ interface SessionCheck {
551
+ active: boolean;
552
+ /** `'aal1'` or `'aal2'`. */
553
+ aal: string;
554
+ /** Whether your tenant requires a second factor. */
555
+ mfa_required: boolean;
556
+ /** Whether this session meets what your tenant requires — the one field to gate on. */
557
+ mfa_satisfied: boolean;
558
+ identity_id: string | null;
559
+ /** The user's organizations in the key's project, read now (first 100). */
560
+ orgs?: OrgClaim[];
561
+ orgs_truncated?: boolean;
562
+ }
563
+ /** A group of your end users (a customer's team, workspace, company). */
564
+ interface Organization {
565
+ id: string;
566
+ /** Free text, 1–120 characters, not unique. */
567
+ name: string;
568
+ created_at: string;
569
+ updated_at: string;
570
+ }
571
+ interface CreateOrganizationParams {
572
+ name: string;
573
+ }
574
+ interface UpdateOrganizationParams {
575
+ name: string;
576
+ }
577
+ /** A named set of permissions inside one organization. */
578
+ interface Role {
579
+ id: string;
580
+ organization_id: string;
581
+ /** Lowercase letters, digits, `_` and `-`; unique in the organization. */
582
+ name: string;
583
+ /** Sorted and de-duplicated by the API. */
584
+ permissions: string[];
585
+ created_at: string;
586
+ updated_at: string;
587
+ }
588
+ interface CreateRoleParams {
589
+ /** 1–64 characters: lowercase letters, digits, `_`, `-`, starting with a letter or digit. */
590
+ name: string;
591
+ /** Lowercase segments joined by `:`, e.g. `'invoices:write'`. `*` and `nautilus:` are reserved. */
592
+ permissions?: string[];
593
+ }
594
+ interface UpdateRoleParams {
595
+ name?: string;
596
+ /** Replaces the whole set. Everyone holding the role has the new set at once. */
597
+ permissions?: string[];
598
+ }
599
+ /** An end user's membership of an organization. */
600
+ interface Member {
601
+ organization_id: string;
602
+ identity_id: string;
603
+ role_id: string;
604
+ /** When the membership ends, or `null` for never. */
605
+ expires_at: string | null;
606
+ created_at: string;
607
+ updated_at: string;
608
+ }
609
+ interface AddMemberParams {
610
+ /** One of your end users. */
611
+ identity_id: string;
612
+ /** A role of this organization. */
613
+ role_id: string;
614
+ /** A future time (at most 10 years ahead) when the membership ends. */
615
+ expires_at?: string | Date | null;
616
+ }
617
+ interface UpdateMemberParams {
618
+ /** Move the member to another role of this organization. */
619
+ role_id?: string;
620
+ /** Leave it out to keep the deadline, `null` to remove it, a time to set it. */
621
+ expires_at?: string | Date | null;
622
+ }
623
+ type AuditActorType = 'team_member' | 'api_key' | 'end_user' | 'system' | 'staff';
624
+ /** Filters of `auditLog.list()`. */
625
+ interface ListAuditLogParams extends ListParams {
626
+ /** One action, e.g. `'key.created'`, `'identity.login'`, `'organization.member_added'`. */
627
+ action?: string;
628
+ actor_id?: string;
629
+ actor_type?: AuditActorType;
630
+ target_id?: string;
631
+ /** Entries at or after this instant. */
632
+ from?: string | Date;
633
+ /** Entries before this instant. Must be later than `from`. */
634
+ to?: string | Date;
635
+ }
636
+ /** One entry of the audit log. */
637
+ interface AuditLogEntry {
638
+ id: string;
639
+ occurred_at: string;
640
+ /** Open set: new actions are added over time. */
641
+ action: string;
642
+ actor: {
643
+ type: AuditActorType | 'unknown' | (string & {});
644
+ id: string | null;
645
+ };
646
+ target: {
647
+ type: string;
648
+ id: string;
649
+ } | null;
650
+ project_id: string | null;
651
+ details: Record<string, unknown>;
652
+ ip: string | null;
653
+ user_agent: string | null;
654
+ }
655
+ type LifecycleRuleKind = 'delete_unverified' | 'remind_unverified' | 'expire_roles' | 'delete_inactive';
656
+ /** Settings of each rule kind. */
657
+ interface LifecycleRuleParamsByKind {
658
+ delete_unverified: {
659
+ delete_after_days: number;
660
+ warn_before_days: number;
661
+ };
662
+ remind_unverified: {
663
+ remind_after_hours: number;
664
+ };
665
+ expire_roles: Record<string, never>;
666
+ delete_inactive: {
667
+ inactive_after_months: number;
668
+ warn_before_days: number;
669
+ include_without_address?: boolean;
670
+ };
671
+ }
672
+ interface LifecycleRule {
673
+ kind: LifecycleRuleKind;
674
+ configured: boolean;
675
+ enabled: boolean;
676
+ params: Record<string, unknown> | null;
677
+ /** Whether your plan includes the rule. */
678
+ available: boolean;
679
+ /** `'plan'` when a plan change switched the rule off. */
680
+ disabled_reason: 'plan' | null;
681
+ enabled_at: string | null;
682
+ updated_at: string | null;
683
+ /** How many people have something scheduled by this rule. */
684
+ pending: number;
685
+ }
686
+ interface LifecycleRules {
687
+ /** Your plan: `free`, `pro`, `advanced` or `contract`. */
688
+ tier: string;
689
+ /** All four kinds, set up or not. */
690
+ rules: LifecycleRule[];
691
+ }
692
+ interface SetLifecycleRuleParams<K extends LifecycleRuleKind = LifecycleRuleKind> {
693
+ enabled: boolean;
694
+ /** Leave out to keep the saved settings. Required on a first write (except `expire_roles`). */
695
+ params?: LifecycleRuleParamsByKind[K];
696
+ }
697
+ interface LifecycleRulePreview {
698
+ kind: LifecycleRuleKind;
699
+ available: boolean;
700
+ /** How many people the rule would schedule right now. */
701
+ matching_now: number;
702
+ /** `true` when the count stopped at 1,000. */
703
+ capped: boolean;
704
+ }
705
+ interface ListLifecycleActionsParams extends ListParams {
706
+ status?: 'pending' | 'done' | 'cancelled' | 'failed';
707
+ kind?: LifecycleRuleKind;
708
+ identity_id?: string;
709
+ }
710
+ interface LifecycleAction {
711
+ id: string;
712
+ kind: LifecycleRuleKind;
713
+ step: 'remind' | 'warn' | 'delete' | 'revoke';
714
+ identity_id: string;
715
+ status: 'pending' | 'done' | 'cancelled' | 'failed';
716
+ basis_at: string;
717
+ due_at: string;
718
+ /** Why it was cancelled or failed (`rule_disabled`, `verified`, `signed_in`, …). */
719
+ reason: string | null;
720
+ created_at: string;
721
+ finished_at: string | null;
722
+ }
723
+
724
+ /**
725
+ * The account's audit log, read-only: who did what, to what, when and from where. The
726
+ * key reads its own project's entries and the account-wide ones.
727
+ *
728
+ * Any key with the `gate` scope reads the whole log, including the IP addresses and
729
+ * user agents of your team members. Keep `gate` keys on your backend.
730
+ */
731
+ declare class AuditLogResource {
732
+ private readonly http;
733
+ constructor(http: HttpClient);
734
+ /**
735
+ * One page of entries, newest first. Filters combine; keep the same filters while
736
+ * paging with `cursor`.
737
+ *
738
+ * @example
739
+ * const page = await gate.auditLog.list({ action: 'identity.login', actor_id: userId });
740
+ */
741
+ list(params?: ListAuditLogParams, options?: RequestOptions): Promise<Paginated<AuditLogEntry>>;
329
742
  }
330
743
 
331
744
  declare class IdentitiesResource {
332
745
  private readonly http;
333
746
  constructor(http: HttpClient);
334
747
  /**
335
- * Creates a new identity (end-user) bound to your tenant.
336
- * The `traits.tenant_id` is injected automatically by the Edge Gateway.
748
+ * Creates a new identity (end user) bound to your tenant. Answers `201`.
749
+ *
750
+ * A `409 conflict` means you already have an end user with this address; the same
751
+ * address at another tenant is a different person and does not conflict. Traits are
752
+ * `email` and `locale` only — keep your own fields in custom metadata.
337
753
  */
338
754
  create(params: CreateIdentityParams, options?: RequestOptions): Promise<Identity>;
339
755
  /**
340
- * Returns an identity by ID.
341
- * The Gateway will reject the request if the identity does not belong to your tenant.
756
+ * Returns an identity by ID. An identity of another tenant is refused.
342
757
  */
343
758
  get(identityId: string, options?: RequestOptions): Promise<Identity>;
344
759
  /**
@@ -346,19 +761,19 @@ declare class IdentitiesResource {
346
761
  *
347
762
  * @example
348
763
  * await gate.identities.patch('identity_123', [
349
- * { op: 'replace', path: '/traits/custom_data/role', value: 'admin' }
764
+ * { op: 'replace', path: '/traits/email', value: 'new@example.com' },
350
765
  * ]);
351
766
  */
352
767
  patch(identityId: string, ops: JsonPatchOp[], options?: RequestOptions): Promise<Identity>;
353
768
  /**
354
- * Deletes an identity from Kratos.
355
- * The Gateway will reject the request if the identity does not belong to your tenant.
769
+ * Deletes an identity, its sessions and its memberships. Fires `identity.deleted`.
356
770
  */
357
771
  delete(identityId: string, options?: RequestOptions): Promise<void>;
358
772
  /**
359
- * Activates or deactivates an identity. An `inactive` identity cannot log in —
360
- * Kratos rejects its credentials automatically — until it is reactivated.
361
- * The identity is not deleted. Fires the `identity.state_changed` webhook event.
773
+ * Activates or deactivates an identity. An `inactive` identity cannot sign in, its
774
+ * sessions stop working (until it is active again), its access tokens introspect as
775
+ * `active: false`, and no signed token is issued for it. Nothing is deleted. Fires the
776
+ * `identity.state_changed` webhook event.
362
777
  *
363
778
  * @example
364
779
  * await gate.identities.setState('identity_123', 'inactive');
@@ -374,13 +789,178 @@ declare class IdentitiesResource {
374
789
  * a fresh verification email. Resolves on `204 No Content`.
375
790
  */
376
791
  resendVerification(identityId: string, options?: RequestOptions): Promise<void>;
792
+ /**
793
+ * Reads an identity's custom metadata (`metadata_public`) — the place for your own
794
+ * fields about a user. `{}` when nothing is set.
795
+ */
796
+ getMetadata(identityId: string, options?: RequestOptions): Promise<Record<string, unknown>>;
797
+ /**
798
+ * Merges `patch` into an identity's custom metadata and returns the result. Keys you
799
+ * leave out are kept; a key set to `null` is removed.
800
+ *
801
+ * @example
802
+ * await gate.identities.updateMetadata(id, { plan: 'pro', seats: null });
803
+ */
804
+ updateMetadata(identityId: string, patch: Record<string, unknown>, options?: RequestOptions): Promise<Record<string, unknown>>;
805
+ /**
806
+ * The organizations of the key's project that an end user belongs to, with the role
807
+ * held in each and its permissions — live, oldest membership first, cursor-paged. This
808
+ * is the complete list that `orgs_truncated: true` (in a token, an introspection or a
809
+ * session check) points to.
810
+ */
811
+ listOrganizations(identityId: string, params?: ListParams, options?: RequestOptions): Promise<Paginated<OrgClaim>>;
812
+ }
813
+
814
+ /**
815
+ * Automatic deletion of unconfirmed and inactive accounts, and removal of expired roles —
816
+ * four rules per project, all off until you switch them on. Only `delete_unverified` is
817
+ * in the Free plan; switching on another is `403 plan_required`.
818
+ */
819
+ declare class LifecycleResource {
820
+ private readonly http;
821
+ constructor(http: HttpClient);
822
+ /** All four rules of the key's project, set up or not, and the plan. */
823
+ listRules(options?: RequestOptions): Promise<LifecycleRules>;
824
+ /**
825
+ * Creates, changes, or switches a rule on or off. Leave `params` out to keep the saved
826
+ * settings. Switching off or changing settings cancels what the rule had scheduled.
827
+ *
828
+ * @example
829
+ * await gate.lifecycle.setRule('delete_unverified', {
830
+ * enabled: true,
831
+ * params: { delete_after_days: 14, warn_before_days: 3 },
832
+ * });
833
+ */
834
+ setRule<K extends LifecycleRuleKind>(kind: K, params: SetLifecycleRuleParams<K>, options?: RequestOptions): Promise<LifecycleRule>;
835
+ /** Removes a rule and cancels what it scheduled. Succeeds also when there was none. */
836
+ deleteRule(kind: LifecycleRuleKind, options?: RequestOptions): Promise<void>;
837
+ /**
838
+ * How many people a rule with these settings would affect right now. Writes nothing;
839
+ * works whether or not the rule is on or in your plan. `expire_roles` takes `{}`.
840
+ */
841
+ previewRule<K extends LifecycleRuleKind>(kind: K, params: LifecycleRuleParamsByKind[K], options?: RequestOptions): Promise<LifecycleRulePreview>;
842
+ /** What the rules have scheduled and done, oldest first, cursor-paged. */
843
+ listActions(params?: ListLifecycleActionsParams, options?: RequestOptions): Promise<Paginated<LifecycleAction>>;
844
+ }
845
+
846
+ /**
847
+ * Organizations of your end users — their teams, workspaces, companies — with named
848
+ * roles and members. Everything here acts on **the project the API key belongs to**.
849
+ *
850
+ * Gate keeps the data and reports it (introspection, the session check, the signed
851
+ * token's `orgs` claim); it does not enforce permissions. Ask with {@link can}.
852
+ */
853
+ declare class OrganizationsResource {
854
+ private readonly http;
855
+ readonly roles: RolesResource;
856
+ readonly members: MembersResource;
857
+ constructor(http: HttpClient);
858
+ /** Creates an organization. `name` is free text, 1–120 characters, not unique. */
859
+ create(params: CreateOrganizationParams, options?: RequestOptions): Promise<Organization>;
860
+ /** Lists the project's organizations, oldest first, cursor-paged. */
861
+ list(params?: ListParams, options?: RequestOptions): Promise<Paginated<Organization>>;
862
+ get(orgId: string, options?: RequestOptions): Promise<Organization>;
863
+ /** Renames an organization. */
864
+ update(orgId: string, params: UpdateOrganizationParams, options?: RequestOptions): Promise<Organization>;
865
+ /** Deletes an organization with its roles and memberships. */
866
+ delete(orgId: string, options?: RequestOptions): Promise<void>;
867
+ /**
868
+ * *May this end user do this in this organization?* — answered from the role the user
869
+ * holds there and its permissions, as they are now. `permission` must match exactly
870
+ * (no wildcards). An organization of another project looks like a missing one
871
+ * (`no_such_organization`). An invalid permission (upper case, `*`, the `nautilus:`
872
+ * prefix) is a `400` error, never `allowed: false`. Not billed, not audited.
873
+ */
874
+ authorize(orgId: string, identityId: string, permission: string, options?: RequestOptions): Promise<AuthorizeResult>;
875
+ /**
876
+ * `true` when the end user holds `permission` in the organization — the boolean of
877
+ * {@link authorize}. Errors are thrown, not turned into `false`.
878
+ *
879
+ * @example
880
+ * if (!(await gate.organizations.can(orgId, userId, 'invoices:write'))) {
881
+ * throw new Error('Forbidden');
882
+ * }
883
+ */
884
+ can(orgId: string, identityId: string, permission: string, options?: RequestOptions): Promise<boolean>;
885
+ }
886
+ /** Roles of one organization: named sets of permissions. */
887
+ declare class RolesResource {
888
+ private readonly http;
889
+ constructor(http: HttpClient);
890
+ /**
891
+ * Defines a role. `409 conflict` if the name is taken in the organization,
892
+ * `409 limit_reached` past 100 roles.
893
+ */
894
+ create(orgId: string, params: CreateRoleParams, options?: RequestOptions): Promise<Role>;
895
+ list(orgId: string, params?: ListParams, options?: RequestOptions): Promise<Paginated<Role>>;
896
+ get(orgId: string, roleId: string, options?: RequestOptions): Promise<Role>;
897
+ /**
898
+ * Renames a role and/or **replaces** its permissions. Everyone who holds it has the new
899
+ * permissions the next time they are read.
900
+ */
901
+ update(orgId: string, roleId: string, params: UpdateRoleParams, options?: RequestOptions): Promise<Role>;
902
+ /** Deletes a role. A role someone holds is refused with `409 conflict`. */
903
+ delete(orgId: string, roleId: string, options?: RequestOptions): Promise<void>;
904
+ }
905
+ /** Members of one organization: end users holding one role each. */
906
+ declare class MembersResource {
907
+ private readonly http;
908
+ constructor(http: HttpClient);
909
+ /**
910
+ * Gives an end user a role in the organization. `404` if the identity is not one of
911
+ * your end users, `409` if they already are a member (change the role with
912
+ * {@link update}).
913
+ */
914
+ add(orgId: string, params: AddMemberParams, options?: RequestOptions): Promise<Member>;
915
+ list(orgId: string, params?: ListParams, options?: RequestOptions): Promise<Paginated<Member>>;
916
+ /**
917
+ * Changes a member's role and/or deadline. `expires_at`: leave it out to keep the
918
+ * deadline, `null` to remove it, a time to set it.
919
+ */
920
+ update(orgId: string, identityId: string, params: UpdateMemberParams, options?: RequestOptions): Promise<Member>;
921
+ /** Removes the membership. The person stays your end user. */
922
+ remove(orgId: string, identityId: string, options?: RequestOptions): Promise<void>;
923
+ }
924
+
925
+ /** End users' sessions: list, revoke, and check one is good enough to act on. */
926
+ declare class SessionsResource {
927
+ private readonly http;
928
+ constructor(http: HttpClient);
929
+ /** The active sessions of one of your end users (a bare array, not paged). */
930
+ list(identityId: string, options?: RequestOptions): Promise<GateSession[]>;
931
+ /**
932
+ * Revokes every session of an end user — "log out everywhere". Signed tokens already
933
+ * issued keep verifying until their own `exp` (at most 15 minutes).
934
+ *
935
+ * Idempotent: a user of yours with no session at all is a success too. An id that is not
936
+ * one of your users — another account's, unknown, or one you deleted — is
937
+ * `403 forbidden`. `404 not_found` is rare: a user removed from the identity service
938
+ * other than through `identities.delete()`, which Gate still remembered as yours.
939
+ */
940
+ revokeAll(identityId: string, options?: RequestOptions): Promise<void>;
941
+ /**
942
+ * Revokes one session by its id. Revoking an already revoked session of your user is a
943
+ * success too; an id that is not one of your users' sessions is `404`.
944
+ */
945
+ revoke(sessionId: string, options?: RequestOptions): Promise<void>;
946
+ /**
947
+ * Whether an end user's session meets what your tenant requires — the one call to
948
+ * gate your own routes on. Gate on `mfa_satisfied`. Also lists the user's
949
+ * organizations in the key's project, read now.
950
+ *
951
+ * Throws `VerneAPIError` with `status 401` for a dead, foreign or unknown session, and
952
+ * `code 'mfa_required'` (`403`) when a second factor is owed: start a step-up flow with
953
+ * `gate.createLoginFlow({ aal: 'aal2', session_token })`.
954
+ */
955
+ check(sessionToken: string, options?: RequestOptions): Promise<SessionCheck>;
377
956
  }
378
957
 
379
958
  declare class SettingsResource {
380
959
  private readonly http;
381
960
  constructor(http: HttpClient);
382
961
  /**
383
- * Returns the tenant's security settings (passwordless / MFA).
962
+ * Returns the tenant's security settings (passwordless / MFA). With `mfa_enabled`,
963
+ * `sessions.check()` refuses a session without a second factor (`403 mfa_required`).
384
964
  */
385
965
  getSecurity(options?: RequestOptions): Promise<SecuritySettings>;
386
966
  /**
@@ -408,42 +988,94 @@ declare class SettingsResource {
408
988
  * ]);
409
989
  */
410
990
  updateOidcProviders(providers: OidcProvider[], options?: RequestOptions): Promise<OidcProvider[]>;
991
+ /**
992
+ * The project's social-login return URLs — the addresses on your backend that receive
993
+ * the user (with a one-time `code`) after a social login. The first is the default.
994
+ */
995
+ getReturnUrls(options?: RequestOptions): Promise<string[]>;
996
+ /**
997
+ * Replaces the project's return URLs (at most 10; an empty list switches the feature
998
+ * off) and returns them as they will be matched — exactly, never by prefix. `https`
999
+ * only, except `http://localhost` and `http://127.0.0.1`; no query, fragment, `@` or
1000
+ * wildcard (`400 invalid_payload` names the entry).
1001
+ */
1002
+ updateReturnUrls(urls: string[], options?: RequestOptions): Promise<string[]>;
411
1003
  }
412
1004
 
413
1005
  declare class TokensResource {
414
1006
  private readonly http;
415
- private readonly apiKey;
416
- constructor(http: HttpClient, apiKey: string);
1007
+ private readonly verifiers;
1008
+ constructor(http: HttpClient);
417
1009
  /**
418
- * Exchanges the Gate API key for a short-lived access token.
1010
+ * Exchanges the Gate API key for a short-lived access token (`gat_live_…`), for
1011
+ * `subject` — a user or a service account. Answers `201`.
419
1012
  *
420
- * Note: This endpoint does not use the Authorization header — the API key
421
- * is passed in the request body per the Gate API specification.
1013
+ * The key travels in the `Authorization` header like on every other call. (Until 1.4.0
1014
+ * this SDK sent it in the body without the header, which the API refuses with `401`.)
422
1015
  */
423
1016
  create(params: CreateTokenParams, options?: RequestOptions): Promise<AccessToken>;
424
1017
  /**
425
- * Validates a Gate access token and returns its decoded attributes.
1018
+ * Validates a Gate access token and returns its attributes. An unknown, expired or
1019
+ * foreign token, or one whose user you deactivated or deleted, is `{ active: false }`.
1020
+ *
1021
+ * When the subject is an end user, `orgs` lists their organizations in the key's
1022
+ * project, read at the moment you ask.
426
1023
  */
427
1024
  introspect(accessToken: string, options?: RequestOptions): Promise<IntrospectResult>;
1025
+ /**
1026
+ * Gets a **signed JWT** (ES256) for a signed-in end user's session, for a platform that
1027
+ * verifies signatures instead of calling Gate (Supabase, an API gateway). The session
1028
+ * token is the credential: no API key is sent.
1029
+ *
1030
+ * The token is valid for 15 minutes and **cannot be revoked**; its `orgs` claim is as old
1031
+ * as the token. Every call mints a new one — ask again before the one you hold expires.
1032
+ * A revoked or expired session, or a deactivated user, is a `401`.
1033
+ *
1034
+ * @example
1035
+ * const { token } = await gate.tokens.createSigned(sessionToken);
1036
+ */
1037
+ createSigned(sessionToken: string, options?: RequestOptions): Promise<SignedToken>;
1038
+ /**
1039
+ * Verifies a Gate signed token offline against your tenant's public keys (fetched once
1040
+ * and cached) and returns its claims. Throws `VerneTokenError` when the token must not
1041
+ * be trusted — wrong issuer (another tenant), bad signature, expired, any algorithm but
1042
+ * ES256. See {@link GateTokenVerifier} to verify without a Gate client.
1043
+ *
1044
+ * @example
1045
+ * const claims = await gate.tokens.verifySigned(token, { tenantId: 'your-tenant-id' });
1046
+ * claims.sub; // the end user's id
1047
+ */
1048
+ verifySigned(token: string, options: VerifySignedOptions): Promise<SignedTokenClaims>;
428
1049
  }
429
1050
 
430
1051
  declare class Gate {
431
1052
  readonly identities: IdentitiesResource;
432
1053
  readonly tokens: TokensResource;
433
1054
  readonly settings: SettingsResource;
1055
+ readonly organizations: OrganizationsResource;
1056
+ readonly sessions: SessionsResource;
1057
+ readonly auditLog: AuditLogResource;
1058
+ readonly lifecycle: LifecycleResource;
434
1059
  private readonly http;
435
1060
  constructor(config: ServiceConfig);
436
1061
  /**
437
- * Checks whether a given subject is allowed to perform an action on a resource.
438
- * This is the core building block for enforcing authorization in your services.
1062
+ * Asks whether a subject may perform an action on a resource. The answer is a decision;
1063
+ * your service enforces it.
1064
+ *
1065
+ * - `resource: 'organization:<id>'` — may this end user do `action` (a permission) in
1066
+ * that organization? `reason` is one of a fixed set and `role` is the role they hold.
1067
+ * Prefer `gate.organizations.authorize()` / `can()`, which build the resource.
1068
+ * - any other resource — only *is `subject` one of your active end users?*; `reason` is
1069
+ * `owned_by_tenant` (allowed), `subject_inactive`, `not_owned_by_tenant` or
1070
+ * `not_a_known_identity`, and `message` says it in a sentence for your logs.
439
1071
  *
440
1072
  * @example
441
- * const result = await gate.authorize({
442
- * subject: 'usr_123',
443
- * action: 'relay.messages.read',
444
- * resource: 'tenant:ten_001',
1073
+ * const decision = await gate.authorize({
1074
+ * subject: userId,
1075
+ * action: 'invoices:write',
1076
+ * resource: `organization:${orgId}`,
445
1077
  * });
446
- * if (!result.allowed) throw new Error('Forbidden');
1078
+ * if (!decision.allowed) throw new Error('Forbidden');
447
1079
  */
448
1080
  authorize(params: AuthorizeParams, options?: RequestOptions): Promise<AuthorizeResult>;
449
1081
  /**
@@ -457,16 +1089,26 @@ declare class Gate {
457
1089
  */
458
1090
  getEnabledProviders(tenantId: string, options?: RequestOptions): Promise<string[]>;
459
1091
  /**
460
- * Initializes a Kratos login flow using your Gate API key. Call this from
461
- * your server and pass the returned flow JSON to your browser-side code to
462
- * render social login buttons. The flow already contains only the providers
463
- * your tenant has enabled.
1092
+ * Starts a Kratos login flow with your Gate API key and returns it; render its
1093
+ * `ui.nodes` or post credentials to its `ui.action`. With a return URL registered,
1094
+ * pass `return_to` and always a `state` for social login. A step-up (second factor) is
1095
+ * `{ aal: 'aal2', session_token }`.
464
1096
  *
465
1097
  * @example
466
- * const flow = await gate.createLoginFlow();
467
- * // send `flow` to the browser as JSON
1098
+ * const flow = await gate.createLoginFlow({ state: csrfBoundValue });
1099
+ */
1100
+ createLoginFlow(params?: LoginFlowParams, options?: RequestOptions): Promise<LoginFlow>;
1101
+ /**
1102
+ * Starts a Kratos registration flow. Refused with `402 ACTIVE_USER_LIMIT_REACHED` when
1103
+ * the account is past its active-user limit and the month to move up has ended.
1104
+ */
1105
+ createRegistrationFlow(params?: RegistrationFlowParams, options?: RequestOptions): Promise<LoginFlow>;
1106
+ /**
1107
+ * Exchanges the one-time `code` your return URL received after a social login for the
1108
+ * user's session. The code works once, for about 60 seconds, and only with a key of the
1109
+ * project that started the login (otherwise `404`). Keep the session token server-side.
468
1110
  */
469
- createLoginFlow(options?: RequestOptions): Promise<LoginFlow>;
1111
+ exchangeOidcCode(code: string, options?: RequestOptions): Promise<OidcExchangeResult>;
470
1112
  }
471
1113
 
472
1114
  /** The Telegram end-user captured during login. */
@@ -498,13 +1140,23 @@ interface LoginStatus {
498
1140
  /** Present once completed — the authenticated Telegram user. */
499
1141
  user?: TelegramUser;
500
1142
  }
501
- /** Result of introspecting a Passepartout access token. */
1143
+ /**
1144
+ * Result of introspecting a Passepartout access token. An unknown, expired or foreign
1145
+ * token, or one whose user was deactivated or deleted, is `{ active: false }`.
1146
+ */
502
1147
  interface TokenIntrospection {
503
1148
  active: boolean;
504
1149
  subject?: string;
505
1150
  tenant_id?: string;
506
1151
  scopes?: string[];
507
1152
  expires_at?: string;
1153
+ /**
1154
+ * The user's organizations in the key's project, with the role held in each and its
1155
+ * permissions — read live, the first 100 in the order they joined.
1156
+ */
1157
+ orgs?: OrgClaim[];
1158
+ /** `true`, and only then, when there are more than `orgs` shows. */
1159
+ orgs_truncated?: boolean;
508
1160
  }
509
1161
 
510
1162
  /**
@@ -530,6 +1182,10 @@ declare class Passepartout {
530
1182
  * Returns the current state of a login attempt. While the user hasn't
531
1183
  * finished, `status` is `pending`; once done it becomes `completed` and
532
1184
  * carries the `access_token` and `user`.
1185
+ *
1186
+ * Throws `VerneAPIError` with `403 account_disabled` when the person is deactivated,
1187
+ * and `402 ACTIVE_USER_LIMIT_REACHED` when a *new* person signs in past the account's
1188
+ * active-user limit (retrying does not help; existing users are unaffected).
533
1189
  */
534
1190
  loginStatus(nonce: string, options?: RequestOptions): Promise<LoginStatus>;
535
1191
  /**
@@ -545,7 +1201,14 @@ interface Message {
545
1201
  id: string;
546
1202
  /** Dot-notated event name (e.g. 'user.created'). */
547
1203
  event_type: string;
548
- /** Delivery status after being accepted by Relay. */
1204
+ /**
1205
+ * Always `'accepted'`.
1206
+ *
1207
+ * It records that Relay took the event, not what each subscriber endpoint did
1208
+ * with it afterwards — per-endpoint delivery state lives in the Console under
1209
+ * Dashboard → Relay. A literal type rather than `string`, so a `switch` over
1210
+ * it cannot grow a branch that never runs.
1211
+ */
549
1212
  status: 'accepted';
550
1213
  /** ISO 8601 timestamp of when the event was accepted. */
551
1214
  timestamp: string;
@@ -557,8 +1220,13 @@ interface SendMessageParams {
557
1220
  /** Arbitrary JSON payload delivered to subscribers. */
558
1221
  payload: Record<string, unknown>;
559
1222
  /**
560
- * Optional idempotency key to prevent duplicate delivery within a 24-hour window.
561
- * If a message with the same key was already accepted, a 409 is returned.
1223
+ * Optional idempotency key, deduplicating within a 12-hour window.
1224
+ *
1225
+ * Sending the same key twice does not create a second event and does not
1226
+ * fail: the second call returns `202` with the *originally* accepted message,
1227
+ * same `id` and same `timestamp`. So a retry of a request whose response you
1228
+ * never saw needs no special handling — there is no duplicate to tell apart
1229
+ * from a success.
562
1230
  */
563
1231
  idempotency_key?: string;
564
1232
  /** Restrict delivery to endpoints listening on these channels. */
@@ -566,9 +1234,14 @@ interface SendMessageParams {
566
1234
  }
567
1235
  /** Query parameters for listing sent events. */
568
1236
  interface ListMessagesParams {
569
- /** Number of items per page. Max 100. Defaults to 20. */
1237
+ /** Number of items per page. Defaults to 20; anything above 100 is clamped to 100. */
570
1238
  limit?: number;
571
- /** Pagination cursor from a previous response. */
1239
+ /**
1240
+ * Pagination cursor from a previous response's `next_cursor`.
1241
+ *
1242
+ * `next_cursor` is `null` on the last page, so paginate until `has_more` is
1243
+ * `false` rather than until `data` comes back empty.
1244
+ */
572
1245
  cursor?: string;
573
1246
  /** Filter results by event type. */
574
1247
  event_type?: string;
@@ -581,7 +1254,13 @@ declare class MessagesResource {
581
1254
  constructor(http: HttpClient);
582
1255
  /**
583
1256
  * Publish an event to all endpoints subscribed to the given event_type.
584
- * Automatically retries once on 429 (respects Retry-After header).
1257
+ * Retries once on a short `429` (a `Retry-After` of a minute or less). The monthly
1258
+ * message limit (`429 RELAY_MESSAGE_LIMIT_REACHED`, `Retry-After` until the 1st) is
1259
+ * thrown at once, with `retryAfter` on the error.
1260
+ *
1261
+ * Safe to retry beyond that, if you pass an `idempotency_key`: a repeat
1262
+ * returns the originally accepted message rather than creating a second event
1263
+ * or failing.
585
1264
  *
586
1265
  * @returns The accepted message with its assigned ID and timestamp.
587
1266
  */
@@ -597,6 +1276,14 @@ declare class Relay {
597
1276
  constructor(config: ServiceConfig);
598
1277
  }
599
1278
 
1279
+ /**
1280
+ * The unified client. Pass one API key (`apiKey`) and use any service its scopes allow;
1281
+ * a call to a service the key has no scope for is refused by the API with `403 forbidden`.
1282
+ *
1283
+ * @example
1284
+ * const verne = new Verne({ apiKey: process.env.VERNE_API_KEY });
1285
+ * await verne.gate.organizations.create({ name: 'Acme' });
1286
+ */
600
1287
  declare class Verne {
601
1288
  private readonly config;
602
1289
  private _relay?;
@@ -608,6 +1295,64 @@ declare class Verne {
608
1295
  get gate(): Gate;
609
1296
  get passepartout(): Passepartout;
610
1297
  get clockwork(): Clockwork;
1298
+ private serviceConfig;
1299
+ }
1300
+
1301
+ interface GateTokenVerifierConfig {
1302
+ /** Your tenant id. The issuer is `<baseUrl>/auth/<tenantId>`. */
1303
+ tenantId: string;
1304
+ /** Defaults to https://api.vernesoft.com — the same base URL as the API client. */
1305
+ baseUrl?: string;
1306
+ /** Allowed clock skew in seconds for `exp` and `nbf`. Default 10. */
1307
+ clockToleranceSeconds?: number;
1308
+ /** Timeout of a key-set request in milliseconds. Default 10000. */
1309
+ timeoutMs?: number;
1310
+ }
1311
+ /**
1312
+ * Verifies Gate signed tokens (ES256 JWTs) offline, against your tenant's published
1313
+ * public keys. No API key is involved: anything that receives a token can verify it.
1314
+ *
1315
+ * What it checks: the algorithm is ES256 (anything else, `none` included, is refused),
1316
+ * the key is the one the token's `kid` names in your tenant's key set, the signature,
1317
+ * `iss` is **your** issuer (a token of another Gate tenant is refused even though its
1318
+ * signature is valid), `exp` and `nbf`, and that `sub` is present.
1319
+ *
1320
+ * What it cannot check: whether the session behind the token is still alive. A signed
1321
+ * token lives 15 minutes and cannot be revoked; for anything that must follow a
1322
+ * revocation or a role change at once, ask `gate.sessions.check()` or introspection.
1323
+ *
1324
+ * The key set is cached for its `Cache-Control: max-age` (five minutes) and refetched
1325
+ * once when a token names a key it has not seen — which is how a key rotation is
1326
+ * followed. Keep one verifier per tenant and reuse it.
1327
+ *
1328
+ * @example
1329
+ * const verifier = new GateTokenVerifier({ tenantId: process.env.VERNE_TENANT_ID! });
1330
+ * const claims = await verifier.verify(token);
1331
+ * claims.sub; // the end user's id
1332
+ * claims.orgs; // [{ id, role, permissions }]
1333
+ */
1334
+ declare class GateTokenVerifier {
1335
+ readonly issuer: string;
1336
+ readonly jwksUrl: string;
1337
+ private readonly toleranceSeconds;
1338
+ private readonly timeoutMs;
1339
+ private keys;
1340
+ private fetchedAt;
1341
+ private maxAgeMs;
1342
+ private inflight?;
1343
+ constructor(config: GateTokenVerifierConfig);
1344
+ /**
1345
+ * Verifies `token` and returns its claims. Throws {@link VerneTokenError} (with a
1346
+ * `reason`) when the token must not be trusted.
1347
+ */
1348
+ verify(token: string): Promise<SignedTokenClaims>;
1349
+ private keyFor;
1350
+ /**
1351
+ * Fetches the key set. With `required` false a failure keeps the keys already cached
1352
+ * (a stale key set is still the right one unless a token says otherwise).
1353
+ */
1354
+ private refresh;
1355
+ private load;
611
1356
  }
612
1357
 
613
1358
  /**
@@ -621,27 +1366,75 @@ declare class VerneError extends Error {
621
1366
  * Contains structured information from the API error envelope.
622
1367
  */
623
1368
  declare class VerneAPIError extends VerneError {
624
- /** Machine-readable error code (e.g. 'invalid_payload', 'unauthorized'). */
1369
+ /**
1370
+ * Machine-readable error code, e.g. `'invalid_payload'`, `'forbidden'`, `'conflict'`,
1371
+ * `'mfa_required'`, `'ACTIVE_USER_LIMIT_REACHED'`. Codes are an open set: compare
1372
+ * against the ones you handle and treat the rest generically. `'unknown'` when the
1373
+ * response carried no string code — which the API's own routes no longer do; it is
1374
+ * left for a body that did not come from the gateway (a proxy in between, an identity
1375
+ * service response passed through on `/auth/*`).
1376
+ */
625
1377
  readonly code: string;
626
1378
  /** HTTP status code. */
627
1379
  readonly status: number;
628
- /** Request ID to include when contacting support. */
629
- readonly requestId: string;
630
- constructor({ code, message, status, requestId, }: {
1380
+ /**
1381
+ * Request ID to include when contacting support.
1382
+ * Undefined when the response did not carry one.
1383
+ */
1384
+ readonly requestId?: string;
1385
+ /**
1386
+ * The members of `error` other than `code`, `message` and `request_id` — for example
1387
+ * `plan`, `limit` and `current` on a `402` limit refusal, `kind` on a Clockwork one,
1388
+ * `required_aal` and `current_aal` on `403 mfa_required`. Empty when there are none.
1389
+ */
1390
+ readonly details: Record<string, unknown>;
1391
+ /**
1392
+ * Seconds from the `Retry-After` header, when the response had one — on a `429` it is
1393
+ * how long to wait. For `RELAY_MESSAGE_LIMIT_REACHED` it runs to the 1st of next month.
1394
+ */
1395
+ readonly retryAfter?: number;
1396
+ /** Raw parsed error body, kept so unrecognised shapes stay diagnosable. */
1397
+ readonly body?: unknown;
1398
+ constructor({ code, message, status, requestId, details, retryAfter, body, }: {
631
1399
  code: string;
632
1400
  message: string;
633
1401
  status: number;
634
- requestId: string;
1402
+ requestId?: string;
1403
+ details?: Record<string, unknown>;
1404
+ retryAfter?: number;
1405
+ body?: unknown;
635
1406
  });
636
- static fromResponse(status: number, body: APIErrorBody): VerneAPIError;
1407
+ /**
1408
+ * Builds an error from a parsed response body of *unknown* shape.
1409
+ *
1410
+ * Every API route answers with the envelope `{ error: { code, message, request_id,
1411
+ * ...extra } }`. Other shapes are still read, as a fallback that costs nothing: the
1412
+ * string form `{ error: "<sentence>" }` Clockwork used before October 2026, and an
1413
+ * identity-service body whose `code` is a number (still what Kratos' own `/auth/*`
1414
+ * self-service routes answer). Anything unrecognised degrades — to code `'unknown'`,
1415
+ * to `HTTP <status>` — rather than throwing: an error path must never be the thing
1416
+ * that fails.
1417
+ */
1418
+ static fromResponse(status: number, body: unknown, retryAfter?: number): VerneAPIError;
1419
+ }
1420
+ /** Why a signed token was refused by {@link VerneTokenError}. */
1421
+ type TokenErrorReason = 'malformed' | 'unsupported_alg' | 'unknown_kid' | 'bad_signature' | 'wrong_issuer' | 'expired' | 'not_yet_valid' | 'missing_claim' | 'jwks_unavailable';
1422
+ /**
1423
+ * Thrown by signed-token verification (`GateTokenVerifier`, `gate.tokens.verifySigned`)
1424
+ * when a token must not be trusted. `reason` says why.
1425
+ */
1426
+ declare class VerneTokenError extends VerneError {
1427
+ readonly reason: TokenErrorReason;
1428
+ constructor(reason: TokenErrorReason, message: string);
637
1429
  }
638
- /** Shape of the API error envelope returned by all Verne services. */
1430
+ /** Shape of the API error envelope documented for all Verne services. */
639
1431
  interface APIErrorBody {
640
1432
  error: {
641
1433
  code: string;
642
1434
  message: string;
643
1435
  request_id: string;
1436
+ [extra: string]: unknown;
644
1437
  };
645
1438
  }
646
1439
 
647
- export { type APIErrorBody, type AccessToken, type AuthorizeParams, type AuthorizeResult, Clockwork, type CreateCronJobParams, type CreateDelayedJobParams, type CreateIdentityParams, type CreateTokenParams, type CronJob, type DelayedJob, type Execution, Gate, type Identity, type IdentityTraits, type IntrospectResult, type JsonPatchOp, type ListMessagesParams, type ListMessagesResponse, type LoginFlow, type LoginStart, type LoginStatus, type Message, type OidcProvider, type Paginated, Passepartout, Relay, type RequestOptions, type SecuritySettings, type SendMessageParams, type ServiceConfig, type TelegramUser, type TokenIntrospection, type UpdateCronJobParams, Verne, VerneAPIError, type VerneConfig, VerneError };
1440
+ export { type APIErrorBody, type AccessToken, type AddMemberParams, type AuditActorType, type AuditLogEntry, type AuthorizeParams, type AuthorizeResult, Clockwork, type ClockworkUsage, type CreateCronJobParams, type CreateDelayedJobParams, type CreateIdentityParams, type CreateOrganizationParams, type CreateRoleParams, type CreateTokenParams, type CronJob, type DelayedJob, type Execution, Gate, type GateSession, GateTokenVerifier, type GateTokenVerifierConfig, type Identity, type IdentityTraits, type IntrospectResult, type JsonPatchOp, type LifecycleAction, type LifecycleRule, type LifecycleRuleKind, type LifecycleRuleParamsByKind, type LifecycleRulePreview, type LifecycleRules, type ListAuditLogParams, type ListLifecycleActionsParams, type ListMessagesParams, type ListMessagesResponse, type ListParams, type LoginFlow, type LoginFlowParams, type LoginStart, type LoginStatus, type Member, type Message, type OidcExchangeResult, type OidcProvider, type OrgClaim, type Organization, type OrganizationAuthorizeReason, type OwnershipAuthorizeReason, type Paginated, Passepartout, type RegistrationFlowParams, Relay, type RequestOptions, type Role, type SecuritySettings, type SendMessageParams, type ServiceConfig, type SessionCheck, type SetLifecycleRuleParams, type SignedToken, type SignedTokenClaims, type TelegramUser, type TokenErrorReason, type TokenIntrospection, type UpdateCronJobParams, type UpdateMemberParams, type UpdateOrganizationParams, type UpdateRoleParams, type VerifySignedOptions, Verne, VerneAPIError, type VerneConfig, VerneError, VerneTokenError };