8 min read

Most of 2.2.0 is about the day after launch: a customer who cannot sign in, a teammate who should see one part of the panel and not the rest, a user base sitting in another system, and money that changes hands somewhere Rekey never sees. Here is what is new, what each piece is for, and the steps or the request that uses it. Several of these are also short walkthroughs, about a minute each, on guides.

The support console

Open an end-user and the common remedies are one row of buttons under their profile, headed Support actions. Each one is a single call, and each is recorded in the activity trail with your operator id.

  • Unlock account clears the lockout the failed sign-ins built up. Unlocking an account that is not locked is a no-op and still answers 200.
  • Resend verification mints a fresh token and sends the verification mail again. The button appears only while the address is unverified.
  • Send password reset starts the same reset the customer's own forgot-password link starts. The token goes to them, never to you.
  • Reset devices frees every active device slot and signs them out on those machines. Blocked devices are left blocked.
  • Sign out everywhere revokes every live session. Access tokens already minted run to their own expiry; this ends the ability to obtain new ones.
The Rekey panel end-user page showing the tab strip and the Support actions row
One end-user, with the support actions and the tab strip above them.

The two that put mail in somebody's inbox open a dialog rather than firing on click. Resending verification takes an optional reason; the password reset requires one, because at the recipient's end an unrequested reset mail and someone who got into your panel look the same. The reason is stored against your operator id, so six months later the trail says who and why.

The end-user page, in tabs

That page used to be one long scroll. It is now a breadcrumb and a tab strip: Overview, Subscriptions, Devices, Credits, Security and Data & privacy. Overview keeps the profile, the support actions, four stat tiles and recent activity. Subscriptions and Credits are shown only to an operator who may read billing, so a support teammate restricted away from money does not see them at all.

Links you already have to the old single page still land on the overview, so a bookmark or a link pasted in a ticket keeps working.

Devices

A session can now be bound to a machine. Every endpoint that mints a session takes an optional device, the device is registered before any token is issued, and the access token carries a dev claim that refresh keeps bound to that machine.

POST /api/v1/auth/sign-in
{
  "email": "[email protected]",
  "password": "...",
  "device": { "fingerprint": "jordan-macbook-9f2c", "label": "MacBook Pro" }
}

A max_devices FEATURE entitlement caps how many a person may hold at once. Over the cap, sign-in answers DEVICE_LIMIT_REACHED and puts the active devices in details, so your app can offer to release one instead of showing a dead end. Set authConfig.deviceBinding to required when sign-in must name a device, or leave it optional.

The Devices tab on a Rekey end-user page, listing two active devices with release and block actions
The Devices tab: what each machine is, when it was last seen, and what you can do to it.

The Devices tab lists every machine the person has signed in from, newest activity first, filtered by All, Active, Released or Blocked. Release frees the slot and ends that device's sessions, leaving their other sessions alone. Block refuses sign-in from that fingerprint and takes an optional reason recorded on the security event; Unblock lifts it. Release all is the same as the support row's Reset devices.

Customers can do the routine part themselves. GET /api/v1/users/me/devices lists their own machines, and the SDKs reach it under their own naming: the React client with listMyDevices() and releaseMyDevice(), the Node SDK with devices.listMine() and devices.releaseMine(). Blocking stays with operators.

What a teammate may do

Until now a workspace MEMBER either saw an Application or did not. Now you can also say what they may do inside it. On the Team page, each member row carries a Scopes block: tick Restrict, then set a level for each of the seven areas and save.

The Team page in the Rekey panel, showing members, application access and the invite form
The Team page. Scopes live on each MEMBER row, next to the application grants.

The areas are End-users, Billing, Sign-in configuration, Developer, Organizations, Activity and Overview. Each takes None, Read or Read & write, and write always implies read. A grant decides which applications; scopes decide what. The two only ever narrow each other, and a caller gets the intersection.

A member who cannot see an Application still gets a 404, so the existence of an app is not leaked. One who can see it but lacks the scope gets a 403 naming the scope they need. /me returns the caller's scopes and the panel builds its navigation from them, so a restricted teammate sees a smaller panel rather than a wall of refusals.

What is never a scope

Grants, roles, invitations, lifecycle, impersonation and erasure stay with owners and admins. No scope grants them, which is what stops a restricted member from widening their own access. Nothing changes in an existing workspace until an admin restricts somebody.

Deciding what Rekey sends

Under Developer the Email section is now four tabs: Settings, Templates, Delivery and Suppressions.

Settings holds the master switch for the Application, headed Email is on or Email is off. Turning it off stops every mail this Application sends to its end-users: verification, reset, magic link, welcome, dunning. Workspace mail, such as operator invitations, is separate and unaffected.

