Skip to content

JavaScript Widget ​

Embed address autocomplete on your website using a widget public key (pk_live_...). Widget keys are designed for browser use — unlike server API tokens, they cannot access the full API surface.

How it works ​

  1. Create a Widget (JS) key in the Tensoit client panel and add allowed domains.
  2. Load the SDK script and stylesheet on your page.
  3. The SDK exchanges your public key for a short-lived session (sess_..., 15 minutes) via POST /widget/v2/session on https://api.tensoit.pl.
  4. Autocomplete requests use Authorization: Bearer sess_... and are billed like server API hits.

The /session endpoint is not counted toward API usage — only autocomplete data endpoints are.

Quick start ​

html
<link rel="stylesheet" href="https://tensoit.pl/css/v1/tensoit-autocomplete.css">
<script src="https://tensoit.pl/js/v1/tensoit-autocomplete.js"></script>
<script>
  TensoitAutocomplete.init({
    publicKey: 'pk_live_YOUR_KEY_HERE',
    autoFillPostal: true,
    selectors: {
      city: '#city',
      street: '#street',
      number: '#number',
      postal: '#postal'
    }
  });
</script>

Configuration ​

OptionRequiredDefaultDescription
publicKeyYes—Widget key from the client panel (pk_live_...)
selectorsYes—CSS selectors for form fields (at minimum city)
apiBaseNohttps://api.tensoit.pl/widget/v2Default production widget API; local script hosts still use /api/widget/v2
themeNoautoauto, light, or dark
autoFillPostalNotrueWhen enabled, fetches postal codes after city and building number are set (street may be empty); if exactly one result is returned, it is filled into the postal field automatically
streetPrefixNoomittedStreet label format. dot: a leading ulica / ul. becomes ul., and aleja becomes al.. empty: ulica / ul. are removed, while aleja still becomes al.. A name without those features is unchanged. Omit the option to keep the stored name

Auto-fill postal code ​

When autoFillPostal is true (default), the SDK calls the postal endpoint after the building number is completed — either by choosing a suggestion or when the number field loses focus. If the API returns exactly one postal code, the SDK writes it into the postal field without opening the dropdown.

The auto-fill runs only when:

  • a postal selector is configured,
  • city and number have values (street may be empty — some localities have no street name),
  • the postal field is still empty.

To require manual postal lookup only, disable the feature:

javascript
TensoitAutocomplete.init({
  publicKey: 'pk_live_YOUR_KEY_HERE',
  autoFillPostal: false,
  selectors: {
    city: '#city',
    street: '#street',
    number: '#number',
    postal: '#postal'
  }
});

New widget keys created in the client panel include autoFillPostal: true in the generated embed snippet so the default behavior is visible in your integration code.

Street label ​

streetPrefix appends ?streetPrefix= to the existing street GET. The database search stays unchanged. In the response, suggestions is the stored name and labels is the text for the list and the field.

After a choice, the street field shows the label (al. Jana Pawła II or Jana Pawła II). Number and postal requests send the original from suggestions (Aleja Jana Pawła II, ulica Jana Pawła II). Editing the field by hand clears that original.

Any value other than dot or empty is ignored in JS. The API returns 400.

javascript
TensoitAutocomplete.init({
  publicKey: 'pk_live_YOUR_KEY_HERE',
  streetPrefix: 'dot',
  selectors: {
    city: '#city',
    street: '#street',
    number: '#number',
    postal: '#postal'
  }
});

The same field works on REST without a path change:

text
GET /v2/street/Warszawa/Jana?streetPrefix=dot
POST /v2/street   { "city": "Warszawa", "street": "Jana", "streetPrefix": "empty" }

Add the next optional parameter the same way: a query parameter on GET and the same field in the POST JSON.

Selectors ​

Any valid CSS selector is supported:

javascript
selectors: {
  city: '#city',
  street: '[name="street"]',
  number: '.house-number',
  postal: 'input[data-field="postal"]'
}

If a selector does not match an element, the SDK logs a clear error in the browser console.

Place inputs in the DOM in this order for the best user experience:

  1. City — user starts typing the locality
  2. Street — suggestions depend on the selected city
  3. Building number — suggestions depend on city (street is optional); completing this field triggers postal auto-fill
  4. Postal code — last; often filled automatically when autoFillPostal is enabled

The SDK does not reorder fields in the page — it only binds to your selectors. If postal code appears before street or number in the layout, users may fill it manually too early and miss automatic completion after the number field.

Example markup:

html
<input id="city" name="city" autocomplete="off">
<input id="street" name="street" autocomplete="off">
<input id="number" name="number" autocomplete="off">
<input id="postal" name="postal_code" autocomplete="off">

Wire the same IDs in selectors as in Quick start.

Building numbers with a slash (e.g. 7/28) ​

Polish addresses often use compound numbers such as 7/28. These cannot be passed reliably in a GET URL path — %2F is treated as a path separator and the route returns 404.

The SDK always uses POST for number and postal lookups (request body: { city, street, number }). You do not need extra configuration.

If you call the REST API directly without the SDK, use:

  • POST /v2/number/ with JSON body
  • POST /v2/postal/ with JSON body

Reserve GET paths for simple values without / in the number segment.

Domain allowlist ​

Each widget key has one or more allowed domain patterns:

PatternMatches
shop.example.comexact host only
*.example.comone subdomain level, e.g. www.example.com, app.example.com

The browser must send an Origin header (automatic for fetch from your page). Requests from unlisted domains receive 403 Forbidden.

SDK versioning ​

Production integrations should pin a major version:

https://tensoit.pl/js/v1/tensoit-autocomplete.js
https://tensoit.pl/css/v1/tensoit-autocomplete.css

When a new major version is released (v2), existing v1 URLs keep working. Update the version in your HTML when you are ready to migrate.

Session lifecycle ​

  • Sessions expire after 15 minutes (configurable server-side).
  • The SDK refreshes the session automatically about 1 minute before expiry.
  • If a session expires mid-request, the next autocomplete call triggers a new session.

Security notes ​

  • Never put server API tokens (id|secret) in frontend code.
  • Widget keys are public identifiers scoped to allowed domains and autocomplete endpoints only.
  • Always use HTTPS in production.

API endpoints (widget) ​

MethodPathAuth
POST/widget/v2/sessionX-Public-Key: pk_live_... + Origin
GET/widget/v2/city/{city}Bearer sess_...
GET/widget/v2/street/{city}/{street}Bearer sess_...
GET/widget/v2/number/{city}/{street}/{number?}Bearer sess_...
POST/widget/v2/number/Bearer sess_...
GET/widget/v2/postal/{city}/{street}/{number?}Bearer sess_...
POST/widget/v2/postal/Bearer sess_...

Response shape matches the standard v2 autocomplete format (suggestions array). With streetPrefix, a labels array of the same length is added.

Example ​

See tensoit-autocomplete.example.js for a copy-paste starter snippet.

Local smoke test ​

  1. Create a widget key in the client panel and allow your test domain (e.g. 127.0.0.1 or your staging host).
  2. On the API app (local: http://127.0.0.1:8000/widget-demo.html?pk=pk_live_...) open the demo page with your public key.
  3. Type a city prefix (e.g. Skier) and confirm suggestions appear; check Network tab for POST /widget/v2/session then GET /widget/v2/city/....

Production embed uses the same SDK URLs as in Quick start; only publicKey and selectors change per site.