4  Pivekimab Sunirine in Blastic Plasmacytoid Dendritic Cell Neoplasm

A Visual Summary of the CADENZA Phase I/II Trial

Author
Affiliation

Based on Pemmaraju N, Marconi G, Montesinos P, et al.

J Clin Oncol 2026; DOI: 10.1200/JCO-25-02083

Published

August 19, 2026

NoteKey Findings at a Glance

Pivekimab sunirine (PVEK) — a first-in-class CD123-targeted antibody–drug conjugate — achieved a 75% composite complete response (CCR) rate in frontline de novo BPDCN, with median overall survival of 16.6 months, and a manageable safety profile.

5 Background

  • Blastic plasmacytoid dendritic cell neoplasm (BPDCN) is an ultra-rare (≈0.05 per 100,000), aggressive hematologic malignancy involving skin, bone marrow, blood, lymph nodes, and CNS.

  • It is characterized by universal overexpression of CD123 (interleukin-3 receptor-α) on neoplastic blasts, making CD123 an attractive therapeutic target.

  • The median age at diagnosis is approximately 65 years, with a 3:1 male predominance.

  • Until recently, treatment options were largely limited to chemotherapy regimens adapted from acute leukemias, with historically poor outcomes.

  • Tagraxofusp, a CD123-directed cytotoxin, became the first and only approved targeted therapy for BPDCN.

  • The CADENZA trial (NCT03386513) evaluated a novel approach: pivekimab sunirine (PVEK), a CD123-targeted antibody–drug conjugate.

5.1 Mechanism of Action

Show code
library(ggplot2)

# Create a conceptual diagram of the ADC mechanism
mechanism_data <- data.frame(
  step = factor(c(
    "1. CD123\nBinding",
    "2. ADC\nInternalization",
    "3. Linker\nCleavage",
    "4. Payload\nRelease",
    "5. DNA\nAlkylation",
    "6. Cell\nDeath"
  ), levels = c(
    "1. CD123\nBinding",
    "2. ADC\nInternalization",
    "3. Linker\nCleavage",
    "4. Payload\nRelease",
    "5. DNA\nAlkylation",
    "6. Cell\nDeath"
  )),
  component = c("Antibody", "Antibody", "Linker", "Payload", "Payload", "Effect"),
  y = c(1, 1, 1, 1, 1, 1),
  description = c(
    "High-affinity\nanti-CD123 Ab",
    "Receptor-mediated\nendocytosis",
    "Cleavable\nlinker",
    "IGN pseudodimer\nreleased",
    "Single-strand\nDNA breaks",
    "Tumor cell\napoptosis"
  )
)

color_palette <- c(
  "Antibody" = "#2166ac",
  "Linker"   = "#66c2a5",
  "Payload"  = "#d53e4f",
  "Effect"   = "#3a3a3a"
)

ggplot(mechanism_data, aes(x = step, y = y)) +
  geom_segment(
    data = data.frame(x = 1:5, xend = 2:6, y = 1, yend = 1),
    aes(x = x, xend = xend, y = y, yend = yend),
    inherit.aes = FALSE,
    arrow = arrow(length = unit(0.25, "cm"), type = "closed"),
    color = "#888888", linewidth = 1.2
  ) +
  geom_point(aes(color = component), size = 18) +
  geom_text(aes(label = gsub("\\d+\\. ", "", gsub("\n", " ", as.character(step)))),
            size = 3, fontface = "bold", color = "white") +
  geom_text(aes(label = description, y = y - 0.15),
            size = 2.8, color = "#444444") +
 scale_color_manual(values = color_palette, name = "Component") +
  scale_x_discrete() +
  scale_y_continuous(limits = c(0.7, 1.2)) +
  labs(
    title = "Pivekimab Sunirine (PVEK): Mechanism of Action",
    subtitle = "CD123-targeted antibody–drug conjugate with indolinobenzodiazepine payload"
  ) +
  theme_void(base_size = 13) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 16, color = "#1a5276"),
    plot.subtitle = element_text(hjust = 0.5, size = 11, color = "#555555",
                                 margin = margin(b = 15)),
    legend.position = "bottom",
    plot.margin = margin(20, 20, 20, 20)
  )
Figure 5.1: Schematic overview of PVEK mechanism: anti-CD123 antibody binds to CD123 on BPDCN blasts, delivering an indolinobenzodiazepine pseudodimer payload that alkylates DNA and causes single-strand breaks.

6 Study Design

The CADENZA trial was an international, phase I/II, open-label, multicenter study. Patients received PVEK 0.045 mg/kg once every 3 weeks (day 1 of a 21-day cycle) as a ≤30-minute infusion.

