IDCanopy Developers
Umbrella APIAddress Autocomplete

Widget

The drop-in dropdown component, every option, events, styling, accessibility and a full example

widget.js attaches an address dropdown to an input you already have and fills the form fields when the user picks a suggestion. It follows the client rules for you: debounce, minimum length, session handling, stale responses and attribution. It has no dependencies. If you want your own dropdown instead, see Build your own UI.

Add it

The script is served by the service itself. Where the Umbrella gateway exposes it, and how the widget authenticates through the gateway, is pending verification; it is written as BASE_URL.

<script src="BASE_URL/widget.js"></script>
<script>
  AddressAutocomplete.attach({
    input: '#address',
    apiKey: 'BROWSER_KEY',
    countries: ['DE'],
    fields: {
      street: '#street',
      house_number: '#nr',
      postal_code: '#plz',
      city: '#city',
      country_code: '#cc',
    },
    onSelect: function (address, suggestion, response) {
      // address is the resolved address; response is the full resolve payload
    },
  });
</script>

attach throws AddressAutocomplete: input not found if the selector matches nothing, so call it after the input exists (end of body, or on DOMContentLoaded).

Options

OptionDefaultNotes
inputnoneElement or selector. Required
apiKeynoneSent as the X-API-Key header. Required on a live service, not needed against Mock
endpointdirectory of the scriptBase URL of the service, without a trailing slash. The widget appends /v1/predict and /v1/resolve. If the script is loaded in a way that leaves no script element to read (for example as a module), set it explicitly
countriesservice defaultArray of one or more country codes to restrict results to, for example ['DE'] or ['DE', 'FR']. See Country and address type
nearnone{lat, lng} to bias results toward a point. Send it only if the user has agreed to share a position
minChars3Characters before the first call. Below the service minimum a lower value only wastes calls
debounceMs250Wait after the last keystroke before calling predict
fieldsnoneMap from an address field name to a selector, filled on select. Any address field can be used: formatted, street, house_number, postal_code, city, district, state, country, country_code, lat, lng, precision
onSelect(address, suggestion, response)noneCalled after a successful resolve. address is response.address; response.verification is set when verification is enabled
onError(err)noneCalled on a failed predict or resolve
crosscheckservice settingA boolean the widget passes on to resolve. The service honours it only in demo setups; in production the setting is service-wide, so leave it out

The widget does not send language; the service default (de) applies. It also does not expose a way to set session, which it manages itself.

attach returns a handle:

MethodDoes
close()Closes the list
destroy()Closes the list, removes the wrapper and replaces the input with a copy of itself so the widget's listeners are gone. Any reference you hold to the old input element is stale afterwards, and the copy keeps the role and aria-* attributes the widget set

Events and callbacks

On a successful pick, in this order:

  1. The input is set to the suggestion's text and the list closes.
  2. The widget calls resolve. If it fails, onError is called, no field is filled and no event fires.
  3. The fields targets are filled (a null value, such as a missing lat, becomes an empty string).
  4. onSelect(address, suggestion, response) is called.
  5. A bubbling address:selected CustomEvent is dispatched on the input. Its detail is the full resolve response, {address, verification}, not the bare address.
document.querySelector('#address').addEventListener('address:selected', function (e) {
  console.log(e.detail.address.formatted, e.detail.address.precision);
});

The widget sets values programmatically and does not dispatch input or change events on the input or on the fields targets. A framework that tracks form state through those events (React controlled inputs, Vue v-model) will not see the new values. Read them in onSelect and update your own state there.

suggestion is the object the user picked: {id, text, main_text, secondary_text}.

Always check address.precision. A street-level pick has no house_number; the user still has to complete it.

Behaviour

  • Calls. After the debounce, and when the trimmed text is at least minChars long, it sends POST {endpoint}/v1/predict with input, session, countries and near. On a pick it sends POST {endpoint}/v1/resolve with id and session. Both are JSON with Content-Type: application/json and the X-API-Key header when apiKey is set.
  • Session. The widget takes session from each predict response and sends it on later calls. After a successful resolve it drops it, so the next address entry starts a new session. It does not drop it when the user clears the field without picking, so keystrokes of an abandoned entry and the next entry share one session.
  • Stale responses. Each predict gets a sequence number and a response that is no longer the latest is ignored. Requests are not cancelled, only ignored. One gap: deleting text below minChars closes the list but does not invalidate a request already in flight, so its response can reopen the list.
  • Empty results. The list stays closed and the field stays editable. There is no "no results" message.
  • Attribution. A non-empty attribution from the API is rendered as the last row of the list. Keep it visible; it is a licence obligation.
  • Closing. Esc closes the list, and so does leaving the field (after 150 ms, so a click on a suggestion still registers).
  • Several fields. You can call attach once per input. The stylesheet is injected once.

Accessibility

The widget sets the ARIA combobox pattern: role="combobox", aria-autocomplete="list", aria-expanded and aria-controls on the input, aria-activedescendant pointing at the highlighted option, role="listbox" on the list and role="option" with aria-selected on each suggestion. The input gets autocomplete="off".

KeyAction
Down, UpMove the highlight through the suggestions. It does not wrap, and Up from the field goes to the first item
EnterPicks the highlighted suggestion. With nothing highlighted the key is left alone, so the form submits as usual
EscCloses the list

