Guide to the User entity
Getting started
The User entity holds identity, role, subscription, and payment processor data used across auth, billing, and authorization. Wasp manages passwords, sessions, and social logins for you so those tables are not in your Prisma schema. See Wasp Auth docs if you want the deeper internals.
You’ll find the User entity in your Prisma schema: schema.prisma.
model User {
id Int @id @default(autoincrement())
email String? @unique
username String?
createdAt DateTime @default(now())
isAdmin Boolean @default(false)
paymentProcessorUserId String? @unique
lemonSqueezyCustomerPortalUrl String? // You can delete this if you're not using Lemon Squeezy as your payments processor.
subscriptionPlan String?
subscriptionStatus String?
sendEmail Boolean @default(false)
datePaid DateTime?
credits Int @default(3)
gptResponses GptResponse[]
contactFormMessages ContactFormMessage[]
tasks Task[]
files File[]
}
We keep the essentials on the User — identity details, subscription data, and payment-processor info. Wasp handles the auth-side entities (passwords, sessions, social logins), so you don’t manage those in schema.prisma.
Stripe and Subscriptions
Subscriptions are processed by Stripe. Related fields on the User entity map billing status to feature access.
// src/payment/stripe/webhook.ts
import { emailSender } from "wasp/server/email";
if (subscription.status === 'past_due') {
const updatedCustomer = await context.entities.User.update({
where: {
id: customer.id,
},
data: {
subscriptionStatus: 'past_due',
},
});
if (updatedCustomer.email) {
await emailSender.send({
to: updatedCustomer.email,
subject: 'Your Payment is Past Due',
text: 'Please update your payment information to continue using our service.',
html: '...',
});
}
}
-
paymentProcessorUserId — Customer ID from the payment processor; created at checkout and used to identify the customer.
-
subscriptionPlan — The user’s plan, set by the app and used to gate features. Defaults: hobby, pro, and a one-time credits10 purchase.
-
subscriptionStatus — Billing status from the processor, used to allow/deny access. Defaults: active, past_due, cancel_at_period_end, deleted.
-
credits (optional) — Trial balance (defaults to 3). Users can buy more via a one-time Stripe product (e.g., credits10).
Subscription Statuses
We treat a user as “subscribed” once subscriptionStatus is set. This field is updated by Stripe via your webhook and encodes the user’s current state. By default, the template recognizes four statuses:
-
active — The user has paid and has full access.
-
cancel_at_period_end — The user canceled; access continues until the billing period ends.
-
deleted — The canceled period has ended; access is removed.
-
past_due — Auto-renewal failed (e.g., expired card). You decide the response—common patterns include limiting features and emailing the user to update payment details.
import { emailSender } from "wasp/server/email";
if (subscription.status === 'past_due') {
const updatedCustomer = await context.entities.User.update({
where: {
id: customer.id,
},
data: {
subscriptionStatus: 'past_due',
},
});
if (updatedCustomer.email) {
await emailSender.send({
to: updatedCustomer.email,
subject: 'Your Payment is Past Due',
text: 'Please update your payment information to continue using our service.',
html: '...',
});
}
}
Subscription Plans
The subscriptionPlan field gates which features a user can access. By default, there are three plans: hobby, pro, and a one-time credits10 purchase. To add more, create products/price IDs in Stripe, update the envs in .env.server, and adjust the relevant code. See the Payments Integration Guide [link to payments] for details.
Roles
We currently support two roles: admin and user, controlled by the isAdmin field on the User entity.
model User {
id Int @id @default(autoincrement())
email String? @unique
username String?
createdAt DateTime @default(now())
isAdmin Boolean @default(false)
//...
}
Admin privileges
Admins can access the Admin Dashboard, including the user table to search, view, and—when needed — edit/update user records manually.
General user access
Regular users can use the signed-in app but not the Admin Dashboard. You can further gate specific features by applying the patterns from the authorization guide.