Automatic Deletion of Inactive Accounts

Accounts nobody uses are a liability: they are personal data you keep for no reason, and under data-protection rules such as the GDPR you are expected to keep personal data only as long as you need it. Gate can clean them up for you, on terms you set, and warns people by email before it deletes their account. People without an email address (Telegram sign-ins) are never touched unless you explicitly allow deleting them without a warning.

It is made of four rules. A rule belongs to one project, does nothing until you switch it on, and applies to the end users of that project only.

RulekindWhat it doesPlan
Delete unconfirmed accountsdelete_unverifiedA person signs up and never confirms their email address. They get a warning, then the account is deleted.Free and above
Remind people to confirm their emailremind_unverifiedOne reminder email to people who have not confirmed their address.Pro and above
Remove roles when their deadline passesexpire_rolesA member of an organization whose expires_at has passed loses the membership, and the role with it.Pro and above
Delete inactive accountsdelete_inactiveA person has not signed in for a number of months. They get a warning, then the account is deleted unless they sign in first.Pro and above

Turning on a rule your plan does not include is refused with 403 and the code plan_required.


In the dashboard

Open Users & sign-in → Rules (/dashboard/gate?tab=rules). The tab shows the four rules for the project selected in the project switcher. Each has an on/off switch and its settings.

  • Preview. Before you switch a deletion rule on, the tab tells you how many people it would affect right now. A rule also acts on people who already exist, not only on new sign-ups, so look at the number first.
  • Confirmation. Switching on a rule that deletes accounts, or removes roles, asks you to confirm.
  • Plan. A rule your plan does not include is locked, with a link to the plans. If a plan change switched a rule off, the tab says so; your settings are kept, and the rule stays off until you switch it on again.
  • Activity. Under the rules is the list of what has been scheduled and done in the project: pending, done, cancelled and failed, with the reason for a cancellation. People appear by identity ID; the list holds no email addresses.

Settings and bounds

Delete unconfirmed accounts

{ "delete_after_days": 14, "warn_before_days": 3 }
FieldBoundsMeaning
delete_after_days2 to 365Days from sign-up to deletion.
warn_before_days1 to delete_after_days minus 1How many days before deletion the warning email goes out.

Only people whose email address is still unconfirmed are concerned. Signing up with Telegram counts as confirmed, so those accounts are never touched by this rule.

Remind people to confirm their email

{ "remind_after_hours": 24 }
FieldBoundsMeaning
remind_after_hours1 to 720Hours after sign-up that the reminder is sent.

One reminder per person, ever. Only people who sign up after you switch the rule on are reminded, and not registrations older than 30 days.

Remove roles when their deadline passes

This rule takes no settings (send {} or leave params out). The deadlines are on the members themselves: see Role deadlines. When a deadline passes, the membership is removed, and the person loses the role and what it gave them. It is the only rule that acts without an email, because nobody's account is deleted.

While the rule is off, or your plan does not include it, deadlines are stored but not applied: members keep their role after the deadline until you remove it yourself.

Delete inactive accounts

{ "inactive_after_months": 12, "warn_before_days": 30, "include_without_address": false }
FieldBoundsMeaning
inactive_after_months1 to 120Months without a sign-in before the account is deleted. The warning goes out warn_before_days days earlier.
warn_before_days7 to 90, and shorter than the inactivity periodDays of notice. A warning shorter than a week is not a warning.
include_without_addresstrue or false (default false)Whether to also delete people who have no email address.

People who sign in with Telegram have no email address, so nothing can warn them. With include_without_address off, they are never touched. With it on, they are deleted without any warning once they have been inactive for the period. Switch it on only if that is what you want to tell your users.

What counts as activity. The date of a person's last sign-in, by any method. Sign-ins were not recorded before this feature existed, so a person with no recorded sign-in counts as active from the day they joined your project, but not from before recording began. Nobody is deleted because of a sign-in we never saw: the earliest possible deletion is the inactivity period after that point.


Who a rule touches

A rule acts only on a person who is all of these:

  • an end user of this project: an account made with the user schema (the ordinary Gate accounts), or, for the inactive-accounts rule with include_without_address, a Passepartout (Telegram) account;
  • active. An account you deactivated yourself is your decision, and is left alone;
  • a member of this project and no other. A person who has accounts in two of your projects is never touched by a rule of either, and neither is an account whose project we cannot tell.

How it runs

You do not run anything. The rules are checked periodically: role deadlines within about a minute, unconfirmed accounts every few minutes, inactive accounts about hourly. A person who matches a rule is scheduled, and the schedule is visible in the Activity list and through GET /v1/gate/lifecycle/actions.

