ResytechResytech Docs

Contact & Subscribe Forms

Capture leads and mailing-list signups from your marketing site with the ResytechApi CRM intake controllers, including Google reCAPTCHA v3 setup.

The ResytechApi client exposes two public intake endpoints for your marketing site:

  • api.subscribeForm.subscribe(...) — email-only mailing-list signup. Creates a Contact with obtain_method = subscribe_form and contact_consent = true.
  • api.contactForm.submit(...) — full contact / lead-capture form. Creates a Contact and sends a notification email to the operator's support address.

Both endpoints are public (no booking session required) and both are protected on the server with Google reCAPTCHA v3. You need to configure a reCAPTCHA key pair before either form will accept submissions in production.

Prerequisites: Google reCAPTCHA v3

1. Generate keys in the Google reCAPTCHA admin

  1. Sign in to the Google reCAPTCHA admin console.
  2. Pick a Label (e.g. My Marketing Site).
  3. Choose reCAPTCHA v3.
  4. Add every domain that will embed your form, without https:// and without paths. For example:
    • yourbusiness.com
    • www.yourbusiness.com
    • staging.yourbusiness.com
  5. Accept the terms and click Submit.

Google returns two values:

KeyWhere it's usedSensitivity
Site keyEmbedded in your page HTML and passed to grecaptcha.execute() in the browserPublic — safe to commit
Secret keyPasted into the Resytech dashboard so the server can verify tokensSecret — never expose to the browser

2. Store the secret key in the Resytech dashboard

In the Resytech dashboard, navigate to CRM > Settings and paste the secret key into the reCAPTCHA Secret field, then save.

  • The page shows Configured / Not configured rather than the value itself — the secret is never returned by the API once stored.
  • Use the Clear stored secret action to remove it (e.g. if you're rotating keys).
  • A location with no secret configured will reject every contact / subscribe submission with "Resytech Location Not Setup".

One reCAPTCHA key pair per Resytech Location. If you run multiple locations and want them to share a key, paste the same secret into each location's settings.

3. Load reCAPTCHA on your page

Add the reCAPTCHA loader script and your site key to the page that hosts the form:

<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>

Then, when the user submits the form, obtain a fresh token with grecaptcha.execute() and pass it to the API call. Tokens are single-use and expire after ~2 minutes — always generate one at submit time, never reuse one.

const recaptchaSiteKey = 'YOUR_SITE_KEY';

async function getRecaptchaToken(action) {
  return new Promise((resolve, reject) => {
    grecaptcha.ready(() => {
      grecaptcha.execute(recaptchaSiteKey, { action }).then(resolve).catch(reject);
    });
  });
}

The action string is a label that Google uses for analytics and risk scoring. Pick a short, human-readable name per form — e.g. subscribe, contact.


SubscribeForm Controller

api.subscribeForm.subscribe(request) adds an email address to the operator's CRM as a consented contact.

const token = await getRecaptchaToken('subscribe');

const result = await api.subscribeForm.subscribe({
  email: 'jane@example.com',
  token,
  location: 'location-uuid'
});

if (result.success) {
  console.log('Subscribed!');
} else {
  console.error(result.message);
}

SubscribeFormRequest

FieldTypeRequiredDescription
emailstringYesEmail address to subscribe
tokenstringYesSingle-use reCAPTCHA v3 token obtained via grecaptcha.execute()
locationstringYesLocation UUID (the operator/venue receiving the signup)

SubscribeFormResponse

Extends ApiResponseBase — see ResytechApi Overview for the full shape. No additional fields.


ContactForm Controller

api.contactForm.submit(request) records a full contact-form / lead-capture submission. The server creates a Contact record and emails the operator's support address with the submitted details.

const token = await getRecaptchaToken('contact');

const result = await api.contactForm.submit({
  firstName: 'Jane',
  lastName: 'Doe',
  email: 'jane@example.com',
  phone: '555-123-4567',
  message: 'Hi, I have a question about your kayak tours.',
  token,
  location: 'location-uuid'
});

if (result.success) {
  console.log('Inquiry sent');
} else {
  console.error(result.message);
}

ContactFormRequest

All fields are required by the server.

FieldTypeRequiredDescription
firstNamestringYesSubmitter's first name
lastNamestringYesSubmitter's last name
emailstringYesSubmitter's email address
phonestringYesSubmitter's phone number
messagestringYesFree-text message body
tokenstringYesSingle-use reCAPTCHA v3 token
locationstringYesLocation UUID

ContactFormResponse

Extends ApiResponseBase. No additional fields.


Full Example: Subscribe form on a marketing page

<!doctype html>
<html>
  <head>
    <script src="https://js.resytech.com/latest/resytech.js"></script>
    <script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
  </head>
  <body>
    <form id="subscribe-form">
      <input type="email" name="email" required />
      <button type="submit">Subscribe</button>
      <p id="status"></p>
    </form>

    <script>
      const api = new ResytechApi();
      const recaptchaSiteKey = 'YOUR_SITE_KEY';
      const locationUuid = 'YOUR_LOCATION_UUID';

      async function getRecaptchaToken(action) {
        return new Promise((resolve) => {
          grecaptcha.ready(() => {
            grecaptcha.execute(recaptchaSiteKey, { action }).then(resolve);
          });
        });
      }

      document.getElementById('subscribe-form').addEventListener('submit', async (e) => {
        e.preventDefault();
        const status = document.getElementById('status');
        const email = e.target.email.value;

        status.textContent = 'Subscribing...';

        const token = await getRecaptchaToken('subscribe');
        const result = await api.subscribeForm.subscribe({
          email,
          token,
          location: locationUuid
        });

        status.textContent = result.success
          ? 'Thanks! You\'re subscribed.'
          : `Sorry — ${result.message}`;
      });
    </script>
  </body>
</html>

The same pattern applies to api.contactForm.submit(...) — swap the form fields, the action label (contact), and the API call.


Troubleshooting

SymptomLikely cause
Response says "Resytech Location Not Setup"The Location's reCAPTCHA secret hasn't been saved in the dashboard yet. See Step 2 above.
Response says "Captcha verification failed" (or 403 in release builds)The site key in your page doesn't match the secret saved in the dashboard, the token expired (>2 min old), or the token was already consumed. Always generate a fresh token at submit time.
Tokens are generated but the call still fails verificationDouble-check that the domain hosting your form is listed in the reCAPTCHA admin console for this key. v3 rejects tokens from non-allow-listed origins.
You want different secrets per environmentUse one reCAPTCHA key pair per environment (production / staging) and save each Location's appropriate secret in its dashboard settings.

On this page