Population Description N
PAP (Primary Analysis) Frontline de novo BPDCN 20
All Frontline De novo + PCHM 33
Relapsed/Refractory 1–3 prior systemic therapies 51
Total All BPDCN patients 84
Endpoint Type Definition
CCR rate Primary CR + CRc per investigator assessment (PAP)
Duration of CCR Key Secondary Time from first CCR to relapse/death
OS Secondary Time from first dose to death
ORR Secondary CR + CRc + CRh + CRi + PR
Bridge to SCT Exploratory Patients proceeding to stem-cell transplant

7 Patient Demographics

Show code
library(ggplot2)
library(dplyr)
library(tidyr)

# ── Demographics data from Table 1 ───────────────────────────────────
demo_data <- tibble(
  Characteristic = c(
    "Age ≥65 years", "Male", "White", 
    "ECOG PS 0", "ECOG PS 1",
    "Skin involvement", "Bone marrow involvement",
    "Peripheral blood", "Lymph nodes"
  ),
  `Frontline De Novo\n(n = 20)` = c(85, 85, 75, 35, 65, 90, 45, 5, 30),
  `All Frontline\n(n = 33)`     = c(91, 82, 82, 36, 64, 94, 49, 9, 36),
  `Relapsed/Refractory\n(n = 51)` = c(59, 82, 82, 35, 59, 67, 47, 20, 35)
)

demo_long <- demo_data |>
  pivot_longer(-Characteristic, names_to = "Group", values_to = "Percentage") |>
  mutate(
    Characteristic = factor(Characteristic, levels = rev(demo_data$Characteristic)),
    Group = factor(Group, levels = c(
      "Frontline De Novo\n(n = 20)",
      "All Frontline\n(n = 33)",
      "Relapsed/Refractory\n(n = 51)"
    ))
  )

group_colors <- c(
  "Frontline De Novo\n(n = 20)" = "#2166ac",
  "All Frontline\n(n = 33)"     = "#67a9cf",
  "Relapsed/Refractory\n(n = 51)" = "#d6604d"
)

ggplot(demo_long, aes(x = Percentage, y = Characteristic, fill = Group)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.7, alpha = 0.9) +
  geom_text(
    aes(label = paste0(Percentage, "%")),
    position = position_dodge(width = 0.75),
    hjust = -0.15, size = 3.2, fontface = "bold"
  ) +
  scale_fill_manual(values = group_colors) +
  scale_x_continuous(
    limits = c(0, 110),
    breaks = seq(0, 100, 25),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Baseline Patient Characteristics",
    subtitle = "CADENZA Trial — PVEK in BPDCN (N = 84)",
    x = "Patients (%)",
    y = NULL,
    fill = "Treatment Group"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", size = 16, color = "#1a5276"),
    plot.subtitle = element_text(size = 12, color = "#555555", margin = margin(b = 15)),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    axis.text.y = element_text(size = 11),
    plot.margin = margin(15, 25, 15, 15)
  )
Figure 7.1: Baseline demographic and clinical characteristics across treatment groups.

7.1 Prior Therapies in Relapsed/Refractory Patients

Show code
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.family'] = 'sans-serif'
import numpy as np

therapies = [
    'Non-intensive\nchemotherapy',
    'Tagraxofusp',
    'Intensive\nchemotherapy',
    'Prior HSCT',
    'Radiotherapy'
]
percentages = [63, 57, 49, 31, 18]

fig, ax = plt.subplots(figsize=(10, 5))

colors = ['#4393c3', '#2166ac', '#92c5de', '#d6604d', '#f4a582']

bars = ax.barh(
    range(len(therapies)), percentages,
    color=colors, edgecolor='white', linewidth=1.5, height=0.65
)

for bar, pct in zip(bars, percentages):
    ax.text(
        bar.get_width() + 1.5, bar.get_y() + bar.get_height()/2,
        f'{pct}%', va='center', ha='left', fontsize=12, fontweight='bold',
        color='#333333'
    )

ax.set_yticks(range(len(therapies)))
ax.set_yticklabels(therapies, fontsize=12)
ax.set_xlim(0, 80)
(0.0, 80.0)
Show code
ax.set_xlabel('Patients (%)', fontsize=12)
ax.set_title(
    'Prior Therapies — Relapsed/Refractory BPDCN (n = 51)',
    fontsize=15, fontweight='bold', color='#1a5276', pad=15
)
ax.invert_yaxis()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.tick_params(left=False)
ax.grid(axis='x', alpha=0.3, linestyle='--')

plt.tight_layout()
plt.show()
Figure 7.2: Prior therapies received by patients with relapsed/refractory BPDCN (n = 51).

8 Efficacy Results

8.1 Response Rates

Show code
library(ggplot2)
library(dplyr)
library(tidyr)

