@rekey.dev/node 2.1.0 → 2.2.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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, MeInclude, MeIncludedFor, ListPage, Paged, JwksDto, ChangePasswordRequest, CheckoutResultDto, ConsumeCreditsRequest, ConsumeCreditsResultDto, CreateCheckoutRequest, CreditBalanceDto, CreditLedgerEntryDto, CurrentUserDto, EndUserDto, EndUserLicenseDto, FeatureCheckDto, ForgotPasswordRequest, ForgotPasswordResultDto, LicenseVerifyResultDto, LicenseDeactivateRequest, LicenseDeactivateResultDto, DeviceDto, EndUserDeviceDto, TrialEligibilityDto, DeviceStatusType, MfaVerifyRequest, OAuthAuthServerMetadata, OAuthIntrospectionResponse, OrganizationDto, OrganizationInvitationDto, OrganizationMemberDto, OrganizationWithRoleDto, OrganizationRoleDefDto, OrganizationRole, OrganizationBaseRole, PublicPlanDto, ProvidersListDto, ResetPasswordRequest, SignInOutcomeDto, SignInRequest, DeviceBindingRequest, SignUpRequest, SubscriptionDto, UsageAggregateDto, UsageMeterCatalogueEntryDto, UsageRecordDto, UsageRemainingDto, KeyGrantCreditsRequest, SelfCreditLedgerEntryDto, 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, CurrentUserDto, EndUserLicenseDto, FeatureCheckDto, PublicPlanDto, PublicPlanCheckoutDto, MeInclude, MeIncluded, MeIncludedFor, MeIncludedFields, ResolvedEntitlementsDto, 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, UsageRemainingDto, UsageMeterRemainingDto, UsageMeterCatalogueEntryDto, KeyGrantCreditsRequest, SelfCreditLedgerEntryDto, CreditWebhookData, 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<{
@@ -383,15 +397,37 @@ declare class AuthClient {
383
397
  /**
384
398
  * Resolve the end-user behind a presented access token.
385
399
  *
386
- * @throws {RekeyError} `USER_TOKEN_INVALID` (401) if expired/forged/wrong-secret.
400
+ * Pass `include` to get more of what a backend needs to authorise the
401
+ * request in the same round trip: `entitlements` (as `billing.getEntitlements`
402
+ * returns them), `device` (the device the session is bound to, or null),
403
+ * `subscription` (as `billing.getSubscription` returns it), `organization`
404
+ * (the active organization with the caller's role) and `licenses` (as
405
+ * `{ items, truncated }`, the first 100 of `licenses.listMine`). With a literal list
406
+ * (inline or `as const`) the return type gains exactly those properties;
407
+ * with a list typed `MeInclude[]` they are optional, since the compiler
408
+ * cannot know which it holds.
409
+ *
410
+ * @example
411
+ * ```ts
412
+ * const me = await rekey.auth.getCurrentUser(accessToken, { include: ['entitlements', 'device'] });
413
+ * if (!me.entitlements.features.reports) throw new Forbidden();
414
+ * me.device?.status; // 'ACTIVE' when bound; a released or blocked device is a 401 instead
415
+ * ```
416
+ *
417
+ * @throws {RekeyError} `USER_TOKEN_INVALID` (401) if expired/forged/wrong-secret, or
418
+ * if the session's device was released or blocked.
387
419
  * @throws {RekeyError} `USER_TOKEN_WRONG_APPLICATION` (401) if the token was issued
388
420
  * by a different Application than the calling secret key represents.
421
+ * @throws {RekeyError} `VALIDATION_ERROR` (400) for an unknown `include` value.
422
+ * @throws {RekeyError} `BILLING_DISABLED` (403) or `API_KEY_SCOPE_INSUFFICIENT` (403)
423
+ * when `entitlements`, `subscription` or `licenses` is asked for and billing is off,
424
+ * or the key lacks `billing:read`.
389
425
  */
390
- getCurrentUser(accessToken: string): Promise<EndUserDto & {
391
- activeOrganizationId: string | null;
392
- }>;
426
+ getCurrentUser<const L extends readonly MeInclude[] = []>(accessToken: string, options?: {
427
+ include?: L;
428
+ }): Promise<CurrentUserDto & MeIncludedFor<L>>;
393
429
  /**
394
- * Update the end-user behind a presented access token their OWN record,
430
+ * Update the end-user behind a presented access token, their OWN record,
395
431
  * and only ever their own: the token identifies the subject, so there is no
396
432
  * user id to pass and no way to aim this at anyone else.
397
433
  *
@@ -419,21 +455,21 @@ declare class AuthClient {
419
455
  */
420
456
  updateCurrentUser(accessToken: string, input: {
421
457
  metadata?: Record<string, unknown> | null;
422
- }): Promise<EndUserDto & {
423
- activeOrganizationId: string | null;
424
- }>;
458
+ }): Promise<CurrentUserDto>;
425
459
  /**
426
460
  * Exchange a refresh token for a fresh {access, refresh} pair. The presented
427
- * refresh is revoked atomically call this **once** and store the new
461
+ * refresh is revoked atomically, call this **once** and store the new
428
462
  * `refreshToken` from the response immediately.
429
463
  *
430
464
  * @throws {RekeyError} `REFRESH_TOKEN_REUSED` (401) if you replay an already-used token.
431
465
  * This is a strong signal the original was leaked; treat as compromise.
432
466
  * @throws {RekeyError} `REFRESH_TOKEN_EXPIRED` (401) after the 30-day refresh window.
433
467
  */
