Skip to main content
This guide covers security best practices when integrating with the Widgetic API.

API Key Security

Storage

Never expose API keys in client-side code, version control, or public repositories.
Do:
  • Store keys in environment variables
  • Use secrets management services (AWS Secrets Manager, Vault, etc.)
  • Rotate keys periodically
Don’t:
  • Commit keys to git
  • Include keys in frontend bundles
  • Share keys via unencrypted channels

Key Types

Making, Rotating and Deleting Keys

You make and manage your own keys. In the dashboard, open Workspace settings → API keys. With the API, use the key calls below. {id} is your own account id, which GET /tokens/validate returns as data.userId. Your plan must include API access.
  • GET /users/{id}/keys lists your keys, with what each is limited to (preset and scopes).
  • POST /users/{id}/keys makes one (send an Idempotency-Key). Only a person signed in on widgetic.com can make a key, so make keys in the dashboard: a request made with an API key or an SDK token is refused with 403. The secret is in the answer once, in key, and cannot be shown again. Send a preset (read_only, content or full_access) or a list of scopes to limit what the key can do, and neither for a key with full access. See Limit what a key can do.
  • PUT /users/{id}/keys/{keyId} renames a key or switches it off with isActive: false. Switching a key back on needs a person signed in too. DELETE /users/{id}/keys/{keyId} deletes it.
  • GET /users/{id}/api-keys/{keyId}/logs shows what a key was used for.
To rotate a key, make the new one in the dashboard, switch your servers over, then delete the old one. A key that is switched off or deleted is refused at once by the server that took the request, and within a minute by the others. The limit of a key cannot be changed once it is made: to widen or narrow it, rotate the key the same way.
A key cannot make another key, so a leaked key cannot be used to get back in after you revoke it. If a key leaks, delete it (in the dashboard, or with another key) and make a new one in the dashboard.

Client-Side Authentication

A browser must never hold an API key (wgt_usr_, wdg_org_, or wgt_prt_). Keep your API key on your server and have your backend make API calls on behalf of your application, or—when mounting Widgetic’s editors in your page for one of your customers—mint a short-lived editor session on your server:
Never send a partner key (wgt_prt_) or user key (wgt_usr_) to a browser: it carries the access of your whole account. Use an editor session instead—a session reaches only that customer’s content, only the calls the editors need, and can be bound to your page origin and revoked at any time.

Domain Validation

You can limit the websites an embedded widget may appear on. If you list nothing, the widget works everywhere.
  1. Open Allowed Domains in the dashboard (for a team workspace, use that workspace’s settings; only its admins and owners can change the list)
  2. Add each website: example.com also covers www.example.com, and *.example.com covers the domain and every subdomain
  3. Leave localhost out: a page on your own machine always works, however short the list is (see below)
You can also keep the list with the API, using your own account id as {id}. GET /users/{id}/workspace/domains reads it, POST adds a website with the body { "domain": "example.com" }, PUT /users/{id}/workspace/domains/{domainId} changes one, and DELETE removes one. A wildcard such as *.example.com is one entry, however many subdomains it covers. The list of an ordinary account holds up to 50 websites, and the 51st answers 409 with the code BUSINESS_STATE_CONFLICT. If you administer a partner account, your list has no limit: your customers may publish on their own domains, and you can add each of those by code, one POST for each website. A change applies within about 15 seconds. Once a list exists, the lookup that returns a widget’s content answers 403 with the code DOMAIN_NOT_ALLOWED to a page on any other website, and the widget’s frame page asks the browser not to show it anywhere else. A very long list, which only a partner can have (more than about fifty websites of ordinary length), does not fit in that request to the browser, so the frame page is then shown without it; the lookup, and the entries a visitor sends, still refuse every website that is not on the list. Widgetic’s own sites always work, and so does your own machine. If you are a partner, your platform’s website always works too (see below). The lookup never refuses a page on localhost, on 127.0.0.1 (or any address from 127.0.0.0 to 127.255.255.255), on [::1] or on a name under .localhost, on any port, and the widget’s frame page may be shown by localhost, 127.0.0.1 and names under .localhost, on any port, over http or https. You can build and test an integration without listing your laptop: only the person who runs such a page can see it, so listing it would protect nothing. A list that cannot be read never blocks a widget. The frame of a workflow, and its oEmbed card, follow the list of the owner of the workflow’s first composition in the same two ways: the frame asks the browser not to show it on another website, and the card answers 403 there. See Show a workflow where you cannot add a tag. If you use the Partner API, the allowedDomains of your partner account is added to the list of the person who administers it, and applies to the widgets made with your API keys.

