# Permissions API (navigator.permissions.query)

> How navigator.permissions.query() reads a permission state and rejects unsupported names, with examples for change events and API absence.

The `navigator.permissions.query()` method reads the permission state for a named feature. Its promise fulfills with a `PermissionStatus`. The status is `"granted"` or `"denied"`; otherwise, it is `"prompt"`.

## Where it is supported

BCD records `Permissions.query()` from Chrome 43 and Firefox 46. Its Safari entry begins at version 16. The permission names accepted by the method differ by browser, so support for the interface does not establish support for a particular name.

## Syntax

```js
const status = await navigator.permissions.query({ name: 'geolocation' });
```

The method accepts one `PermissionDescriptor` and returns a `Promise<PermissionStatus>`.

## Parameters

| Member | Type | Meaning |
|---|---|---|
| `name` | string | The permission name to query. Each implementation registers the names it supports. |

Some registered permissions use descriptor types with more members. The Permissions specification defines `name` as a `DOMString`, not an enum, because implementations support different powerful features.

## Exceptions

The query algorithm rejects the promise with `InvalidStateError` when the associated document is not fully active. It rejects with `TypeError` when the permission name is unsupported. Conversion of the supplied descriptor can also reject the promise with the conversion's exception.

:::caution
The specification says: `If rootDesc["name"] is not supported, return a promise rejected with a TypeError`. Catch the rejection when querying a name that may not be registered in the current browser ([Permissions query algorithm](https://www.w3.org/TR/permissions/#dom-permissions-query)).
:::

## Examples

These examples distinguish an unavailable query from a permission state returned by the API.

### Reading a state with a fallback

This function checks for the API, handles an unsupported name, and returns a fallback value without treating it as a user denial.

```js
async function readGeolocationPermission() {
  if (!('permissions' in navigator)) {
    return 'unknown';
  }

  try {
    const status = await navigator.permissions.query({ name: 'geolocation' });
    return status.state;
  } catch (error) {
    return error instanceof TypeError ? 'unsupported-name' : 'unknown';
  }
}

console.log(await readGeolocationPermission());
```

The fallback preserves the distinction between a missing query surface and the specified `"denied"` state.

### Listening for state changes

`PermissionStatus` is an `EventTarget`. Its `change` event lets the page read the new state after it changes.

```js
if ('permissions' in navigator) {
  navigator.permissions.query({ name: 'notifications' }).then((status) => {
    console.log(status.state);
    status.addEventListener('change', () => console.log(status.state));
  }).catch(() => console.log('notifications permission query unavailable'));
} else {
  console.log('Permissions API unavailable');
}
```

The rejection branch also covers a browser that exposes `Permissions` without registering `notifications`.

## See also

- [Notification.requestPermission()](/reference/notifications/permissions/)
- [Geolocation API](/reference/capabilities/geolocation/)
- [Permissions specification (W3C)](https://www.w3.org/TR/permissions/)
- [Permissions.query() (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query)