434
- refresh(refreshToken: string): Promise<AuthResultDto>;
468
+ refresh(refreshToken: string, options?: {
469
+ device?: DeviceBindingRequest;
470
+ }): Promise<AuthResultDto>;
435
471
  /**
436
- * Revoke a refresh token. Idempotent no-op for unknown tokens. The
472
+ * Revoke a refresh token. Idempotent, no-op for unknown tokens. The
437
473
  * access token paired with this refresh remains valid until its short
438
474
  * (15 min) expiry; for true "log out everywhere" semantics, also clear
439
475
  * the access token from your client.
@@ -442,14 +478,14 @@ declare class AuthClient {
442
478
  signedOut: true;
443
479
  }>;
444
480
  /**
445
- * Request a password reset for an email. Always succeeds never tells you
481
+ * Request a password reset for an email. Always succeeds, never tells you
446
482
  * whether the email exists.
447
483
  *
448
484
  * **Branch on the result.** When the Application has an email transport
449
485
  * (BYO Resend/SMTP, or a deployment-wide `RESEND_DEFAULT_API_KEY`) Rekey sends
450
486
  * 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.
487
+ * the original contract and hands the raw token to you, a secret-key caller
488
+ * only, so you can deliver it with your own provider.
453
489
  *
454
490
  * @example
455
491
  * ```ts
@@ -473,7 +509,7 @@ declare class AuthClient {
473
509
  }>;
474
510
  /**
475
511
  * Authenticated password change. Pass the user's *current* access token.
476
- * On success, every refresh token for the user is revoked other devices
512
+ * On success, every refresh token for the user is revoked, other devices
477
513
  * are signed out.
478
514
  */