Deletion takes two steps, in this order, and the second can never happen without the first:

  1. The warning email goes to the person. It names the project and the date of deletion.
  2. The deletion happens on that date, not before. If the rule has been changed in between, or the warning went out later than planned, the person still gets the full notice, and the deletion is never earlier than the date in the email.

Everything is re-checked right before an email is sent and right before an account is deleted. If the person has confirmed their email, signed in, been deleted another way, or been deactivated, or if the rule has been switched off, changed, removed, or is no longer in your plan, the scheduled step is cancelled and nothing is sent or deleted. Switching a rule off, changing its settings, or deleting it cancels what it had scheduled; a warning that has already gone out is not recalled and is never sent twice to the same person for the same stretch of inactivity.

If the email cannot be delivered, it is retried for up to 72 hours. If it still has not gone out, the step is marked failed and the person is left as they are: there is no deletion without a warning that was sent.

Deletion is the same deletion as DELETE /v1/gate/identities/{id}: the identity and its sessions are deleted, together with the records that tie the person to your projects and organizations and their active-user row for the current month, and your endpoints receive an identity.deleted webhook.

Some records are not removed with the person. Each stays until its own retention runs out, and is then deleted:

  • Audit-log entries that name the person (a sign-in, with the address and user agent): 7 days on Free, 30 on Pro, 90 on Advanced. See Audit Log.
  • Webhook and delivery history, including the identity.deleted delivery itself: 7 days on Free, 90 on Pro, 180 on Advanced and Contract, as on Plans, limits and refusals.
  • Active-user counts of closed months, which are what was billed: thirteen months.
  • The finished entries of the Activity list, which name the person by ID and hold no address: 90 days.

To be careful with large backlogs, a rule schedules at most a few hundred people per pass, so switching a rule on in a project with many old accounts spreads the emails over several passes instead of sending them in one burst.

The emails

The emails are sent from Verne's address (noreply@vernesoft.com) and carry your project's name in the subject and the text. They are in English for now; sending from your own domain, and in your users' languages, is planned.

If your project is still called "Default project", the emails use the name of your account instead, because "Default project" tells the reader nothing. Set the account name under Settings in the dashboard.

There are three, one for each rule that writes to the person: a warning for an unconfirmed account, a reminder to confirm, and a warning for an inactive account. Each says what will happen, on what date (UTC), and what the person can do to keep the account: confirm the email address, or sign in once.


API

All routes need an API key with the gate scope. The project is the key's project: nothing in the path or the body names one. Errors come in the usual { "error": { "code", "message", "request_id" } } shape (see Error Format).

MethodPathDescription
GET/v1/gate/lifecycle/rulesAll four rules, set up or not
PUT/v1/gate/lifecycle/rules/{kind}Create, change, switch on or off
DELETE/v1/gate/lifecycle/rules/{kind}Remove the rule and cancel what it scheduled
POST/v1/gate/lifecycle/rules/{kind}/previewHow many people a rule would affect right now
GET/v1/gate/lifecycle/actionsWhat has been scheduled and done

{kind} is one of delete_unverified, remind_unverified, expire_roles, delete_inactive. Anything else is 404.

List the rules

curl https://api.vernesoft.com/v1/gate/lifecycle/rules \
  -H 'Authorization: Bearer vrn_9f8a7...'

(The real response lists all four kinds; two are shown.)

FieldMeaning
tierYour plan: free, pro, advanced or contract.
configuredA rule has been saved for this kind.
enabledThe rule is running. Nothing is on by default.
paramsThe saved settings, or null.
availableWhether your plan includes the rule. On Free only delete_unverified is true.
disabled_reason"plan" when a plan change switched the rule off. Your settings are kept. Moving back up does not switch it on again; send PUT with "enabled": true.
enabled_at, updated_atWhen the rule was last switched on, and last written.
pendingHow many people have something scheduled by this rule.

Create or change a rule

curl -X PUT https://api.vernesoft.com/v1/gate/lifecycle/rules/delete_unverified \
  -H 'Authorization: Bearer vrn_9f8a7...' \
  -H 'Content-Type: application/json' \
  -d '{
    "enabled": true,
    "params": { "delete_after_days": 14, "warn_before_days": 3 }
  }'

enabled is required. params may be left out to keep the saved settings, which is how you switch a rule off and on again without retyping them; on a first write it is required, except for expire_roles, which takes none. Unknown fields are refused.

Switching a rule off, or changing its settings, cancels what it had scheduled. Switching a rule off is never refused. Switching one on that your plan does not include is:

{
  "error": {
    "code": "plan_required",
    "message": "The Free plan does not include this rule. Move to Pro to use it.",
    "request_id": "req_..."
  }
}
StatusCodeWhen
400invalid_payloadA setting is out of bounds or missing; the message names it.
403plan_requiredThe rule is not in your plan.
404not_foundNo such rule.

