TouchBasePro
Add the people who submit your forms to a TouchBasePro subscriber list, filling the list custom fields from the answers you mapped.
TouchBasePro is an add-on for Forms. Once your TouchBasePro account is connected, every form you edit gains a TouchBasePro panel in its sidebar where you describe one or more actions. When the form is submitted each action adds the person to the subscriber list you chose, filling that subscriber's details from the answers you mapped onto them.
Nothing else about the form changes: validation, messages, redirects, notification emails and the Submissions list all behave as they did before. TouchBasePro delivery runs after the entry is stored, so a provider refusal cannot erase it.
Where to find it
Two places, because there are two kinds of setting. The account connection is site-wide, at XPAC → Integrations on the TouchBasePro tab, whose screen is headed TouchBasePro Settings. What each form sends is per-form, in the TouchBasePro panel of the document sidebar while you edit that form.
Forms has to be active
The plugin does nothing on its own. Without XPAC Forms it registers no settings tab, no panel and no routes, and shows an admin notice asking for Forms instead.
Connecting your account
Get an API key from your TouchBasePro account. The plugin reads your subscriber lists, reads each list's custom fields, and adds subscribers, so the key needs to be allowed to do all three.
Paste it into the Api Key field at XPAC → Integrations → TouchBasePro and save. That single key serves every form on the site.
Missing credentials do not hide your actions
The TouchBasePro panel and its saved actions remain visible when the key is missing or revoked. Account-backed pickers show the connection error, and a submission still creates its Forms entry before the queued delivery is marked failed. Correct the key, then retry it from XPAC → Forms → Deliveries.
Treat the key as a secret
The settings screen uses a password control, but the value is still a credential stored in WordPress. Limit settings access, use a revocable key, and rotate it if it ever leaks.
Adding someone to a list
Edit the form, open the TouchBasePro panel and press the round + button (Add action). The Add subscriber modal opens.
Map the fields. Each row pairs a Local field from your form with a Remote field on the list, and you add as many rows as you need. One of them has to be Email; read the warning below before you save.
Add subscriber is the only action there is, so a saved action always means "add this person to this list". A form can hold as many actions as you like — two lists from a single submission — and with the Conditional Logic plugin installed an action can also carry conditions, which is how someone is added only when their answers warrant it.
Mapping fields
- Local field lists every field in the form that holds a value, under its label, or under its field name where the label is blank.
- Remote field lists Email and Name first, then every custom field on the list you chose. Email and Name are the subscriber's own two details; everything below them is a custom field you created in TouchBasePro.
- The custom fields are read per list, so changing Select list changes what the Remote field dropdown offers.
- A row with either half left empty is ignored, so a half-finished mapping costs nothing.
- Two rows using the same Local field do not both send — only the last one counts. One answer cannot be sent to two places.
- Name is the exception to that: map two different form fields to Name and both are sent, joined by a space. A first-name field and a surname field mapped to Name arrive as one full name, in the order the rows appear.
One row must map to Email
TouchBasePro identifies a subscriber by their email address, so the action editor will not let you save an action whose mapping has no Email row: Save stays disabled and says what is missing, and a warning sits under the mapping rows until you add it.
The row on its own is not quite enough — make that form field required as well. If the row is there but the visitor left the field blank there is no address to send, so no subscriber is added and the action is recorded as a failed delivery.
If the fields cannot be read, the panel says so
When a list's custom fields cannot be read — the key was refused, TouchBasePro was unreachable, or that particular list cannot be requested — the mapping rows are replaced by TouchBasePro's own explanation. There is no empty Remote field dropdown to mistake for a list that simply has no custom fields.
Because the rows are replaced, a mapping you had already saved is not editable while the error stands. Fix the cause, or pick a different list: if its fields appear, the problem is the first list rather than your key.
Both dropdowns are read once per editing session
The list dropdown is filled the first time the panel renders, and each list's custom fields the first time you pick that list; both are then held for the rest of the session. A list or a custom field you create in TouchBasePro while the editor is open will not appear until you reload the page.
Keep multi-answer fields off the Email row
Multiple selected answers are flattened into one comma-separated display value for a subscriber field. That is useful for a text field but is not an email address, so map a single Email field to Email. A File Upload answer is not uploaded into TouchBasePro and should stay in the form's own record.
Consent and re-subscription
The form must establish subscription consent
TouchBasePro's create-subscriber call does not provide a confirmation-email setting in this integration. Capture subscription consent explicitly on the form and keep the stored entry.
Consent to track opens and clicks defaults to No, and can instead send Yes or derive the answer from a selected form field. This records tracking consent only; it is not permission to join the mailing list.
Re-add people who unsubscribed before is off by default. When off, an ineligible address is an expected no-op. Turn it on only where the form captures a fresh grant of consent. Restarting their automated emails is another explicit switch.
Deliveries, retries and duplicate limits
Forms writes the entry first and schedules TouchBasePro afterwards. The visitor does not wait, and a remote failure cannot erase the submission. The Deliveries screen retains TouchBasePro's explanation. Each accepted action is marked done, so a retry of one failed action does not repeat its successful siblings.
Configuration, authentication and validation failures reported as 400/401/403/404/410/422 are left failed for an administrator to fix. Rate limits, 5xx replies and transport failures are retried after about 1, 5, 30 and 120 minutes, for at most five attempts. Restore the key or action configuration, then retry a permanent failure manually.
TouchBasePro exposes no documented request idempotency key. If it accepted a subscriber but the site timed out before receiving the response, a later attempt can repeat the request. Forms can mark it complete only after an acceptance response, so treat this path as at-least-once on ambiguous transport failures.
Editing and removing an action
Each row in the panel shows the action's name — Add subscriber — with a pencil (Edit action) that reopens the modal and a red X (Remove action) that asks Are you sure you want to remove the item? before acting. Because every row carries the same name, rows are told apart only by their position.
Use the switch on a row to pause or resume that action without losing its settings. The X removes the selected row after confirmation; other actions keep their order and settings.
For developers
The API key is stored in the shared xpac option under the touchbasepro_api_key
key (packages/TouchBasePro/Addon.php:160), written by a password field on the
Integrations settings module (:73-107, field at :95-101). The module is
registered without show_in_rest, which is off by default
(packages/Shared/Settings/Settings.php:213), so the option is not exposed on
/wp/v2/settings; it is readable and writable on the settings module's own routes,
wp-react-settings/xpac/read and …/update, both gated on the module capability,
manage_options by default (packages/Shared/Settings/Rest.php:27-36,
Settings.php:473). The settings screen also prints the whole module option into the
page as a data-values attribute (packages/Shared/Settings/MenuPage.php:204). The
password control masks ordinary screen display but does not change how the value is
stored or which capability can load the page.
Client sends the key as an Authorization: Bearer header
(packages/TouchBasePro/Client.php:49) — never a query parameter, so it does not
reach request-URL logs. The base URL is https://api.touchbasepro.com/email/
(Client.php:10). Its timeout defaults to ten seconds and is filterable through
xpac_touch_base_pro_request_timeout; queued delivery runs outside the visitor's
request.
Per-form actions live in the form's form_settings post meta on the xpac-form post
type (Forms/core/base/PostType.php:20, :25), under a touchBasePro key,
registered on the Forms meta schema (Addon.php:208-249) and therefore readable and
writable over the REST API at /wp-json/wp/v2/xpac-form/<id>. The default is an empty
items array (:260-267):
{
"touchBasePro": {
"items": [
{
"status": true,
"action": "subscribe",
"list": "abc123",
"map": [{ "local": "email", "remote": "[email]" }]
}
]
}
}status and action are both declared in the schema (Addon.php:218-223), and
action is only ever subscribe — the single value checked at :424. Items carry
additionalProperties: true (:242), which is what lets Conditional Logic store its
own condition object on an item with no schema entry of its own. Write status when
you write this meta directly: Forms' shared filter reads $item['status'] with no
fallback (Forms/core/base/Utils.php:58) and Conditional Logic's listener declares a
non-nullable bool (ConditionalLogic/Forms/Addon.php:285-289).
remote holds either one of two sentinels or a TouchBasePro custom-field Key.
[email] becomes the subscriber's EmailAddress and [fullname] is appended to a
Name array that is joined with a space before sending; anything else becomes a
CustomFields entry {Key, Value}. The mapping is reduced into a local => remote
array first, so duplicate local values collapse to the last one, while two distinct
locals pointing at [fullname] both survive. Resubscribe, autoresponder restart and
ConsentToTrack are stored per action; tracking consent can be No, Yes or resolved
from a form field.
Two routes back the editor UI, both requiring manage_options (Addon.php:274-298):
GET /wp-json/xpac/v1/form/touchBasePro/lists
GET /wp-json/xpac/v1/form/touchBasePro/(?P<list>[a-zA-Z0-9_-]+)/fieldslists answers { lists: [{ id, name }] } mapped from the API's ListID and Name
(:341-352). fields answers { fields: [{ value, label }] } mapped from Key and
FieldName, with [email]/Email and [fullname]/Name prepended (:393-410). The
list regex is [a-zA-Z0-9_-]+ — wide enough that a format change on TouchBasePro's
side reaches the plugin and is reported in its own words, rather than missing the route
and getting a generic WordPress 404. Missing credentials return a 400 REST error;
provider 401 and 403 statuses are preserved, while other upstream failures return 502.
The editor validates both response shapes and renders the error instead of treating a
malformed response as an empty list.
Client inspects transport and HTTP outcomes and raises the provider's explanation
with its status code. subscribe() remains registered through
xpac_forms_submit_callbacks, but Forms schedules it after the entry exists and
Deliveries\Runner invokes it off-request. Accepted subscriber actions record stable
keys, so later attempts skip them.
The package fires no hooks of its own — there is no do_action and no
apply_filters anywhere in its PHP — and registers no blocks. That is a fact
about its design rather than a gap in this reference: everything it does is a callback
on somebody else's contract, so there are no signatures of its own to document. It
attaches to five Forms hooks — xpac_forms_init to bootstrap (Addon.php:144, fired
at Forms/Bootstrap.php:93 on init priority 200),
xpac_forms_post_meta_schema and xpac_forms_post_meta_default_values for the shape
of its settings (fired at Forms/core/base/PostType.php:126 and :118),
xpac_forms_post_script_dependencies to load its editor panel (PostType.php:329),
and xpac_forms_submit_callbacks for the send — plus rest_api_init for its routes
and plugins_loaded/admin_notices in Bootstrap.php. All five Forms registrations
happen inside init() whether or not a key is currently configured; that keeps saved
actions inspectable and lets a missing key become a visible delivery failure.
On the editor side the panel is contributed from JavaScript through the
xpac-forms-form-panels filter (assets/packages/touchbasepro/index.js:25, applied at
assets/packages/forms/hooks/useFormPanels.js:62). It declares no priority, which is
why it sorts below every core Forms panel and why its position relative to other
add-ons' panels is not stable
(assets/packages/forms/hooks/useSortedAndFilteredPanels.js:18-21). Its action modal
fires xpac-forms-after-settings-modal-content — a Forms-owned extension point, not
one of its own (assets/packages/forms/constants.js:10) — passing the action's current
options with an onChange callback and the module id xpac-touchbasepro — the same
spelling the PHP side passes, exported as HANDLE from
assets/packages/touchbasepro/constants.js
(assets/packages/touchbasepro/Components/Modal.js:147-155); Conditional Logic is the
only listener (assets/packages/conditional-logic/forms/admin.js:76).
The editor bundle is one entry, assets/packages/touchbasepro/index.js →
dist/packages/touchbasepro/settings.js
(bin/webpack-config/entry-points/packages/touchbasepro.js:2), registered rather than
enqueued (Addon.php:166-178, vendor/xpac/assets-manager/AssetsManager.php:957) and
pulled in as a dependency of the Forms editor script.
The package's only licensing touchpoint is Updater::register() in Bootstrap.php:54.
Nothing in Addon.php checks a licence.