# ── Response data from Table 2 ───────────────────────────────────────
response_df <- tibble(
  Group = factor(c(
    "Frontline De Novo\n(PAP, n = 20)",
    "All Frontline\n(n = 33)",
    "Relapsed/Refractory\n(n = 51)"
  ), levels = c(
    "Frontline De Novo\n(PAP, n = 20)",
    "All Frontline\n(n = 33)",
    "Relapsed/Refractory\n(n = 51)"
  )),
  CCR  = c(75, 70, 14),
  CRh  = c(0, 12, 4),
  CRi_PR = c(5, 3, 17),
  No_Response = c(20, 15, 65),
  CCR_lower = c(51, 51, 6),
  CCR_upper = c(91, 84, 26)
)

# Reshape for stacked bars
resp_long <- response_df |>
  select(Group, CCR, CRh, CRi_PR, No_Response) |>
  pivot_longer(-Group, names_to = "Response", values_to = "Percentage") |>
  mutate(Response = factor(Response, 
    levels = c("No_Response", "CRi_PR", "CRh", "CCR"),
    labels = c("No Response", "CRi/PR", "CRh", "CCR (CR + CRc)")
  ))

resp_colors <- c(
  "CCR (CR + CRc)" = "#2166ac",
  "CRh"            = "#67a9cf",
  "CRi/PR"         = "#d1e5f0",
  "No Response"    = "#e0e0e0"
)

# ORR labels
orr_labels <- tibble(
  Group = response_df$Group,
  ORR = c(80, 85, 35),
  y_pos = c(82, 87, 37)
)

ggplot(resp_long, aes(x = Group, y = Percentage, fill = Response)) +
  geom_col(width = 0.65, color = "white", linewidth = 0.5) +
  # CI error bars for CCR
  geom_errorbar(
    data = response_df,
    aes(x = Group, ymin = CCR_lower, ymax = CCR_upper, y = NULL, fill = NULL),
    inherit.aes = FALSE,
    width = 0.15, linewidth = 0.8, color = "#333333"
  ) +
  # CCR percentage label
  geom_text(
    data = response_df,
    aes(x = Group, y = CCR / 2, label = paste0(CCR, "%"),
        fill = NULL),
    inherit.aes = FALSE,
    color = "white", fontface = "bold", size = 6
  ) +
  # ORR annotation
  geom_text(
    data = orr_labels,
    aes(x = Group, y = y_pos + 5, label = paste0("ORR: ", ORR, "%"),
        fill = NULL),
    inherit.aes = FALSE,
    color = "#1a5276", fontface = "bold", size = 4.2
  ) +
  scale_fill_manual(values = resp_colors, name = "Response Category") +
  scale_y_continuous(
    limits = c(0, 105),
    breaks = seq(0, 100, 25),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Best Overall Response Rates — PVEK in BPDCN",
    subtitle = "Primary endpoint: CCR (CR + CRc) in PAP | Error bars: 95% CIs",
    x = NULL,
    y = "Patients (%)"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 17, color = "#1a5276"),
    plot.subtitle = element_text(size = 12, color = "#555555", margin = margin(b = 15)),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 12, face = "bold"),
    plot.margin = margin(15, 20, 15, 15)
  ) +
  guides(fill = guide_legend(reverse = TRUE))
Figure 8.1: Best overall response rates across patient populations. Error bars represent 95% Clopper–Pearson CIs for CCR.
ImportantPrimary Endpoint Met

The CCR rate in the PAP (75%; 95% CI, 51–91%) successfully rejected the null hypothesis of 10%, meeting the primary objective. The relapsed/refractory cohort (14%; 95% CI, 6–26%) also rejected its null hypothesis of 5%.

8.2 Overall Survival — Kaplan-Meier Analysis

Show code
library(ggplot2)
library(dplyr)

# ── Reconstruct approximate KM curves from published at-risk tables ──
# PAP (n = 20): At-risk: 20, 20, 16, 14, 12, 11, 9, 5, 2, 0
# Events:         0,  0,  4,  6,  8,  8, 10,10,10,10
# Frontline (n=33): At-risk: 33,33,27,24,21,18,13,8,5,1
# R/R (n=51): At-risk: 51,40,22,15,12,10,6,5,5,2

