@rekey.dev/node 2.0.0 → 2.2.0-rc.1

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.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @rekey.dev/node server SDK for Rekey.
2
+ * @rekey.dev/node, server SDK for Rekey.
3
3
  *
4
4
  * One client instance per Application. Construct with the Application's
5
5
  * secret key (`rp_live_…` or `rp_test_…`) and the URL of your Rekey
6
- * deployment. Never ship the secret key to the browser for browser code
6
+ * deployment. Never ship the secret key to the browser, for browser code
7
7
  * use `@rekey.dev/react` with the Application's public key instead.
8
8
  *
9
9
  * @example Smoke-test your credentials
@@ -19,15 +19,15 @@
19
19
  * console.log(`Connected to "${me.name}" (${me.slug})`);
20
20
  * ```
21
21
  */
22
- import type { ApplicationDto, AuthResultDto, ListPage, Paged, JwksDto, ChangePasswordRequest, CheckoutResultDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, CreateCheckoutRequest, CreditBalanceDto, CreditLedgerEntryDto, EndUserDto, ForgotPasswordRequest, ForgotPasswordResultDto, LicenseVerifyResultDto, MfaVerifyRequest, OAuthAuthServerMetadata, OAuthIntrospectionResponse, OrganizationDto, OrganizationInvitationDto, OrganizationMemberDto, OrganizationWithRoleDto, PlanDto, ProvidersListDto, ResetPasswordRequest, SignInOutcomeDto, SignInRequest, SignUpRequest, SubscriptionDto, UsageAggregateDto, UsageRecordDto, ValidateCouponRequest, ValidateCouponResultDto } from '@rekey.dev/shared-types';
22
+ import type { ApplicationDto, AuthResultDto, ListPage, Paged, JwksDto, ChangePasswordRequest, CheckoutResultDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, CreateCheckoutRequest, CreditBalanceDto, CreditLedgerEntryDto, EndUserDto, ForgotPasswordRequest, ForgotPasswordResultDto, LicenseVerifyResultDto, LicenseDeactivateRequest, LicenseDeactivateResultDto, DeviceDto, EndUserDeviceDto, TrialEligibilityDto, DeviceStatusType, MfaVerifyRequest, OAuthAuthServerMetadata, OAuthIntrospectionResponse, OrganizationDto, OrganizationInvitationDto, OrganizationMemberDto, OrganizationWithRoleDto, OrganizationRoleDefDto, OrganizationRole, OrganizationBaseRole, PlanDto, ProvidersListDto, ResetPasswordRequest, SignInOutcomeDto, SignInRequest, DeviceBindingRequest, SignUpRequest, SubscriptionDto, UsageAggregateDto, UsageRecordDto, ValidateCouponRequest, ValidateCouponResultDto } from '@rekey.dev/shared-types';
23
23
  import { RekeyError } from '@rekey.dev/shared-types/error';
24
- export type { ApplicationDto, EndUserDto, ApiKeyDto, AuthResultDto, MfaChallengeResultDto, MfaVerifyRequest, SignInOutcomeDto, SignInRequest, SignUpRequest, RefreshRequest, ForgotPasswordRequest, ForgotPasswordResultDto, ResetPasswordRequest, ChangePasswordRequest, PlanDto, SubscriptionDto, CreateCheckoutRequest, CheckoutResultDto, CouponDto, ProvidersListDto, BillingProviderInfoDto, ValidateCouponRequest, ValidateCouponResultDto, CouponDiscountTypeValue, PlanIntervalType, PlanKindType, LicenseKindType, CreditReasonType, CreditBalanceDto, CreditLedgerEntryDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, OrganizationDto, OrganizationWithRoleDto, OrganizationMemberDto, OrganizationInvitationDto, OrganizationRole, LicenseDto, LicenseStatusType, LicenseVerifyResultDto, UsageRecordDto, UsageAggregateDto, SubscriptionStatusType, RekeyErrorShape, AuthConfig, BillingConfig, BillingProvider, TokenAlg, JwkRsaPublic, JwksDto, OAuthIntrospectionResponse, OAuthAuthServerMetadata, } from '@rekey.dev/shared-types';
24
+ export type { ApplicationDto, EndUserDto, ApiKeyDto, AuthResultDto, MfaChallengeResultDto, MfaVerifyRequest, SignInOutcomeDto, SignInRequest, DeviceBindingRequest, SignUpRequest, RefreshRequest, ForgotPasswordRequest, ForgotPasswordResultDto, ResetPasswordRequest, ChangePasswordRequest, PlanDto, SubscriptionDto, CreateCheckoutRequest, CheckoutResultDto, CouponDto, ProvidersListDto, BillingProviderInfoDto, ValidateCouponRequest, ValidateCouponResultDto, BillingProviderCapabilities, TrialEligibilityDto, TrialEligibilityItemDto, TrialPolicyType, CouponDiscountTypeValue, PlanIntervalType, PlanKindType, LicenseKindType, CreditReasonType, CreditBalanceDto, CreditLedgerEntryDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, OrganizationDto, OrganizationWithRoleDto, OrganizationRoleDefDto, OrganizationMemberDto, OrganizationInvitationDto, OrganizationRole, OrganizationBaseRole, LicenseDto, LicenseStatusType, LicenseVerifyResultDto, LicenseActivationDto, LicenseDeactivateRequest, LicenseDeactivateResultDto, DeviceDto, EndUserDeviceDto, DeviceLimitDetails, DeviceStatusType, UsageRecordDto, UsageAggregateDto, SubscriptionStatusType, RekeyErrorShape, AuthConfig, BillingConfig, BillingProvider, TokenAlg, JwkRsaPublic, JwksDto, OAuthIntrospectionResponse, OAuthAuthServerMetadata, } from '@rekey.dev/shared-types';
25
25
  /**
26
26
  * Default per-request deadline, in milliseconds. Matches the timeout the Rekey
27
27
  * API itself uses when it POSTs your outbound webhooks.
28
28
  *
29
- * Without a deadline the effective timeout is undici's `headersTimeout` five
30
- * minutes so a single unreachable Rekey deployment can pin one of your
29
+ * Without a deadline the effective timeout is undici's `headersTimeout`, five
30
+ * minutes, so a single unreachable Rekey deployment can pin one of your
31
31
  * request handlers for that long. Ten seconds is long enough for any endpoint
32
32
  * this SDK calls and short enough to fail a page instead of hanging it.
33
33
  */
@@ -36,13 +36,13 @@ export declare const DEFAULT_TIMEOUT_MS = 10000;
36
36
  export interface RekeyConfig {
37
37
  /** Base URL of the Rekey API. e.g. `https://rekey.example.com` */
38
38
  apiUrl: string;
39
- /** Secret key for one Application `rp_live_…` or `rp_test_…`. Never ship to the browser. */
39
+ /** Secret key for one Application, `rp_live_…` or `rp_test_…`. Never ship to the browser. */
40
40
  secretKey: string;
41
41
  /** Optional fetch override (test stubs, custom keep-alive agents, etc.). */
42
42
  fetch?: typeof fetch;
43
43
  /**
44
44
  * Deadline for every request this client makes, in milliseconds.
45
- * Default {@link DEFAULT_TIMEOUT_MS} (10 000). Pass `0` to disable only do
45
+ * Default {@link DEFAULT_TIMEOUT_MS} (10 000). Pass `0` to disable, only do
46
46
  * that if something upstream of you already bounds the call.
47
47
  *
48
48
  * On expiry the promise rejects with a `RekeyError` whose code is
@@ -50,7 +50,7 @@ export interface RekeyConfig {
50
50
  */
51
51
  timeoutMs?: number | undefined;
