Documentation

Quickstart

Everything a page needs to run an EduFacturing widget: the script tag, the attributes, the Content-Security-Policy directives, and every error the widget can show a visitor.

API https://developers.edufacturing.com CDN https://cdn.edufacturing.com v1

01Install

<script type="module" src="https://cdn.edufacturing.com/embed.js" data-ef-api="https://developers.edufacturing.com"></script>

<ef-printer-advisor
    ef-key="pk_ef_live_your_publishable_key"
    ef-locale="en"
    ef-theme="auto"></ef-printer-advisor>

Load embed.js once per page, anywhere — it is a module, so it does not block rendering. Then place as many elements as you like. Each one fetches its own configuration and renders inside its own Shadow DOM, so your CSS and ours never meet.

data-ef-api on the script tag is optional; it overrides the compiled-in API host. An ef-api attribute on a single element overrides it for that element only.

02Attributes

AttributeValuesDefault
ef-keyYour publishable keyrequired
ef-localeen, bg from <html lang>, else en
ef-themeauto, dark, lightauto
ef-apiAbsolute URL of the portal https://developers.edufacturing.com

03Theming

The widget reads CSS custom properties from its own host element. Set them from your stylesheet and nothing else changes:

ef-printer-advisor{
  --ef-bg:#fff;
  --ef-surface:#f4f6f8;
  --ef-border:rgba(0,0,0,.12);
  --ef-text:#1a2129;
  --ef-text-strong:#000;
  --ef-muted:#5a6b76;
  --ef-accent:#e8752a;
  --ef-accent-ink:#fff;
  --ef-radius:12px;
  --ef-font:"Inter",system-ui,sans-serif;
}

Everything inside is Shadow DOM: your page cannot accidentally restyle the widget, and the widget cannot restyle your page.

04Content-Security-Policy

If your site sends a CSP header — and it should — these are the directives the widget needs. Nothing else is contacted.

DirectiveAddWhy
script-srchttps://cdn.edufacturing.com embed.js and the lazily loaded advisor chunk
connect-srchttps://developers.edufacturing.com The one call that validates your key and returns the widget config
worker-srcblob: The advisor scores the catalogue in a Web Worker. A cross-origin worker has to be started through a small blob shim; if blob: is refused, the widget falls back to computing on the main thread — slower, still correct.
frame-src the quote / agent host, if you use those elements Only <ef-quote-calculator> and <ef-agent-chat> use an iframe; the advisor does not.

No style-src entry is normally needed: the widget’s styles live inside its Shadow DOM and are installed as a constructed stylesheet (adoptedStyleSheets), which style-src does not gate. On a browser without that API the widget falls back to a <style> element inside its shadow root, and that fallback does need style-src 'unsafe-inline'.

05The configuration call

GET https://developers.edufacturing.com/v1/widgets/printer-advisor/config?key=pk_ef_live_…
Origin: https://your-shop.example

200 {
  "widget": "printer-advisor",
  "plan": "pro",
  "locale": "en",
  "module": "https://cdn.edufacturing.com/w/printer-advisor.js?v=…",
  "worker": "https://cdn.edufacturing.com/w/printer-advisor.worker.js?v=…",
  "urls":  { "quoteCalculator": null, "agentChat": null },
  "quota": { "used": 41, "limit": 10000, "period": "2026-08" }
}

The key may also travel as an X-EF-Key header. The response is never cached and never contains a secret.

06Every error the visitor can see

StatusCodeWhat went wrong
401key_missingNo ef-key attribute.
401key_invalidThe key does not exist.
401key_revokedYou revoked it in the dashboard.
401secret_key_in_browser A sk_ key reached the browser endpoint. Replace it with the publishable key and rotate the secret.
403origin_required The request carried no Origin. Widgets must run in a page.
403origin_not_allowed This domain is not on the key. Add it in the dashboard.
429rate_limited Burst limit for the plan. Retry-After says when.
429quota_exceeded Monthly calls are used up. Upgrade or wait for the next period.
404unknown_widgetNo such element name.

Refused requests do not count against your quota — a misconfigured page cannot spend your plan.

07Domains

A key carries a list of hosts. Accepted forms:

  • shop.example.com — exactly that host
  • *.example.com — any subdomain, but not example.com itself; add it separately if you need it
  • localhost:5173 — for local development; the port is part of the origin, so :5174 is a different entry

A pattern that would cover a whole suffix (*.com) is refused when you save it, and the dashboard tells you which entries were dropped.

08Where the recommendation comes from

The advisor runs @edufacturing/reco-core against the EduFacturing knowledge core — the same rule set as our other tools. Each machine card lists the rule ids that supported or penalised it; hovering a rule shows its citation.

Catalogue prose (a machine’s pros and cons, a material’s description) is written in Bulgarian and is shown only with ef-locale="bg". The English widget explains from structured specifications — price, technology, build volume, layer height, use-case tags — rather than from machine translation.

09Limits, and what the publishable key does not protect

Three independent limits sit in front of the configuration call:

LimitScopeOn the free plan
Monthly quotaYour whole account, across every key100 calls
BurstYour whole account — adding a second key does not raise it 30 / minute
Per-address burstOne visitor’s IP, whatever key it uses 60 / minute

Refused requests never touch the monthly quota, so a misconfigured page or a hostile visitor cannot spend your plan by being refused.

What the key does not protect

The publishable key is meant to be read. Anyone can open your page source, copy it and send requests with a forged Origin header — the header is only enforced by browsers, not by curl. That is the same trade-off every publishable-key widget makes. What the allow-list buys you is that the key is useless inside another website: a competitor cannot drop it into their own page and get a working widget. What it does not buy you is protection from someone deliberately burning your quota from a script. The per-address burst limit above is what makes that expensive; if that is not enough for your case, write to us before you rely on it.

Rotate a key from the dashboard the moment it appears somewhere it should not; revocation takes effect on the next request, with no cache in between.