# List API keys

Source: https://api-docs.magicdoor.com/api-reference/api-keys/list-api-keys

Returns a page of every API key in the company, whoever issued it, so an administrator can see what exists. A key's secret is never included. Requires the `api_keys:read` permission.

`GET https://auth.magicdoor.com/company-portal/api-keys`

**Authentication:** bearer token in the `Authorization` header, as `Bearer <token>`.

## Query parameters

- `page` (integer) — Which page to return, starting at 1. Default: `1`. Example: `1`.
- `pageSize` (integer) — How many keys to return per page. Between 1 and 100. Default: `50`. Example: `50`.
- `userId` (string) — Return only the keys issued by this user, as a Snowflake id in decimal-string form. Leave it out to list every key in the company. Example: `1824903117441335296`.
- `revoked` (boolean) — `true` returns only revoked keys, `false` only keys that are still live. Leave it out to get both. Example: `false`.

## Example request

```bash
curl --request GET \
  --url 'https://auth.magicdoor.com/company-portal/api-keys?page=1&pageSize=50&userId=1824903117441335296&revoked=false' \
  --header 'Authorization: Bearer <token>'
```

## Responses

### 200 — A page of API keys

- `items` (ApiKey[]) — The keys on this page.
  - `id` (string) — This key's id, a Snowflake sent as a decimal string. It is also the middle segment of the key string the integration holds.
  - `name` (string) — The label the issuer gave the key, so keys can be told apart.
  - `userId` (string) — The user the key acts as. The key can never do more than this user can.
  - `permissions` (string[]) — What the key may do, as `resource:action` strings. This is the ceiling rather than a guarantee: the set is narrowed to the user's current permissions every time the key is exchanged. Absent when the key is unscoped, which means it carries whatever its user can do.
  - `created` (string, date-time) — When the key was issued, in the company's timezone and with no offset.
  - `expires` (string, date-time) — When the key stops working, in the company's timezone. Absent for a key that does not expire on its own.
  - `lastUsed` (string, date-time) — When the key was last exchanged for an access token, in the company's timezone. Absent while the key has never been used.
  - `revoked` (string, date-time) — When the key was revoked, in the company's timezone. Absent while the key is still live.
- `totalCount` (integer) — How many keys match across every page.
- `totalPages` (integer) — How many pages the matching keys fill at this page size.
- `page` (integer) — The page that was returned.
- `pageSize` (integer) — How many keys were asked for per page.

```json
{
  "items": [
    {
      "id": "1830472847398404096",
      "name": "Nightly rent-roll sync",
      "userId": "1824903117441335296",
      "permissions": [
        "properties:read",
        "units:read",
        "leases:read"
      ],
      "created": "2026-08-17T21:04:11",
      "expires": "2027-01-01T00:00:00",
      "lastUsed": "2026-08-17T23:15:02"
    },
    {
      "id": "1827365512004698112",
      "name": "Retired reporting job",
      "userId": "1824903117441335296",
      "created": "2026-05-02T09:30:00",
      "lastUsed": "2026-07-28T04:00:00",
      "revoked": "2026-08-01T11:22:47"
    }
  ],
  "totalCount": 2,
  "totalPages": 1,
  "page": 1,
  "pageSize": 50
}
```

### 400 — `page` was below 1, or `pageSize` was outside 1 to 100

### 401 — The access token is missing, expired, or invalid

### 403 — The caller does not hold the `api_keys:read` permission

```json
{
  "type": "https://httpstatuses.com/403",
  "title": "Forbidden",
  "status": 403,
  "detail": "Invalid permission requirement api_keys:read",
  "instance": "/company-portal/api-keys",
  "traceId": "0HN7GK9C2M4PQ"
}
```

The second key in the example above is unscoped — it has no `permissions` field at all, so it carries whatever its user can do at the moment it is exchanged. It is also revoked, so exchanging it now fails.