52
52
  /**
53
- * Client-wide abort signal aborting it cancels every in-flight request
53
+ * Client-wide abort signal, aborting it cancels every in-flight request
54
54
  * (server shutdown, request-scoped cancellation). Composed with, not
55
55
  * replaced by, any per-call `signal`.
56
56
  */
@@ -62,7 +62,7 @@ export interface RekeyConfig {
62
62
  * addable without a new overload.
63
63
  */
64
64
  export interface RekeyRequestOptions {
65
- /** JSON request body. Omit for GET/DELETE a present body sets `Content-Type`. */
65
+ /** JSON request body. Omit for GET/DELETE, a present body sets `Content-Type`. */
66
66
  body?: unknown;
67
67
  /** Extra headers, merged over the SDK's own (`Authorization`, `Content-Type`). */
68
68
  headers?: Record<string, string> | undefined;
@@ -80,7 +80,7 @@ export interface RekeyCallOptions {
80
80
  }
81
81
  export { RekeyError };
82
82
  /**
83
- * Outbound webhook event registry the events Rekey can POST to your app
83
+ * Outbound webhook event registry, the events Rekey can POST to your app
84
84
  * (verify them with `verifyWebhookSignature` below). `WEBHOOK_EVENTS` carries
85
85
  * `{ name, description }` pairs for introspection/autocomplete;
86
86
  * `KNOWN_WEBHOOK_EVENTS` is just the names. Mirrors the API's registry exactly.
@@ -89,7 +89,7 @@ export { RekeyError };
89
89
  * ```ts
90
90
  * import { WEBHOOK_EVENTS, isKnownWebhookEvent, type WebhookEventEnvelope } from '@rekey.dev/node';
91
91
  *
92
- * for (const e of WEBHOOK_EVENTS) console.log(`${e.name} ${e.description}`);
92
+ * for (const e of WEBHOOK_EVENTS) console.log(`${e.name}, ${e.description}`);
93
93
  *
94
94
  * const event = req.body as WebhookEventEnvelope; // after verifyWebhookSignature(...)
95
95
  * if (event.type === 'subscription.activated') unlockPlan(event.data);
@@ -102,7 +102,7 @@ export type { WebhookEventType, WebhookEventEnvelope } from '@rekey.dev/shared-t
102
102
  * this subscriber the rest of the period they paid for?
103
103
  *
104
104
  * Exported because a cancel confirmation has to say which outcome the customer
105
- * is about to get, and it has to say so BEFORE the call there is no response
105
+ * is about to get, and it has to say so BEFORE the call, there is no response
106
106
  * to read it off. It is the same function the API decides from, not a
107
107
  * description of it, so a UI built on it cannot promise a behaviour the server
108
108
  * does not have. See its docblock for the cases that still end immediately.
@@ -131,23 +131,27 @@ export declare class Rekey {
131
131
  private readonly config;
132
132
  /** Operations on the calling Application itself. */
133
133
  readonly applications: ApplicationsClient;
134
- /** Auth operations sign-in, sign-up, sessions, passkeys, magic-link. */
134
+ /** Auth operations, sign-in, sign-up, sessions, passkeys, magic-link. */
135
135
  readonly auth: AuthClient;
136
- /** Billing operations plans, checkout, subscriptions, coupons. */
136
+ /** Billing operations, plans, checkout, subscriptions, coupons. */
137
137
  readonly billing: BillingClient;
138
- /** End-user organizations create, invite, members, role changes. */
138
+ /** End-user organizations, create, invite, members, role changes. */
139
139
  readonly organizations: OrganizationsClient;
140
140
  /** License key verification + activation. */
141
141
  readonly licenses: LicensesClient;
142
- /** Usage metering record events, aggregate windows. */
142
+ /** Devices, list and release the machines an end-user signs in from. */
143
+ readonly devices: DevicesClient;
144
+ /** End-users, lookup by id or email, bulk import. */
145
+ readonly users: UsersClient;
146
+ /** Usage metering, record events, aggregate windows. */
143
147
  readonly usage: UsageClient;
144
- /** Prepaid credits balance reads, idempotent drawdown, ledger. */
148
+ /** Prepaid credits, balance reads, idempotent drawdown, ledger. */
145
149
  readonly credits: CreditsClient;
146
- /** MCP validate Rekey-issued MCP tokens from your own MCP server. */
150
+ /** MCP, validate Rekey-issued MCP tokens from your own MCP server. */
147
151
  readonly mcp: McpClient;
148
152
  constructor(config: RekeyConfig);
149
153
  /**
150
- * A clone of this client with different call options the per-call knob for
154
+ * A clone of this client with different call options, the per-call knob for
151
155
  * every wrapped method.
152
156
  *
153
157
  * Each namespace method (`billing.getPlans()`, `auth.signIn()`, …) has a
@@ -167,7 +171,7 @@ export declare class Rekey {
167
171
  * });
168
172
  * ```
169
173
  *
170
- * Cheap it rebuilds the namespace objects, holds no connections, and
174
+ * Cheap, it rebuilds the namespace objects, holds no connections, and
171
175
  * shares the same `fetch`.
172
176
  */
173
177
  with(options: RekeyCallOptions): Rekey;
@@ -175,7 +179,7 @@ export declare class Rekey {
175
179
  * Call a Rekey endpoint this SDK does not wrap yet.
176
180
  *
177
181
  * This is a **supported** escape hatch, not an internal: when the API grows a
178
- * route before the SDK does, use this instead of hand-rolling `fetch` you
182
+ * route before the SDK does, use this instead of hand-rolling `fetch`, you
179
183
  * keep the auth header, the `{ success, data }` unwrapping, the `RekeyError`
180
184
  * mapping (including transport failures) and the deadline. It takes an
181
185
  * options object precisely so a future knob does not need a new overload.
@@ -200,7 +204,7 @@ export declare class Rekey {
200
204
  }
201
205
  /**
202
206
  * MCP helpers for customers running their OWN MCP server behind Rekey auth.
203
- * The hosted MCP server (account tools) is consumed by MCP clients directly
207
+ * The hosted MCP server (account tools) is consumed by MCP clients directly,
204
208
  * this client is for the "bring your own MCP server" path: validate incoming
205
209
  * Rekey-issued tokens, and read the OAuth metadata.
206
210
  */
@@ -230,7 +234,7 @@ declare class ApplicationsClient {
230
234
  constructor(client: Rekey);
231
235
  /**
232
236
  * Verify credentials and fetch the calling Application. Use this as your
233
- * SDK smoke test if it returns, your secret key is good and you're
237
+ * SDK smoke test, if it returns, your secret key is good and you're
234
238
  * pointed at the right Rekey deployment.
235
239
  *
236
240
  * @example
@@ -252,7 +256,7 @@ declare class AuthClient {
252
256
  * (e.g. `getCurrentUser(accessToken)`) and a `refreshToken` to renew it.
253
257
  *
254
258
  * Unless the Application turns `authConfig.sendVerificationEmailOnSignUp`
255
- * off, Rekey also emails the verification link best-effort, so it never
259
+ * off, Rekey also emails the verification link, best-effort, so it never
256
260
  * fails the sign-up, and `sendVerificationEmail` re-sends on demand.
257
261
  *
258
262
  * @example
@@ -261,7 +265,7 @@ declare class AuthClient {
261
265
  * email: 'alice@example.com',
262
266
  * password: 'correct-horse-battery-staple',
263
267
  * });
264
- * // store both in your session the access token expires in 15 minutes
268
+ * // store both in your session, the access token expires in 15 minutes
265
269
  * ```
266
270
  *
267
271
  * @throws {RekeyError} `EMAIL_ALREADY_EXISTS` (409) if the email is taken in this Application.
@@ -278,14 +282,14 @@ declare class AuthClient {
278
282
  * Prompt the user for their TOTP / backup code and call
279
283
  * `mfaVerify({ mfaChallengeToken, code })` to receive a real session.
280
284
  *
281
- * **Branch on `result.mfaRequired` before reading `accessToken`** the
285
+ * **Branch on `result.mfaRequired` before reading `accessToken`**, the
282
286
  * MFA-required branch has no session tokens.
283
287
  *
284
- * @throws {RekeyError} `INVALID_CREDENTIALS` (401) single code on purpose.
288
+ * @throws {RekeyError} `INVALID_CREDENTIALS` (401), single code on purpose.
285
289
  * Don't try to distinguish wrong-email from wrong-password from the SDK side either.
286
290
  * @throws {RekeyError} `EMAIL_NOT_VERIFIED` (403) when the Application sets
287
291
  * `authConfig.requireEmailVerification` and the user hasn't confirmed their
288
- * address. The password was correct prompt for the emailed link (or call
292
+ * address. The password was correct, prompt for the emailed link (or call
289
293
  * `sendVerificationEmail`), not for the password again.
290
294
  */
291
295
  signIn(input: SignInRequest): Promise<SignInOutcomeDto>;
@@ -316,18 +320,23 @@ declare class AuthClient {
316
320
  magicLinkToken: string | null;
317
321
  }>;
318
322
  /**
319
- * Consume a magic-link token. Returns `SignInOutcome` branch on
323
+ * Consume a magic-link token. Returns `SignInOutcome`, branch on
320
324
  * `mfaRequired` before reading `accessToken`. For MFA-enrolled users
321
325
  * the response carries `mfaChallengeToken` and you must complete via
322
326
  * `mfaVerify(...)`.
323
327
  */
324
328
  verifyMagicLink(input: {
325
329
  token: string;
330
+ /** Bind the session to a device, see docs/devices.md. */
331
+ device?: {
332
+ fingerprint: string;
333
+ label?: string;
334
+ };
326
335
  }): Promise<SignInOutcomeDto>;
327
336
  /**
328
337
  * Begin a passkey authentication ceremony. Returns the WebAuthn options
329
338
  * to forward to the browser (`navigator.credentials.get(...)`) along
330
- * with `expectedChallenge` bind the challenge to your session and
339
+ * with `expectedChallenge`, bind the challenge to your session and
331
340
  * pass both back via `verifyPasskeyAuthentication(...)`.
332
341
  */
333
342
  startPasskeyAuthentication(input?: {
@@ -338,12 +347,17 @@ declare class AuthClient {
338
347
  }>;
339
348
  /**
340
349
  * Complete a passkey authentication. Returns the same `SignInOutcome`
341
- * shape as `signIn` but passkeys are themselves a strong factor, so
350
+ * shape as `signIn`, but passkeys are themselves a strong factor, so
342
351
  * `mfaRequired` will always be `false` in practice.
343
352
  */
344
353
  verifyPasskeyAuthentication(input: {
345
354
  response: unknown;
346
355
  expectedChallenge: string;
356
+ /** Bind the session to a device, see docs/devices.md. */
357
+ device?: {
358
+ fingerprint: string;
359
+ label?: string;
360
+ };
347
361
  }): Promise<SignInOutcomeDto>;
348
362
  /**
349
363
  * Begin a passkey registration ceremony for an authenticated user.
@@ -366,7 +380,7 @@ declare class AuthClient {
366
380
  /**
367
381
  * List the user's registered passkeys, newest first.
368
382
  *
369
- * Returns `{items, page}` `page.total` is the number of passkeys the user
383
+ * Returns `{items, page}`, `page.total` is the number of passkeys the user
370
384
  * has, independent of the window served.
371
385
  */
372
386
  listPasskeys(accessToken: string, page?: ListPage): Promise<Paged<{
@@ -391,7 +405,7 @@ declare class AuthClient {
391
405
  activeOrganizationId: string | null;
392
406
  }>;
393
407
  /**
394
- * Update the end-user behind a presented access token their OWN record,
408
+ * Update the end-user behind a presented access token, their OWN record,
395
409
  * and only ever their own: the token identifies the subject, so there is no
396
410
  * user id to pass and no way to aim this at anyone else.
397
411
  *
@@ -424,16 +438,18 @@ declare class AuthClient {
424
438
  }>;
425
439
  /**
426
440
  * Exchange a refresh token for a fresh {access, refresh} pair. The presented
427
- * refresh is revoked atomically call this **once** and store the new
441
+ * refresh is revoked atomically, call this **once** and store the new
428
442
  * `refreshToken` from the response immediately.
429
443
  *
430
444
  * @throws {RekeyError} `REFRESH_TOKEN_REUSED` (401) if you replay an already-used token.
431
445
  * This is a strong signal the original was leaked; treat as compromise.
432
446
  * @throws {RekeyError} `REFRESH_TOKEN_EXPIRED` (401) after the 30-day refresh window.
433
447
  */
434
- refresh(refreshToken: string): Promise<AuthResultDto>;
448
+ refresh(refreshToken: string, options?: {
449
+ device?: DeviceBindingRequest;
450
+ }): Promise<AuthResultDto>;
435
451
  /**
436
- * Revoke a refresh token. Idempotent no-op for unknown tokens. The
452
+ * Revoke a refresh token. Idempotent, no-op for unknown tokens. The
437
453
  * access token paired with this refresh remains valid until its short
438
454
  * (15 min) expiry; for true "log out everywhere" semantics, also clear
439
455
  * the access token from your client.
@@ -442,14 +458,14 @@ declare class AuthClient {
442
458
  signedOut: true;
443
459
  }>;
444
460
  /**
445
- * Request a password reset for an email. Always succeeds never tells you
461
+ * Request a password reset for an email. Always succeeds, never tells you
446
462
  * whether the email exists.
447
463
  *
448
464
  * **Branch on the result.** When the Application has an email transport
449
465
  * (BYO Resend/SMTP, or a deployment-wide `RESEND_DEFAULT_API_KEY`) Rekey sends
450
466
  * the mail itself and `resetToken` is null. With no transport it falls back to
451
- * the original contract and hands the raw token to you a secret-key caller
452
- * only so you can deliver it with your own provider.
467
+ * the original contract and hands the raw token to you, a secret-key caller
468
+ * only, so you can deliver it with your own provider.
453
469
  *
454
470
  * @example
455
471
  * ```ts
@@ -473,7 +489,7 @@ declare class AuthClient {
473
489
  }>;
474
490
  /**
475
491
  * Authenticated password change. Pass the user's *current* access token.
476
- * On success, every refresh token for the user is revoked other devices
492
+ * On success, every refresh token for the user is revoked, other devices
477
493
  * are signed out.
478
494
  */
479
495
  changePassword(accessToken: string, input: ChangePasswordRequest): Promise<{
@@ -481,8 +497,8 @@ declare class AuthClient {
481
497
  }>;
482
498
  /**
483
499
  * Revoke every refresh token for the calling user. "Sign out of all
484
- * devices." The caller's access token remains valid until 15-min expiry
485
- * clear it client-side for full logout.
500
+ * devices." The caller's access token remains valid until 15-min expiry,
501
+ * clear it client-side for full logout.
486
502
  */
487
503
  signOutEverywhere(accessToken: string): Promise<{
488
504
  revokedCount: number;
@@ -503,7 +519,7 @@ declare class AuthClient {
503
519
  verificationToken: string | null;
504
520
  }>;
505
521
  /**
506
- * Re-send a verification link to an address, with **no session** the
522
+ * Re-send a verification link to an address, with **no session**, the
507
523
  * sessionless sibling of `sendVerificationEmail`.
508
524
  *
509
525
  * This is the route for a user locked out by
@@ -511,17 +527,17 @@ declare class AuthClient {
511
527
  * session `sendVerificationEmail` needs, so a user whose first mail never
512
528
  * arrived cannot ask for another. Takes the address instead of a token.
513
529
  *
514
- * **Branch on the result**, exactly as with `requestPasswordReset` the
530
+ * **Branch on the result**, exactly as with `requestPasswordReset`, the
515
531
  * contract is the same one. It never throws for an unknown address and never
516
532
  * discloses whether the address exists, is already verified, or was mailed:
517
533
  * a publishable-key caller gets one constant body whatever happened. A
518
- * secret-key caller this SDK gets the real outcome, and the raw
534
+ * secret-key caller, this SDK, gets the real outcome, and the raw
519
535
  * `verificationToken` when the Application has no email transport configured,
520
536
  * so you can deliver it with your own provider.
521
537
  *
522
538
  * Pass `verifyUrl` containing `{token}` to template the link target. Unlike
523
539
  * `sendVerificationEmail`, nothing is sent and no token is minted when no
524
- * link can be built at all pass `verifyUrl`, or set the Application URL
540
+ * link can be built at all, pass `verifyUrl`, or set the Application URL
525
541
  * (Panel → Application → Auth). Mailing a locked-out user a verification
526
542
  * message with no button in it helps nobody.
527
543
  *
@@ -565,7 +581,7 @@ declare class AuthClient {
565
581
  userAgent: string | null;
566
582
  ip: string | null;
567
583
  }>>;
568
- /** Revoke one session by id. Idempotent `{ revoked: false }` if it isn't this user's. */
584
+ /** Revoke one session by id. Idempotent, `{ revoked: false }` if it isn't this user's. */
569
585
  revokeSession(accessToken: string, sessionId: string): Promise<{
570
586
  revoked: boolean;
571
587
  }>;
@@ -578,7 +594,7 @@ declare class AuthClient {
578
594
  /**
579
595
  * Begin TOTP enrollment: mints a secret (as an `otpauthUrl` for the QR) and
580
596
  * 10 single-show backup codes. **Not enrolled until `confirmMfaSetup(...)`.**
581
- * Only SHA-256 hashes of the backup codes are stored show them once.
597
+ * Only SHA-256 hashes of the backup codes are stored, show them once.
582
598
  */
583
599
  mfaSetup(accessToken: string): Promise<{
584
600
  otpauthUrl: string;
@@ -591,7 +607,7 @@ declare class AuthClient {
591
607
  }>;
592
608
  /**
593
609
  * Verify a TOTP or backup code as a step-up check (does NOT issue a session).
594
- * Backup codes are single-use consumed on success. Returns `{ ok }`.
610
+ * Backup codes are single-use, consumed on success. Returns `{ ok }`.
595
611
  */
596
612
  mfaChallenge(accessToken: string, code: string): Promise<{
597
613
  ok: boolean;
@@ -609,10 +625,16 @@ declare class AuthClient {
609
625
  }>;
610
626
  /**
611
627
  * Exchange the provider `code` for a Rekey session. Returns a
612
- * `SignInOutcome` branch on `mfaRequired` before reading `accessToken`.
628
+ * `SignInOutcome`, branch on `mfaRequired` before reading `accessToken`.
613
629
  * Verify the `state` CSRF value yourself before calling.
614
630
  */
615
- completeOAuth(provider: string, code: string): Promise<SignInOutcomeDto>;
631
+ completeOAuth(provider: string, code: string, options?: {
632
+ /** Bind the session to a device, see docs/devices.md. */
633
+ device?: {
634
+ fingerprint: string;
635
+ label?: string;
636
+ };
637
+ }): Promise<SignInOutcomeDto>;
616
638
  /** List the OAuth providers linked to the current user. */
617
639
  listOAuthIdentities(accessToken: string): Promise<Array<{
618
640
  provider: string;
@@ -625,7 +647,7 @@ declare class AuthClient {
625
647
  authorizationUrl: string;
626
648
  }>;
627
649
  /**
628
- * Complete an OAuth link attaches the provider identity to the current
650
+ * Complete an OAuth link, attaches the provider identity to the current
629
651
  * user. Refuses on unverified provider emails (account-takeover guard) or
630
652
  * when the provider account already belongs to a different user.
631
653
  */
@@ -648,7 +670,7 @@ declare class AuthClient {
648
670
  *
649
671
  * Re-exported from `@rekey.dev/shared-types` so the SDK, the API and the panel
650
672
  * all name one shape. Every list method returns {@link Paged}, whose `page`
651
- * tells you whether there is another window you no longer have to infer it
673
+ * tells you whether there is another window, you no longer have to infer it
652
674
  * by asking for one row more than you need.
653
675
  */
654
676
  export type { ListPage, PageMeta, Paged } from '@rekey.dev/shared-types';
@@ -665,8 +687,25 @@ declare class OrganizationsClient {
665
687
  membership: {
666
688
  id: string;
667
689
  role: 'OWNER';
690
+ baseRole: 'OWNER';
668
691
  };
669
692
  }>;
693
+ /**
694
+ * List the organization roles assignable in this Application.
695
+ *
696
+ * Returns the built-in OWNER / ADMIN / MEMBER plus any custom roles the
697
+ * operator defined (`editor`, `content-manager`, …). Call this to populate a
698
+ * role picker. An org OWNER/ADMIN assigns roles with their own session via
699
+ * `setMemberRole` / `invite`, so their UI needs to know which names exist.
700
+ *
701
+ * `baseRole` is the authority tier a name maps to. Gate your own features on
702
+ * that, never on the name: Rekey enforces the tier and treats the name as
703
+ * opaque, so a `content-manager` on tier MEMBER can do exactly what MEMBER
704
+ * can and nothing more.
705
+ *
706
+ * Bounded by construction, being an operator-curated catalog, so it is not paged.
707
+ */
708
+ listRoles(accessToken: string): Promise<OrganizationRoleDefDto[]>;
670
709
  /**
671
710
  * List organizations the calling user belongs to, with their role.
672
711
  *
@@ -689,12 +728,17 @@ declare class OrganizationsClient {
689
728
  */
690
729
  listMembers(accessToken: string, organizationId: string, page?: ListPage): Promise<Paged<OrganizationMemberDto>>;
691
730
  /**
692
- * Invite a user. Returns the raw token ONCE surface via your own
731
+ * Invite a user. Returns the raw token ONCE, surface via your own
693
732
  * email/share channel. OWNER + ADMIN only.
694
733
  */
695
- invite(accessToken: string, organizationId: string, input: {
734
+ invite(accessToken: string, organizationId: string,
735
+ /**
736
+ * `role` is a catalog NAME from `listRoles()`, not a fixed tier. Omit it to
737
+ * use the Application's default organization role.
738
+ */
739
+ input: {
696
740
  email: string;
697
- role: 'OWNER' | 'ADMIN' | 'MEMBER';
741
+ role?: OrganizationRole;
698
742
  }): Promise<{
699
743
  invitation: OrganizationInvitationDto;
700
744
  token: string;
@@ -707,19 +751,23 @@ declare class OrganizationsClient {
707
751
  * Change a member's role. OWNER manages anyone; ADMIN manages MEMBER
708
752
  * only. Last-OWNER guard refuses demoting the only OWNER.
709
753
  */
710
- setMemberRole(accessToken: string, organizationId: string, targetEndUserId: string, input: {
711
- role: 'OWNER' | 'ADMIN' | 'MEMBER';
754
+ setMemberRole(accessToken: string, organizationId: string, targetEndUserId: string,
755
+ /** `role` is a catalog NAME from `listRoles()`, not a fixed tier. */
756
+ input: {
757
+ role: OrganizationRole;
712
758
  }): Promise<{
713
759
  id: string;
714
760
  organizationId: string;
715
761
  endUserId: string;
716
- role: 'OWNER' | 'ADMIN' | 'MEMBER';
762
+ role: OrganizationRole;
763
+ /** The tier the name maps to. Gate your own features on this. */
764
+ baseRole: OrganizationBaseRole;
717
765
  }>;
718
766
  /**
719
767
  * Remove a member (or self). Refuses removing the last OWNER.
720
768
  *
721
769
  * Idempotent: `removed` is `false` when the target was not a member (e.g.
722
- * already removed) a no-op removal is not an error. Branch on `removed`
770
+ * already removed), a no-op removal is not an error. Branch on `removed`
723
771
  * rather than assuming it is always `true`.
724
772
  */
725
773
  removeMember(accessToken: string, organizationId: string, targetEndUserId: string): Promise<{
@@ -727,7 +775,7 @@ declare class OrganizationsClient {
727
775
  }>;
728
776
  /**
729
777
  * Self-leave. An OWNER cannot leave (payment + benefits are tied to the
730
- * owner `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
778
+ * owner, `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
731
779
  * first, or demote yourself to ADMIN if there is another OWNER.
732
780
  */
733
781
  leave(accessToken: string, organizationId: string): Promise<{
@@ -748,7 +796,7 @@ declare class OrganizationsClient {
748
796
  }>;
749
797
  /**
750
798
  * Make `organizationId` the active org for this session (member-only).
751
- * Returns a fresh {accessToken, refreshToken} pair carrying the active org
799
+ * Returns a fresh {accessToken, refreshToken} pair carrying the active org,
752
800
  * **store both**. Subsequent entitlement reads (`billing.getEntitlements`)
753
801
  * then default to this org's view + shared pool without passing
754
802
  * `organizationId` explicitly. The active org survives token refresh until
@@ -756,7 +804,7 @@ declare class OrganizationsClient {
756
804
  */
757
805
  switch(accessToken: string, organizationId: string): Promise<AuthResultDto>;
758
806
  /**
759
- * Clear the active org switch the session back to the personal pool.
807
+ * Clear the active org, switch the session back to the personal pool.
760
808
  * Returns a fresh token pair (no active org); **store both**.
761
809
  */
762
810
  clearActive(accessToken: string): Promise<AuthResultDto>;
@@ -767,7 +815,7 @@ declare class LicensesClient {
767
815
  /**
768
816
  * Verify a license key + record an activation for this machine. Call
769
817
  * once at app startup; you'll get a deterministic body (`ok=false` for
770
- * invalid licenses never an HTTP error so your software can loop
818
+ * invalid licenses, never an HTTP error, so your software can loop
771
819
  * on the result without try/catch noise).
772
820
  *
773
821
  * `machineFingerprint` should be a stable identifier you derive client-
@@ -789,6 +837,157 @@ declare class LicensesClient {
789
837
  machineFingerprint: string;
790
838
  label?: string;
791
839
  }): Promise<LicenseVerifyResultDto>;
840
+ /**
841
+ * Give back the seat this machine holds; call it before a re-image or on
842
+ * uninstall so the next machine can verify. Same deterministic body as
843
+ * `verify`; `released: false` means the machine held no seat.
844
+ *
845
+ * @example
846
+ * ```ts
847
+ * await rekey.licenses.deactivate({ key, machineFingerprint });
848
+ * ```
849
+ */
850
+ deactivate(input: LicenseDeactivateRequest): Promise<LicenseDeactivateResultDto>;
851
+ }
852
+ /**
853
+ * End-users' devices (docs/devices.md), both surfaces.
854
+ *
855
+ * `list` / `release` are the SERVER surface: secret key only, addressed by
856
+ * end-user id, because they read and mutate OTHER users' devices.
857
+ *
858
+ * `listMine` / `releaseMine` are the END-USER surface, the one
859
+ * `DEVICE_LIMIT_REACHED` tells you to offer. They take that user's own access
860
+ * token and act only on their own devices, so they are what a "your signed-in
861
+ * machines" screen calls, and what lets a user release a machine themselves
862
+ * instead of contacting support.
863
+ */
864
+ declare class DevicesClient {
865
+ private readonly client;
866
+ constructor(client: Rekey);
867
+ /** An end-user's devices, newest activity first. Optional `status` filter. */
868
+ list(endUserId: string, options?: {
869
+ status?: DeviceStatusType;
870
+ limit?: number;
871
+ offset?: number;
872
+ }): Promise<Paged<DeviceDto>>;
873
+ /** Release a device: gives its slot back and revokes every session on it. */
874
+ release(deviceId: string, endUserId: string): Promise<{
875
+ device: DeviceDto;
876
+ sessionsRevoked: number;
877
+ }>;
878
+ /**
879
+ * The calling end-user's OWN devices, newest activity first.
880
+ *
881
+ * `GET /api/v1/users/me/devices`, authorized by the user's access token
882
+ * rather than by an end-user id. Operator notes (`blockedReason`) and IPs are
883
+ * not on this surface, which is why it resolves to `EndUserDeviceDto`.
884
+ *
885
+ * The device the current session is bound to is the one whose `id` matches
886
+ * the access token's `dev` claim (see {@link VerifiedAccessTokenClaims}), so
887
+ * a "your devices" screen can mark "this device" without a second call.
888
+ *
889
+ * @example
890
+ * ```ts
891
+ * const { items } = await rekey.devices.listMine(accessToken, { status: 'ACTIVE' });
892
+ * ```
893
+ */
894
+ listMine(accessToken: string, options?: {
895
+ status?: DeviceStatusType;
896
+ limit?: number;
897
+ offset?: number;
898
+ }): Promise<Paged<EndUserDeviceDto>>;
899
+ /**
900
+ * Release one of the calling end-user's own devices.
901
+ *
902
+ * This is the flow `DEVICE_LIMIT_REACHED` names: that refusal carries
903
+ * `details.limit` and `details.devices` (typed as `DeviceLimitDetails`), so
904
+ * you can show the user their machines and release one here rather than
905
+ * leaving them at a dead end.
906
+ *
907
+ * Gives the slot back and revokes every session minted on that device,
908
+ * INCLUDING the current one when it is the same device, so treat a release of
909
+ * `claims.dev` as a sign-out. Idempotent for an already-released device; a
910
+ * BLOCKED device refuses with `DEVICE_BLOCKED` (only an operator can unblock).
911
+ *
912
+ * @example
913
+ * ```ts
914
+ * try {
915
+ * await rekey.auth.signIn({ email, password, device: { fingerprint } });
916
+ * } catch (e) {
917
+ * if (e instanceof RekeyError && e.code === 'DEVICE_LIMIT_REACHED') {
918
+ * const { devices } = e.details as DeviceLimitDetails;
919
+ * // …let the user pick one, then, with a token from a session that has one:
920
+ * await rekey.devices.releaseMine(accessToken, devices[0]!.id);
921
+ * }
922
+ * }
923
+ * ```
924
+ */
925
+ releaseMine(accessToken: string, deviceId: string): Promise<{
926
+ device: EndUserDeviceDto;
927
+ sessionsRevoked: number;
928
+ }>;
929
+ }
930
+ /**
931
+ * Server-side end-user lookup (secret key only). `/users/me` answers "who is
932
+ * this token"; these answer "who is this id / email" for a backend that holds
933
+ * no token.
934
+ */
935
+ declare class UsersClient {
936
+ private readonly client;
937
+ constructor(client: Rekey);
938
+ /** Exact, case-insensitive email match in the calling Application. Throws END_USER_NOT_FOUND. */
939
+ getByEmail(email: string): Promise<EndUserDto>;
940
+ /** By id, scoped to the calling Application. Throws END_USER_NOT_FOUND. */
941
+ get(endUserId: string): Promise<EndUserDto>;
942
+ /**
943
+ * Import up to 500 users from another auth system in one call. Password
944
+ * hashes (argon2id or bcrypt) are stored as given and verified as-is at
945
+ * sign-in; bcrypt is upgraded to argon2id on first success. Existing
946
+ * addresses are skipped, never updated.
947
+ *
948
+ * @example
949
+ * ```ts
950
+ * const { created, skipped } = await rekey.users.import([
951
+ * { email: 'a@example.com', passwordHash: '$2b$10$…', emailVerified: true },
952
+ * { email: 'b@example.com', oauthIdentities: [{ provider: 'google', providerAccountId: '1234' }] },
953
+ * ]);
954
+ * ```
955
+ */
956
+ import(users: ImportUserInput[]): Promise<ImportUsersResult>;
957
+ }
958
+ export interface ImportUserInput {
959
+ email: string;
960
+ /** `$argon2id$…` or `$2a$`/`$2b$`/`$2y$…`. Omit for OAuth-only users. */
961
+ passwordHash?: string;
962
+ emailVerified?: boolean;
963
+ role?: string;
964
+ metadata?: Record<string, unknown>;
965
+ oauthIdentities?: Array<{
966
+ provider: string;
967
+ providerAccountId: string;
968
+ email?: string;
969
+ }>;
970
+ }
971
+ export interface ImportUsersResult {
972
+ created: Array<{
973
+ id: string;
974
+ email: string;
975
+ }>;
976
+ skipped: Array<{
977
+ email: string;
978
+ reason: string;
979
+ }>;
980
+ /**
981
+ * OAuth identities that were NOT linked because the provider account is
982
+ * already attached to another end-user in this Application. The user was
983
+ * still created; their sign-in through that provider lands on the OTHER
984
+ * account until one of the two is fixed.
985
+ */
986
+ unlinked: Array<{
987
+ email: string;
988
+ provider: string;
989
+ providerAccountId: string;
990
+ }>;
792
991
  }
793
992
  declare class UsageClient {
794
993
  private readonly client;
@@ -822,7 +1021,7 @@ declare class UsageClient {
822
1021
  }): Promise<UsageAggregateDto>;
823
1022
  }
824
1023
  /**
825
- * A credit subject pass `endUserId` for a personal balance, or
1024
+ * A credit subject, pass `endUserId` for a personal balance, or
826
1025
  * `organizationId` for a shared org pool (owner+beneficiary billing).
827
1026
  */
828
1027
  export type CreditSubject = {
@@ -831,11 +1030,11 @@ export type CreditSubject = {
831
1030
  organizationId: string;
832
1031
  };
833
1032
  /**
834
- * Prepaid credits the "lead pack" / pay-as-you-go drawdown model. The
1033
+ * Prepaid credits, the "lead pack" / pay-as-you-go drawdown model. The
835
1034
  * customer's backend grants credits (by selling a CREDIT-kind plan, which
836
1035
  * grants automatically on payment) and draws them down per unit consumed.
837
1036
  *
838
- * All calls are server-to-server (secret key) and scoped to a `CreditSubject` —
1037
+ * All calls are server-to-server (secret key) and scoped to a `CreditSubject`,
839
1038
  * an end-user's personal balance, or an organization's shared pool.
840
1039
  */
841
1040
  declare class CreditsClient {
@@ -848,7 +1047,7 @@ declare class CreditsClient {
848
1047
  * `code: "CREDITS_INSUFFICIENT"` (HTTP 402) when the balance is too low.
849
1048
  *
850
1049
  * Pass `idempotencyKey` (e.g. the lead id) so a retried call never
851
- * double-charges a repeat returns the original result with `applied: false`.
1050
+ * double-charges, a repeat returns the original result with `applied: false`.
852
1051
  */
853
1052
  consume(input: ConsumeCreditsRequest & CreditSubject): Promise<ConsumeCreditsResultDto>;
854
1053
  /**
@@ -858,7 +1057,7 @@ declare class CreditsClient {
858
1057
  */
859
1058
  listLedger(subject: CreditSubject, limit?: number, offset?: number): Promise<Paged<CreditLedgerEntryDto>>;
860
1059
  }
861
- /** What an end-user (or org) is entitled to right now from active subs. */
1060
+ /** What an end-user (or org) is entitled to right now, from active subs. */
862
1061
  export interface EntitlementsDto {
863
1062
  /** Feature flags + numeric limits, keyed by code. Gate your app on these. */
864
1063
  features: Record<string, boolean | number | string>;
@@ -881,7 +1080,7 @@ export interface EntitlementsDto {
881
1080
  * default 300) AND (b) the signature matches a constant-time compare.
882
1081
  *
883
1082
  * Use against the `X-Rekey-Signature` header and the raw request body
884
- * BYTES (not the parsed JSON any reserialization breaks the HMAC).
1083
+ * BYTES (not the parsed JSON, any reserialization breaks the HMAC).
885
1084
  *
886
1085
  * @example
887
1086
  * ```ts
@@ -907,16 +1106,37 @@ export declare function verifyWebhookSignature(args: {
907
1106
  }): boolean;
908
1107
  /** Verified claims of an RS256 end-user access token. */
909
1108
  export interface VerifiedAccessTokenClaims {
910
- /** Always `"eu_access"` other token types are refused. */
1109
+ /** Always `"eu_access"`, other token types are refused. */
911
1110
  typ: 'eu_access';
912
1111
  /** EndUser id. */
913
1112
  sub: string;
914
- /** Application the token is bound to check it against YOUR application id. */
1113
+ /** Application the token is bound to, check it against YOUR application id. */
915
1114
  applicationId: string;
916
1115
  /** Active organization id, when the session is acting as an org. */
917
1116
  oid?: string;
918
1117
  /** Operator id when this is an impersonation session (treat with care). */
919
1118
  imp?: string;
1119
+ /**
1120
+ * Refresh-token family this access token belongs to, the `sid` claim.
1121
+ *
1122
+ * Optional because the API only mints it when the session flow supplies one
1123
+ * (see `issueUserAccessToken`), so treat an absent `sid` as "this deployment
1124
+ * or flow did not bind one", never as a session that ended.
1125
+ *
1126
+ * Offline verification cannot see a revocation, the whole point of not
1127
+ * calling the API. Use this to correlate a token with your own record of the
1128
+ * session, so you can drop one you already know is gone.
1129
+ */
1130
+ sid?: string;
1131
+ /**
1132
+ * Device this session is bound to, the `dev` claim. Present only when the
1133
+ * sign-in bound a device.
1134
+ *
1135
+ * This is the id that matches a row from `devices.listMine()`, so a client
1136
+ * can mark "this device" in its own device list, and the id to pass to
1137
+ * `devices.releaseMine()` to sign the CURRENT machine out.
1138
+ */
1139
+ dev?: string;
920
1140
  /** App `tokenGeneration` at mint time (the API checks this; offline can't). */
921
1141
  gen?: number;
922
1142
  iat: number;
@@ -927,7 +1147,7 @@ export interface VerifyAccessTokenOptions {
927
1147
  * The Application this token must belong to. **Required.**
928
1148
  *
929
1149
  * This helper verifies RS256 tokens against the deployment's JWKS, and the
930
- * RS256 keypair is deployment-wide `SigningKey` has no `applicationId`
1150
+ * RS256 keypair is deployment-wide, `SigningKey` has no `applicationId`
931
1151
  * column, and `eu_access` tokens carry no `iss`/`aud`. So a token minted for
932
1152
  * ANY Application on the same deployment is cryptographically valid here.
933
1153
  * Without this, a multi-app self-host accepts another Application's end-user
@@ -935,24 +1155,24 @@ export interface VerifyAccessTokenOptions {
935
1155
  *
936
1156
  * (The HS256 default path is not affected: that key is derived per
937
1157
  * Application as `HMAC-SHA256(JWT_SECRET, applicationId:tokenGeneration)`, so
938
- * a foreign token fails the signature. This is the RS256 opt-in only which
1158
+ * a foreign token fails the signature. This is the RS256 opt-in only, which
939
1159
  * is exactly the path this function exists for.)
940
1160
  *
941
1161
  * Required rather than optional-with-a-warning: a security check nobody is
942
1162
  * forced to make is one most callers will not make, and the docblock used to
943
- * tell them to compare `claims.applicationId` afterwards which made the
1163
+ * tell them to compare `claims.applicationId` afterwards, which made the
944
1164
  * shortest correct path the insecure one. 2.0.0 is not out yet, so this
945
1165
  * breaks rc callers rather than a stable contract.
946
1166
  */
947
1167
  applicationId: string;
948
1168
  /**
949
- * URL of the deployment's JWKS `https://<your-rekey>/.well-known/jwks.json`.
1169
+ * URL of the deployment's JWKS, `https://<your-rekey>/.well-known/jwks.json`.
950
1170
  * Fetched lazily and cached in-process for `cacheTtlMs` (default 5 minutes);
951
1171
  * an unknown `kid` triggers one immediate refetch so freshly rotated keys
952
1172
  * are picked up without waiting out the TTL.
953
1173
  */
954
1174
  jwksUrl?: string;
955
- /** Pre-fetched key set skips all network access. Takes precedence over `jwksUrl`. */
1175
+ /** Pre-fetched key set, skips all network access. Takes precedence over `jwksUrl`. */
956
1176
  jwks?: JwksDto;
957
1177
  /** Optional fetch override (test stubs, custom agents). */
958
1178
  fetch?: typeof fetch;
@@ -972,14 +1192,14 @@ export interface VerifyAccessTokenOptions {
972
1192
  now?: () => number;
973
1193
  }
974
1194
  /**
975
- * Verify an end-user ACCESS token **offline** no round-trip to the Rekey
1195
+ * Verify an end-user ACCESS token **offline**, no round-trip to the Rekey
976
1196
  * API. Works only for Applications that opted into RS256 tokens
977
1197
  * (`authConfig.tokenAlg = "RS256"`, Panel → Application → Auth); the default
978
1198
  * HS256 tokens are symmetric and can only be verified by the API itself
979
1199
  * (use `rekey.auth.getCurrentUser(token)` for those).
980
1200
  *
981
1201
  * Checks performed (same posture as the API's verifier):
982
- * - header `alg` must be `RS256` and `kid` must exist in the JWKS
1202
+ * - header `alg` must be `RS256` and `kid` must exist in the JWKS,
983
1203
  * a strict allowlist, immune to alg-confusion;
984
1204
  * - RSA-SHA256 signature against that public key;
985
1205
  * - `exp` in the future, `typ === "eu_access"` (refresh/MFA/MCP tokens
@@ -989,6 +1209,11 @@ export interface VerifyAccessTokenOptions {
989
1209
  * user deletion. The 15-minute access lifetime bounds both; for hard
990
1210
  * revocation guarantees keep using `auth.getCurrentUser`.
991
1211
  *
1212
+ * Nor can it see any server-side revocation: a locally verified token stays
1213
+ * valid until it expires, even after sign-out everywhere, a password change,
1214
+ * a session revoke or a device release. Call the API when immediate
1215
+ * revocation matters.
1216
+ *
992
1217
  * Node-only (uses `node:crypto`). Returns the verified claims; throws
993
1218
  * `RekeyError` on any failure.
994
1219
  *
@@ -1011,21 +1236,21 @@ export interface VerifyAccessTokenOptions {
1011
1236
  * it moved inside: the shortest correct path should not be the one nobody
1012
1237
  * takes.
1013
1238
  *
1014
- * @throws {RekeyError} `TOKEN_ALG_NOT_RS256` token is HS256 (app hasn't opted in) or another alg.
1015
- * @throws {RekeyError} `TOKEN_KID_UNKNOWN` `kid` not in the JWKS (forged, or key deleted).
1016
- * @throws {RekeyError} `USER_TOKEN_EXPIRED` `exp` passed; refresh the session.
1017
- * @throws {RekeyError} `USER_TOKEN_INVALID` malformed, bad signature, or wrong `typ`.
1239
+ * @throws {RekeyError} `TOKEN_ALG_NOT_RS256`, token is HS256 (app hasn't opted in) or another alg.
1240
+ * @throws {RekeyError} `TOKEN_KID_UNKNOWN`, `kid` not in the JWKS (forged, or key deleted).
1241
+ * @throws {RekeyError} `USER_TOKEN_EXPIRED`, `exp` passed; refresh the session.
1242
+ * @throws {RekeyError} `USER_TOKEN_INVALID`, malformed, bad signature, or wrong `typ`.
1018
1243
  */
1019
1244
  export declare function verifyAccessToken(token: string, options: VerifyAccessTokenOptions): Promise<VerifiedAccessTokenClaims>;
1020
1245
  declare class BillingClient {
1021
1246
  private readonly client;
1022
1247
  constructor(client: Rekey);
1023
1248
  /**
1024
- * List the calling Application's active plans. Public pricing pages
1249
+ * List the calling Application's active plans. Public, pricing pages
1025
1250
  * typically render straight from this. Application API key only; no
1026
1251
  * user JWT needed.
1027
1252
  *
1028
- * `amount` is in the smallest currency unit (cents/paise/sen) never
1253
+ * `amount` is in the smallest currency unit (cents/paise/sen), never
1029
1254
  * a float. Format on display: `${amount / 100} ${currency}`.
1030
1255
  */
1031
1256
  getPlans(page?: ListPage): Promise<Paged<PlanDto>>;
@@ -1036,7 +1261,7 @@ declare class BillingClient {
1036
1261
  * Pass the user's access token (the SDK puts it in `X-Rekey-User-Token`).
1037
1262
  *
1038
1263
  * `opts.includeEnded` falls back to the most recent CANCELED/EXPIRED
1039
- * subscription **only when the answer would otherwise be null** for a
1264
+ * subscription **only when the answer would otherwise be null**, for a
1040
1265
  * billing page that has to tell a former subscriber what they were on and
1041
1266
  * when it ended, rather than showing them the same blank state as somebody
1042
1267
  * who never subscribed. It can never replace a live subscription, so it is
@@ -1053,13 +1278,22 @@ declare class BillingClient {
1053
1278
  /**
1054
1279
  * Start a hosted-checkout session. Returns the URL to redirect the user
1055
1280
  * to and the local PENDING Subscription row. Subscription activation
1056
- * happens via the provider's webhook not synchronously here.
1281
+ * happens via the provider's webhook, not synchronously here.
1057
1282
  *
1058
1283
  * Pass `couponCode` to apply a discount. The whole checkout fails if the
1059
1284
  * coupon doesn't validate (typed `RekeyError` with the precise reason).
1060
1285
  *
1286
+ * A buyer who has already used their free trial is refused with
1287
+ * `BILLING_TRIAL_ALREADY_USED` (409). The escape hatch is
1288
+ * `allowWithoutTrial: true` AND a fresh `Idempotency-Key`, but send it only
1289
+ * after the buyer has been told they are paying today. Read
1290
+ * {@link getTrialEligibility} and render the paid price instead of retrying
1291
+ * blindly: a buyer who merely abandoned a trial checkout still reads
1292
+ * `eligible: true`, and acknowledging on their behalf charges them today for
1293
+ * the trial the next checkout was about to grant.
1294
+ *
1061
1295
  * If the Application's billing subject is **org** (Panel → Application →
1062
- * Billing → Subject), an individual can't hold a subscription you MUST
1296
+ * Billing → Subject), an individual can't hold a subscription, you MUST
1063
1297
  * pass `organizationId` of a team the user owns/admins. Omitting it throws
1064
1298
  * `RekeyError` `code: "BILLING_ORGANIZATION_REQUIRED"`.
1065
1299
  *
@@ -1077,6 +1311,74 @@ declare class BillingClient {
1077
1311
  createCheckout(accessToken: string, input: CreateCheckoutRequest & {
1078
1312
  couponCode?: string;
1079
1313
  }): Promise<CheckoutResultDto>;
1314
+ /**
1315
+ * Put the calling end-user on the Application's free tier
1316
+ * (`billingConfig.defaultPlanSlug`). No payment provider is involved and none
1317
+ * needs to be configured: the plan costs nothing.
1318
+ *
1319
+ * This is how a freemium product hands a new signup their included credits,
1320
+ * licence or quota. `defaultPlanSlug` alone covers only the read-time half
1321
+ * (feature flags and included usage); CREDIT and LICENSE entitlements are
1322
+ * stateful and need a real subscription, which is what this creates.
1323
+ *
1324
+ * **Idempotent**, and the answer says which happened: `activated: true` is a
1325
+ * first activation (201, `subscription.activated` emitted), `activated:
1326
+ * false` means they were already entitled and nothing was written,
1327
+ * re-provisioned or re-announced.
1328
+ *
1329
+ * Pass `organizationId` on an org-billed Application; the caller must be an
1330
+ * OWNER or ADMIN of it. Omit it and the session's active organization is used.
1331
+ *
1332
+ * @throws {RekeyError} `BILLING_NO_FREE_PLAN` (404) when the Application
1333
+ * nominates no default plan; `BILLING_FREE_PLAN_NOT_FREE` (409) when that
1334
+ * plan charges money, use {@link createCheckout} instead;
1335
+ * `BILLING_FREE_TIER_ALREADY_CLAIMED` (409) when the plan grants credits or a
1336
+ * licence and this caller already claimed it for a different beneficiary.
1337
+ *
1338
+ * @example
1339
+ * ```ts
1340
+ * const { subscription, activated } = await rekey.billing.subscribe(accessToken);
1341
+ * if (activated) welcomeWithStarterCredits(subscription);
1342
+ * ```
1343
+ */
1344
+ subscribe(accessToken: string, input?: {
1345
+ organizationId?: string;
1346
+ }): Promise<{
1347
+ subscription: SubscriptionDto;
1348
+ activated: boolean;
1349
+ }>;
1350
+ /**
1351
+ * Whether THIS buyer may start each plan's free trial, under the
1352
+ * Application's `trialPolicy`.
1353
+ *
1354
+ * Read this before offering a trial: a buyer who is not eligible should be
1355
+ * shown the paid price, not a trial that checkout refuses with
1356
+ * `BILLING_TRIAL_ALREADY_USED`. Feed the result straight into
1357
+ * `<PricingTable trialEligibility={…}>` from `@rekey.dev/react`.
1358
+ *
1359
+ * **Advisory.** The authoritative decision is taken under a lock at checkout,
1360
+ * so two tabs can both read `eligible: true` and only one gets the trial.
1361
+ * Treat a 409 at checkout as normal, not as a contradiction.
1362
+ *
1363
+ * **Provider-dependent.** `PLAN_TRIAL_MISCONFIGURED` can be the resolved
1364
+ * provider's answer, so the response echoes `provider`; re-read this when the
1365
+ * buyer changes processor. Pass `country` (ISO 3166-1 alpha-2) to steer the
1366
+ * geo router the way {@link getProviders} does.
1367
+ *
1368
+ * @example
1369
+ * ```ts
1370
+ * const { items, policy } = await rekey.billing.getTrialEligibility(accessToken);
1371
+ * const pro = items.find((i) => i.planSlug === 'pro');
1372
+ * const label = pro?.eligible ? `Start ${pro.trialDays} days free` : 'Subscribe';
1373
+ * ```
1374
+ */
1375
+ getTrialEligibility(accessToken: string, opts?: {
1376
+ organizationId?: string;
1377
+ planSlug?: string;
1378
+ limit?: number;
1379
+ offset?: number;
1380
+ country?: string;
1381
+ }): Promise<TrialEligibilityDto>;
1080
1382
  /**
1081
1383
  * Validate a coupon for the current user against a plan, *without*
1082
1384
  * applying it. Render "$50 off" on a pricing page before submit.
@@ -1090,7 +1392,7 @@ declare class BillingClient {
1090
1392
  /**
1091
1393
  * List the billing providers configured + enabled for this Application,
1092
1394
  * in the order the geo router would prefer them. Forward the end-user's
1093
- * `country` (ISO 3166-1 alpha-2) when you have it the panel/SDK will
1395
+ * `country` (ISO 3166-1 alpha-2) when you have it, the panel/SDK will
1094
1396
  * surface India-specific providers (Razorpay) for IN-country users, etc.
1095
1397
  *
1096
1398
  * Returns the resolved country (echoed back from the server's view of
@@ -1099,7 +1401,7 @@ declare class BillingClient {
1099
1401
  */
1100
1402
  getProviders(country?: string): Promise<ProvidersListDto>;
1101
1403
  /**
1102
- * Resolve the calling end-user's current entitlements feature flags +
1404
+ * Resolve the calling end-user's current entitlements, feature flags +
1103
1405
  * limits, the live credit balance, and the raw entitlement list, unioned
1104
1406
  * across their active subscriptions (and subscriptions of orgs they belong
1105
1407
  * to). Pass `{ organizationId }` (member-only) for that org's view + shared
@@ -1114,10 +1416,24 @@ declare class BillingClient {
1114
1416
  getEntitlements(accessToken: string, opts?: {
1115
1417
  organizationId?: string;
1116
1418
  }): Promise<EntitlementsDto>;
1419
+ /**
1420
+ * The same union as `getEntitlements`, for an end-user you name rather than
1421
+ * one whose token you hold. Secret key only, for a licence server, a
1422
+ * support tool or a batch job.
1423
+ *
1424
+ * @example
1425
+ * ```ts
1426
+ * const { features } = await rekey.billing.getEntitlementsFor(endUserId);
1427
+ * if (features.max_devices !== undefined) capDevices(features.max_devices);
1428
+ * ```
1429
+ */
1430
+ getEntitlementsFor(endUserId: string, opts?: {
1431
+ organizationId?: string;
1432
+ }): Promise<EntitlementsDto>;
1117
1433
  /**
1118
1434
  * Cancel the calling end-user's current subscription.
1119
1435
  *
1120
- * Defaults to cancelling **at period end** the user keeps what they paid
1436
+ * Defaults to cancelling **at period end**, the user keeps what they paid
1121
1437
  * for until the period they already bought runs out. A provider-backed
1122
1438
  * subscription therefore stays ACTIVE with `cancelAt` set, and the provider
1123
1439
  * webhook is what eventually terminates it; read `cancelAt` on the returned
@@ -1125,7 +1441,7 @@ declare class BillingClient {
1125
1441
  * `{ atPeriodEnd: false }` to end it immediately, forfeiting the remainder.
1126
1442
  *
1127
1443
  * PENDING checkouts (and anything with no provider-side record) are
1128
- * cancelled locally straight away regardless of the flag there is nothing
1444
+ * cancelled locally straight away regardless of the flag, there is nothing
1129
1445
  * at the provider to schedule against.
1130
1446
  *
1131
1447
  * Pass `organizationId` when the subscription belongs to a team; the caller