The Templates tab of a Rekey Application's email settings, one row per email event with a sending state
One row per email, with its event key, whether it is sending, and the switch.

Templates is the switch per email. Each row names the event and its key, password_reset, email_verification, magic_link_signin, welcome, mfa_enabled, password_changed and the billing ones, with Turn off or Turn on beside it. Turning off mail the live sign-in configuration depends on is refused with EMAIL_EVENT_REQUIRED_BY_AUTH_CONFIG, in both directions: you cannot switch off the password reset while password sign-in is on, and you cannot switch password sign-in on while the reset mail is off. The row above says so and names the tab that resolves it.

Delivery is the send log, and it is the reason the switches are safe to use. A mail that was not sent is written there as suppressed with the reason, so “why did they never get it” stays answerable. A suppressed send also withholds the reset or magic-link token rather than handing it back to the caller.

Free trials, one per buyer

Trials are available again. A plan carries trialDays and a buyer gets one trial per Application, counted in a ledger rather than recomputed from the plan at every checkout. Ask GET /api/v1/billing/trial-eligibility whether this buyer may have one, and render “Start 14 days free” or “Subscribe” from the answer. A repeat trialist is refused at checkout with BILLING_TRIAL_ALREADY_USED.

The rule, the way out of that refusal, and the one combination that is still refused outright are all in the trials post.

Bringing a user base across

Moving onto Rekey no longer means asking every customer to reset their password. POST /api/v1/users/import takes the hashes you already have, up to 500 users per call, with a secret key.

POST /api/v1/users/import
{
  "users": [
    {
      "email": "[email protected]",
      "passwordHash": "$argon2id$v=19$m=65536,t=3,p=4$...",
      "emailVerified": true
    }
  ]
}

Argon2id and bcrypt are both accepted and verified as given; a bcrypt hash is upgraded to argon2id on that user's first successful sign-in. OAuth identities can be listed alongside, so a social-login user is not re-prompted. The whole batch is validated before any row is written, an address that already exists is skipped rather than updated, and the response says which rows were created and which were skipped with why.

Subscriptions you sold before Rekey

Under Billing, the Imports tab brings those across. Click Import subscriptions, choose what happens to an email Rekey does not know (skip and list them, or create an unlinked end-user), and Preview the import. Rekey reads a paginated endpoint you host and records, per row, what would happen and why.

Nothing is written until you read that preview and press Apply and import, which names the count and then asks you to type the application slug to confirm. An import never overwrites a live subscription and never matches an erased user.

Billing Rekey never sees

The external provider is for sales that happen somewhere else: your own billing stack, an invoicing tool, a marketplace, a platform you are still migrating off. Rekey never calls it and never sends a buyer to it. It is inbound only, so the geo router never picks it and the public provider list never advertises it.

Your system posts signed events to a per-Application endpoint, using the same envelope and signature scheme as the webhooks Rekey sends you:

POST /api/v1/webhooks/billing/external/<app-slug>
X-Rekey-Signature: t=<unix seconds>,v1=<hex>

{ "eventId": "evt_1", "type": "subscription.activated",
  "occurredAt": "2026-09-20T09:18:00Z", "data": { ... } }

subscription.activated is the one event a sender needs: it creates the subscriber if unknown, then activates, renews, recovers from past due, or rebinds a plan change. Posting it on every change in your system is correct and idempotent, keyed on eventId. subscription.canceled, subscription.past_due and the payment events fill in the rest, and ping is the first thing to send while you check the secret, the URL and the signature agree. Rekey activates, renews and cancels from these, provisions entitlements and records payments. Cancelling one of these subscriptions through Rekey is refused, because the money lives elsewhere.

Speed, and what to read before upgrading

The panel got noticeably quicker in this release. A signed-in operator request used to read the operator, the session and the membership before doing anything; those reads are cached per API process now and a warm request makes none of them. On the same data, the application overview page went from 48 statements to 9.

The application overview page in the Rekey panel with stat tiles, a sign-ups chart and configuration links
The application overview, the page that went from 48 database statements to 9.

Revocation is still immediate. Sign-out, sign out everywhere, a revoked session, a password change or reset, and any role, scope, grant or membership change drop the cached entry at once, and across replicas over Redis. The measurements and the trade-offs are in a faster panel.

If you self-host, read upgrading to 2.2.0 before you deploy: this release has migrations that rewrite rows sign-in depends on, and the production compose file now requires secrets it did not require in 2.1.x.

What you can do now in Rekey 2.2.0 | Rekey