Hey guys,
I have a problemem with building my ecommerce with PayloadCMS.
I've have a logic that after order if there is no account with given email its created.
I wanted to send an email so client can set passowrd on its own using
payload.forgotPassword.
Im doing it in afterCreate hook.
Here it is:
export const userWelcomeHook: CollectionAfterChangeHook = async ({
doc,
operation,
previousDoc,
req,
}) => {
// Only handle user creation
if (operation !== 'create') {
return doc
}
console.log(`π [USER WELCOME HOOK] New user created: ${doc.email}`)
try {
console.log(`π§ [USER WELCOME HOOK] Attempting to send welcome email to: ${doc.email}`)
// Try generating the reset token and handle any issues gracefully
let resetToken = null
try {
console.log(`π [USER WELCOME HOOK] Trying to generate reset token for: ${doc.email}`)
// Give user record a moment to be fully persisted
await new Promise(resolve => setTimeout(resolve, 200))
const resetTokenResponse = await req.payload.forgotPassword({
collection: 'users',
data: { email: doc.email },
disableEmail: true, // Don't send default email, we'll send custom one
})
console.log(`π [USER WELCOME HOOK] Reset token response type:`, typeof resetTokenResponse)
console.log(`π [USER WELCOME HOOK] Reset token response:`, resetTokenResponse)
// According to PayloadCMS docs, forgotPassword with disableEmail: true should return the token string
if (typeof resetTokenResponse === 'string' && resetTokenResponse) {
resetToken = resetTokenResponse
} else if (resetTokenResponse && typeof resetTokenResponse === 'object') {
// Try different property names that might contain the token
resetToken = (resetTokenResponse as any).token ||
(resetTokenResponse as any).resetToken ||
(resetTokenResponse as any).passwordResetToken ||
null
}
console.log(`π [USER WELCOME HOOK] Reset token result:`, resetToken ? 'SUCCESS' : 'FAILED')
if (resetToken) {
console.log(`π [USER WELCOME HOOK] Token length:`, resetToken.length)
}
} catch (tokenError) {
console.warn(`β οΈ [USER WELCOME HOOK] Could not generate reset token for ${doc.email}:`, tokenError)
// Continue without token - we'll send a different email
}
// Send welcome email (with or without reset token)
await sendWelcomeEmailWithCredentials({
user: doc,
resetToken: resetToken || null
})
console.log(`β
[USER WELCOME HOOK] Welcome email sent to: ${doc.email}`)
} catch (error) {
console.error(`β [USER WELCOME HOOK] Failed to send welcome email to: ${doc.email}`, error)
// Don't throw - user creation should still succeed
}
return doc
} But I got failure with generation password reset.
I tried both, created user with order or created from admin panel.
What am I missing here?
There are also logs which might help:
π [USER WELCOME HOOK] New user created: gg@hqr.sh
π§ [USER WELCOME HOOK] Attempting to send welcome email to: gg@hqr.sh
π [USER WELCOME HOOK] Trying to generate reset token for: gg@hqr.sh
π [USER WELCOME HOOK] Reset token response type: object
π [USER WELCOME HOOK] Reset token response: null
π [USER WELCOME HOOK] Reset token result: FAILED
π§ [MAIL SERVICE] [WELCOME] User data: {
id: 17,
name: 'GG GG',
roles: [ 'customer' ],
cart: { items: [] },
firstName: null,
lastName: null,
addresses: [],
discordId: null,
discordUsername: null,
discordRoles: [],
accessCodes: [],
tickets: [],
isDiscordVerified: false,
discordVerifiedAt: null,
updatedAt: '2025-06-26T21:18:11.644Z',
createdAt: '2025-06-26T21:18:11.558Z',
email: 'gg@hqr.sh',
loginAttempts: 0
}
π§ [MAIL SERVICE] [WELCOME] Email sent successfully to: gg@hqr.shI think you're not finding the user in your resetPassword local because you're not passing
reqso when the operation tries to find the user it is not within the same transaction and the user record isn't committed yet to the DB.
// We don't want to indicate specifically that an email was not found,
// as doing so could lead to the exposure of registered emails.
// Therefore, we prefer to fail silently.
if (!user) {
await commitTransaction(args.req)
return null
}this is the block of code in the forgotPassword operation that you're hitting by the looks of it
In your hook, add
reqto your local api call
const resetTokenResponse = await req.payload.forgotPassword({
collection: 'users',
data: { email: doc.email },
disableEmail: true, // Don't send default email, we'll send custom one
// ADD THIS LINE
req,
})Gonna check it!
It worked!
Thank you
@969226489549713438β€οΈ
Doing thing in the midnight is sometimes too much π
Star
Discord
online
Get dedicated engineering support directly from the Payload team.