@rekey.dev/node 2.1.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, OrganizationRoleDefDto, OrganizationRole, OrganizationBaseRole, 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, OrganizationRoleDefDto, OrganizationMemberDto, OrganizationInvitationDto, OrganizationRole, OrganizationBaseRole, 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';
@@ -706,7 +728,7 @@ declare class OrganizationsClient {
706
728
  */
707
729
  listMembers(accessToken: string, organizationId: string, page?: ListPage): Promise<Paged<OrganizationMemberDto>>;
708
730
  /**
709
- * 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
710
732
  * email/share channel. OWNER + ADMIN only.
711
733
  */
712
734
  invite(accessToken: string, organizationId: string,
@@ -745,7 +767,7 @@ declare class OrganizationsClient {
745
767
  * Remove a member (or self). Refuses removing the last OWNER.
746
768
  *
747
769
  * Idempotent: `removed` is `false` when the target was not a member (e.g.
748
- * 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`
749
771
  * rather than assuming it is always `true`.
750
772
  */
751
773
  removeMember(accessToken: string, organizationId: string, targetEndUserId: string): Promise<{
@@ -753,7 +775,7 @@ declare class OrganizationsClient {
753
775
  }>;
754
776
  /**
755
777
  * Self-leave. An OWNER cannot leave (payment + benefits are tied to the
756
- * owner `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
778
+ * owner, `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
757
779
  * first, or demote yourself to ADMIN if there is another OWNER.
758
780
  */
759
781
  leave(accessToken: string, organizationId: string): Promise<{
@@ -774,7 +796,7 @@ declare class OrganizationsClient {
774
796
  }>;
775
797
  /**
776
798
  * Make `organizationId` the active org for this session (member-only).
777
- * Returns a fresh {accessToken, refreshToken} pair carrying the active org
799
+ * Returns a fresh {accessToken, refreshToken} pair carrying the active org,
778
800
  * **store both**. Subsequent entitlement reads (`billing.getEntitlements`)
779
801
  * then default to this org's view + shared pool without passing
780
802
  * `organizationId` explicitly. The active org survives token refresh until
@@ -782,7 +804,7 @@ declare class OrganizationsClient {
782
804
  */
783
805
  switch(accessToken: string, organizationId: string): Promise<AuthResultDto>;
784
806
  /**
785
- * 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.
786
808
  * Returns a fresh token pair (no active org); **store both**.
787
809
  */
788
810
  clearActive(accessToken: string): Promise<AuthResultDto>;
@@ -793,7 +815,7 @@ declare class LicensesClient {
793
815
  /**
794
816
  * Verify a license key + record an activation for this machine. Call
795
817
  * once at app startup; you'll get a deterministic body (`ok=false` for
796
- * invalid licenses never an HTTP error so your software can loop
818
+ * invalid licenses, never an HTTP error, so your software can loop
797
819
  * on the result without try/catch noise).
798
820
  *
799
821
  * `machineFingerprint` should be a stable identifier you derive client-
@@ -815,6 +837,157 @@ declare class LicensesClient {
815
837
  machineFingerprint: string;
816
838
  label?: string;
817
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
+ }>;
818
991
  }
819
992
  declare class UsageClient {
820
993
  private readonly client;
@@ -848,7 +1021,7 @@ declare class UsageClient {
848
1021
  }): Promise<UsageAggregateDto>;
849
1022
  }
850
1023
  /**
851
- * A credit subject pass `endUserId` for a personal balance, or
1024
+ * A credit subject, pass `endUserId` for a personal balance, or
852
1025
  * `organizationId` for a shared org pool (owner+beneficiary billing).
853
1026
  */
854
1027
  export type CreditSubject = {
@@ -857,11 +1030,11 @@ export type CreditSubject = {
857
1030
  organizationId: string;
858
1031
  };
859
1032
  /**
860
- * 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
861
1034
  * customer's backend grants credits (by selling a CREDIT-kind plan, which
862
1035
  * grants automatically on payment) and draws them down per unit consumed.
863
1036
  *
864
- * 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`,
865
1038
  * an end-user's personal balance, or an organization's shared pool.
866
1039
  */
867
1040
  declare class CreditsClient {
@@ -874,7 +1047,7 @@ declare class CreditsClient {
874
1047
  * `code: "CREDITS_INSUFFICIENT"` (HTTP 402) when the balance is too low.
875
1048
  *
876
1049
  * Pass `idempotencyKey` (e.g. the lead id) so a retried call never
877
- * double-charges a repeat returns the original result with `applied: false`.
1050
+ * double-charges, a repeat returns the original result with `applied: false`.
878
1051
  */
879
1052
  consume(input: ConsumeCreditsRequest & CreditSubject): Promise<ConsumeCreditsResultDto>;
880
1053
  /**
@@ -884,7 +1057,7 @@ declare class CreditsClient {
884
1057
  */
885
1058
  listLedger(subject: CreditSubject, limit?: number, offset?: number): Promise<Paged<CreditLedgerEntryDto>>;
886
1059
  }
887
- /** 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. */
888
1061
  export interface EntitlementsDto {
889
1062
  /** Feature flags + numeric limits, keyed by code. Gate your app on these. */
890
1063
  features: Record<string, boolean | number | string>;
@@ -907,7 +1080,7 @@ export interface EntitlementsDto {
907
1080
  * default 300) AND (b) the signature matches a constant-time compare.
908
1081
  *
909
1082
  * Use against the `X-Rekey-Signature` header and the raw request body
910
- * BYTES (not the parsed JSON any reserialization breaks the HMAC).
1083
+ * BYTES (not the parsed JSON, any reserialization breaks the HMAC).
911
1084
  *
912
1085
  * @example
913
1086
  * ```ts
@@ -933,16 +1106,37 @@ export declare function verifyWebhookSignature(args: {
933
1106
  }): boolean;
934
1107
  /** Verified claims of an RS256 end-user access token. */
935
1108
  export interface VerifiedAccessTokenClaims {
936
- /** Always `"eu_access"` other token types are refused. */
1109
+ /** Always `"eu_access"`, other token types are refused. */
937
1110
  typ: 'eu_access';
938
1111
  /** EndUser id. */
939
1112
  sub: string;
940
- /** 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. */
941
1114
  applicationId: string;
942
1115
  /** Active organization id, when the session is acting as an org. */
943
1116
  oid?: string;
944
1117
  /** Operator id when this is an impersonation session (treat with care). */
945
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;
946
1140
  /** App `tokenGeneration` at mint time (the API checks this; offline can't). */
947
1141
  gen?: number;
948
1142
  iat: number;
@@ -953,7 +1147,7 @@ export interface VerifyAccessTokenOptions {
953
1147
  * The Application this token must belong to. **Required.**
954
1148
  *
955
1149
  * This helper verifies RS256 tokens against the deployment's JWKS, and the
956
- * RS256 keypair is deployment-wide `SigningKey` has no `applicationId`
1150
+ * RS256 keypair is deployment-wide, `SigningKey` has no `applicationId`
957
1151
  * column, and `eu_access` tokens carry no `iss`/`aud`. So a token minted for
958
1152
  * ANY Application on the same deployment is cryptographically valid here.
959
1153
  * Without this, a multi-app self-host accepts another Application's end-user
@@ -961,24 +1155,24 @@ export interface VerifyAccessTokenOptions {
961
1155
  *
962
1156
  * (The HS256 default path is not affected: that key is derived per
963
1157
  * Application as `HMAC-SHA256(JWT_SECRET, applicationId:tokenGeneration)`, so
964
- * 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
965
1159
  * is exactly the path this function exists for.)
966
1160
  *
967
1161
  * Required rather than optional-with-a-warning: a security check nobody is
968
1162
  * forced to make is one most callers will not make, and the docblock used to
969
- * tell them to compare `claims.applicationId` afterwards which made the
1163
+ * tell them to compare `claims.applicationId` afterwards, which made the
970
1164
  * shortest correct path the insecure one. 2.0.0 is not out yet, so this
971
1165
  * breaks rc callers rather than a stable contract.
972
1166
  */
973
1167
  applicationId: string;
974
1168
  /**
975
- * 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`.
976
1170
  * Fetched lazily and cached in-process for `cacheTtlMs` (default 5 minutes);
977
1171
  * an unknown `kid` triggers one immediate refetch so freshly rotated keys
978
1172
  * are picked up without waiting out the TTL.
979
1173
  */
980
1174
  jwksUrl?: string;
981
- /** 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`. */
982
1176
  jwks?: JwksDto;
983
1177
  /** Optional fetch override (test stubs, custom agents). */
984
1178
  fetch?: typeof fetch;
@@ -998,14 +1192,14 @@ export interface VerifyAccessTokenOptions {
998
1192
  now?: () => number;
999
1193
  }
1000
1194
  /**
1001
- * 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
1002
1196
  * API. Works only for Applications that opted into RS256 tokens
1003
1197
  * (`authConfig.tokenAlg = "RS256"`, Panel → Application → Auth); the default
1004
1198
  * HS256 tokens are symmetric and can only be verified by the API itself
1005
1199
  * (use `rekey.auth.getCurrentUser(token)` for those).
1006
1200
  *
1007
1201
  * Checks performed (same posture as the API's verifier):
1008
- * - 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,
1009
1203
  * a strict allowlist, immune to alg-confusion;
1010
1204
  * - RSA-SHA256 signature against that public key;
1011
1205
  * - `exp` in the future, `typ === "eu_access"` (refresh/MFA/MCP tokens
@@ -1015,6 +1209,11 @@ export interface VerifyAccessTokenOptions {
1015
1209
  * user deletion. The 15-minute access lifetime bounds both; for hard
1016
1210
  * revocation guarantees keep using `auth.getCurrentUser`.
1017
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
+ *
1018
1217
  * Node-only (uses `node:crypto`). Returns the verified claims; throws
1019
1218
  * `RekeyError` on any failure.
1020
1219
  *
@@ -1037,21 +1236,21 @@ export interface VerifyAccessTokenOptions {
1037
1236
  * it moved inside: the shortest correct path should not be the one nobody
1038
1237
  * takes.
1039
1238
  *
1040
- * @throws {RekeyError} `TOKEN_ALG_NOT_RS256` token is HS256 (app hasn't opted in) or another alg.
1041
- * @throws {RekeyError} `TOKEN_KID_UNKNOWN` `kid` not in the JWKS (forged, or key deleted).
1042
- * @throws {RekeyError} `USER_TOKEN_EXPIRED` `exp` passed; refresh the session.
1043
- * @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`.
1044
1243
  */
1045
1244
  export declare function verifyAccessToken(token: string, options: VerifyAccessTokenOptions): Promise<VerifiedAccessTokenClaims>;
1046
1245
  declare class BillingClient {
1047
1246
  private readonly client;
1048
1247
  constructor(client: Rekey);
1049
1248
  /**
1050
- * List the calling Application's active plans. Public pricing pages
1249
+ * List the calling Application's active plans. Public, pricing pages
1051
1250
  * typically render straight from this. Application API key only; no
1052
1251
  * user JWT needed.
1053
1252
  *
1054
- * `amount` is in the smallest currency unit (cents/paise/sen) never
1253
+ * `amount` is in the smallest currency unit (cents/paise/sen), never
1055
1254
  * a float. Format on display: `${amount / 100} ${currency}`.
1056
1255
  */
1057
1256
  getPlans(page?: ListPage): Promise<Paged<PlanDto>>;
@@ -1062,7 +1261,7 @@ declare class BillingClient {
1062
1261
  * Pass the user's access token (the SDK puts it in `X-Rekey-User-Token`).
1063
1262
  *
1064
1263
  * `opts.includeEnded` falls back to the most recent CANCELED/EXPIRED
1065
- * subscription **only when the answer would otherwise be null** for a
1264
+ * subscription **only when the answer would otherwise be null**, for a
1066
1265
  * billing page that has to tell a former subscriber what they were on and
1067
1266
  * when it ended, rather than showing them the same blank state as somebody
1068
1267
  * who never subscribed. It can never replace a live subscription, so it is
@@ -1079,13 +1278,22 @@ declare class BillingClient {
1079
1278
  /**
1080
1279
  * Start a hosted-checkout session. Returns the URL to redirect the user
1081
1280
  * to and the local PENDING Subscription row. Subscription activation
1082
- * happens via the provider's webhook not synchronously here.
1281
+ * happens via the provider's webhook, not synchronously here.
1083
1282
  *
1084
1283
  * Pass `couponCode` to apply a discount. The whole checkout fails if the
1085
1284
  * coupon doesn't validate (typed `RekeyError` with the precise reason).
1086
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
+ *
1087
1295
  * If the Application's billing subject is **org** (Panel → Application →
1088
- * Billing → Subject), an individual can't hold a subscription you MUST
1296
+ * Billing → Subject), an individual can't hold a subscription, you MUST
1089
1297
  * pass `organizationId` of a team the user owns/admins. Omitting it throws
1090
1298
  * `RekeyError` `code: "BILLING_ORGANIZATION_REQUIRED"`.
1091
1299
  *
@@ -1103,6 +1311,74 @@ declare class BillingClient {
1103
1311
  createCheckout(accessToken: string, input: CreateCheckoutRequest & {
1104
1312
  couponCode?: string;
1105
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>;
1106
1382
  /**
1107
1383
  * Validate a coupon for the current user against a plan, *without*
1108
1384
  * applying it. Render "$50 off" on a pricing page before submit.
@@ -1116,7 +1392,7 @@ declare class BillingClient {
1116
1392
  /**
1117
1393
  * List the billing providers configured + enabled for this Application,
1118
1394
  * in the order the geo router would prefer them. Forward the end-user's
1119
- * `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
1120
1396
  * surface India-specific providers (Razorpay) for IN-country users, etc.
1121
1397
  *
1122
1398
  * Returns the resolved country (echoed back from the server's view of
@@ -1125,7 +1401,7 @@ declare class BillingClient {
1125
1401
  */
1126
1402
  getProviders(country?: string): Promise<ProvidersListDto>;
1127
1403
  /**
1128
- * Resolve the calling end-user's current entitlements feature flags +
1404
+ * Resolve the calling end-user's current entitlements, feature flags +
1129
1405
  * limits, the live credit balance, and the raw entitlement list, unioned
1130
1406
  * across their active subscriptions (and subscriptions of orgs they belong
1131
1407
  * to). Pass `{ organizationId }` (member-only) for that org's view + shared
@@ -1140,10 +1416,24 @@ declare class BillingClient {
1140
1416
  getEntitlements(accessToken: string, opts?: {
1141
1417
  organizationId?: string;
1142
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>;
1143
1433
  /**
1144
1434
  * Cancel the calling end-user's current subscription.
1145
1435
  *
1146
- * 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
1147
1437
  * for until the period they already bought runs out. A provider-backed
1148
1438
  * subscription therefore stays ACTIVE with `cancelAt` set, and the provider
1149
1439
  * webhook is what eventually terminates it; read `cancelAt` on the returned
@@ -1151,7 +1441,7 @@ declare class BillingClient {
1151
1441
  * `{ atPeriodEnd: false }` to end it immediately, forfeiting the remainder.
1152
1442
  *
1153
1443
  * PENDING checkouts (and anything with no provider-side record) are
1154
- * cancelled locally straight away regardless of the flag there is nothing
1444
+ * cancelled locally straight away regardless of the flag, there is nothing
1155
1445
  * at the provider to schedule against.
1156
1446
  *
1157
1447
  * Pass `organizationId` when the subscription belongs to a team; the caller