479
515
  changePassword(accessToken: string, input: ChangePasswordRequest): Promise<{
@@ -481,8 +517,8 @@ declare class AuthClient {
481
517
  }>;
482
518
  /**
483
519
  * 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.
520
+ * devices." The caller's access token remains valid until 15-min expiry,
521
+ * clear it client-side for full logout.
486
522
  */
487
523
  signOutEverywhere(accessToken: string): Promise<{
488
524
  revokedCount: number;
@@ -503,7 +539,7 @@ declare class AuthClient {
503
539
  verificationToken: string | null;
504
540
  }>;
505
541
  /**
506
- * Re-send a verification link to an address, with **no session** the
542
+ * Re-send a verification link to an address, with **no session**, the
507
543
  * sessionless sibling of `sendVerificationEmail`.
508
544
  *
509
545
  * This is the route for a user locked out by
@@ -511,17 +547,17 @@ declare class AuthClient {
511
547
  * session `sendVerificationEmail` needs, so a user whose first mail never
512
548
  * arrived cannot ask for another. Takes the address instead of a token.
513
549
  *
514
- * **Branch on the result**, exactly as with `requestPasswordReset` the
550
+ * **Branch on the result**, exactly as with `requestPasswordReset`, the
515
551
  * contract is the same one. It never throws for an unknown address and never
516
552
  * discloses whether the address exists, is already verified, or was mailed:
517
553
  * a publishable-key caller gets one constant body whatever happened. A
518
- * secret-key caller this SDK gets the real outcome, and the raw
554
+ * secret-key caller, this SDK, gets the real outcome, and the raw
519
555
  * `verificationToken` when the Application has no email transport configured,
520
556
  * so you can deliver it with your own provider.
521
557
  *
522
558
  * Pass `verifyUrl` containing `{token}` to template the link target. Unlike
523
559
  * `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
560
+ * link can be built at all, pass `verifyUrl`, or set the Application URL
525
561
  * (Panel → Application → Auth). Mailing a locked-out user a verification
526
562
  * message with no button in it helps nobody.
527
563
  *
@@ -565,7 +601,7 @@ declare class AuthClient {
565
601
  userAgent: string | null;
566
602
  ip: string | null;
567
603
  }>>;
568
- /** Revoke one session by id. Idempotent `{ revoked: false }` if it isn't this user's. */
604
+ /** Revoke one session by id. Idempotent, `{ revoked: false }` if it isn't this user's. */
569
605
  revokeSession(accessToken: string, sessionId: string): Promise<{
570
606
  revoked: boolean;
571
607
  }>;
@@ -578,7 +614,7 @@ declare class AuthClient {
578
614
  /**
579
615
  * Begin TOTP enrollment: mints a secret (as an `otpauthUrl` for the QR) and
580
616
  * 10 single-show backup codes. **Not enrolled until `confirmMfaSetup(...)`.**
581
- * Only SHA-256 hashes of the backup codes are stored show them once.
617
+ * Only SHA-256 hashes of the backup codes are stored, show them once.
582
618
  */
583
619
  mfaSetup(accessToken: string): Promise<{
584
620
  otpauthUrl: string;
@@ -591,7 +627,7 @@ declare class AuthClient {
591
627
  }>;
592
628
  /**
593
629
  * 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 }`.
630
+ * Backup codes are single-use, consumed on success. Returns `{ ok }`.
595
631
  */
596
632
  mfaChallenge(accessToken: string, code: string): Promise<{
597
633
  ok: boolean;
@@ -609,10 +645,16 @@ declare class AuthClient {
609
645
  }>;
610
646
  /**
611
647
  * Exchange the provider `code` for a Rekey session. Returns a
612
- * `SignInOutcome` branch on `mfaRequired` before reading `accessToken`.
648
+ * `SignInOutcome`, branch on `mfaRequired` before reading `accessToken`.
613
649
  * Verify the `state` CSRF value yourself before calling.
614
650
  */
615
- completeOAuth(provider: string, code: string): Promise<SignInOutcomeDto>;
651
+ completeOAuth(provider: string, code: string, options?: {
652
+ /** Bind the session to a device, see docs/devices.md. */
653
+ device?: {
654
+ fingerprint: string;
655
+ label?: string;
656
+ };
657
+ }): Promise<SignInOutcomeDto>;
616
658
  /** List the OAuth providers linked to the current user. */
617
659
  listOAuthIdentities(accessToken: string): Promise<Array<{
618
660
  provider: string;
@@ -625,7 +667,7 @@ declare class AuthClient {
625
667
  authorizationUrl: string;
626
668
  }>;
627
669
  /**
628
- * Complete an OAuth link attaches the provider identity to the current
670
+ * Complete an OAuth link, attaches the provider identity to the current
629
671
  * user. Refuses on unverified provider emails (account-takeover guard) or
630
672
  * when the provider account already belongs to a different user.
631
673
  */
@@ -648,7 +690,7 @@ declare class AuthClient {
648
690
  *
649
691
  * Re-exported from `@rekey.dev/shared-types` so the SDK, the API and the panel
650
692
  * 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
693
+ * tells you whether there is another window, you no longer have to infer it
652
694
  * by asking for one row more than you need.
653
695
  */
654
696
  export type { ListPage, PageMeta, Paged } from '@rekey.dev/shared-types';
@@ -706,7 +748,7 @@ declare class OrganizationsClient {
706
748
  */
707
749
  listMembers(accessToken: string, organizationId: string, page?: ListPage): Promise<Paged<OrganizationMemberDto>>;
708
750
  /**
709
- * Invite a user. Returns the raw token ONCE surface via your own
751
+ * Invite a user. Returns the raw token ONCE, surface via your own
710
752
  * email/share channel. OWNER + ADMIN only.
711
753
  */
712
754
  invite(accessToken: string, organizationId: string,
@@ -745,7 +787,7 @@ declare class OrganizationsClient {
745
787
  * Remove a member (or self). Refuses removing the last OWNER.
746
788
  *
747
789
  * 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`
790
+ * already removed), a no-op removal is not an error. Branch on `removed`
749
791
  * rather than assuming it is always `true`.
750
792
  */
751
793
  removeMember(accessToken: string, organizationId: string, targetEndUserId: string): Promise<{
@@ -753,7 +795,7 @@ declare class OrganizationsClient {
753
795
  }>;
754
796
  /**
755
797
  * Self-leave. An OWNER cannot leave (payment + benefits are tied to the
756
- * owner `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
798
+ * owner, `ORGANIZATION_OWNER_CANNOT_LEAVE`); transfer ownership via support
757
799
  * first, or demote yourself to ADMIN if there is another OWNER.
758
800
  */
759
801
  leave(accessToken: string, organizationId: string): Promise<{
@@ -774,7 +816,7 @@ declare class OrganizationsClient {
774
816
  }>;
775
817
  /**
776
818
  * Make `organizationId` the active org for this session (member-only).
777
- * Returns a fresh {accessToken, refreshToken} pair carrying the active org
819
+ * Returns a fresh {accessToken, refreshToken} pair carrying the active org,
778
820
  * **store both**. Subsequent entitlement reads (`billing.getEntitlements`)
779
821
  * then default to this org's view + shared pool without passing
780
822
  * `organizationId` explicitly. The active org survives token refresh until
@@ -782,7 +824,7 @@ declare class OrganizationsClient {
782
824
  */
783
825
  switch(accessToken: string, organizationId: string): Promise<AuthResultDto>;
784
826
  /**
785
- * Clear the active org switch the session back to the personal pool.
827
+ * Clear the active org, switch the session back to the personal pool.
786
828
  * Returns a fresh token pair (no active org); **store both**.
787
829
  */
788
830
  clearActive(accessToken: string): Promise<AuthResultDto>;
@@ -793,7 +835,7 @@ declare class LicensesClient {
793
835
  /**
794
836
  * Verify a license key + record an activation for this machine. Call
795
837
  * 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
838
+ * invalid licenses, never an HTTP error, so your software can loop
797
839
  * on the result without try/catch noise).
798
840
  *
799
841
  * `machineFingerprint` should be a stable identifier you derive client-
@@ -815,6 +857,173 @@ declare class LicensesClient {
815
857
  machineFingerprint: string;
816
858
  label?: string;
817
859
  }): Promise<LicenseVerifyResultDto>;
860
+ /**
861
+ * Give back the seat this machine holds; call it before a re-image or on
862
+ * uninstall so the next machine can verify. Same deterministic body as
863
+ * `verify`; `released: false` means the machine held no seat.
864
+ *
865
+ * @example
866
+ * ```ts
867
+ * await rekey.licenses.deactivate({ key, machineFingerprint });
868
+ * ```
869
+ */
870
+ deactivate(input: LicenseDeactivateRequest): Promise<LicenseDeactivateResultDto>;
871
+ /**
872
+ * The signed-in end-user's own licences, newest first:
873
+ * `GET /api/v1/users/me/licenses`, authorized by their access token. In an
874
+ * org-billed Application whose session acts for an organization, that
875
+ * organization's pooled licences are included too.
876
+ *
877
+ * No raw keys: only a hash is stored, so each row carries its display
878
+ * `keyPrefix`. Needs `billing:read` on a secret key.
879
+ *
880
+ * @example
881
+ * ```ts
882
+ * const { items } = await rekey.licenses.listMine(accessToken);
883
+ * const active = items.filter((l) => l.status === 'ACTIVE');
884
+ * ```
885
+ */
886
+ listMine(accessToken: string, page?: ListPage): Promise<Paged<EndUserLicenseDto>>;
887
+ }
888
+ /**
889
+ * End-users' devices (docs/devices.md), both surfaces.
890
+ *
891
+ * `list` / `release` are the SERVER surface: secret key only, addressed by
892
+ * end-user id, because they read and mutate OTHER users' devices.
893
+ *
894
+ * `listMine` / `releaseMine` are the END-USER surface, the one
895
+ * `DEVICE_LIMIT_REACHED` tells you to offer. They take that user's own access
896
+ * token and act only on their own devices, so they are what a "your signed-in
897
+ * machines" screen calls, and what lets a user release a machine themselves
898
+ * instead of contacting support.
899
+ */
900
+ declare class DevicesClient {
901
+ private readonly client;
902
+ constructor(client: Rekey);
903
+ /** An end-user's devices, newest activity first. Optional `status` filter. */
904
+ list(endUserId: string, options?: {
905
+ status?: DeviceStatusType;
906
+ limit?: number;
907
+ offset?: number;
908
+ }): Promise<Paged<DeviceDto>>;
909
+ /** Release a device: gives its slot back and revokes every session on it. */
910
+ release(deviceId: string, endUserId: string): Promise<{
911
+ device: DeviceDto;
912
+ sessionsRevoked: number;
913
+ }>;
914
+ /**
915
+ * The calling end-user's OWN devices, newest activity first.
916
+ *
917
+ * `GET /api/v1/users/me/devices`, authorized by the user's access token
918
+ * rather than by an end-user id. Operator notes (`blockedReason`) and IPs are
919
+ * not on this surface, which is why it resolves to `EndUserDeviceDto`.
920
+ *
921
+ * The device the current session is bound to is the one whose `id` matches
922
+ * the access token's `dev` claim (see {@link VerifiedAccessTokenClaims}), so
923
+ * a "your devices" screen can mark "this device" without a second call.
924
+ *
925
+ * @example
926
+ * ```ts
927
+ * const { items } = await rekey.devices.listMine(accessToken, { status: 'ACTIVE' });
928
+ * ```
929
+ */
930
+ listMine(accessToken: string, options?: {
931
+ status?: DeviceStatusType;
932
+ limit?: number;
933
+ offset?: number;
934
+ }): Promise<Paged<EndUserDeviceDto>>;
935
+ /**
936
+ * Release one of the calling end-user's own devices.
937
+ *
938
+ * This is the flow `DEVICE_LIMIT_REACHED` names: that refusal carries
939
+ * `details.limit` and `details.devices` (typed as `DeviceLimitDetails`), so
940
+ * you can show the user their machines and release one here rather than
941
+ * leaving them at a dead end.
942
+ *
943
+ * Gives the slot back and revokes every session minted on that device,
944
+ * INCLUDING the current one when it is the same device, so treat a release of
945
+ * `claims.dev` as a sign-out. Idempotent for an already-released device; a
946
+ * BLOCKED device refuses with `DEVICE_BLOCKED` (only an operator can unblock).
947
+ *
948
+ * @example
949
+ * ```ts
950
+ * try {
951
+ * await rekey.auth.signIn({ email, password, device: { fingerprint } });
952
+ * } catch (e) {
953
+ * if (e instanceof RekeyError && e.code === 'DEVICE_LIMIT_REACHED') {
954
+ * const { devices } = e.details as DeviceLimitDetails;
955
+ * // …let the user pick one, then, with a token from a session that has one:
956
+ * await rekey.devices.releaseMine(accessToken, devices[0]!.id);
957
+ * }
958
+ * }
959
+ * ```
960
+ */
961
+ releaseMine(accessToken: string, deviceId: string): Promise<{
962
+ device: EndUserDeviceDto;
963
+ sessionsRevoked: number;
964
+ }>;
965
+ }
966
+ /**
967
+ * Server-side end-user lookup (secret key only). `/users/me` answers "who is
968
+ * this token"; these answer "who is this id / email" for a backend that holds
969
+ * no token.
970
+ */
971
+ declare class UsersClient {
972
+ private readonly client;
973
+ constructor(client: Rekey);
974
+ /** Exact, case-insensitive email match in the calling Application. Throws END_USER_NOT_FOUND. */
975
+ getByEmail(email: string): Promise<EndUserDto>;
976
+ /** By id, scoped to the calling Application. Throws END_USER_NOT_FOUND. */
977
+ get(endUserId: string): Promise<EndUserDto>;
978
+ /**
979
+ * Import up to 500 users from another auth system in one call. Password
980
+ * hashes (argon2id or bcrypt) are stored as given and verified as-is at
981
+ * sign-in; bcrypt is upgraded to argon2id on first success. Existing
982
+ * addresses are skipped, never updated.
983
+ *
984
+ * @example
985
+ * ```ts
986
+ * const { created, skipped } = await rekey.users.import([
987
+ * { email: 'a@example.com', passwordHash: '$2b$10$…', emailVerified: true },
988
+ * { email: 'b@example.com', oauthIdentities: [{ provider: 'google', providerAccountId: '1234' }] },
989
+ * ]);
990
+ * ```
991
+ */
992
+ import(users: ImportUserInput[]): Promise<ImportUsersResult>;
993
+ }
994
+ export interface ImportUserInput {
995
+ email: string;
996
+ /** `$argon2id$…` or `$2a$`/`$2b$`/`$2y$…`. Omit for OAuth-only users. */
997
+ passwordHash?: string;
998
+ emailVerified?: boolean;
999
+ role?: string;
1000
+ metadata?: Record<string, unknown>;
1001
+ oauthIdentities?: Array<{
1002
+ provider: string;
1003
+ providerAccountId: string;
1004
+ email?: string;
1005
+ }>;
1006
+ }
1007
+ export interface ImportUsersResult {
1008
+ created: Array<{
1009
+ id: string;
1010
+ email: string;
1011
+ }>;
1012
+ skipped: Array<{
1013
+ email: string;
1014
+ reason: string;
1015
+ }>;
1016
+ /**
1017
+ * OAuth identities that were NOT linked because the provider account is
1018
+ * already attached to another end-user in this Application. The user was
1019
+ * still created; their sign-in through that provider lands on the OTHER
1020
+ * account until one of the two is fixed.
1021
+ */
1022
+ unlinked: Array<{
1023
+ email: string;
1024
+ provider: string;
1025
+ providerAccountId: string;
1026
+ }>;
818
1027
  }
819
1028
  declare class UsageClient {
820
1029
  private readonly client;
@@ -846,9 +1055,50 @@ declare class UsageClient {
846
1055
  endUserId?: string;
847
1056
  organizationId?: string;
848
1057
  }): Promise<UsageAggregateDto>;
1058
+ /**
1059
+ * The signed-in end-user's included quota, usage and remaining units this
1060
+ * period, per meter (or one meter with `{ meter }`). Computed by the code
1061
+ * `record` enforces with: a record of more than `remaining` is the one that
1062
+ * is refused (402 `USAGE_QUOTA_EXCEEDED`) or, on a priced meter, charged.
1063
+ *
1064
+ * Reads the personal quota, or the active organization's in an Application
1065
+ * that bills organizations; `{ organizationId }` (member-only) picks one.
1066
+ *
1067
+ * @example
1068
+ * ```ts
1069
+ * const { meters } = await rekey.usage.getRemaining(accessToken, { meter: 'api_calls' });
1070
+ * if (meters[0].remaining === 0) showUpgradePrompt();
1071
+ * ```
1072
+ */
1073
+ getRemaining(accessToken: string, opts?: {
1074
+ meter?: string;
1075
+ organizationId?: string;
1076
+ }): Promise<UsageRemainingDto>;
1077
+ /**
1078
+ * The same answer as `getRemaining`, for a subject you name instead of one
1079
+ * whose token you hold: `{ endUserId }`, `{ organizationId }`, or both to
1080
+ * read the organization as that member. Secret key only.
1081
+ */
1082
+ getRemainingFor(subject: {
1083
+ endUserId: string;
1084
+ organizationId?: string;
1085
+ } | {
1086
+ organizationId: string;
1087
+ endUserId?: string;
1088
+ }, opts?: {
1089
+ meter?: string;
1090
+ }): Promise<UsageRemainingDto>;
1091
+ /**
1092
+ * The Application's usage meters: the slugs `record` takes, their units,
1093
+ * whether each accepts records, and its fallback credit price. Secret key.
1094
+ */
1095
+ listMeters(opts?: {
1096
+ limit?: number;
1097
+ offset?: number;
1098
+ }): Promise<Paged<UsageMeterCatalogueEntryDto>>;
849
1099
  }
850
1100
  /**
851
- * A credit subject pass `endUserId` for a personal balance, or
1101
+ * A credit subject, pass `endUserId` for a personal balance, or
852
1102
  * `organizationId` for a shared org pool (owner+beneficiary billing).
853
1103
  */
854
1104
  export type CreditSubject = {
@@ -857,11 +1107,11 @@ export type CreditSubject = {
857
1107
  organizationId: string;
858
1108
  };
859
1109
  /**
860
- * Prepaid credits the "lead pack" / pay-as-you-go drawdown model. The
1110
+ * Prepaid credits, the "lead pack" / pay-as-you-go drawdown model. The
861
1111
  * customer's backend grants credits (by selling a CREDIT-kind plan, which
862
1112
  * grants automatically on payment) and draws them down per unit consumed.
863
1113
  *
864
- * All calls are server-to-server (secret key) and scoped to a `CreditSubject` —
1114
+ * All calls are server-to-server (secret key) and scoped to a `CreditSubject`,
865
1115
  * an end-user's personal balance, or an organization's shared pool.
866
1116
  */
867
1117
  declare class CreditsClient {
@@ -874,7 +1124,7 @@ declare class CreditsClient {
874
1124
  * `code: "CREDITS_INSUFFICIENT"` (HTTP 402) when the balance is too low.
875
1125
  *
876
1126
  * Pass `idempotencyKey` (e.g. the lead id) so a retried call never
877
- * double-charges a repeat returns the original result with `applied: false`.
1127
+ * double-charges, a repeat returns the original result with `applied: false`.
878
1128
  */
879
1129
  consume(input: ConsumeCreditsRequest & CreditSubject): Promise<ConsumeCreditsResultDto>;
880
1130
  /**
@@ -883,8 +1133,33 @@ declare class CreditsClient {
883
1133
  * subject); `limit` is capped at 200 server-side.
884
1134
  */
885
1135
  listLedger(subject: CreditSubject, limit?: number, offset?: number): Promise<Paged<CreditLedgerEntryDto>>;
1136
+ /**
1137
+ * Grant credits to an end-user or organization pool with the Application
1138
+ * key. Needs a key minted with the elevated `credits:grant` scope named:
1139
+ * `*` does not include it, so a default key gets 403
1140
+ * `API_KEY_SCOPE_INSUFFICIENT`.
1141
+ *
1142
+ * `idempotencyKey` is required; a repeat returns the original entry with
1143
+ * `applied: false` and grants nothing. `amount` is 1 to 1,000,000 per call.
1144
+ *
1145
+ * @example
1146
+ * ```ts
1147
+ * await rekey.credits.grant({ endUserId, amount: 500, idempotencyKey: `referral:${referralId}` });
1148
+ * ```
1149
+ */
1150
+ grant(input: KeyGrantCreditsRequest): Promise<ConsumeCreditsResultDto>;
1151
+ /**
1152
+ * The signed-in end-user's own credit ledger, newest first (the active
1153
+ * organization's pool in an Application that bills organizations, or the one
1154
+ * `{ organizationId }` names, member-only). Entries carry no `metadata`.
1155
+ */
1156
+ listMyLedger(accessToken: string, opts?: {
1157
+ organizationId?: string;
1158
+ limit?: number;
1159
+ offset?: number;
1160
+ }): Promise<Paged<SelfCreditLedgerEntryDto>>;
886
1161
  }
887
- /** What an end-user (or org) is entitled to right now from active subs. */
1162
+ /** What an end-user (or org) is entitled to right now, from active subs. */
888
1163
  export interface EntitlementsDto {
889
1164
  /** Feature flags + numeric limits, keyed by code. Gate your app on these. */
890
1165
  features: Record<string, boolean | number | string>;
@@ -907,7 +1182,7 @@ export interface EntitlementsDto {
907
1182
  * default 300) AND (b) the signature matches a constant-time compare.
908
1183
  *
909
1184
  * Use against the `X-Rekey-Signature` header and the raw request body
910
- * BYTES (not the parsed JSON any reserialization breaks the HMAC).
1185
+ * BYTES (not the parsed JSON, any reserialization breaks the HMAC).
911
1186
  *
912
1187
  * @example
913
1188
  * ```ts
@@ -933,16 +1208,37 @@ export declare function verifyWebhookSignature(args: {
933
1208
  }): boolean;
934
1209
  /** Verified claims of an RS256 end-user access token. */
935
1210
  export interface VerifiedAccessTokenClaims {
936
- /** Always `"eu_access"` other token types are refused. */
1211
+ /** Always `"eu_access"`, other token types are refused. */
937
1212
  typ: 'eu_access';
938
1213
  /** EndUser id. */
939
1214
  sub: string;
940
- /** Application the token is bound to check it against YOUR application id. */
1215
+ /** Application the token is bound to, check it against YOUR application id. */
941
1216
  applicationId: string;
942
1217
  /** Active organization id, when the session is acting as an org. */
943
1218
  oid?: string;
944
1219
  /** Operator id when this is an impersonation session (treat with care). */
945
1220
  imp?: string;
1221
+ /**
1222
+ * Refresh-token family this access token belongs to, the `sid` claim.
1223
+ *
1224
+ * Optional because the API only mints it when the session flow supplies one
1225
+ * (see `issueUserAccessToken`), so treat an absent `sid` as "this deployment
1226
+ * or flow did not bind one", never as a session that ended.
1227
+ *
1228
+ * Offline verification cannot see a revocation, the whole point of not
1229
+ * calling the API. Use this to correlate a token with your own record of the
1230
+ * session, so you can drop one you already know is gone.
1231
+ */
1232
+ sid?: string;
1233
+ /**
1234
+ * Device this session is bound to, the `dev` claim. Present only when the
1235
+ * sign-in bound a device.
1236
+ *
1237
+ * This is the id that matches a row from `devices.listMine()`, so a client
1238
+ * can mark "this device" in its own device list, and the id to pass to
1239
+ * `devices.releaseMine()` to sign the CURRENT machine out.
1240
+ */
1241
+ dev?: string;
946
1242
  /** App `tokenGeneration` at mint time (the API checks this; offline can't). */
947
1243
  gen?: number;
948
1244
  iat: number;
@@ -953,7 +1249,7 @@ export interface VerifyAccessTokenOptions {
953
1249
  * The Application this token must belong to. **Required.**
954
1250
  *
955
1251
  * This helper verifies RS256 tokens against the deployment's JWKS, and the
956
- * RS256 keypair is deployment-wide `SigningKey` has no `applicationId`
1252
+ * RS256 keypair is deployment-wide, `SigningKey` has no `applicationId`
957
1253
  * column, and `eu_access` tokens carry no `iss`/`aud`. So a token minted for
958
1254
  * ANY Application on the same deployment is cryptographically valid here.
959
1255
  * Without this, a multi-app self-host accepts another Application's end-user
@@ -961,24 +1257,24 @@ export interface VerifyAccessTokenOptions {
961
1257
  *
962
1258
  * (The HS256 default path is not affected: that key is derived per
963
1259
  * Application as `HMAC-SHA256(JWT_SECRET, applicationId:tokenGeneration)`, so
964
- * a foreign token fails the signature. This is the RS256 opt-in only which
1260
+ * a foreign token fails the signature. This is the RS256 opt-in only, which
965
1261
  * is exactly the path this function exists for.)
966
1262
  *
967
1263
  * Required rather than optional-with-a-warning: a security check nobody is
968
1264
  * 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
1265
+ * tell them to compare `claims.applicationId` afterwards, which made the
970
1266
  * shortest correct path the insecure one. 2.0.0 is not out yet, so this
971
1267
  * breaks rc callers rather than a stable contract.
972
1268
  */
973
1269
  applicationId: string;
974
1270
  /**
975
- * URL of the deployment's JWKS `https://<your-rekey>/.well-known/jwks.json`.
1271
+ * URL of the deployment's JWKS, `https://<your-rekey>/.well-known/jwks.json`.
976
1272
  * Fetched lazily and cached in-process for `cacheTtlMs` (default 5 minutes);
977
1273
  * an unknown `kid` triggers one immediate refetch so freshly rotated keys
978
1274
  * are picked up without waiting out the TTL.
979
1275
  */
980
1276
  jwksUrl?: string;
981
- /** Pre-fetched key set skips all network access. Takes precedence over `jwksUrl`. */
1277
+ /** Pre-fetched key set, skips all network access. Takes precedence over `jwksUrl`. */
982
1278
  jwks?: JwksDto;
983
1279
  /** Optional fetch override (test stubs, custom agents). */
984
1280
  fetch?: typeof fetch;
@@ -998,14 +1294,14 @@ export interface VerifyAccessTokenOptions {
998
1294
  now?: () => number;
999
1295
  }
1000
1296
  /**
1001
- * Verify an end-user ACCESS token **offline** no round-trip to the Rekey
1297
+ * Verify an end-user ACCESS token **offline**, no round-trip to the Rekey
1002
1298
  * API. Works only for Applications that opted into RS256 tokens
1003
1299
  * (`authConfig.tokenAlg = "RS256"`, Panel → Application → Auth); the default
1004
1300
  * HS256 tokens are symmetric and can only be verified by the API itself
1005
1301
  * (use `rekey.auth.getCurrentUser(token)` for those).
1006
1302
  *
1007
1303
  * Checks performed (same posture as the API's verifier):
1008
- * - header `alg` must be `RS256` and `kid` must exist in the JWKS
1304
+ * - header `alg` must be `RS256` and `kid` must exist in the JWKS,
1009
1305
  * a strict allowlist, immune to alg-confusion;
1010
1306
  * - RSA-SHA256 signature against that public key;
1011
1307
  * - `exp` in the future, `typ === "eu_access"` (refresh/MFA/MCP tokens
@@ -1015,6 +1311,11 @@ export interface VerifyAccessTokenOptions {
1015
1311
  * user deletion. The 15-minute access lifetime bounds both; for hard
1016
1312
  * revocation guarantees keep using `auth.getCurrentUser`.
1017
1313
  *
1314
+ * Nor can it see any server-side revocation: a locally verified token stays
1315
+ * valid until it expires, even after sign-out everywhere, a password change,
1316
+ * a session revoke or a device release. Call the API when immediate
1317
+ * revocation matters.
1318
+ *
1018
1319
  * Node-only (uses `node:crypto`). Returns the verified claims; throws
1019
1320
  * `RekeyError` on any failure.
1020
1321
  *
@@ -1037,24 +1338,29 @@ export interface VerifyAccessTokenOptions {
1037
1338
  * it moved inside: the shortest correct path should not be the one nobody
1038
1339
  * takes.
1039
1340
  *
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`.
1341
+ * @throws {RekeyError} `TOKEN_ALG_NOT_RS256`, token is HS256 (app hasn't opted in) or another alg.
1342
+ * @throws {RekeyError} `TOKEN_KID_UNKNOWN`, `kid` not in the JWKS (forged, or key deleted).
1343
+ * @throws {RekeyError} `USER_TOKEN_EXPIRED`, `exp` passed; refresh the session.
1344
+ * @throws {RekeyError} `USER_TOKEN_INVALID`, malformed, bad signature, or wrong `typ`.
1044
1345
  */
1045
1346
  export declare function verifyAccessToken(token: string, options: VerifyAccessTokenOptions): Promise<VerifiedAccessTokenClaims>;
1046
1347
  declare class BillingClient {
1047
1348
  private readonly client;
1048
1349
  constructor(client: Rekey);
1049
1350
  /**
1050
- * List the calling Application's active plans. Public pricing pages
1351
+ * List the calling Application's active plans. Public, pricing pages
1051
1352
  * typically render straight from this. Application API key only; no
1052
1353
  * user JWT needed.
1053
1354
  *
1054
- * `amount` is in the smallest currency unit (cents/paise/sen) never
1355
+ * `amount` is in the smallest currency unit (cents/paise/sen), never
1055
1356
  * a float. Format on display: `${amount / 100} ${currency}`.
1357
+ *
1358
+ * `checkout.ready` is false when a buyer sent to checkout for the plan would
1359
+ * be refused, so a pricing page can hide it. Why is on the operator plan
1360
+ * list, not here. Keep the free tier in: with no provider connected it reads
1361
+ * `ready: false` and still applies to every signed-in user.
1056
1362
  */
1057
- getPlans(page?: ListPage): Promise<Paged<PlanDto>>;
1363
+ getPlans(page?: ListPage): Promise<Paged<PublicPlanDto>>;
1058
1364
  /**
1059
1365
  * Fetch the current end-user's active subscription, or `null` if they
1060
1366
  * have none. Returns the most recent ACTIVE / PENDING / PAST_DUE row.
@@ -1062,7 +1368,7 @@ declare class BillingClient {
1062
1368
  * Pass the user's access token (the SDK puts it in `X-Rekey-User-Token`).
1063
1369
  *
1064
1370
  * `opts.includeEnded` falls back to the most recent CANCELED/EXPIRED
1065
- * subscription **only when the answer would otherwise be null** for a
1371
+ * subscription **only when the answer would otherwise be null**, for a
1066
1372
  * billing page that has to tell a former subscriber what they were on and
1067
1373
  * when it ended, rather than showing them the same blank state as somebody
1068
1374
  * who never subscribed. It can never replace a live subscription, so it is
@@ -1079,13 +1385,22 @@ declare class BillingClient {
1079
1385
  /**
1080
1386
  * Start a hosted-checkout session. Returns the URL to redirect the user
1081
1387
  * to and the local PENDING Subscription row. Subscription activation
1082
- * happens via the provider's webhook not synchronously here.
1388
+ * happens via the provider's webhook, not synchronously here.
1083
1389
  *
1084
1390
  * Pass `couponCode` to apply a discount. The whole checkout fails if the
1085
1391
  * coupon doesn't validate (typed `RekeyError` with the precise reason).
1086
1392
  *
1393
+ * A buyer who has already used their free trial is refused with
1394
+ * `BILLING_TRIAL_ALREADY_USED` (409). The escape hatch is
1395
+ * `allowWithoutTrial: true` AND a fresh `Idempotency-Key`, but send it only
1396
+ * after the buyer has been told they are paying today. Read
1397
+ * {@link getTrialEligibility} and render the paid price instead of retrying
1398
+ * blindly: a buyer who merely abandoned a trial checkout still reads
1399
+ * `eligible: true`, and acknowledging on their behalf charges them today for
1400
+ * the trial the next checkout was about to grant.
1401
+ *
1087
1402
  * If the Application's billing subject is **org** (Panel → Application →
1088
- * Billing → Subject), an individual can't hold a subscription you MUST
1403
+ * Billing → Subject), an individual can't hold a subscription, you MUST
1089
1404
  * pass `organizationId` of a team the user owns/admins. Omitting it throws
1090
1405
  * `RekeyError` `code: "BILLING_ORGANIZATION_REQUIRED"`.
1091
1406
  *
@@ -1103,6 +1418,74 @@ declare class BillingClient {
1103
1418
  createCheckout(accessToken: string, input: CreateCheckoutRequest & {
1104
1419
  couponCode?: string;
1105
1420
  }): Promise<CheckoutResultDto>;
1421
+ /**
1422
+ * Put the calling end-user on the Application's free tier
1423
+ * (`billingConfig.defaultPlanSlug`). No payment provider is involved and none
1424
+ * needs to be configured: the plan costs nothing.
1425
+ *
1426
+ * This is how a freemium product hands a new signup their included credits,
1427
+ * licence or quota. `defaultPlanSlug` alone covers only the read-time half
1428
+ * (feature flags and included usage); CREDIT and LICENSE entitlements are
1429
+ * stateful and need a real subscription, which is what this creates.
1430
+ *
1431
+ * **Idempotent**, and the answer says which happened: `activated: true` is a
1432
+ * first activation (201, `subscription.activated` emitted), `activated:
1433
+ * false` means they were already entitled and nothing was written,
1434
+ * re-provisioned or re-announced.
1435
+ *
1436
+ * Pass `organizationId` on an org-billed Application; the caller must be an
1437
+ * OWNER or ADMIN of it. Omit it and the session's active organization is used.
1438
+ *
1439
+ * @throws {RekeyError} `BILLING_NO_FREE_PLAN` (404) when the Application
1440
+ * nominates no default plan; `BILLING_FREE_PLAN_NOT_FREE` (409) when that
1441
+ * plan charges money, use {@link createCheckout} instead;
1442
+ * `BILLING_FREE_TIER_ALREADY_CLAIMED` (409) when the plan grants credits or a
1443
+ * licence and this caller already claimed it for a different beneficiary.
1444
+ *
1445
+ * @example
1446
+ * ```ts
1447
+ * const { subscription, activated } = await rekey.billing.subscribe(accessToken);
1448
+ * if (activated) welcomeWithStarterCredits(subscription);
1449
+ * ```
1450
+ */
1451
+ subscribe(accessToken: string, input?: {
1452
+ organizationId?: string;
1453
+ }): Promise<{
1454
+ subscription: SubscriptionDto;
1455
+ activated: boolean;
1456
+ }>;
1457
+ /**
1458
+ * Whether THIS buyer may start each plan's free trial, under the
1459
+ * Application's `trialPolicy`.
1460
+ *
1461
+ * Read this before offering a trial: a buyer who is not eligible should be
1462
+ * shown the paid price, not a trial that checkout refuses with
1463
+ * `BILLING_TRIAL_ALREADY_USED`. Feed the result straight into
1464
+ * `<PricingTable trialEligibility={…}>` from `@rekey.dev/react`.
1465
+ *
1466
+ * **Advisory.** The authoritative decision is taken under a lock at checkout,
1467
+ * so two tabs can both read `eligible: true` and only one gets the trial.
1468
+ * Treat a 409 at checkout as normal, not as a contradiction.
1469
+ *
1470
+ * **Provider-dependent.** `PLAN_TRIAL_MISCONFIGURED` can be the resolved
1471
+ * provider's answer, so the response echoes `provider`; re-read this when the
1472
+ * buyer changes processor. Pass `country` (ISO 3166-1 alpha-2) to steer the
1473
+ * geo router the way {@link getProviders} does.
1474
+ *
1475
+ * @example
1476
+ * ```ts
1477
+ * const { items, policy } = await rekey.billing.getTrialEligibility(accessToken);
1478
+ * const pro = items.find((i) => i.planSlug === 'pro');
1479
+ * const label = pro?.eligible ? `Start ${pro.trialDays} days free` : 'Subscribe';
1480
+ * ```
1481
+ */
1482
+ getTrialEligibility(accessToken: string, opts?: {
1483
+ organizationId?: string;
1484
+ planSlug?: string;
1485
+ limit?: number;
1486
+ offset?: number;
1487
+ country?: string;
1488
+ }): Promise<TrialEligibilityDto>;
1106
1489
  /**
1107
1490
  * Validate a coupon for the current user against a plan, *without*
1108
1491
  * applying it. Render "$50 off" on a pricing page before submit.
@@ -1116,7 +1499,7 @@ declare class BillingClient {
1116
1499
  /**
1117
1500
  * List the billing providers configured + enabled for this Application,
1118
1501
  * 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
1502
+ * `country` (ISO 3166-1 alpha-2) when you have it, the panel/SDK will
1120
1503
  * surface India-specific providers (Razorpay) for IN-country users, etc.
1121
1504
  *
1122
1505
  * Returns the resolved country (echoed back from the server's view of
@@ -1125,7 +1508,7 @@ declare class BillingClient {
1125
1508
  */
1126
1509
  getProviders(country?: string): Promise<ProvidersListDto>;
1127
1510
  /**
1128
- * Resolve the calling end-user's current entitlements feature flags +
1511
+ * Resolve the calling end-user's current entitlements, feature flags +
1129
1512
  * limits, the live credit balance, and the raw entitlement list, unioned
1130
1513
  * across their active subscriptions (and subscriptions of orgs they belong
1131
1514
  * to). Pass `{ organizationId }` (member-only) for that org's view + shared
@@ -1140,10 +1523,61 @@ declare class BillingClient {
1140
1523
  getEntitlements(accessToken: string, opts?: {
1141
1524
  organizationId?: string;
1142
1525
  }): Promise<EntitlementsDto>;
1526
+ /**
1527
+ * The same union as `getEntitlements`, for an end-user you name rather than
1528
+ * one whose token you hold. Secret key only, for a licence server, a
1529
+ * support tool or a batch job.
1530
+ *
1531
+ * @example
1532
+ * ```ts
1533
+ * const { features } = await rekey.billing.getEntitlementsFor(endUserId);
1534
+ * if (features.max_devices !== undefined) capDevices(features.max_devices);
1535
+ * ```
1536
+ */
1537
+ getEntitlementsFor(endUserId: string, opts?: {
1538
+ organizationId?: string;
1539
+ }): Promise<EntitlementsDto>;
1540
+ /**
1541
+ * One feature for the calling end-user: `{ key, granted, value }`, where
1542
+ * `value` is what `features[key]` holds in `getEntitlements` (null when
1543
+ * nothing grants it) and `granted` is `Boolean(value)`. The subject is the
1544
+ * one `getCurrentUser(token, { include: ['entitlements'] })` resolves, or
1545
+ * the organization you pass (member-only).
1546
+ *
1547
+ * @example
1548
+ * ```ts
1549
+ * const { value } = await rekey.billing.getFeature(accessToken, 'projects');
1550
+ * if (typeof value === 'number' && count >= value) throw new LimitReached();
1551
+ * ```
1552
+ */
1553
+ getFeature(accessToken: string, key: string, opts?: {
1554
+ organizationId?: string;
1555
+ }): Promise<FeatureCheckDto>;
1556
+ /**
1557
+ * Whether the calling end-user holds a feature: `Boolean(value)`, the test
1558
+ * `if (features[key])` makes, so a false flag, a 0 limit and an unknown key
1559
+ * are all `false`. Use `getFeature` to read a numeric limit.
1560
+ *
1561
+ * @example
1562
+ * ```ts
1563
+ * if (!(await rekey.billing.hasFeature(accessToken, 'reports'))) throw new Forbidden();
1564
+ * ```
1565
+ */
1566
+ hasFeature(accessToken: string, key: string, opts?: {
1567
+ organizationId?: string;
1568
+ }): Promise<boolean>;
1569
+ /** `getFeature` for an end-user you name. Secret key only, like `getEntitlementsFor`. */
1570
+ getFeatureFor(endUserId: string, key: string, opts?: {
1571
+ organizationId?: string;
1572
+ }): Promise<FeatureCheckDto>;
1573
+ /** `hasFeature` for an end-user you name. Secret key only. */
1574
+ hasFeatureFor(endUserId: string, key: string, opts?: {
1575
+ organizationId?: string;
1576
+ }): Promise<boolean>;
1143
1577
  /**
1144
1578
  * Cancel the calling end-user's current subscription.
1145
1579
  *
1146
- * Defaults to cancelling **at period end** the user keeps what they paid
1580
+ * Defaults to cancelling **at period end**, the user keeps what they paid
1147
1581
  * for until the period they already bought runs out. A provider-backed
1148
1582
  * subscription therefore stays ACTIVE with `cancelAt` set, and the provider
1149
1583
  * webhook is what eventually terminates it; read `cancelAt` on the returned
@@ -1151,7 +1585,7 @@ declare class BillingClient {
1151
1585
  * `{ atPeriodEnd: false }` to end it immediately, forfeiting the remainder.
1152
1586
  *
1153
1587
  * PENDING checkouts (and anything with no provider-side record) are
1154
- * cancelled locally straight away regardless of the flag there is nothing
1588
+ * cancelled locally straight away regardless of the flag, there is nothing
1155
1589
  * at the provider to schedule against.
1156
1590
  *
1157
1591
  * Pass `organizationId` when the subscription belongs to a team; the caller