That's what I do. I don't know how good this code is but it seems to work. Happy to see any improvements.
auth.settings.register_onaccept.append(lambda form: send_email(type='register', recipient=auth.user.email, subject='Thanks for registering')) === model === # -*- coding: utf-8 -*- import re db.define_table('mail_queue', Field('status', default='pending'), Field('recipient'), Field('sender'), Field('reply_to'), Field('subject'), Field('message', 'text'), Field('err_msg', 'text'), Field('sent_on', 'datetime'), record_signature) def send_email( queue=False, recipient=settings.email_sender, sender=settings.email_sender, reply_to=settings.email_sender, subject='Message from Pricetack', message='', type='generic', context={}): if queue: body = response.render('emails/%s.html' % type, context) try: db.mail_queue.insert( status='pending', recipient=recipient, sender=sender, reply_to=reply_to, subject=subject, message=body) except Exception, e: app_logging.info(e) return False return True else: if not message: message = response.render('emails/%s.html' % (type,), context) try: result = mail.send(to=recipient, subject=subject, message=message, reply_to=reply_to) except Exception, e: app_logging(e) return result === cronjob === def mail_queue(): rows = db(db.mail_queue.status=='pending').select() for row in rows: try: mail.send(to=row.recipient, subject=row.subject, message=row.message, reply_to=row.reply_to) except Exception, e: row.update_record(status='failed', err_msg=e) else: row.update_record(status='sent', sent_on=datetime.datetime.now())