diff --git a/src/Speckle.Objects/Geometry/Region.cs b/src/Speckle.Objects/Geometry/Region.cs
new file mode 100644
index 00000000..fcc044dd
--- /dev/null
+++ b/src/Speckle.Objects/Geometry/Region.cs
@@ -0,0 +1,97 @@
+using Speckle.Objects.Other;
+using Speckle.Sdk.Common;
+using Speckle.Sdk.Models;
+
+namespace Speckle.Objects.Geometry;
+
+///
+/// Flat polygon, defined by an outer boundary and inner loops.
+///
+[SpeckleType("Objects.Geometry.Region")]
+public class Region : Base, IHasArea, IHasBoundingBox, ITransformable, IDisplayValue>
+{
+ ///
+ /// Boundary of a region.
+ /// Should be a planar, closed, non-self-intersecting ICurve.
+ ///
+ public required ICurve boundary { get; set; }
+
+ ///
+ /// Loops (voids) in the region.
+ /// Each loop should be planar, closed, non-self-intersecting ICurve, located inside the boundary.
+ /// The loops should not intersect or touch each other.
+ ///
+ public required List innerLoops { get; set; } = new();
+
+ ///
+ /// The units of object's coordinates.
+ /// This should be one of
+ ///
+ public required string units { get; set; }
+
+ ///
+ /// Indication whether the region is just a geometry (false) or has a hatch pattern (true).
+ /// It's a distinction for receiving in apps that support both Region and Hatch (aka region with hatch pattern)
+ ///
+ public required bool hasHatchPattern { get; set; }
+
+ ///
+ public double area { get; set; }
+
+ ///
+ public Box? bbox { get; set; }
+
+ ///
+ [DetachProperty]
+ public List displayValue { get; set; } = new();
+
+ ///
+ public bool TransformTo(Transform transform, out ITransformable transformed)
+ {
+ // assign self to the returned object, in case transformation fails
+ transformed = this;
+
+ // transform boundary
+ if (boundary is ITransformable boundaryTransformable)
+ {
+ boundaryTransformable.TransformTo(transform, out ITransformable transformedBoundaryResult);
+ var transformedBoundary = (ICurve)transformedBoundaryResult;
+
+ // transform inner loops
+ var transformedLoops = new List();
+ foreach (var loop in innerLoops)
+ {
+ if (loop is ITransformable loopTransformable)
+ {
+ loopTransformable.TransformTo(transform, out ITransformable transformedLoop);
+ transformedLoops.Add((ICurve)transformedLoop);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // transform display meshes
+ var transformedMeshes = new List();
+ foreach (var mesh in displayValue)
+ {
+ mesh.TransformTo(transform, out ITransformable transformedMesh);
+ transformedMeshes.Add((Mesh)transformedMesh);
+ }
+
+ // if boundary and loops transformations succeeded
+ transformed = new Region
+ {
+ boundary = transformedBoundary,
+ innerLoops = transformedLoops,
+ hasHatchPattern = hasHatchPattern,
+ displayValue = transformedMeshes,
+ units = units,
+ };
+ return true;
+ }
+
+ return false;
+ }
+}