Blog / Sending confirmation emails from Autopilot with Rails and Devise

Abraao Mota
June 21, 2017

We are working on a very exciting startup that is using Autopilot as the marketing automation tool. It is being designed to nudge the users through the funnel. We want to use our Rails application for interactions but if they drop-off and need to be nudged back we want to use Autopilot for the outbound messages. We primarily use email but other integrations are available like SMS and postcards. We have found that Autopilot offers us a large array of information about what our users do in the application. Autopilot gives us the ability to segment users and easily experiment with different wording and timing delays.

To use Autopilot for all outbound messaging meant having to rework our current user confirmation process, handled by Devise, a gem that handles several aspects of user confirmation for Rails.

Unveiling Devise’s magic

By default, Devise sends out an email to confirm the user’s email address. To move control from Rails to Autopilot we start by overriding the call to send a confirmation email by Devise.

We had to update our trigger to this in our user model. It went from being

after_update :send_confirmation_instructions, if: :should_send_confirmation

to being

after_update :send_autopilot_confirmation, if: :should_send_confirmation

In order to be able to confirm a user, they must visit a link sent to them in their confirmation email. This link contains a token generated by Devise, and so our new send_autopilot_confirmation method must generate one of these tokens, save it to the user record, and pass it as a parameter in a call to an Autopilot API client. We create a class AutopilotClient.

This client is responsible for interfacing between our Rails application and the Autopilot API by sending HTTP requests to Autopilot. The expected format of these is detailed in their API documentation.

Linking to Autopilot

Within our AutopilotClient, we must set up a HTTP connection on initialization. Our HTTP client of choice was Faraday, but your mileage may vary.

We want to keep this generic for future uses of this client (right now we will be updating a specific set of user parameters through send_confirmation_email, but we may want to use it for a different type of update at a later stage).

Therefore, we want our send_data method to be private, with the only changing parameter within it to be the request body. All the housekeeping for generating a HTTP request and sending it to Autopilot can be kept in this method (such as header setting and exception handling).

For send_confirmation_email, all we need to do is generate the body, and call send_data with it:

def send_confirmation_email(confirmation_token)
  request_body = {
    "contact": {
      "Email": "#{@user.email}",
      "custom": {
        "string--Confirmation--Token": "#{confirmation_token}",
        "boolean--Email--confirmed": "false"
      }
    }
  }.to_json
  send_data(request_body)
end

Journeying through Autopilot

Now that we have asked Autopilot to update our user and have sent through the event (of the user registering their email address), we must set up an Autopilot Journey to handle this, and send our confirmation email.

Autopilot uses a logic node-based journey creator, and it is fairly simple to set up a flow.

The image below shows how to setup a trigger change on the user field email_confirmed; when it is set to false, send our confirmation email, and finish the user journey.

Autopilot Journey Changing the email_confirmed field triggers the confirmation email to be sent

Any click will confirm you

One of the features we wanted to implement was to confirm a user whichever link they clicked on in the email we send them. This means that every link in the email must visit the Devise confirmation page, and if the link requires it, redirect a user to the intended endpoint of that link.

An example might be a link to our blog; it is customary to send a user a link to further reading with the confirmation email; so linking to our blog would have to visit our Devise user confirmation page, send out a request to confirm the user, and redirect them on their merry way to the blog.

That way they become a confirmed user with no hassle on their part, and allows us to tag them as verified users earlier in the process.

Our solution to this was to simply add the intended link as a url parameter; meaning a link to the blog might become

https://example.com/users/confirmation?confirmation_token=123blabla&redirect_url=https://example.com/blog/

This meant overriding Devise’s ConfirmationsController, and making use of a redirect_url parameter if it was present in the params hash.

Upon inspection of the ConfirmationsController, we can see that the show action delegates the redirection to the private method after_confirmation_path_for.

We can override this, sign the user in upon reaching this far in the process (which will confirm them), and if a redirect_url parameter is present, redirect them to the appropriate location.

def after_confirmation_path_for(resource_name, resource)
  sign_in(resource)
  if params[:redirect_url].present?
    params[:redirect_url]
  else
    root_path
  end
end

Notes on design and version control

We must not forget that the user database on Rails is not synchronised with that of Autopilot, so any status change to an application user must be reflected on our Autopilot contact for future use (send emails only to users we know have confirmed their email).

This requires a final POST request to confirm the user on Autopilot. Contacts are indexed on their email, so we will simply need a method on our AutopilotClient that sets the confirmed_email attribute for that email to be true after receiving confirmation.

This also gives an instant affirmation that the instinct to make our AutopilotClient generic for further requests was correct; sending this new request only requires us to set both of the aforementioned parameters in a new request body and to call send_data again with that body.

The methodology we used means that we have to adjust every link in our emails sent to go through our confirmation page, which can be a lengthy first time set up, but is worth doing for the hassle saved later on.

Autopilot allows you to upload email HTML templates, but you cannot access the raw source code for these once they have been uploaded; the editor for uploaded emails will allow some changes to be added, but isn’t ideal for any non-minor change.

As such, we recommend that these templates are versioned separately because

  • It ensures that the emails you send are consistent
  • You will spend less time trying to customize emails once you’ve got a template you are happy with.

Once these are ready, creating templates for future emails (such as marketing nudges), will likely consist of only changing a limited amount of content and less fidgeting with the email editor.