Your platform is always allowed

If you are a partner, the website where you integrate Widgetic is always allowed, with all of its subdomains. Tell Widgetic which website that is when your partner account is set up, for example acme.com, and Widgetic records it as your platform domain. acme.com and every subdomain of it (app.acme.com, learn.eu.acme.com) can then always show your widgets, and the entries their visitors send (such as votes or form answers) are always accepted from them, whatever your list holds. The widgets your customers make through you are covered too, because they belong to your account.
  • You never have to list it, and a list can never lock it out. It is not an entry of your list, so adding websites to the list, or removing them all, never changes it.
  • It never narrows anything. If you list nothing, your widgets still work on every website, not only on your platform.
  • Widgetic sets it, you read it. GET /partners/mine gives it as platformDomain for each partner you administer, and it is null until Widgetic has set one. To change it, write to partners@widgetic.com.
  • It is one website. Give Widgetic the bare name, such as acme.com; an address like https://www.acme.com/pricing is shortened to it. A different website that also shows your widgets, such as a separate marketing site, is not covered: put it on your list when you keep one.
A domain list keeps your widgets off other people’s pages. It is not access control: the content of a published widget can still be requested by anyone who has its ID, using a plain HTTP request that names no website for the check to read. Do not rely on it to keep content private.
The <widgetic> embed script stops at the refusal: it draws nothing, sets data-widgetic-refused="DOMAIN_NOT_ALLOWED" on the element, and writes one warning to the browser console for each widget. It does not fall back to a copy of the widget on Widgetic’s CDN.

Webhook Signatures

Partner accounts can have Widgetic call their own server when something happens, for example when an invoice is paid. No other kind of account receives webhooks. Every call is signed, with the Standard Webhooks headers webhook-id, webhook-timestamp and webhook-signature, under a secret that only you hold. Before you act on a call:
  • Check the signature over the raw body, with a Standard Webhooks library or the short check in Notifications.
  • Refuse a call whose webhook-timestamp is more than five minutes old.
  • Keep the webhook-id of every event you handled and drop a repeat: delivery is at least once.
If the secret leaks, make a new one with POST /partners/{partnerId}/notifications/webhook-secret. The old one keeps signing for a day, so your receiver can switch without a call being rejected. See Your Own Webhook.

Rate Limiting

Protect your integration from abuse:

Data Protection

Sensitive Data

Don’t store sensitive user data in widget configurations:

Audit Logging

Log API interactions for security auditing:

Your Customers’ Data

If you keep managed users for your customers, Widgetic holds a record for each one. You can read a record and delete it:
Deleting the record removes the managed user, not what you filed under it. The compositions, widgets and uploads stay in your account, and lose only their managedUserId label. To erase a customer’s content as well, list it with the managedUserId filter and delete it first, then delete the record (see Managed Users).

Security Checklist

  • Keys stored in environment variables
  • Keys rotated quarterly
  • Separate keys for dev/staging/production
  • Access logged and monitored
  • Token exchange for browser apps
  • Short token expiration (a token lasts at most 1 hour)
  • Domain validation enabled
  • Partner accounts: your platform domain is set (platformDomain in GET /partners/mine)
  • CSP headers configured
  • Signature checked on the raw body
  • Calls older than five minutes refused
  • Repeats dropped by webhook-id
  • A 2xx answer within ten seconds, with the slow work done afterwards
  • Secret kept out of git, and replaced if it leaks

Reporting Vulnerabilities

Found a security problem in Widgetic? Write to support@widgetic.com, say that it is a security report, and give the steps to reproduce it.