Not provided: a live region that announces the number of suggestions, a "no results" message, and a minimum touch-target size beyond the item padding (8 px vertical). If your accessibility requirements ask for these, add a polite live region next to the input and set a larger padding on .idc-ac-item. The highlight is a background colour only; add a second cue if you need one. Pending verification: no screen reader test was done for this page.

Styling and theming

There are no theme options or CSS variables. The widget injects one <style id="idc-ac-css"> with minimal rules and everything is overridden with ordinary CSS on these classes:

ClassElement
.idc-ac-wrapA div the widget puts around your input, position: relative
.idc-ac-listThe ul dropdown, absolutely positioned under the input, z-index: 1000, white background
.idc-ac-itemOne suggestion (li)
.idc-ac-item[aria-selected=true]The highlighted suggestion
.idc-ac-main, .idc-ac-secThe first line (street and number) and the second line (postcode, city, country)
.idc-ac-attrThe attribution row
.idc-ac-list { background: #1b1f27; border-color: #3a4150; color: #e8ebf0; }
.idc-ac-item[aria-selected=true], .idc-ac-item:hover { background: #2b3a55; }
.idc-ac-attr { color: #9aa4b2; }

Because the widget wraps your input in a block-level div, an input that sits inline in a flex row needs the wrapper styled instead (for example .idc-ac-wrap { flex: 1; }).

Browser keys, CORS and CSP

The widget calls the API from the browser, so you need a key of your own and your site's origin on the service's allowed-origin list (a service-wide list, not per key). A browser key is visible to anyone on the page, and origin checks do not stop scripts. If that is not acceptable, proxy the service calls through your backend and use the API directly.

  • CORS. The calls are cross-origin POST requests with a JSON body and a custom header, so the browser sends a preflight first. The service allows GET and POST and any request header for the origins on its list. A missing origin shows up in the console as a CORS error, not as an HTTP error the widget can report.
  • CSP. If your site sets a Content Security Policy, allow the service origin in script-src (for widget.js) and in connect-src (for the API calls). The widget also injects an inline <style> element and has no nonce support, so a style-src without 'unsafe-inline' blocks that stylesheet. In that case copy the rules above into your own stylesheet; the widget works without its injected styles. Not tested under a strict policy.
  • No pinned version. The script is served as one file with no version in the URL, and it can change when the service does. Pending verification: whether a versioned URL will be offered.

Pointing it at Mock, sandbox and production

TargetendpointapiKeyScript
Mock (German and Austrian sample data only)https://<this portal>/mock/api/services/address/autocompletenot neededhttps://<this portal>/mock/address-autocomplete/widget.js
Sandboxthe Umbrella sandbox base URL from Environments plus the gateway sub-pathyour keyPending verification
Productionthe Umbrella production base URL plus the gateway sub-pathyour keyPending verification

The gateway sub-path (/address/autocomplete), the way the gateway expects the key (the widget sends X-API-Key, the rest of Umbrella uses a bearer token) and the script URL are pending verification.

Full example

A complete page against Mock. Replace <this portal> with the address of the site you are reading this on. Mock holds invented German and Austrian addresses only (the sample, not the coverage of the service): type Haupt or Bahnhofstr 12 Wien.

<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Address entry</title>
  <style>
    form { max-width: 28rem; font: 16px/1.4 system-ui, sans-serif; }
    label { display: block; margin-top: .75rem; }
    input { width: 100%; box-sizing: border-box; padding: .5rem; }
    .idc-ac-item { min-height: 44px; display: flex; flex-direction: column; justify-content: center; }
    #status { margin-top: .75rem; min-height: 1.4em; }
  </style>
</head>
<body>
  <form id="f" autocomplete="off">
    <label>Address <input id="address" type="text"></label>
    <label>Street <input id="street" type="text"></label>
    <label>Number <input id="nr" type="text"></label>
    <label>Postcode <input id="plz" type="text"></label>
    <label>City <input id="city" type="text"></label>
    <label>Country <input id="cc" type="text"></label>
    <p id="status" role="status" aria-live="polite"></p>
  </form>

  <script src="https://<this portal>/mock/address-autocomplete/widget.js"></script>
  <script>
    var statusEl = document.getElementById('status');
    AddressAutocomplete.attach({
      input: '#address',
      endpoint: 'https://<this portal>/mock/api/services/address/autocomplete',
      countries: ['DE'],
      fields: {
        street: '#street',
        house_number: '#nr',
        postal_code: '#plz',
        city: '#city',
        country_code: '#cc',
      },
      onSelect: function (address) {
        statusEl.textContent = address.precision === 'house'
          ? ''
          : 'Please add the house number.';
        if (address.precision !== 'house') document.getElementById('nr').focus();
      },
      onError: function (err) {
        // err.message is 'HTTP <status>' for an API error, or the browser's message for a network or CORS failure
        statusEl.textContent = 'Address suggestions are unavailable. You can type the address by hand.';
      },
    });
  </script>
</body>
</html>

The role="status" paragraph is not part of the widget; it is how this page tells screen-reader users about a missing house number or an error.

Errors the widget reports

onError receives an Error. For an API error its message is HTTP <status>; there is no status property and the response body is not passed on. The statuses are the ones in the integration guide: 401 is a configuration problem (key), 404 on resolve means the suggestion expired, 422 means a country that is not enabled or a bad field, 502 means the address source is unavailable. In every case keep the field editable and let the user type the address by hand. A failed predict closes the list; the next keystroke tries again.

Pending verification: the widget behaviour above is taken from its source and has not been run against the live service.

On this page