Posted on 11/12/2025 12:11:19
I've done this for a Klaviyo implementation. Something along these lines:
1. Subscribe to CheckoutDoneOrderIsComplete
var klaviyoApi = new KlaviyoService(apiKey, _logService);
klaviyoApi.RegisterPlacedOrderEvent(checkOutComplete.Order);
foreach (var orderLine in checkOutComplete.Order.OrderLines)
{
klaviyoApi.RegisterOrderedProductEvent(orderLine);
}
2. In the KlaviyoService class, build a KlaviyoEvent model of type event and add order base data and lines:
private static EventModel<OrderModel> CreateOrderModel(Order order, EventEnum @event)
{
var eventModel = new EventModel<OrderModel>
{
Attributes = new EventAttributes<OrderModel>
{
Metric = new Metric(@event),
Value = order.Price.PriceWithVAT.ToStringWitDot(),
ValueCurrency = order.CurrencyCode,
Profile =
{
UserProfile = order.BuildUserProfile()
},
Properties = new OrderModel
{
Items = order.OrderLines.GetItems(),
Categories = order.OrderLines.GetCategories(),
ItemNames = order.OrderLines.GetItemNames(),
OrderId = order.Id,
BillingAddress = order.GetBillingAddress(),
ShippingAddress = order.GetShippingAddress()
}
}
};
where EventModel looks like this:
public class EventModel<T>
{
[JsonProperty("type")]
public string Type => "event";
[JsonProperty("attributes")]
public EventAttributes<T> Attributes { get; set; } = new EventAttributes<T>();
public string Serialize()
{
return SerializeHelper.Serialize(this);
}
public string GetName()
{
return Attributes.Metric.Data.Attributes.Name.ToString();
}
}
You also need to to submit each order line separately. Never fully understood why but it seems to work OK for us:
private static EventModel<OrderedProductModel> CreateOrderedProductModel(OrderLine orderLine)
{
var eventModel = new EventModel<OrderedProductModel>
{
Attributes = new EventAttributes<OrderedProductModel>
{
Metric = new Metric(EventEnum.OrderedProduct),
Value = orderLine.Price.Price.ToStringWitDot(),
Properties = new OrderedProductModel
{
OrderId = orderLine.OrderId,
ProductId = orderLine.ProductId,
Sku = orderLine.ProductNumber,
Quantity = Convert.ToInt32(orderLine.Quantity),
ProductName = orderLine.ProductName,
Categories = orderLine.GetCategories()
},
Profile =
{
UserProfile = orderLine.Order.BuildUserProfile()
}
}
};
return eventModel;
}
This code uses some helper methods. I can show them but they also largely depend on what it is you want to send.
public static List<string> GetCategories(this OrderLine orderLine)
{
return orderLine.Product?.Groups?.Select(g => g.Name).Distinct().ToList();
}
public static UserProfile BuildUserProfile(this Order order)
{
var userModel = new UserProfile
{
UserAttributes = new UserAttributes
{
FirstName = order.FirstName(),
LastName = order.LastName(),
Email = order.CustomerEmail,
PhoneNumber = order.CustomerPhone,
ExternalId = order.CustomerAccessUserId > 0 ? order.CustomerAccessUserId.ToString() : null,
Location =
{
City = order.CustomerCity,
Country = order.CustomerCountry,
Zip = order.CustomerZip
}
}
};
return userModel;
}
UserAttributes is a class with properties for name, address and so on.
It's been a while so I don't remember all the details, but I can help you figure it out if you need more help.
And instead of building it all yourself, maybe one these packages can help you get started: https://www.nuget.org/packages?q=Klaviyo&includeComputedFrameworks=true&prerel=true
Hope this helps,
Imar