# Approximate survival probabilities from the KM curves in Figure 2
km_data <- bind_rows(
  tibble(
    Time = c(0, 3, 6, 9, 12, 15, 18, 21, 24, 27),
    Survival = c(1.00, 1.00, 0.80, 0.70, 0.60, 0.55, 0.49, 0.49, 0.49, 0.49),
    Lower = c(1.00, 1.00, 0.58, 0.47, 0.36, 0.30, 0.26, 0.26, 0.26, 0.26),
    Upper = c(1.00, 1.00, 0.92, 0.85, 0.78, 0.74, 0.69, 0.69, 0.69, 0.69),
    Group = "Frontline De Novo (PAP)\nn = 20 | Median OS: 16.6 mo",
    n_risk = c(20, 20, 16, 14, 12, 11, 9, 5, 2, 0)
  ),
  tibble(
    Time = c(0, 3, 6, 9, 12, 15, 18, 21, 24, 27),
    Survival = c(1.00, 1.00, 0.82, 0.73, 0.64, 0.55, 0.44, 0.44, 0.44, 0.44),
    Lower = c(1.00, 1.00, 0.64, 0.55, 0.45, 0.36, 0.27, 0.27, 0.27, 0.27),
    Upper = c(1.00, 1.00, 0.92, 0.85, 0.78, 0.70, 0.60, 0.60, 0.60, 0.60),
    Group = "All Frontline\nn = 33 | Median OS: 16.6 mo",
    n_risk = c(33, 33, 27, 24, 21, 18, 13, 8, 5, 1)
  ),
  tibble(
    Time = c(0, 3, 6, 9, 12, 15, 18, 21, 24, 27),
    Survival = c(1.00, 0.78, 0.43, 0.29, 0.31, 0.24, 0.19, 0.19, 0.19, 0.19),
    Lower = c(1.00, 0.65, 0.30, 0.18, 0.18, 0.13, 0.09, 0.09, 0.09, 0.09),
    Upper = c(1.00, 0.88, 0.57, 0.43, 0.44, 0.37, 0.33, 0.33, 0.33, 0.33),
    Group = "Relapsed/Refractory\nn = 51 | Median OS: 5.8 mo",
    n_risk = c(51, 40, 22, 15, 12, 10, 6, 5, 5, 2)
  )
)

group_colors_km <- c(
  "Frontline De Novo (PAP)\nn = 20 | Median OS: 16.6 mo" = "#2166ac",
  "All Frontline\nn = 33 | Median OS: 16.6 mo" = "#4393c3",
  "Relapsed/Refractory\nn = 51 | Median OS: 5.8 mo" = "#d6604d"
)

ggplot(km_data, aes(x = Time, y = Survival, color = Group, fill = Group)) +
  # Confidence bands
  geom_ribbon(aes(ymin = Lower, ymax = Upper), alpha = 0.15, color = NA) +
  # KM step lines
  geom_step(linewidth = 1.3) +
  # Median OS reference lines
  geom_hline(yintercept = 0.5, linetype = "dashed", color = "grey50", linewidth = 0.5) +
  annotate("text", x = 0.5, y = 0.52, label = "50% survival", 
           hjust = 0, size = 3.2, color = "grey40") +
  # Median OS markers
  geom_segment(aes(x = 16.6, xend = 16.6, y = 0, yend = 0.5),
               color = "#2166ac", linetype = "dotted", linewidth = 0.7,
               inherit.aes = FALSE) +
  geom_segment(aes(x = 5.8, xend = 5.8, y = 0, yend = 0.5),
               color = "#d6604d", linetype = "dotted", linewidth = 0.7,
               inherit.aes = FALSE) +
  annotate("text", x = 16.6, y = -0.04, label = "16.6", fontface = "bold",
           color = "#2166ac", size = 3.8) +
  annotate("text", x = 5.8, y = -0.04, label = "5.8", fontface = "bold",
           color = "#d6604d", size = 3.8) +
  scale_color_manual(values = group_colors_km) +
  scale_fill_manual(values = group_colors_km) +
  scale_x_continuous(breaks = seq(0, 27, 3), limits = c(0, 28)) +
  scale_y_continuous(
    breaks = seq(0, 1, 0.2),
    labels = scales::percent_format(),
    limits = c(-0.08, 1.05)
  ) +
  labs(
    title = "Overall Survival — Kaplan-Meier Estimates",
    subtitle = "PVEK 0.045 mg/kg Q3W in BPDCN | Data cutoff: October 2, 2024",
    x = "Time (months)",
    y = "Overall Survival Probability",
    color = "Population",
    fill = "Population"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 17, color = "#1a5276"),
    plot.subtitle = element_text(size = 12, color = "#555555", margin = margin(b = 15)),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    legend.text = element_text(size = 10),
    panel.grid.minor = element_blank(),
    plot.margin = margin(15, 20, 15, 15)
  )
Figure 8.2: Kaplan-Meier estimates of overall survival by treatment population. Shaded regions represent 95% confidence bands.

8.3 Duration of Response and Bridge to SCT

Show code
library(ggplot2)
library(dplyr)
library(tidyr)

# Panel A: Duration of Response
dor_data <- tibble(
  Group = factor(c(
    "Frontline De Novo\n(PAP)",
    "All Frontline",
    "Relapsed/\nRefractory"
  ), levels = c(
    "Frontline De Novo\n(PAP)",
    "All Frontline",
    "Relapsed/\nRefractory"
  )),
  Median_DoR = c(10.6, 9.8, 9.2),
  Lower = c(3.8, 4.6, 2.4),
  Upper = c(24, 24, 24),
  NR = c(TRUE, TRUE, TRUE)
)

