Creating A Custom Signup Page

Hint provides every practice with the ability to have multiple signup pages out of the box, each with custom settings. For most practices, this works. But if you need full control, you might want to build your own signup page. This guide will show you how to do just that using Hint's API.


This is a technical document

Creating your own signup page requires significant technical expertise. This tutorial is intended for web developers, and assumes understanding of terms like servers, api keys, front-end, etc.


When someone signs up for your practice, your end goal is to create a Membership. But Memberships are complicated, because they may have multiple patients, payment info, and particular settings and plans. Thus, many parts of Hint's API work together to create a Membership; specifically: Patient, Membership, Plan, Card, BankAccount , and CustomerInvoice. Creating your signup page will require most, if not all of these endpoints to be used.

How Does it Fit Together?

  • At a minimum, Memberships are made up of Patients, and a Plan.
  • But you likely will want to attach a Card or Bank Account to the owner (Patient) of that Membership.
  • Memberships can generate Customer Invoices, which you can trigger manually, or let them be triggered automatically.

The Data Flow

When you create your own signup page, you're basically acting as a proxy for Hint's normal signup page. Thus, your goal is to get whatever data you need from your user, and then pass it on to the Hint API (and of course, showing the user successes or errors accordingly). Passing data to Hint's API requires a server, since you'll need your secret API key to authenticate with the Hint API. The diagram below shows the general flow.

Note - The "signup data" in the diagram below represents any object you may want to send to Hint, whether Patients, Memberships, Cards/Banks, etc. Thus in a real signup page, this means separate API calls for each object you need to create.


Collecting Payment Information

You do not have to collect payment information. Memberships and Patients can exist without it, but you likely will want to. Hint supports 2 ways to gather payment information. You can collect Card and Bank details using Stripe Elements, and you can allow a bank connection using bank login details through Plaid Link.

Transaction fees are lower for ACH payments made from a patient's bank account. The easiest way to connect a patient's bank account is having them authenticate with their banks' login credentials using Plaid Link. Plaid verifies the bank information and creates a bank account in Stripe that is ready to be charged.

If your patient's do not have, or do not want to use their bank login information, or they want to enter a credit card you can request that information using Stripe Elements.

The technical process is similar for both paths:

1.) The user is shown a flow from either Stripe Elements or Plaid Link
2.) The user completes the flow and a callback is triggered with the details (success or error)
3.) On success, pass the stripe_id generated with the PaymentMethod to Hint's Card or BankAccount endpoints. Note: A card or bank must be attached to an existing patient.

You must use Hint's publishable keys show in the code example below:

// Stripe Production Public Key: 'pk_live_ImPWqYGkbMfjwg22MiAmB6u4'
// Stripe Test Public Key: 'pk_test_Y7byiIyWmjqy4Xy7fjArJdPl'
// Test card/bank numbers:
// Code Example Adapted From:

// Create a Stripe client.
var stripe = Stripe('pk_test_Y7byiIyWmjqy4Xy7fjArJdPl');

// Create an instance of Elements.
var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
  base: {
    color: '#32325d',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'

// Create an instance of the card Element.
var card = elements.create('card', {style: style});

// Add an instance of the card Element into the `card-element` <div>.

// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';

// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {

  // Billing Details hash is optional -
  stripe.createPaymentMethod({type: 'card', card: card, billing_details: extraParams}).then(function(result) {
    if (result.error) {
      // Inform the user if there was an error.
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      // Send the stripe_id to Hint's server.
      post('/cards', {
        stripe_id: stripe_id
// Plaid Public Key: '3d8ae5f373153608c9d3ad43fe88db'
// Plaid Client Id: '5695b686a4ce3935462bb5bb'
// Sandbox passwords are shown at the bottom of the link js modal
// Code Example Adapted From:

<button id="link-button">Link Account</button>
<script type="text/javascript">
(function($) {
  var handler = Plaid.create({
    apiVersion: 'v2',
    // Enter your organization name here
    clientName: 'Plaid Quickstart',
    // Optional, specify an array of ISO-3166-1 alpha-2 country
    // codes to initialize Link; European countries will have GDPR
    // consent panel
    countryCodes: ['US'],
    // Optional, specify a language to localize Link
    language: 'en',
    env: 'sandbox', // or 'production'
    key: '3d8ae5f373153608c9d3ad43fe88db',
    product: ['auth'],
    // Optional, use webhooks to get transaction and error updates
    webhook: '',
    onLoad: function() {
      // Optional, called when Link loads
    onSuccess: function(public_token, metadata) {
      // Send the public_token to Hint's server.
      // The metadata object contains info about the institution the
      // user selected and the account ID selected.
      $.post('/bank_accounts', {
        plaid_public_token: public_token,
        plaid_account_id: metadata.account_id,
        shared: true
    onExit: function(err, metadata) {
      // The user exited the Link flow.
      if (err != null) {
        // The user encountered a Plaid API error prior to exiting.
      // metadata contains information about the institution
      // that the user selected and the most recent API request IDs.
      // Storing this information can be helpful for support.
    onEvent: function(eventName, metadata) {
      // Optionally capture Link flow events, streamed through
      // this callback as your users connect an Item to Plaid.
      // For example:
      // eventName = "TRANSITION_VIEW"
      // metadata  = {
      //   link_session_id: "123-abc",
      //   mfa_type:        "questions",
      //   timestamp:       "2017-09-14T14:42:19.350Z",
      //   view_name:       "MFA",
      // }

  $('#link-button').on('click', function(e) {;


Do Not Send Hint Sensitive Payment Details

Please note the diagram above. Sensitive payment details like card numbers and bank account numbers should never be sent to Hint. If you are collecting payment details, they should always be sent to Stripe first, so they can be tokenized, and then you send the token to Hint.

Unless you are PCI compliant, you probably don't want card/bank numbers to hit your servers either, which is why the Stripe JS library sends this information from the user's client directly to Stripe (more detail here:

Handling Errors

When errors occur, it's critical that you inform the user by showing a useful error message. Hint's API helps you with this by providing useful error messages by default, but you will still need to display them back to the user. For more info on Hint API errors, see our page on Handling Errors

Example Flow of Events and API Calls

What follows is a chronological set of events and API calls that could reasonably happen to create a membership using Hint's API.

  • Sam lands on your page.
  • GET /plans to display all of your available plans to Sam.
  • Sam chooses a plan, fills out his information, as well as the information for his spouse Sierra.
  • Optionally you can quote the price to Sam using POST /quotes
  • Sam accepts your terms of service, the email communication waiver, and confirms he wants to sign up.
  • Sam fills out his credit card details.
  • Using Stripe JS, you get a token for Sam's credit card.
  • POST /patient with Sam's info.
  • POST /patient with Sierra's info.
  • POST /patients/:sam's_id/cards to create a card on Sam's account.
  • POST /memberships using the patient IDs you got from creating Sam and Sierra, as well as the Plan ID you got when Sam selected his plan.

At this point, you would have a membership created that would bill later that night. If you want to bill them immediately, you can use the bill action on the membership to accomplish this.

What’s Next