diff --git a/advanced/metrics.mdx b/advanced/metrics.mdx
index 693c1e8b..929e297a 100644
--- a/advanced/metrics.mdx
+++ b/advanced/metrics.mdx
@@ -77,6 +77,10 @@ All metrics are prefixed with `wa_` and emitted at the same boundaries as the ma
| `wa_recv_total` | `outcome` = `decrypted`, `duplicate`, `undecryptable`, `skmsg` | Inbound messages by decrypt outcome |
| `wa_send_total` | `kind` = `dm`, `group`, `status` | Outgoing send attempts by kind |
| `wa_retry_receipt_total` | `reason` | Retry receipts sent, by reason |
+| `wa_high_retry_total` | `reason` | Retry receipts sent at the high-retry watermark (count ≥ MAX), by reason |
+| `wa_retry_unknown_device_total` | `sender_type` = `primary`, `companion` | Retries from a device not in the registry, by sender type |
+| `wa_retry_refused_total` | — | Retries refused at the MAX_RETRY loop guard |
+| `wa_base_key_collision_total` | — | Base-key collisions that forced a fresh session |
| `wa_iq_total` | `result` = `ok`, `timeout`, `error` | IQ requests by result |
| `wa_reconnect_total` | — | Reconnect attempts |
| `wa_stream_error_total` | — | Stream errors received |
diff --git a/advanced/observability.mdx b/advanced/observability.mdx
index 39ae7a5b..1cca4ef4 100644
--- a/advanced/observability.mdx
+++ b/advanced/observability.mdx
@@ -96,7 +96,7 @@ Spans are grouped under a stable `wa..` naming scheme so you can filte
| `wa.pair.*` | QR code and pair code authentication |
| `wa.media.*` | Upload, download, history sync, sticker packs, media conn refresh |
| `wa.receipt.*` | Receipt processing (delivered, read, played) |
-| `wa.retry.*` | Retry receipt handling |
+| `wa.retry.*` | Retry receipt handling; `wa.retry.handle_receipt` records a `count` field (retry depth) so storm depth is aggregable per sender |
| `wa.pdo.*` | Peer Data Operations (message recovery via primary device) |
| `wa.notif.*` | Notification dispatch (group, devices, chatstate, identity change, privacy token) |
| `wa.session.*` | Signal session establishment and crypto |
diff --git a/changelog/2026-06-17-retry-warn-levels-and-telemetry.mdx b/changelog/2026-06-17-retry-warn-levels-and-telemetry.mdx
new file mode 100644
index 00000000..6689de0d
--- /dev/null
+++ b/changelog/2026-06-17-retry-warn-levels-and-telemetry.mdx
@@ -0,0 +1,34 @@
+---
+title: "June 17, 2026 — Retry log-level parity and observability counters"
+description: "Four new wa_retry_* / wa_base_key_collision_total counters, a retry count field on the handle_receipt span, and two WARN→debug/info corrections that match WA Web's own log levels."
+---
+
+## Observability
+
+**Retry-flow observability counters and WA Web log-level parity ([#887](https://github.com/oxidezap/whatsapp-rust/pull/887))**
+
+### New metrics counters
+
+Four new low-cardinality, PII-safe counters are now emitted on the retry flow when the `metrics` feature is enabled. All four compile to inlined no-ops without the feature.
+
+| Counter | Labels | Emitted when |
+|---------|--------|--------------|
+| `wa_high_retry_total` | `reason` | A retry receipt is sent at the high-retry watermark (count ≥ MAX). Mirrors WA Web's `MessageHighRetryCount` WAM event (id 3132). |
+| `wa_retry_unknown_device_total` | `sender_type` = `primary`, `companion` | A retry arrives from a device not in the registry. Mirrors WA Web's `MdRetryFromUnknownDevice` WAM event (id 2178). |
+| `wa_retry_refused_total` | — | A retry is refused at the MAX_RETRY loop guard. Turns a chronically thrashing peer into an aggregate health signal instead of requiring log scraping. |
+| `wa_base_key_collision_total` | — | A base-key collision forced a fresh session. |
+
+### Tracing span field
+
+`wa.retry.handle_receipt` now records a `count` field (the retry depth for that receipt). This makes retry storm depth aggregable per sender even when the MAX_RETRY cap causes an early return.
+
+### Log-level corrections
+
+Two retry-path `warn!` calls that fired for benign, remote-driven, fully-handled conditions are now emitted at levels matching WA Web's own `WALogger.LOG` (informational):
+
+- **"Refusing retry #N … exceeds max attempts"**: `warn!` → `debug!`. WA Web's `WAWebHandleRetryRequest` uses `LOG` (level 2) for this refusal and reserves `WARN` for real errors (no-requester, device-not-found). The mirror receive-side capped-retry path in `message/retry.rs` already logged at `debug!`.
+- **"Base key collision detected …"**: `warn!` → `info!`. WA Web's `WAWebUpdateLocalSignalSession` uses `LOG` for the same-base-key session delete, and the three sibling branches of `update_local_signal_session` already log at `info`.
+
+Both lines now also include the `message_id` so collisions are correlatable to a specific message during a retry storm.
+
+**No behavior change.** Every modification is a log level, an added log field, a counter increment, or a span field. The retry logic (cap value, base-key save/compare/delete, recovery path) is verbatim parity with the WA Web oracle and is untouched.
diff --git a/docs.json b/docs.json
index 46f7bd29..ea8ff10e 100644
--- a/docs.json
+++ b/docs.json
@@ -138,6 +138,7 @@
"group": "Changelog",
"pages": [
"changelog/overview",
+ "changelog/2026-06-17-retry-warn-levels-and-telemetry",
"changelog/2026-06-15-token-tiny-map",
"changelog/2026-06-14-drop-moka-portable-cache",
"changelog/2026-06-12-binary-size-ci",