p_dor <- ggplot(dor_data, aes(x = Group, y = Median_DoR, fill = Group)) +
  geom_col(width = 0.55, alpha = 0.9, show.legend = FALSE) +
  geom_errorbar(aes(ymin = Lower, ymax = ifelse(NR, Median_DoR + 5, Upper)),
                width = 0.15, linewidth = 0.7) +
  geom_text(aes(label = paste0(Median_DoR, " mo")),
            vjust = -0.8, fontface = "bold", size = 4.5, color = "#333333") +
  annotate("text", x = 3.35, y = 22, label = "Upper CI: NR",
           fontface = "italic", size = 3, color = "grey50") +
  scale_fill_manual(values = c("#2166ac", "#4393c3", "#d6604d")) +
  scale_y_continuous(limits = c(0, 25), breaks = seq(0, 24, 6)) +
  labs(
    title = "Duration of CCR",
    subtitle = "Median (months)",
    x = NULL, y = "Months"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", size = 14, color = "#1a5276"),
    plot.subtitle = element_text(size = 10, color = "#555555"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 10, face = "bold")
  )

# Panel B: Bridge to SCT among CCR responders
sct_data <- tibble(
  Group = factor(c(
    "Frontline De Novo\n(PAP)",
    "All Frontline",
    "Relapsed/\nRefractory"
  ), levels = c(
    "Frontline De Novo\n(PAP)",
    "All Frontline",
    "Relapsed/\nRefractory"
  )),
  SCT_pct = c(53, 52, 29),
  n = c(8, 12, 2),
  N = c(15, 23, 7)
)

p_sct <- ggplot(sct_data, aes(x = Group, y = SCT_pct, fill = Group)) +
  geom_col(width = 0.55, alpha = 0.9, show.legend = FALSE) +
  geom_text(aes(label = paste0(SCT_pct, "%\n(", n, "/", N, ")")),
            vjust = -0.3, fontface = "bold", size = 4, color = "#333333") +
  scale_fill_manual(values = c("#2166ac", "#4393c3", "#d6604d")) +
  scale_y_continuous(limits = c(0, 75), breaks = seq(0, 75, 25),
                     labels = function(x) paste0(x, "%")) +
  labs(
    title = "Bridge to SCT (CCR Responders)",
    subtitle = "Patients proceeding to transplant",
    x = NULL, y = "Patients (%)"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", size = 14, color = "#1a5276"),
    plot.subtitle = element_text(size = 10, color = "#555555"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 10, face = "bold")
  )

# Combine panels
library(gridExtra)
grid.arrange(p_dor, p_sct, ncol = 2)
Figure 8.3: Left: Duration of CCR across populations. Right: Rates of bridge to stem-cell transplant among CCR responders.

8.4 Landmark Survival Rates

Show code
import matplotlib.pyplot as plt
import numpy as np

groups = ['Frontline De Novo\n(PAP, n=20)', 'All Frontline\n(n=33)', 'Relapsed/Refractory\n(n=51)']
os_12mo = [60, 64, 31]
os_18mo = [49, 44, 19]
ci_12mo = [(35.7, 77.6), (44.9, 77.5), (18.2, 44.1)]
ci_18mo = [(26.0, 68.6), (26.7, 60.3), (8.9, 32.7)]

x = np.arange(len(groups))
width = 0.32

fig, ax = plt.subplots(figsize=(11, 5.5))

err12 = [[v - lo for v, (lo, hi) in zip(os_12mo, ci_12mo)],
         [hi - v for v, (lo, hi) in zip(os_12mo, ci_12mo)]]
err18 = [[v - lo for v, (lo, hi) in zip(os_18mo, ci_18mo)],
         [hi - v for v, (lo, hi) in zip(os_18mo, ci_18mo)]]

bars1 = ax.bar(x - width/2, os_12mo, width, label='12-Month OS',
               color='#2166ac', alpha=0.9, edgecolor='white', linewidth=1,
               yerr=err12, capsize=5, error_kw={'linewidth': 1.2, 'color': '#333'})
bars2 = ax.bar(x + width/2, os_18mo, width, label='18-Month OS',
               color='#67a9cf', alpha=0.9, edgecolor='white', linewidth=1,
               yerr=err18, capsize=5, error_kw={'linewidth': 1.2, 'color': '#333'})

for bar, val in zip(bars1, os_12mo):
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 6,
            f'{val}%', ha='center', va='bottom', fontweight='bold', fontsize=11,
            color='#2166ac')

for bar, val in zip(bars2, os_18mo):
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 6,
            f'{val}%', ha='center', va='bottom', fontweight='bold', fontsize=11,
            color='#67a9cf')

