
# Get a member's diagnostics

`GET /v1/organizations/{org_id}/memberships/{membership_id}/diagnostics` · permissions `organizations:read` and `users:read`

Answers "why can't this member sign in?" for an organization admin in one response: the membership, a summary of the organization's policies, a pending invitation, a summary of the member's authenticators, the sign-in state in the context of the organization, the member's directory groups and the member's access to applications. It is read only: nothing changes, and write actions go to their own endpoints. Sign-in activity is read with [List a user's logs](/docs/api/management/users/list-logs) and `org_id`.

Only data that belongs to the organization is returned. Labels, AAGUIDs, IP addresses, user agents, email addresses, CIDR ranges and records from the context of another organization are never part of the response.

## Request

| Field | Type | Required | Description |
|---|---|---|---|
| `org_id` | path | yes | Organization ID (`org_…`). |
| `membership_id` | path | yes | Membership ID (`mem_…`). |

No request body.

## Response

`200 OK` with these fields:

| Field | Description |
|---|---|
| `membership` | The membership as [Get a membership](get.md) returns it: status, source, roles and the suspension reason. |
| `policy` | The policies written on the organization: `authentication_methods` (passkey and password allowed, number of allowed connections), `mfa` (who must use MFA and the allowed factors), `phishing_resistant`, `sso_enforcement` (mode, and `bypass` when the member is on the break-glass list) and `ip_allowlist` (present, and who it applies to). `null` means the organization inherits the environment value for that policy. |
| `invitation` | Only for an `invited` membership: the pending invitation to the user's primary email address, with its delivery and delivery status. `null` otherwise. The email address is not returned. |
| `authenticators` | `classes`: the types of the member's active authenticators. `mfa_enrolled`: `true` when an active authenticator has the role `second_factor` or `both`. |
| `sign_in` | `locked` and `locked_until` (a lock stops the account's sign-in in every context), and the last sign-in in the context of this organization (`last_sign_in_at`, `last_sign_in_amr`). Failure counters are not shown, because they belong to no organization. |
| `groups` | The member's groups in the organization's directories (`data`: `id` `dsg_…` and `directory_id` `dir_…`; at most 100, `truncated` when there are more). Only memberships of a directory user that is linked to this user and active count. Group names are not returned; read them with `GET /v1/directories/{directory_id}/groups/{directory_group_id}`. |
| `applications` | Access to applications in this organization. Applications whose `org_access_mode` is `all_organizations` are open to every member and only counted (`open_count`). For the other applications (`data`, by name, at most 100, `truncated` when there are more): `application_id`, `name`, `access_mode`, `allowed`, and `reason` when access is refused (`application_not_enabled`: the application is not enabled for the organization; `application_not_assigned`: neither the membership nor one of its directory groups is assigned). The decision follows the same rule as the check at sign-in. |

```json
{
  "membership": {
    "id": "mem_01j9xn5p9s0t4v6w8x0y2z4a6c",
    "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
    "user_id": "usr_01j9xh2k6p7q1r3s5t7v9w1x3y",
    "status": "suspended",
    "source": "scim",
    "reason": "scim_deprovisioned",
    "roles": [],
    "expires_at": null,
    "created_at": "2026-10-12T09:31:00.123456Z",
    "activated_at": "2026-10-12T09:31:00.123456Z",
    "suspended_at": "2026-10-20T07:00:00Z",
    "removed_at": null,
    "updated_at": "2026-10-20T07:00:00Z",
    "version": 3
  },
  "policy": {
    "authentication_methods": null,
    "mfa": { "required_for": "all", "allowed_factors": ["totp", "webauthn"] },
    "phishing_resistant": null,
    "sso_enforcement": { "mode": "org_context", "bypass": false },
    "ip_allowlist": null
  },
  "invitation": null,
  "authenticators": { "mfa_enrolled": false, "classes": ["webauthn"] },
  "sign_in": {
    "locked": false,
    "locked_until": null,
    "last_sign_in_at": "2026-10-19T08:15:42Z",
    "last_sign_in_amr": ["fed"]
  },
  "groups": {
    "data": [{ "id": "dsg_01j9xp4r8s2t4v6w8x0y2z4a6b", "directory_id": "dir_01j9xm1n5p7q9r1s3t5v7w9x1y" }],
    "truncated": false
  },
  "applications": {
    "open_count": 2,
    "data": [
      {
        "application_id": "app_01j9xq6s0t2v4w6x8y0z2a4b6c",
        "name": "Finance portal",
        "access_mode": "assigned_only",
        "allowed": false,
        "reason": "application_not_assigned"
      }
    ],
    "truncated": false
  }
}
```

## Errors

| HTTP | code | When |
|---|---|---|
| 404 | `not_found` | The membership does not exist in this organization, or it is `removed`. |
| 401 | `unauthenticated` | The credential is missing, invalid or revoked. |
| 403 | `insufficient_permissions` | The credential lacks `organizations:read` or `users:read`. |
| 429 | `rate_limited` | The rate limit was exceeded; retry after the `Retry-After` period. |
| 503 | `service_unavailable` | A dependency is temporarily unavailable; retry after the `Retry-After` period. |

## Example

```bash
curl -i "https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/memberships/mem_01j9xn5p9s0t4v6w8x0y2z4a6c/diagnostics" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
```

## Personal data

This endpoint carries personal data: organization memberships and invitations, and credentials. [Personal data in the KeenAuth APIs](/docs/policies/personal-data) describes what is stored, how long it is kept and how it is deleted. Do not send special categories of personal data (health, biometric data, religion, political opinions, sexual orientation) in any field.