Remove a rule

curl -X DELETE https://api.vernesoft.com/v1/gate/lifecycle/rules/delete_unverified \
  -H 'Authorization: Bearer vrn_9f8a7...'

Returns 204 No Content, also when there was no such rule. What it had scheduled is cancelled; the history of what it did stays in the actions list.

Preview a rule

Asks how many people a rule with these settings would affect right now. It writes nothing and works whether or not the rule is on, so you can look before you switch it on.

curl -X POST https://api.vernesoft.com/v1/gate/lifecycle/rules/delete_inactive/preview \
  -H 'Authorization: Bearer vrn_9f8a7...' \
  -H 'Content-Type: application/json' \
  -d '{ "params": { "inactive_after_months": 12, "warn_before_days": 30, "include_without_address": false } }'

matching_now counts the people who would be scheduled at once: for the two deletion rules, those who would get the warning email now (or, with include_without_address, be deleted without one); for expire_roles, the members whose deadline has already passed. capped is true when the count stopped at 1,000. For remind_unverified the count is not meaningful, because that rule only looks at sign-ups made after it is switched on.

List the activity

curl 'https://api.vernesoft.com/v1/gate/lifecycle/actions?status=pending&limit=20' \
  -H 'Authorization: Bearer vrn_9f8a7...'
{
  "data": [
    {
      "id": "0f4b8c1e-5d1a-4a55-9d0e-6b0f4f3c2a10",
      "kind": "delete_unverified",
      "step": "warn",
      "identity_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "status": "pending",
      "basis_at": "2026-09-24T08:12:00Z",
      "due_at": "2026-10-05T08:12:00Z",
      "reason": null,
      "created_at": "2026-10-08T09:31:00Z",
      "finished_at": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}
QueryMeaning
statuspending, done, cancelled or failed.
kindOne of the four rule kinds.
identity_idOnly this person's actions.
limit1 to 100, default 20.
cursorThe next_cursor of the previous page.

Oldest first. Each item:

FieldMeaning
stepremind, warn, delete or revoke (a role removed).
statuspending, done, cancelled or failed.
basis_atThe moment the period was counted from: sign-up, or the last activity.
due_atWhen the step is due.
reasonWhy it was cancelled or failed: rule_disabled, rule_changed, rule_removed, plan, verified, signed_in, identity_gone, identity_deleted, deactivated, not_in_scope, no_address, not_warned.
finished_atWhen it was done, cancelled or given up.

The list names people by identity ID and holds no email addresses. Read the profile from GET /v1/gate/identities/{id} while the account exists. History is kept for 90 days.


The identity.deleted webhook

When a rule deletes an account, your endpoints receive identity.deleted like for any deletion, with one extra field in data.payload:

{
  "event_id":   "a1b2c3d4-...",
  "event_type": "identity.deleted",
  "tenant_id":  "550e8400-...",
  "created_at": "2026-10-22T08:14:00Z",
  "data": {
    "identity_id": "f47ac10b-...",
    "payload": { "reason": "inactive_account_expired" }
  }
}
payload.reasonRule
unverified_account_expiredDelete unconfirmed accounts
inactive_account_expiredDelete inactive accounts

A deletion you make yourself through the API has no reason. See Webhooks.


Role deadlines

expires_at is a field of an organization member. It is stored whatever your plan; whether anything acts on it is the expire_roles rule above.

Set it when you add a member, or later:

curl -X POST https://api.vernesoft.com/v1/gate/organizations/{org_id}/members \
  -H 'Authorization: Bearer vrn_9f8a7...' \
  -H 'Content-Type: application/json' \
  -d '{
    "identity_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "role_id": "7c1d9d0a-3d5e-4c58-8a76-2d3f6b1d0e11",
    "expires_at": "2027-01-31T23:59:59Z"
  }'
  • expires_at is a time in the future, at most 10 years ahead. A past time is refused with 400, not obeyed: a typo would otherwise remove a person.
  • Leaving expires_at out of a PATCH keeps the deadline; null clears it.
  • Members are returned with expires_at (null for no deadline) when you add them, change them, and list them.
  • The dashboard has no screen for organization members yet, so deadlines are set through the API.

When your plan changes

If you move to a plan that does not include a rule you had switched on, the rule is switched off, not deleted. Its settings are kept, what it had scheduled is cancelled, and nobody is deleted by it. We email the account's address once, naming the projects and the rules that stopped. For expire_roles, that means role deadlines stop being applied: people keep their role and its access after the deadline until you remove it.

Moving back up does not switch a rule on again. Open Users & sign-in → Rules, or send PUT with "enabled": true.

See Plans, limits and refusals for what each plan includes.