ax.set_xticks(x)
ax.set_xticklabels(groups, fontsize=11, fontweight='bold')
ax.set_ylabel('Overall Survival (%)', fontsize=12)
ax.set_ylim(0, 100)
(0.0, 100.0)
Show code
ax.set_title('Landmark Overall Survival Rates',
             fontsize=16, fontweight='bold', color='#1a5276', pad=15)
ax.legend(fontsize=11, frameon=False, loc='upper right')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.grid(axis='y', alpha=0.3, linestyle='--')

plt.tight_layout()
plt.show()
Figure 8.4: Landmark overall survival rates at 12 and 18 months across treatment populations.

9 Safety Profile

9.1 Adverse Events Overview

Show code
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

aes = [
    'Peripheral edema', 'Fatigue', 'Infusion-related reaction',
    'Nausea', 'Hypokalemia', 'Constipation', 'Anemia',
    'Thrombocytopenia', 'Diarrhea', 'Headache', 'Neutropenia',
    'Fall', 'Insomnia', 'Hypoalbuminemia', 'Decreased appetite',
    'Asthenia', 'Pyrexia', 'Dyspnea', 'Cough'
]

frontline_denovo = [65, 25, 10, 15, 15, 20, 15, 5, 25, 40, 0, 20, 25, 15, 10, 10, 5, 30, 20]
all_frontline    = [73, 30, 9, 21, 30, 18, 27, 12, 21, 27, 15, 18, 27, 21, 12, 12, 9, 21, 15]
rr               = [41, 24, 37, 20, 14, 20, 14, 24, 16, 10, 18, 14, 8, 12, 16, 16, 16, 8, 10]

data = np.array([frontline_denovo, all_frontline, rr]).T

fig, ax = plt.subplots(figsize=(10, 10))

cmap = sns.color_palette("YlOrRd", as_cmap=True)

im = ax.imshow(data, cmap=cmap, aspect='auto', vmin=0, vmax=75)

for i in range(len(aes)):
    for j in range(3):
        val = data[i, j]
        color = 'white' if val > 40 else '#333333'
        ax.text(j, i, f'{val}%', ha='center', va='center',
                fontsize=10, fontweight='bold', color=color)

ax.set_xticks([0, 1, 2])
ax.set_xticklabels(
    ['Frontline De Novo\n(n = 20)', 'All Frontline\n(n = 33)',
     'Relapsed/Refractory\n(n = 51)'],
    fontsize=11, fontweight='bold'
)
ax.set_yticks(range(len(aes)))
ax.set_yticklabels(aes, fontsize=11)
ax.xaxis.set_ticks_position('top')
ax.xaxis.set_label_position('top')

cbar = plt.colorbar(im, ax=ax, shrink=0.6, label='Patients (%)')

ax.set_title('Treatment-Emergent Adverse Events (≥15% in any group)',
             fontsize=15, fontweight='bold', color='#1a5276', pad=60)

plt.tight_layout()
plt.show()
Figure 9.1: Heatmap of treatment-emergent adverse events (≥15% in any group) by treatment population. Cell values represent the percentage of patients experiencing each event.

9.2 Grade ≥3 Adverse Events

Show code
library(ggplot2)
library(dplyr)

grade3_data <- tibble(
  AE = c(
    "Thrombocytopenia /\n↓ platelet count",
    "Neutropenia",
    "Leukopenia /\n↓ WBC count",
    "Peripheral edema",
    "Anemia",
    "Pneumonia",
    "Febrile neutropenia",
    "↓ Lymphocyte count"
  ),
  Percentage = c(24, 16, 14, 12, 8, 7, 6, 6)
) |>
  mutate(AE = factor(AE, levels = rev(AE)))

ggplot(grade3_data, aes(x = Percentage, y = AE)) +
  geom_segment(aes(x = 0, xend = Percentage, yend = AE),
               color = "#d6604d", linewidth = 1.5) +
  geom_point(size = 6, color = "#d6604d") +
  geom_text(aes(label = paste0(Percentage, "%")),
            hjust = -0.6, fontface = "bold", size = 4, color = "#333333") +
  scale_x_continuous(limits = c(0, 32), breaks = seq(0, 30, 10),
                     labels = function(x) paste0(x, "%")) +
  labs(
    title = "Grade ≥3 Adverse Events (≥5% of All Patients)",
    subtitle = "Safety population (N = 84)",
    x = "Patients (%)",
    y = NULL
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", size = 16, color = "#1a5276"),
    plot.subtitle = element_text(size = 11, color = "#555555", margin = margin(b = 15)),
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    axis.text.y = element_text(size = 11),
    plot.margin = margin(15, 30, 15, 15)
  )
Figure 9.2: Grade ≥3 adverse events reported in ≥5% of all patients (N = 84).

9.3 Special Safety Topics

Show code
library(knitr)

