diff --git a/Processes/UrQMD/UrQMD.cc b/Processes/UrQMD/UrQMD.cc
index b60edba1b5fdc636723c70cbfcee76e92281af4f..fba5dc1833be9f813d14b6dfd9cb3c872192594c 100644
--- a/Processes/UrQMD/UrQMD.cc
+++ b/Processes/UrQMD/UrQMD.cc
@@ -28,29 +28,20 @@ using SetupStack = corsika::setup::Stack;
 using SetupParticle = corsika::setup::Stack::StackIterator;
 using SetupProjectile = corsika::setup::StackView::StackIterator;
 
-template <typename TParticle> // need template here, as this is called both with
-                              // SetupParticle as well as SetupProjectile
-CrossSectionType UrQMD::GetCrossSection(TParticle const& vProjectile,
-                                        corsika::particles::Code vTargetCode) const {
-  using namespace units::si;
-
-  // TODO: energy cuts, return 0 for non-hadrons
-
-  auto const projectileCode = vProjectile.GetPID();
-  auto const projectileEnergyLab = vProjectile.GetEnergy();
-
+CrossSectionType UrQMD::GetCrossSection(particles::Code vProjectileCode,
+                                        corsika::particles::Code vTargetCode,
+                                        HEPEnergyType vLabEnergy, int vAProjectile = 1) {
   // the following is a translation of ptsigtot() into C++
-  if (projectileCode != particles::Code::Nucleus &&
+  if (vProjectileCode != particles::Code::Nucleus &&
       !IsNucleus(vTargetCode)) { // both particles are "special"
-    auto const mProj = particles::GetMass(projectileCode);
+    auto const mProj = particles::GetMass(vProjectileCode);
     auto const mTar = particles::GetMass(vTargetCode);
-    double sqrtS =
-        sqrt(units::si::detail::static_pow<2>(mProj) +
-             units::si::detail::static_pow<2>(mTar) + 2 * projectileEnergyLab * mTar) *
-        (1 / 1_GeV);
+    double sqrtS = sqrt(units::si::detail::static_pow<2>(mProj) +
+                        units::si::detail::static_pow<2>(mTar) + 2 * vLabEnergy * mTar) *
+                   (1 / 1_GeV);
 
     // we must set some UrQMD globals first...
-    auto const [ityp, iso3] = ConvertToUrQMD(projectileCode);
+    auto const [ityp, iso3] = ConvertToUrQMD(vProjectileCode);
     inputs_.spityp[0] = ityp;
     inputs_.spiso3[0] = iso3;
 
@@ -62,8 +53,7 @@ CrossSectionType UrQMD::GetCrossSection(TParticle const& vProjectile,
     int two = 2;
     return sigtot_(one, two, sqrtS) * 1_mb;
   } else {
-    int const Ap =
-        (projectileCode == particles::Code::Nucleus) ? vProjectile.GetNuclearA() : 1;
+    int const Ap = vAProjectile;
     int const At = IsNucleus(vTargetCode) ? particles::GetNucleusA(vTargetCode) : 1;
 
     double const maxImpact = nucrad_(Ap) + nucrad_(At) + 2 * options_.CTParam[30 - 1];
@@ -72,6 +62,26 @@ CrossSectionType UrQMD::GetCrossSection(TParticle const& vProjectile,
   }
 }
 
+template <typename TParticle> // need template here, as this is called both with
+                              // SetupParticle as well as SetupProjectile
+CrossSectionType UrQMD::GetCrossSection(TParticle const& vProjectile,
+                                        corsika::particles::Code vTargetCode) const {
+  // TODO: return 0 for non-hadrons?
+
+  auto const projectileCode = vProjectile.GetPID();
+  auto const projectileEnergyLab = vProjectile.GetEnergy();
+
+  if (projectileCode == particles::Code::K0Long) {
+    return 0.5 *
+           (GetCrossSection(particles::Code::K0, vTargetCode, projectileEnergyLab) +
+            GetCrossSection(particles::Code::K0Bar, vTargetCode, projectileEnergyLab));
+  }
+
+  int const Ap =
+      (projectileCode == particles::Code::Nucleus) ? vProjectile.GetNuclearA() : 1;
+  return GetCrossSection(projectileCode, vTargetCode, projectileEnergyLab, Ap);
+}
+
 bool UrQMD::CanInteract(particles::Code vCode) const {
   // According to the manual, UrQMD can use all mesons, baryons and nucleons
   // which are modeled also as input particles. I think it is safer to accept
@@ -82,7 +92,7 @@ bool UrQMD::CanInteract(particles::Code vCode) const {
       particles::Code::Nucleus, particles::Code::Proton,      particles::Code::AntiProton,
       particles::Code::Neutron, particles::Code::AntiNeutron, particles::Code::PiPlus,
       particles::Code::PiMinus, particles::Code::KPlus,       particles::Code::KMinus,
-      particles::Code::K0,      particles::Code::K0Bar};
+      particles::Code::K0,      particles::Code::K0Bar,       particles::Code::K0Long};
 
   return std::find(std::cbegin(validProjectileCodes), std::cend(validProjectileCodes),
                    vCode) != std::cend(validProjectileCodes);
@@ -109,7 +119,7 @@ GrammageType UrQMD::GetInteractionLength(SetupParticle& vParticle) const {
 corsika::process::EProcessReturn UrQMD::DoInteraction(SetupProjectile& vProjectile) {
   using namespace units::si;
 
-  auto const projectileCode = vProjectile.GetPID();
+  auto projectileCode = vProjectile.GetPID();
   auto const projectileEnergyLab = vProjectile.GetEnergy();
   auto const& projectileMomentumLab = vProjectile.GetMomentum();
   auto const& projectilePosition = vProjectile.GetPosition();
@@ -159,6 +169,12 @@ corsika::process::EProcessReturn UrQMD::DoInteraction(SetupProjectile& vProjecti
     rsys_.bdist = nucrad_(targetA) + nucrad_(1) + 2 * options_.CTParam[30 - 1];
     rsys_.ebeam = (projectileEnergyLab - vProjectile.GetMass()) * (1 / 1_GeV);
 
+    if (projectileCode == particles::Code::K0Long) {
+      projectileCode = fBooleanDist(fRNG) ? particles::Code::K0 : particles::Code::K0Bar;
+    } else if (projectileCode == particles::Code::K0Short) {
+      throw std::runtime_error("K0Short should not interact");
+    }
+
     auto const [ityp, iso3] = ConvertToUrQMD(projectileCode);
     // todo: conversion of K_long/short into strong eigenstates;
     inputs_.spityp[0] = ityp;
@@ -188,7 +204,11 @@ corsika::process::EProcessReturn UrQMD::DoInteraction(SetupProjectile& vProjecti
       originalCS.RotateToZ(projectileMomentumLab);
 
   for (int i = 0; i < sys_.npart; ++i) {
-    auto const code = ConvertFromUrQMD(isys_.ityp[i], isys_.iso3[i]);
+    auto code = ConvertFromUrQMD(isys_.ityp[i], isys_.iso3[i]);
+    if (code == particles::Code::K0 || code == particles::Code::K0Bar) {
+      code = fBooleanDist(fRNG) ? particles::Code::K0Short : particles::Code::K0Long;
+    }
+
     // "coor_.p0[i] * 1_GeV" is likely off-shell as UrQMD doesn't preserve masses well
     auto momentum = geometry::Vector(
         zAxisFrame,
@@ -245,6 +265,8 @@ std::pair<int, int> corsika::process::UrQMD::ConvertToUrQMD(
       {-311, {-106, 1}},   // K0bar
       {2212, {1, 1}},      // p
       {2112, {1, -1}},     // n
+      {-2212, {-1, -1}},   // pbar
+      {-2112, {-1, 1}},    // nbar
       {221, {102, 0}},     // eta
       {213, {104, 2}},     // rho+
       {-213, {104, -2}},   // rho-
diff --git a/Processes/UrQMD/UrQMD.h b/Processes/UrQMD/UrQMD.h
index 3be0168ac3aaf327a79a952869b0d60fec12e0a0..bdbca146ea015ce66174b7c2b2b99f619636c466 100644
--- a/Processes/UrQMD/UrQMD.h
+++ b/Processes/UrQMD/UrQMD.h
@@ -18,6 +18,7 @@
 #include <corsika/units/PhysicalUnits.h>
 
 #include <array>
+#include <random>
 #include <utility>
 
 namespace corsika::process::UrQMD {
@@ -38,8 +39,12 @@ namespace corsika::process::UrQMD {
     bool CanInteract(particles::Code) const;
 
   private:
+    static corsika::units::si::CrossSectionType GetCrossSection(
+        particles::Code, particles::Code, corsika::units::si::HEPEnergyType, int);
     corsika::random::RNG& fRNG =
         corsika::random::RNGManager::GetInstance().GetRandomStream("UrQMD");
+
+    std::uniform_int_distribution<int> fBooleanDist{0, 1};
   };
 
   namespace constants {
diff --git a/Processes/UrQMD/testUrQMD.cc b/Processes/UrQMD/testUrQMD.cc
index 3e347a827eaf5564836d5f82cabb55dbce471ffe..81b4215475cf2d927ed6a7e86cb20462798c5c3b 100644
--- a/Processes/UrQMD/testUrQMD.cc
+++ b/Processes/UrQMD/testUrQMD.cc
@@ -144,7 +144,7 @@ TEST_CASE("UrQMD") {
     particles::Code validProjectileCodes[] = {
         particles::Code::PiPlus,  particles::Code::PiMinus, particles::Code::Proton,
         particles::Code::Neutron, particles::Code::KPlus,   particles::Code::KMinus,
-        particles::Code::K0,      particles::Code::K0Bar};
+        particles::Code::K0,      particles::Code::K0Bar,   particles::Code::K0Long};
 
     for (auto code : validProjectileCodes) {
       auto [stack, view] = setupStack(code, 100_GeV, nodePtr, cs);
@@ -205,4 +205,25 @@ TEST_CASE("UrQMD") {
     REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() ==
             Approx(0).margin(1e-2));
   }
+
+  SECTION("K0Long projectile") {
+    auto [env, csPtr, nodePtr] = setupEnvironment(particles::Code::Oxygen);
+    auto [stackPtr, secViewPtr] =
+        setupStack(particles::Code::K0Long, 400_GeV, nodePtr, *csPtr);
+
+    // must be assigned to variable, cannot be used as rvalue?!
+    auto projectile = secViewPtr->GetProjectile();
+    auto const projectileMomentum = projectile.GetMomentum();
+
+    [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(projectile);
+
+    REQUIRE(sumCharge(*secViewPtr) ==
+            particles::GetChargeNumber(particles::Code::K0Long) +
+                particles::GetChargeNumber(particles::Code::Oxygen));
+
+    auto const secMomSum =
+        sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem());
+    REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() ==
+            Approx(0).margin(1e-2));
+  }
 }