# Web App Origin Association file

> How an origin confirms a scope_extensions request with a well-known JSON file, including its location, shape, parser failures, and fallback navigation.

The Web App Origin Association file lets an origin confirm that a web app may include that origin in its extended scope. The origin serves the JSON file at `/.well-known/web-app-origin-association`.

## How it works

The app manifest lists another origin in `scope_extensions`. That origin confirms the request by using the app's manifest `id` as a top-level key in its association file. The optional `scope` member limits the accepted URL space on the confirming origin; the explainer defines `/` as its default.

Each side controls its own declaration. Removing either the manifest entry or the matching association-file entry removes the pair described by the explainer.

:::caution
Chromium's parser ignores an entry whose top-level key is not a valid URL and reports `Associated app ignored. Manifest ID is not a valid URL.` It also reports `Associated app ignored, type object expected.` when the value is not an object ([Chromium parser source](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/webapps/services/web_app_origin_association/web_app_origin_association_parser.cc)).
:::

## Examples

The first example shows the association file itself; the second keeps a normal cross-origin link available when display mode cannot establish whether association succeeded.

### Confirming one app

This file confirms the app whose manifest `id` is `https://app.example/id` and limits its accepted scope on the confirming origin to `/docs`.

```json
{
  "https://app.example/id": {
    "scope": "/docs"
  }
}
```

Serve the JSON from `https://support.example/.well-known/web-app-origin-association`, because the confirming origin owns the file.

### Keeping navigation usable with `display-mode`

The `display-mode` media feature reports the display mode in which the app is shown. That value is not an association-success signal, so keep the ordinary link as the navigation path in both branches.

```js
const link = document.createElement('a');
link.href = 'https://support.example/docs';
link.textContent = 'Open documentation';

if ('matchMedia' in globalThis && matchMedia('(display-mode: standalone)').matches) {
  link.dataset.surface = 'standalone';
} else {
  link.dataset.surface = 'browser-fallback';
}

document.body.append(link);
```

The fallback does not guess why the standalone query is false. It leaves the cross-origin URL available for the browser to navigate normally.

## See also

- [scope_extensions manifest member](/reference/manifest/scope-extensions/)
- [id manifest member](/reference/manifest/id/)
- [Scope Extensions for Web App Manifest (GitHub)](https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-explainer.md)
- [display-mode (MDN)](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/display-mode)