safety_special <- data.frame(
  Event = c(
    "Peripheral Edema",
    "Peripheral Edema",
    "Peripheral Edema",
    "Peripheral Edema",
    "Infusion-Related Reactions",
    "Infusion-Related Reactions",
    "Infusion-Related Reactions",
    "VOD (on-treatment)",
    "VOD (post-SCT)",
    "Capillary Leak Syndrome"
  ),
  Detail = c(
    "Median time to onset: 55 days (range 0–217)",
    "Most events (59%) first occurred in cycle 3",
    "70% received diuretic treatment; 56% resolved",
    "Only 1% led to treatment discontinuation",
    "Mostly mild-to-moderate (max grade 3: 4%)",
    "Median duration: 1 day (range 1–22)",
    "Preventable with prophylactic corticosteroids",
    "2 events (both resolved; led to discontinuation)",
    "5 of 19 post-SCT patients (4 with R/R BPDCN)",
    "No CLS events reported"
  )
)

kable(safety_special, 
      col.names = c("Event", "Key Details"),
      align = c("l", "l"))
Table 9.1: Summary of special safety events of interest.
Event Key Details
Peripheral Edema Median time to onset: 55 days (range 0–217)
Peripheral Edema Most events (59%) first occurred in cycle 3
Peripheral Edema 70% received diuretic treatment; 56% resolved
Peripheral Edema Only 1% led to treatment discontinuation
Infusion-Related Reactions Mostly mild-to-moderate (max grade 3: 4%)
Infusion-Related Reactions Median duration: 1 day (range 1–22)
Infusion-Related Reactions Preventable with prophylactic corticosteroids
VOD (on-treatment) 2 events (both resolved; led to discontinuation)
VOD (post-SCT) 5 of 19 post-SCT patients (4 with R/R BPDCN)
Capillary Leak Syndrome No CLS events reported

10 Comparative Context

Show code
import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(1, 3, figsize=(14, 5.5))

# Panel A: CCR rates
drugs = ['PVEK\n(CADENZA)', 'Tagraxofusp\n(Pivotal)']
ccr = [75, 72]
orr = [80, 90]

x = np.arange(len(drugs))
w = 0.3
axes[0].bar(x - w/2, ccr, w, label='CCR (CR+CRc)', color='#2166ac', alpha=0.9)
axes[0].bar(x + w/2, orr, w, label='ORR', color='#67a9cf', alpha=0.9)

for i, (c, o) in enumerate(zip(ccr, orr)):
    axes[0].text(i - w/2, c + 2, f'{c}%', ha='center', fontweight='bold', fontsize=11, color='#2166ac')
    axes[0].text(i + w/2, o + 2, f'{o}%', ha='center', fontweight='bold', fontsize=11, color='#67a9cf')

axes[0].set_xticks(x)
axes[0].set_xticklabels(drugs, fontsize=11, fontweight='bold')
axes[0].set_ylabel('Response Rate (%)', fontsize=11)
axes[0].set_ylim(0, 110)
(0.0, 110.0)
Show code
axes[0].set_title('Frontline Response Rates', fontsize=13, fontweight='bold', color='#1a5276')
axes[0].legend(fontsize=9, frameon=False)
axes[0].spines['top'].set_visible(False)
axes[0].spines['right'].set_visible(False)

# Panel B: Dosing comparison
labels = ['PVEK', 'Tagraxofusp']
infusion_days = [1, 5]
colors_dose = ['#2166ac', '#d6604d']
axes[1].bar(labels, infusion_days, color=colors_dose, width=0.5, alpha=0.9)
for i, v in enumerate(infusion_days):
    axes[1].text(i, v + 0.15, f'{v} day{"s" if v > 1 else ""}',
                ha='center', fontweight='bold', fontsize=12)
axes[1].set_ylabel('Infusion Days per Cycle', fontsize=11)
axes[1].set_ylim(0, 7)
(0.0, 7.0)
Show code
axes[1].set_title('Dosing Convenience', fontsize=13, fontweight='bold', color='#1a5276')
axes[1].spines['top'].set_visible(False)
axes[1].spines['right'].set_visible(False)

# Panel C: Key AEs
ae_labels = ['CLS', 'Peripheral\nEdema', 'Infusion\nReactions']
pvek_ae = [0, 54, 26]
tagra_ae = [21, 0, 0]

x_ae = np.arange(len(ae_labels))
axes[2].bar(x_ae - w/2, pvek_ae, w, label='PVEK', color='#2166ac', alpha=0.9)
axes[2].bar(x_ae + w/2, tagra_ae, w, label='Tagraxofusp', color='#d6604d', alpha=0.9)
for i, (p, t) in enumerate(zip(pvek_ae, tagra_ae)):
    if p > 0:
        axes[2].text(i - w/2, p + 1.5, f'{p}%', ha='center', fontsize=10,
                    fontweight='bold', color='#2166ac')
    if t > 0:
        axes[2].text(i + w/2, t + 1.5, f'{t}%', ha='center', fontsize=10,
                    fontweight='bold', color='#d6604d')

