Security and troubleshooting

What is public and what is secret, the content security policy to allow, and how to fix the quote form's errors.

What is public and what is secret#

  • The quote form's key is public. It sits in your page's source. It only works from the websites you listed, only while the form is turned on, and only for the form's own product and journey. To replace it, press New key on the quote form's page. Tokens from a key last one hour, and the form gets new ones as needed.
  • The API application's client secret is secret. Keep it on your server. With getToken, only the short-lived token reaches the browser, and it only works from the API application's allowed origins. If a secret leaks, rotate it on the API application's page.
  • Each application's share token. A token from the key (or from getToken) is one anyone can get, so it can't reach an application on its own. The form keeps the share token of the application it started and sends it in an X-Share-Token header with every request about that application. Subcontext answers any other request about an existing application with 404. A link that opens a quote carries a signed token for its application, never the quote id alone.
  • The live connection. The form never puts the application's private link token in an address. It swaps it for a short-lived ticket first.
  • Rate limits. Subcontext limits how many requests one IP address can make in a short time, including token requests. Over the limit, requests are refused with status 429.
  • Payments are taken by Stripe in Stripe's own secure fields. Card and bank details never reach your page or Subcontext.

Content security policy#

A content security policy (CSP) is a header some websites send to control what a page may load. If your site has one, add these:

Directive Add Why
script-src https://js.subcontext.com The quote form script (and the webchat or form guide, if you use them).
connect-src https://subcontext.com wss://subcontext.com Talking to Subcontext, and the live connection (wss:// is a secure WebSocket).
style-src 'unsafe-inline' The form adds its stylesheet as a <style> tag and uses some inline styles (including each form's colours).
script-src https://js.stripe.com Stripe's script. Only if the journey takes payment.
connect-src https://api.stripe.com Only if the journey takes payment.
frame-src https://js.stripe.com https://hooks.stripe.com https://checkout.stripe.com Stripe's card fields, the bank's card check (3-D Secure) and the Direct Debit setup. Only if the journey takes payment.
font-src, style-src your font hosts If you load fonts for fontFamily or displayFont.

Troubleshooting#

First, turn on debug: true to see detailed logs in the browser console. Errors and warnings are always logged, starting [Subcontext.Quote]. When something goes badly wrong, the form shows the message (with its code in brackets, if there is one) and a "Try again" button, and calls onError.

Nothing appears#

What you see What to do
Console: a data-subcontext-quote element needs data-key, data-product and data-journey Add the missing attribute to the placeholder.
Error: Subcontext.Quote.init: missing required option 'container' (or productSlug, journeySlug) Add the missing option.
Error: Subcontext.Quote.init: missing required option 'getToken' (or 'key') Give key or getToken.
Error: Subcontext.Quote.init: container not found The selector matched nothing when init() ran. Run it after the element is on the page.
Nothing at all, no errors Check the script loaded (your CSP may block it), and that data-base-url or apiBase, if you set one, is https://subcontext.com.

The form shows an error message#

Message Code (status) What to do
Unknown quote form key. invalid_key (401) The key is wrong, was replaced with a new one, or the quote form was removed. Copy the current code from the quote form's page.
This quote form has been turned off. form_disabled (403) Press Turn on on the quote form's page.
This account can't take quotes right now. not_entitled (403) Your organisation's subscription has lapsed. Ask Subcontext.
This website isn't on the quote form's list of allowed websites. origin_not_allowed (403) Add this page's exact origin under Websites that may use it. Remember www. and non-www. are different.
This quote isn't available right now. not_available (409) The product or journey isn't live. Ask Subcontext if you're not sure why.
The quote form couldn't start (429). (429) Too many token requests from this IP address. Wait a few minutes.
Resource not found not_found The product or journey slug is wrong for your organisation, or the product has nothing published yet.
The request took too long to complete: please check your connection and try again. timeout No answer within 20 seconds. Check the connection and try again.
We couldn't generate a quote with the details provided. rate_unavailable The product has no price for these answers.

Errors with your own server (getToken)#

Message What to do
origin 'https://...' is not allowlisted for this OAuth application (forbidden) Add the page's origin to the API application's allowed origins. (With a key, this means the quote form's websites changed after the page loaded. Reload the page.)
this OAuth application is server-only and may not be used from a browser context (forbidden) The API application has no allowed origins. Add your website.
OAuth token is not valid for this resource (forbidden) Your token request didn't ask for resource=https://subcontext.com/api/v1.
From the token request: The requested resource must be https://subcontext.com/api/v1. (invalid_target) Your token request is missing resource, or it has the wrong value.
getToken() must return a string or { access_token, expires_in } Make getToken resolve to the token string or that object.

Other problems#

What you see What to do
this quote form's token starts only its own quote (forbidden) With a key, productSlug and journeySlug must be the quote form's own. The first page shows, but saving it fails. Copy them from the quote form's page.
Banner: We couldn't save your last answers: retrying… Saving a page failed and the form is trying again. Check the browser's network tab for the failing request.
Console: cable subscription rejected: ticket invalid or expired, or the WebSocket keeps reconnecting The live connection was refused. Check your CSP allows wss://subcontext.com. The form still works without live updates; only the chat's live answers are missing.
Console: webchat not loaded: ... The webchat option didn't have supported: true.
Console: formGuide skipped: agentId missing ... Give an agentId in formGuide or at the top level.

Need help?#

Email Subcontext at hello@subcontext.com.

Stuck, or something missing?

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

Contact us