> ## Documentation Index
> Fetch the complete documentation index at: https://developer.plugchoice.com/llms.txt
> Use this file to discover all available pages before exploring further.

# In a browser

> Open Link from a web page, without the mobile SDK.

export const Screen = ({name, alt, caption, wide = false}) => {
  const style = wide ? {
    width: '100%'
  } : {
    width: '280px',
    maxWidth: '100%'
  };
  return <figure style={{
    margin: '1.5rem 0',
    textAlign: 'center'
  }}>
      <img className="block dark:hidden" src={`/images/sdk/${name}-light.webp`} alt={alt} style={{
    ...style,
    margin: '0 auto',
    borderRadius: '12px',
    border: '1px solid rgba(128, 128, 128, 0.25)'
  }} />
      <img className="hidden dark:block" src={`/images/sdk/${name}-dark.webp`} alt={alt} style={{
    ...style,
    margin: '0 auto',
    borderRadius: '12px',
    border: '1px solid rgba(128, 128, 128, 0.25)'
  }} />
      {caption ? <figcaption style={{
    marginTop: '0.5rem',
    fontSize: '0.875rem',
    opacity: 0.7
  }}>{caption}</figcaption> : null}
    </figure>;
};

Link also runs in a browser, on a phone or a computer. Use it from your web app or customer portal. There's no SDK to install: your server creates a client session and sends the browser to its `web_url`.

<Screen name="web-consent" wide alt="Link's consent screen in a desktop browser" caption="Link in a desktop browser" />

## Open Link

<Steps>
  <Step title="Create a client session">
    Your server creates a client session for your user, as in [Get started](/sdk/get-started), scoped to what the user is about to do. Add a `redirect_uri` to send the user back when they're done.
  </Step>

  <Step title="Send the browser to web_url">
    The answer's `web_url` already holds the client secret. For an action other than `add`, append the action and its charger. For `add` at one of the scope's sites, append `site_id`.
  </Step>

  <Step title="Confirm the outcome">
    When the user comes back, your server confirms the link session with [Get a link session](/sdk/api/get-link-session).
  </Step>
</Steps>

```ts server.ts theme={null}
app.get('/chargers/:chargerId/network', requireUser, async (req, res) => {
  const { chargerId } = req.params;
  const response = await fetch('https://api.plugchoice.com/sdk/v1/client-sessions', {
    method: 'POST',
    headers: {
      // Your integration's access token, as in Get started.
      Authorization: `Bearer ${await plugchoiceToken()}`,
      'Content-Type': 'application/json',
      'Plugchoice-Version': '2026-10-07',
    },
    body: JSON.stringify({
      external_user_id: req.user.id,
      scope: { chargers: [chargerId] },
      redirect_uri: 'https://app.example.com/plugchoice/done',
    }),
  });
  if (!response.ok) throw new Error(`Plugchoice: HTTP ${response.status}`);
  const { web_url } = await response.json();

  res.redirect(`${web_url}&action=network&charger_id=${encodeURIComponent(chargerId)}`);
});
```

The parameters go in the URL's fragment, after `#`:

| Parameter | |
| - | - |
| `cs` | The client secret. `web_url` has it already. |
| `action` | `add` (the default), `network`, `setup`, `reconnect`, or a later one. See [Actions](/sdk/concepts/actions). |
| `charger_id` | The charger, for every action but `add`. |
| `site_id` | For `add`: one of the scope's sites. |

## The client secret in the URL

Browsers never send a URL's fragment to a server, so the secret doesn't reach server logs or a `Referer` header. The hosted flow reads it and removes it from the address bar at once. If the user reloads the page, the link session carries on.

In a browser, Link can't ask your app for a new secret, as the mobile SDK does. Open `web_url` within 60 minutes of creating the client session, or the user sees an error. Create a new client session for every visit rather than storing the URL. Once open, a link session lasts up to 6 hours.

## When the user is done

With a `redirect_uri`, the hosted flow sends the browser there, with two query parameters added:

| Parameter | |
| - | - |
| `status` | `success`, `cancelled` or `error` |
| `session_id` | The link session |

```
https://app.example.com/plugchoice/done?status=success&session_id=0b8a6f3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b
```

Anyone can type these parameters, so don't trust them. Read the outcome with [Get a link session](/sdk/api/get-link-session), and show what it says.

Without a `redirect_uri`, the hosted flow tells the user they can close the page.

<Note>
  A `redirect_uri` must be on your integration's allow-list. To add one, email [support@plugchoice.com](mailto:support@plugchoice.com).
</Note>

## What works in a browser

A browser can't join a charger's Wi-Fi or reach chargers on the local network, so some setups need the mobile SDK. In a browser, the hosted flow only offers what it can do there.

| Charger | In a browser | In your app with the SDK |
| - | - | - |
| [Volt Time](/sdk/chargers/volt-time) | Added by serial number and pincode. Wi-Fi over Bluetooth in Chrome and Edge. | Everything, including Wi-Fi over Bluetooth |
| [Zaptec](/sdk/chargers/zaptec) | Yes: the owner signs in with their Zaptec account | Yes |
| [Alfen](/sdk/chargers/alfen) | Only set up by hand | Guided setup on the home Wi-Fi |
| [Peblar](/sdk/chargers/peblar) | Only set up by hand | Guided setup over the charger's own Wi-Fi |
| [Other OCPP chargers](/sdk/chargers/other) | Set up by hand | Set up by hand |

Bluetooth in a browser works in Chrome and Edge on Android, Windows, macOS and ChromeOS. It doesn't work in Safari, so not in any browser on an iPhone, nor in Firefox. There, the hosted flow skips the Wi-Fi step and waits for the charger to come online another way, such as a network cable or a SIM.

In Chrome or Edge, the browser asks which device to use: the user taps **Connect**, then picks the charger, which is named after its Chargepoint ID.

<Screen name="web-start" wide alt="The hosted flow in a desktop browser, asking the user to connect to the charger over Bluetooth" caption="Connecting to a charger over Bluetooth in Chrome" />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.