Privacy and security#
- The widget key is public. On its own it doesn't allow anything.
- Only your addresses. The widget only works on pages at the addresses Subcontext has allowed for your key. No cookies or logins are used.
- Signed identity proves which firm and adviser the page is for (see Signed identity). A trial firm's "claim your account" link is only offered on pages with a valid pass.
- Firms can be switched off by Subcontext at any time; the adviser then sees "Not enabled yet".
- The adviser approves every message before it is sent.
- No double sends. Each send carries a unique reference, so if the network retries it, the message still only goes once.
- Rate limits. To prevent abuse, Subcontext limits how often the widget
can be used:
- writing, rewriting and sending messages: 30 an hour for each firm on your key, and 30 an hour from each IP address;
- accepting the terms: 5 an hour for each firm on your key, and 10 an hour from each IP address;
- and 300 requests every 5 minutes from each IP address overall.
Over a limit, requests fail with HTTP 429 for a while. Try again later. - Links in the widget (terms, account, "Powered by Subcontext") open in a new tab.
Content security policy#
If your portal sends a content security policy (CSP, a header that tells the browser which sites a page may load from), add:
script-src https://js.subcontext.comconnect-src https://subcontext.com
The widget only makes ordinary web requests (no WebSockets), so it doesn't
need wss:.
You don't need a style-src entry in current browsers. Older browsers
(before Chrome 73, Firefox 101 and Safari 16.4) fall back to a <style>
tag. For those, put your page's CSP nonce on the script tag (the widget
passes it on), or allow style-src 'unsafe-inline'.
If connect-src blocks the widget, the console shows a network warning
naming the address it tried to reach.
The widget's requests all go to https://subcontext.com/api/v1/nurture/,
which helps when reading your browser's network tab or your own proxy logs.
Troubleshooting#
When something is wrong, the widget shows the adviser a short message and logs the reason in the browser console. Refusals are always logged as warnings, with the request, the code, the HTTP status and an explanation, for example:
[Subcontext.Nurture] GET /api/v1/nurture/config failed: origin_not_allowed (403): This page's origin (https://portal.example) isn't allowed to use this widget key.
Requests that got no answer (network, timeout) and a firm with no
channel it can use are logged the same way. Your page hears about it too, through the
subcontext:nurture:refused and subcontext:nurture:error events. For more
detail, add data-debug="true".
No button#
| What you see | What it means |
|---|---|
[Subcontext.Nurture] init() needs integrationKey and firmId in the console |
init() was called without a key or firm id, or they were empty or unreplaced merge fields. |
| Nothing in the console | The tag is missing data-integration-key or data-firm-id, so the widget is waiting for init(). Or the tag has data-enabled="false", or destroy() was called. |
Your init() options seem to be ignored |
init() was called with no options (which does nothing on a running widget), or the options had no key or firm id (there will be a console warning, and the running widget is kept). |
"Not enabled yet"#
The console warning gives the code:
| Code (HTTP status) | What it means | What to do |
|---|---|---|
invalid_key (401) |
The widget key isn't recognised, or it has been replaced. | Check the key, or ask Subcontext for the current one. |
key_disabled (403) |
The widget key has been switched off. | Contact Subcontext. |
origin_not_allowed (403) |
The page's address isn't on the allowed list. | Ask Subcontext to add it (exactly as shown in the message). |
unknown_firm (404) |
No firm with that id, and your portal doesn't offer trials. | Check the firm id, or ask Subcontext to set the firm up. |
feature_disabled (403) |
Nurture is switched off for this firm. | Contact Subcontext. |
identity_required (401) |
No signed pass was sent, and your portal requires one. | Add data-identity-token. |
identity_invalid (401) |
The pass is malformed, the signature is wrong, the signing key is unknown or switched off, or the expiry is more than a day away. | Check how you create the pass (see Signed identity). |
identity_mismatch (403) |
The firm id or adviser id on the page isn't the one in the pass. | Create the pass with exactly the ids you put in the page. |
no_channels (refused event only) |
The firm has no channel it can send on. The console says No channel (SMS, email or WhatsApp) is available to this firm. |
Ask Subcontext to set up the firm's channels. |
Other messages#
| What you see | What it means |
|---|---|
| "This page has been open too long: reload it to carry on." | identity_expired: the pass has expired. Create a new pass each time the page is rendered. |
| "Something went wrong" right after opening | The request failed (network), took more than 15 seconds (timeout), or got an unexpected reply (bad_response). Check your CSP and the console. |
| The "Set up Nurture with AI" screen | The firm isn't set up yet and your portal offers trials. Accepting creates a real trial account for the firm. If it says "this page doesn't carry your firm's name", add firmName. |
| The terms screen for each new adviser | Each adviser accepts once, and again when the terms change. If the terms change while the form is open (terms_version_stale, 409), the new terms are shown. |
| A channel option is greyed out | Hover over it to see why: the client has no mobile or email, the client opted out, the channel isn't set up for the firm, or it is locked on a trial account. |
| "The additional context is too large (64KB at most)." | subcontext is bigger than 64 KB as JSON. |
| An upgrade prompt after Generate | upgrade_required (409): the trial's monthly limit is used up. |
| A draft tab shows an error | The draft failed, the client has opted out of that channel (blocked), or writing it took over 45 seconds. "Try another draft" tries again. |
| A message under the form after Generate | validation_failed, invalid_phone, channel_unavailable (422), contact_opted_out or not_provisioned (409). The message explains the problem. |
| "The message couldn't be delivered." | Sending failed on every channel. If only some failed, the sent screen says "Couldn't deliver by ..." and names them. |
HTTP 429, Rate limit exceeded. Please try again later. |
Too many requests. See the rate limits in Privacy and security. |