Security and troubleshooting

Privacy and security, rate limits, content security policy, and what each message and error code means.

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.com
  • connect-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:

Code
[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.

Stuck, or something missing?

Get in touch and a person on the team will help.

Contact us