Skip to content
Ultivo Toolkit 1.0 is here! Get 10% off your first year with LAUNCH10 until September 30. See pricing
Documentation menu

Integrations

An integration lets a form hand something off to an external service. Ultivo Toolkit ships with three spam providers: Google reCAPTCHA v2, Google reCAPTCHA v3 and Cloudflare Turnstile.

Setting one up#

  1. Go to Forms → Integrations and pick a provider. The cards show what each one asks of your visitors; the one you select opens its key fields underneath, with a Keys saved or Keys missing status.
  2. That's it. Every form is protected by default.

You run one provider for the whole site: you pick reCAPTCHA or Turnstile, not both. Keys you already entered for the other providers stay stored, so switching back costs nothing.

A form can opt out: under Settings in the form builder there's a Spam Protection checkbox, on by default. Turn it off for a form that doesn't need it, an internal form behind a login, say. The widget renders just above the submit button, and nothing is loaded on the front end for a form with the checkbox off.

Choose None on the Integrations page to switch spam protection off site-wide. With no provider set up, nothing renders and nothing is verified: a half-configured captcha never blocks a form.

Which one to pick#

reCAPTCHA v2 shows the familiar "I'm not a robot" checkbox. Visible, well understood, one extra click for the visitor.

reCAPTCHA v3 is invisible and scores each submission between 0.0 (almost certainly a bot) and 1.0 (almost certainly human). Submissions below the threshold are refused. The threshold is site-wide and defaults to 0.5. A quick way to test the refusal path: set it to 1.0 temporarily, submit as a human, and watch it get rejected.

Cloudflare Turnstile is a privacy-friendly alternative. How visible it is (managed, non-interactive or invisible) is set in your Cloudflare dashboard, not here.

Testing locally#

Both providers tie a key to a domain, so a key from your live site will not work on localhost or a .local development domain: the widget refuses to render and tells you the domain is invalid. Either add the development hostname to the key's domain list, or use the test key pairs both providers publish: they work on any domain, and each has an "always passes" and an "always fails" variant, so you can exercise both paths.

When the provider is unreachable#

If Google or Cloudflare cannot be reached at all, the submission is allowed through. An outage at a third party should not lock every visitor out of your contact form. A submission the provider actively rejects is blocked, with a generic message: the provider's own error code is never shown to the visitor.

Registering your own integration#

Integrations are a registry, so you can add your own:

add_filter( 'ultivo/integrations', function ( $classes ) {
    $classes[] = My_Integration::class;
    return $classes;
} );

class My_Integration extends \ULTIVO\Integration {

    public function __construct() {
        $this->id       = 'my_captcha';
        $this->label    = 'My captcha';
        $this->tagline  = 'Short line for the card';
        $this->icon     = 'dashicons-shield';
        $this->group    = 'spam';
        $this->settings = [
            'api_key' => [ 'label' => 'API key', 'type' => 'password', 'default' => '' ],
        ];
    }

    public function render_field( array $form, array $conf ): string {
        return '<div class="my-captcha" data-key="' . esc_attr( $this->get( 'api_key' ) ) . '"></div>';
    }

    public function verify( array $form, array $posted, array $conf ): ?string {
        return $this->looks_human() ? null : __( 'Please try again.', 'my-plugin' );
    }
}

Your integration appears as a card on the Forms → Integrations page, alongside the three built-in providers. Field types for $settings: text, password, number.

One group works today#

$group is spam or action. Only spam integrations are picked up right now: Integrations::for_form() returns the one provider the site has selected, and nothing else. An action integration (a mailing list, a webhook) can be registered, and its settings render on the Integrations page, but none of its methods will run: there is no per-form wiring for that group yet. It returns when the first action integration is built.

So for now, write spam integrations. dispatch() is documented below for completeness, not because it fires.

The four seams#

Implement only what you need: none of these are required:

Method When it runs
enqueue_frontend( $form, $conf ) Assets, loaded only for forms that use this integration
render_field( $form, $conf ) Markup placed just above the submit button
verify( $form, $posted, $conf ) Before anything is saved. Return a string to refuse the submission, null to let it through
dispatch( $form, $values, $entry_id, $conf ) After the entry is stored and notifications are sent, reserved for action integrations, so it never fires today

A spam provider fills in the first three. $conf is passed to all four and is currently always empty; it exists for the per-form configuration action integrations will need.

Settings are site-wide only. There is no per-form settings schema: a form decides whether it uses spam protection, not how it is configured.

Keeping keys out of the database#

Filter a setting to read it from wp-config.php instead:

add_filter( 'ultivo/integrations/setting', function ( $value, $id, $key ) {
    if ( 'my_integration' === $id && 'api_key' === $key && defined( 'MY_API_KEY' ) ) {
        return MY_API_KEY;
    }
    return $value;
}, 10, 3 );