axes[2].set_xticks(x_ae)
axes[2].set_xticklabels(ae_labels, fontsize=10, fontweight='bold')
axes[2].set_ylabel('Incidence (%)', fontsize=11)
axes[2].set_ylim(0, 65)
(0.0, 65.0)
Show code
axes[2].set_title('Key Safety Signals', fontsize=13, fontweight='bold', color='#1a5276')
axes[2].legend(fontsize=9, frameon=False)
axes[2].spines['top'].set_visible(False)
axes[2].spines['right'].set_visible(False)

# Caveat
fig.text(0.5, -0.02,
         '⚠ Cross-trial comparisons should be interpreted with caution due to differences in study design and patient populations.',
         ha='center', fontsize=9, fontstyle='italic', color='#888888')

plt.suptitle('PVEK vs. Tagraxofusp — Contextual Comparison (Frontline BPDCN)',
             fontsize=16, fontweight='bold', color='#1a5276', y=1.03)
plt.tight_layout()
plt.show()
Figure 10.1: Comparison of frontline response rates and key characteristics between PVEK (CADENZA) and tagraxofusp (pivotal study). Note: cross-trial comparisons should be interpreted with caution.

11 Summary & Implications

Show code
library(ggplot2)

summary_data <- data.frame(
  Metric = factor(c(
    "CCR Rate\n(Frontline PAP)",
    "ORR\n(Frontline PAP)",
    "Median OS\n(Frontline)",
    "Bridge to SCT\n(Frontline CCR)",
    "CCR Rate\n(R/R)",
    "Median OS\n(R/R)"
  ), levels = rev(c(
    "CCR Rate\n(Frontline PAP)",
    "ORR\n(Frontline PAP)",
    "Median OS\n(Frontline)",
    "Bridge to SCT\n(Frontline CCR)",
    "CCR Rate\n(R/R)",
    "Median OS\n(R/R)"
  ))),
  Value = c(75, 80, 16.6, 53, 14, 5.8),
  Unit = c("%", "%", "months", "%", "%", "months"),
  Category = c("Frontline", "Frontline", "Frontline", "Frontline", "R/R", "R/R"),
  Display = c("75%", "80%", "16.6 mo", "53%", "14%", "5.8 mo"),
  bar_val = c(75, 80, 61, 53, 14, 21)  # Normalized for display
)

ggplot(summary_data, aes(x = bar_val, y = Metric, fill = Category)) +
  geom_col(width = 0.6, alpha = 0.9) +
  geom_text(aes(label = Display), hjust = -0.15,
            fontface = "bold", size = 5.5, color = "#333333") +
  scale_fill_manual(
    values = c("Frontline" = "#2166ac", "R/R" = "#d6604d"),
    name = "Setting"
  ) +
  scale_x_continuous(limits = c(0, 110)) +
  labs(
    title = "CADENZA Trial — Key Results at a Glance",
    subtitle = "Pivekimab sunirine 0.045 mg/kg Q3W in BPDCN",
    x = NULL, y = NULL
  ) +
  theme_void(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 18, color = "#1a5276",
                              margin = margin(b = 5)),
    plot.subtitle = element_text(size = 12, color = "#555555",
                                 margin = margin(b = 20)),
    axis.text.y = element_text(size = 12, hjust = 1, margin = margin(r = 10)),
    legend.position = "bottom",
    legend.title = element_text(face = "bold", size = 12),
    legend.text = element_text(size = 11),
    plot.margin = margin(20, 50, 20, 20)
  )
Figure 11.1: Visual summary of key findings from the CADENZA trial of pivekimab sunirine in BPDCN.
NoteClinical Implications
  1. High frontline efficacy: 75% CCR in an older population (median age 74 years) with convenient Q3W outpatient dosing
  2. Effective bridge to transplant: Over half of frontline responders proceeded to SCT
  3. No capillary leak syndrome: A key differentiator from tagraxofusp
  4. Activity post-tagraxofusp: Responses observed regardless of prior tagraxofusp exposure, potentially due to a different resistance mechanism
  5. Manageable safety: Peripheral edema and infusion reactions were the most common AEs, predominantly low-grade and reversible

TipRendering This Document

To render this Quarto document, ensure the following are installed:

R packages:

install.packages(c("ggplot2", "dplyr", "tidyr", "knitr", "gridExtra", "scales"))

Python packages:

pip install matplotlib numpy seaborn

Render command:

quarto render pvek_bpdcn_analysis.qmd

Source: Pemmaraju N, Marconi G, Montesinos P, et al. Pivekimab sunirine in blastic plasmacytoid dendritic cell neoplasm. J Clin Oncol. 2026. DOI: 10.1200/JCO-25-02083. ClinicalTrials.gov: NCT03386513.