Custom Email with Loops

How would you recommend implementing the Loops (https://loops.so/docs) email service within Wasp? Our team prefers Loops over Mailgun or SendGrid and we would like to take advantage of the email auth provided by Wasp. Is there a preferred solution to accomplish this? I've attempted the SMTP route, which Loops supports (https://loops.so/docs/smtp). However, I continue to receive a 450 error when setting the requested body to auth.email.passwordReset.getEmailContentFn to send the Loops Transactional email. See pics for code. Am I do something wrong with this method or is there a better way to approach this? It's worth noting that the email content is created and sourced from Loops so bypassing the coded email content from Wasp is key here. Please advise on how to proceed and if further info is needed to diagnose. Thank you!
No description
No description
No description
No description
14 Replies
kapa.ai
kapa.aiβ€’2mo ago
Hi @KarateSheets. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Hmm, I don't know enough to give you a confident answer yet. The knowledge sources provided do not contain specific information about integrating the Loops email service with Wasp. However, Wasp does support several email providers out of the box, including SMTP, which you mentioned Loops supports. You can set up an email sender in the main.wasp file as shown in the Wasp documentation (https://wasp-lang.dev/docs/advanced/email). Regarding the 450 error you're encountering, it's hard to diagnose without more specific information. It could be related to the SMTP settings or the way the email content is being handled. As for bypassing the coded email content from Wasp, you might be able to customize the email content as shown in the Wasp documentation (https://wasp-lang.dev/docs/auth/email#passwordreset-passwordresetconfig-). However, this assumes that the email content is being generated within Wasp, which might not be the case if you're using Loops. I would recommend reaching out to the Wasp community or the Loops support for more specific guidance. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @KarateSheets, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
miho
mihoβ€’2mo ago
Hmmm I haven't played around with using Loops with Wasp πŸ˜„ I guess I'll have to try it out to see what might be happening. Is it possible to use Loops for free to test it out?
KarateSheets
KarateSheetsβ€’2mo ago
Yes it is! It does require an account, of course.
MEE6
MEE6β€’2mo ago
Wohooo @KarateSheets, you just became a Waspeteer level 1!
KarateSheets
KarateSheetsβ€’2mo ago
@miho Hello, following up here. I'm sure you haven't had time to test implementing Loops but wanted to make sure this didn't get lost. Appreciate any help you can provide.
miho
mihoβ€’2mo ago
Yep, it's on my TODO for today πŸ™‚ I'll ping you here when I try out Loops with Wasp!
KarateSheets
KarateSheetsβ€’2mo ago
You're the best. Thank you!
miho
mihoβ€’2mo ago
I'm not able to create an account right now, I'm not sure if Loops has some issues πŸ€·β€β™‚οΈ Have you tried using Wasp's SMTP provider to send the emails? - https://loops.so/docs/smtp - https://wasp-lang.dev/docs/advanced/email#using-the-smtp-provider I guess that then you can modify the emails that Wasp sends for email confirmation to send the JSON that Loops expects? From the docs:
Then, when it comes to sending emails, instead of the content of an email, you send an API-like request body like this: { "transactionalId": "clomzp89u035xl50px7wrl0ri", "email": "[email protected]", "dataVariables": { "confirmationUrl": "https://myapp.com/confirm/12345/" } } This JSON needs to be converted to a string and then sent as the email body.
Oh, that's exactly what you did πŸ˜„ let me figure out the error... since it seems that your setup should work!
miho
mihoβ€’2mo ago
Aw yes, you are returning a single string in the getEmailContentsFn function. That's not really possible, where did you find that syntax? Check the docs: https://wasp-lang.dev/docs/auth/email#passwordreset-passwordresetconfig-
import { GetPasswordResetEmailContentFn } from 'wasp/server/auth'

export const getPasswordResetEmailContent: GetPasswordResetEmailContentFn = ({
passwordResetLink,
}) => ({
subject: 'Password reset',
text: `Click the link below to reset your password: ${passwordResetLink}`,
html: `
<p>Click the link below to reset your password</p>
<a href="${passwordResetLink}">Reset password</a>
`,
})
import { GetPasswordResetEmailContentFn } from 'wasp/server/auth'

export const getPasswordResetEmailContent: GetPasswordResetEmailContentFn = ({
passwordResetLink,
}) => ({
subject: 'Password reset',
text: `Click the link below to reset your password: ${passwordResetLink}`,
html: `
<p>Click the link below to reset your password</p>
<a href="${passwordResetLink}">Reset password</a>
`,
})
You'd want to put the JSON.stringify bit as the text and html values πŸ™‚
Email | Wasp
Wasp supports e-mail authentication out of the box, along with email verification and "forgot your password?" flows. It provides you with the server-side implementation and email templates for all of these flows.
KarateSheets
KarateSheetsβ€’2mo ago
Thanks for looking into this. Unfortunately, adding the stringified Loops object to the html key does not seem to work. I created this function in email.ts and replaced getPasswordResetEmailContent in main.wasp . Still getting a 450 error. Failed to send email Error: Message failed: 450 Unexpected end of JSON input The other issue this method presents is that I do not have access to the user's submitted email address in the forgot password form. To test I am hardcoding in the body. Is there a way to pass that address through if I'm able to get SMTP working? Loops will require this address to send the transactional email. export const smtpPasswordReset = ({ passwordResetLink }: {passwordResetLink: string}) => ({ subject: 'Password reset', text: Click the link below to reset your password: ${passwordResetLink}, html: JSON.stringify({ transactionalId: process.env.LOOPS_RESET_PASSWORD_TRANS_ID!, email: '[email protected]', dataVariables: { passwordResetLink, recipient: '[email protected]' } }), }); Any other suggestions? I'm not sold that SMTP will be the best way forward here. Thanks again for all of your help.
miho
mihoβ€’2mo ago
Have you tried setting the text value as well? This email is sent to the user's email later in the flow. Does that help? Or you really need to user's email in this fn?
KarateSheets
KarateSheetsβ€’5w ago
Unfortunately, it doesn't help. We need the ability to trigger Loops via the SDK to send the designed transactional email. Setting the text field did resolve the error. Good call out! We do still need access to the user's email address in this situation, rather than hardcoding. Do you know any way around this? I suppose it would be possible to parse from the passwordRestLink, no?
miho
mihoβ€’5w ago
You could try parsing the URL, getting the token, finding the user by token. But the problem is that the token info is stored in a JSONlike filed in the DB, so you'd need to do some imprecise search. Give it a go and let me know if it works. Wasp should expose this info to you ideally!
KarateSheets
KarateSheetsβ€’5w ago
Looks like the email address is encoded in the token FYI to grab the email address from the passwordResetLink
const url = new URL(passwordResetLink);
const token = url.searchParams.get('token');
const { email } = JSON.parse(atob(token!.split('.')[1]));
const url = new URL(passwordResetLink);
const token = url.searchParams.get('token');
const { email } = JSON.parse(atob(token!.split('.')[1]));
Want results from more Discord servers?
Add your server