# Employer at sign-up

> A picker that suggests brands as people type, instead of a free text field.

Source: https://new.wasmenia.com/en/developers/use-cases/onboarding

Instead of a text field where each user writes their organisation as they like, a picker suggests from the archive as they type and stores the brand’s slug. Each organisation gets one identity in your database, with its logo ready for the team page.

- `GET https://new.wasmenia.com/api/v1/search/suggest`
- `GET https://new.wasmenia.com/api/v1/brands/{slug}`

- **Products**: Search, identity data
- **Pulls**: One per brand picked the first time
- **Search**: Not counted

## The problem, and the fix

When each user types their organisation themselves, one organisation gets many spellings: «stc», «Saudi Telecom», «STC Group». You can’t tell how many people share an employer, and you can’t show its logo.

A picker that suggests from the archive and stores `slug` gives each organisation one id. You know who works where, can show the logo on team pages, invoices and reports, and the logo updates when the organisation changes its identity.

## Who it’s for

### Hiring and team platforms

The employer on profiles and team pages.

### Government and education platforms

The organisation a user belongs to, by id not free text.

### Events and conferences

Each attendee’s organisation on their badge and in the attendee list.

## Build it

### Get a key

Open a developer account in the console and make a key. The free plan is enough to start, and the key stays on your server in an environment variable.

### A suggestions route of yours

A route on your server takes what the user typed, calls search with your key, and returns slug, names and image.

### The field in the form

Wait 250 ms after the last letter, cancel the previous request, and make the list work by keyboard.

### Store the slug

`slug` on the user’s record, not the name or the logo link.

### Show the logo

Read the brand’s record once and keep it; read it again when `updatedAt` changes.

### When it isn’t there

Let the name be entered as it is, and point the user to «Request a logo» if you like.

## The suggestions route

`brand-picker.js`

```javascript
// On your server, behind your own route (say /brand-suggest): it holds the key.
const base = 'https://new.wasmenia.com';

/** Suggestions for what the user has typed so far. */
export async function suggest(text, signal) {
  const q = text.trim();
  if (q.length < 2) return []; // shorter is answered empty and not counted

  const url = new URL('/api/v1/search/suggest', base);
  url.searchParams.set('q', q);
  const res = await fetch(url, {
    signal,
    headers: { Authorization: `Bearer ${process.env.WASMENIA_API_KEY}` },
  });

  if (res.status === 429) {
    // Over your plan's requests a minute: wait, then try again.
    const wait = Number(res.headers.get('Retry-After') ?? 60);
    throw Object.assign(new Error('rate_limited'), { wait });
  }
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const { items } = await res.json();
  return items.map((b) => ({
    slug: b.slug,          // what you store
    label: b.nameAr,
    secondary: b.nameEn,   // may be null
    image: b.thumbUrl,     // may be null: show the name alone
    verified: b.isVerified,
  }));
}
```

## With the input field

```javascript
// In the page: it asks your route, never Wasmenia with your key.
let timer;
let pending; // the request still in flight, if any
input.addEventListener('input', () => {
  clearTimeout(timer);
  timer = setTimeout(async () => {
    pending?.abort(); // a newer keystroke makes the older answer useless
    pending = new AbortController();
    try {
      const url = '/brand-suggest?q=' + encodeURIComponent(input.value);
      const res = await fetch(url, { signal: pending.signal });
      if (res.status === 429) return pauseFor(Number(res.headers.get('Retry-After') ?? 60));
      render(await res.json());
    } catch (e) {
      if (e.name === 'AbortError') return; // replaced by a newer request
      showError();
    }
  }, 250);
});
```

## How many pulls it costs

Search isn’t counted. A pull is when you read the brand’s record after a pick: if you keep the record, a thousand users from 40 different organisations are 40 pulls, then one per organisation that changes.

## Questions

### Should I forbid free text?

No. Not every organisation is in the archive. Suggest first, and accept the name as typed when the user doesn’t find theirs.

### Why not call from the browser?

The key is a secret, and browsers on other sites can’t send it. A small route on your server holds it.

**Build it step by step**

The request, the wait between letters, cancelling the previous request, and 429.
