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

# User identities

> Use one user ID while retaining phone numbers and usernames as optional aliases.

Use `id` to identify a user, group, or bot. Phone numbers are separate, optional aliases.

| Resource | ID format         |
| -------- | ----------------- |
| User     | `pmfa_lid_<id>`   |
| Group    | `pmfa_gid_<id>`   |
| Bot      | `pmfa_botid_<id>` |

```json theme={null}
{
  "id": "pmfa_lid_100000011111111",
  "phoneNumber": "+15550001111",
  "username": "example"
}
```

Use `id` as your contact's primary key. Update `phoneNumber` and `username` when
present; a changed or hidden alias does not create a new user.

## Resolve an alias

These reads require the `contacts:read` scope.

Resolve a phone number or username before creating a local contact:

```bash theme={null}
curl "https://api.polymorfa.com/messaging/{session}/identities/resolve?phoneNumber=%2B15550001111" \
  -H "Authorization: Bearer $POLYMORFA_TOKEN"
```

```bash theme={null}
curl "https://api.polymorfa.com/messaging/{session}/identities/resolve?username=example" \
  -H "Authorization: Bearer $POLYMORFA_TOKEN"
```

The response uses the same identity shape:

```json theme={null}
{
  "data": {
    "id": "pmfa_lid_100000011111111",
    "phoneNumber": "+15550001111",
    "username": "example"
  }
}
```

Some usernames require a four-digit key. A response with
`"keyRequired": true` contains no guessed identity. Repeat the lookup with
`usernameKey=1234` after obtaining the key from the user.

You can also pass an existing `id` to read its known aliases.

## Verify a user's encryption identity

Request WhatsApp's identity verification code with the stable user
ID:

```bash theme={null}
curl "https://api.polymorfa.com/messaging/{session}/users/pmfa_lid_100000011111111/security-code" \
  -H "Authorization: Bearer $POLYMORFA_TOKEN"
```

```json theme={null}
{
  "data": {
    "id": "pmfa_lid_100000011111111",
    "phoneNumber": "+15550001111",
    "username": "example",
    "numericCode": "123456789012345678901234567890123456789012345678901234567890",
    "qrCode": "base64-encoded-display-payload"
  }
}
```

Compare `numericCode` with the code shown by the other user. To use `qrCode`,
base64-decode it and render the resulting bytes as the QR content for
comparison. A changed code means the account's
identity or linked-device set changed and must be verified again. The endpoint
accepts only a `pmfa_lid_` user ID; resolve phone numbers and usernames first.
Polymorfa returns WhatsApp's display payload and never exposes the underlying
identity-key payload.

## Hidden phone numbers

A missing `phoneNumber` does not mean the user is unresolved. Continue using
their `id`. To ask a contact without a known phone number to share their phone number, send a
consent request:

```json theme={null}
{
  "conversation": { "id": "pmfa_lid_100000011111111" },
  "content": { "requestPhoneNumber": {} }
}
```

The recipient can decline. If they accept, Polymorfa emits a
`message.received` webhook with `type: "phone_number_shared"`; the sender's
`id` is unchanged and `phoneNumber` is added as an alias.

## Upsert rule

For every contact, participant, sender, voter, presence source, and blocklist
entry:

1. Upsert by `id`.
2. Update `phoneNumber` and `username` only when they are present.
3. Never create a second record because an alias appeared, changed, or
   disappeared.

This rule prevents one person from becoming separate phone-number, username,
and ID records.

## Linked devices

Device lookups return the user's `id` or `phoneNumber`, plus a numeric `device`
value. Together, they identify a linked device:

```json theme={null}
{
  "id": "pmfa_lid_100000011111111",
  "device": 4
}
```
