diff --git a/wiring/inc/spark_wiring_ledger_v1.h b/wiring/inc/spark_wiring_ledger_v1.h
new file mode 100644
index 0000000000..36661bfe11
--- /dev/null
+++ b/wiring/inc/spark_wiring_ledger_v1.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2023 Particle Industries, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#pragma once
+
+#include "spark_wiring_variant.h"
+
+namespace particle {
+
+/**
+ * A ledger.
+ *
+ * Use `Particle.ledger()` to create an instance of this class.
+ */
+class Ledger {
+public:
+ /**
+ * A callback invoked when the ledger data has been synchronized with the Cloud.
+ *
+ * @param ledger Ledger instance.
+ * @param userData User data.
+ */
+ typedef void (*OnSyncCallback)(Ledger ledger, void* userData);
+
+ /**
+ * Default constructor.
+ *
+ * Constructs an invalid ledger instance.
+ */
+ Ledger();
+
+ /**
+ * Set the ledger data.
+ *
+ * This method replaces the current contents of the ledger.
+ *
+ * @param data New ledger data.
+ * @return 0 on success, otherwise an error code defined by `Error::Type`.
+ */
+ int set(const Variant& data);
+
+ /**
+ * Update the ledger data.
+ *
+ * This method partially updates the contents of the ledger.
+ *
+ * @param data New ledger data.
+ * @return 0 on success, otherwise an error code defined by `Error::Type`.
+ */
+ int merge(const Variant& data);
+
+ /**
+ * Get the ledger data.
+ *
+ * @return Ledger data.
+ */
+ Variant get() const;
+
+ /**
+ * Get the last time, in milliseconds since the Unix epoch, when the ledger was synchronized with the Cloud.
+ *
+ * @return Time the ledger was synchronized with the Cloud, or 0 if the ledger was never synchronized.
+ */
+ time64_t lastSyncedAt() const;
+
+ /**
+ * Get the ledger name.
+ *
+ * @return Ledger name.
+ */
+ const char* name() const;
+
+ /**
+ * Set a callback to be invoked when the ledger data has been synchronized with the Cloud.
+ *
+ * @param callback Callback.
+ * @param userData User data.
+ */
+ void onSync(OnSyncCallback callback, void* userData = nullptr);
+ // TODO: Add an overload taking a functor object
+};
+
+} // namespace particle
diff --git a/wiring/inc/spark_wiring_ledger_v2.h b/wiring/inc/spark_wiring_ledger_v2.h
new file mode 100644
index 0000000000..d36c0b83ab
--- /dev/null
+++ b/wiring/inc/spark_wiring_ledger_v2.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2023 Particle Industries, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#pragma once
+
+#include "spark_wiring_variant.h"
+#include "spark_wiring_print.h"
+
+namespace particle {
+
+class LedgerData;
+
+/**
+ * A ledger.
+ *
+ * Use `Particle.ledger()` to create an instance of this class.
+ */
+class Ledger {
+public:
+ /**
+ * A callback invoked when the ledger data has been synchronized with the Cloud.
+ *
+ * @param ledger Ledger instance.
+ * @param userData User data.
+ */
+ typedef void (*OnSyncCallback)(Ledger ledger, void* userData);
+
+ /**
+ * Default constructor.
+ *
+ * Constructs an invalid ledger instance.
+ */
+ Ledger();
+
+ /**
+ * Set the ledger data.
+ *
+ * This method replaces the current contents of the ledger.
+ *
+ * @param data New ledger data.
+ * @return 0 on success, otherwise an error code defined by `Error::Type`.
+ */
+ int set(const LedgerData& data);
+
+ /**
+ * Update the ledger data.
+ *
+ * This method replaces the current contents of the ledger.
+ *
+ * @param data New ledger data.
+ * @return 0 on success, otherwise an error code defined by `Error::Type`.
+ */
+ int update(const LedgerData& data);
+
+ /**
+ * Get the ledger data.
+ *
+ * @return Ledger data.
+ */
+ LedgerData get() const;
+
+ /**
+ * Get the last time, in milliseconds since the Unix epoch, the ledger was synchronized with the Cloud.
+ *
+ * @return Time the ledger was synchronized with the Cloud, or 0 if the ledger was never synchronized.
+ */
+ time64_t lastSyncedAt() const;
+
+ /**
+ * Get the ledger name.
+ *
+ * @return Ledger name.
+ */
+ const char* name() const;
+
+ /**
+ * Set a callback to be invoked when the ledger data has been synchronized with the Cloud.
+ *
+ * @param callback Callback.
+ * @param userData User data.
+ */
+ void onSync(OnSyncCallback callback, void* userData = nullptr);
+ // TODO: Add an overload taking a functor object
+};
+
+/**
+ * Ledger data.
+ */
+class LedgerData {
+public:
+ /**
+ * Set the value of an entry.
+ *
+ * @param name Entry name.
+ * @param value Entry value.
+ * @return `true` if the value was set, otherwise `false`.
+ */
+ bool set(const char* name, Variant value);
+
+ /**
+ * Get the value of an entry.
+ *
+ * @param name Entry name.
+ * @return Entry value.
+ */
+ Variant get(const char* name) const;
+
+ /**
+ * Check if an entry with a given name exists.
+ *
+ * @param name Entry name.
+ * @return `true` if the entry exists, otherwise `false`.
+ */
+ bool has(const char* name) const;
+
+ /**
+ * Remove an entry.
+ *
+ * @param name Entry name.
+ * @return `true` if the entry was removed, otherwise `false`.
+ */
+ bool remove(const char* name);
+
+private:
+ enum EntryFlag {
+ SET = 0x01,
+ REMOVED = 0x02
+ };
+
+ struct Entry {
+ Variant data;
+ int flags;
+ };
+
+ Map entries_;
+};
+
+} // namespace particle
diff --git a/wiring/inc/spark_wiring_map.h b/wiring/inc/spark_wiring_map.h
new file mode 100644
index 0000000000..a9179d47d7
--- /dev/null
+++ b/wiring/inc/spark_wiring_map.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 Particle Industries, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "spark_wiring_vector.h"
+
+namespace particle {
+
+template>
+class Map {
+public:
+ typedef std::tuple Entry;
+
+ Map() = default;
+ Map(const Map& map);
+ Map(Map&& map);
+
+ bool set(KeyT key, ValueT value);
+ ValueT get(const KeyT& key, ValueT defaultValue = ValueT()) const;
+ ValueT take(const KeyT& key, ValueT defaultValue = ValueT());
+ bool remove(const KeyT& key);
+ bool has(const KeyT& key) const;
+
+ const Vector& entries() const;
+
+ int size() const;
+ bool isEmpty() const;
+
+ bool reserve(int n);
+ int capacity() const;
+ bool trimToSize();
+
+ ValueT& operator[](int index);
+
+ // TODO: Iterator-based API
+
+ Map& operator=(Map map);
+
+ bool operator==(const Map& map);
+
+ friend void swap(Map& map1, Map& map2);
+
+private:
+ Vector entries_; // Sorted
+ CompareT cmp_;
+};
+
+} // namespace particle
diff --git a/wiring/inc/spark_wiring_variant.h b/wiring/inc/spark_wiring_variant.h
new file mode 100644
index 0000000000..e82ccfc9e1
--- /dev/null
+++ b/wiring/inc/spark_wiring_variant.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2023 Particle Industries, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "spark_wiring_string.h"
+#include "spark_wiring_stream.h"
+#include "spark_wiring_vector.h"
+#include "spark_wiring_map.h"
+
+namespace particle {
+
+class Variant;
+
+typedef Vector VariantArray;
+typedef Map VariantMap;
+
+class Variant {
+public:
+ enum Type {
+ // INVALID,
+ NULL_,
+ BOOL,
+ INT,
+ INT64,
+ DOUBLE,
+ STRING,
+ ARRAY,
+ MAP
+ };
+
+ Variant() = default;
+
+ Variant(bool value);
+ Variant(char value);
+ Variant(unsigned char value);
+ Variant(short value);
+ Variant(unsigned short value);
+ Variant(int value);
+ Variant(unsigned value);
+ Variant(long value);
+ Variant(unsigned long value);
+ Variant(long long value);
+ Variant(unsigned long long value);
+ Variant(double value);
+ Variant(const char* value);
+ Variant(String value);
+ Variant(VariantArray value);
+ Variant(VariantMap value);
+
+ Variant(const Variant& variant);
+ Variant(Variant&& variant);
+
+ Type type() const;
+
+ // bool isValid() const;
+ bool isNull() const;
+ bool isBool() const;
+ bool isInt() const;
+ bool isInt64() const;
+ bool isDouble() const;
+ bool isString() const;
+ bool isArray() const;
+ bool isMap() const;
+
+ bool toBool() const;
+ int toInt() const;
+ int64_t toInt64() const;
+ double toDouble() const;
+ String toString() const;
+ VariantArray toArray() const;
+ VariantMap toMap() const;
+
+ // TODO: Add helper methods for in-place modification of array, map and string variants -
+ // see the Vector, Map and String classes respectively
+
+ String toJson() const;
+ void toJson(Print& stream) const;
+
+ template
+ T valueAs();
+
+ template
+ T& valueRef();
+
+ template
+ const T& valueRef() const;
+
+ Variant& operator=(Variant variant);
+
+ bool operator==(const Variant& variant) const;
+ bool operator<(const Variant& variant) const;
+
+ // Conversion operators
+ operator bool() const;
+ operator char() const;
+ operator unsigned char() const;
+ operator short() const;
+ operator unsigned short() const;
+ operator int() const;
+ operator unsigned() const;
+ operator int64_t() const;
+ operator double() const;
+ operator const char*() const;
+ operator String() const;
+ operator VariantArray() const;
+ operator VariantMap() const;
+
+ static Variant fromJson(const char* str);
+ static Variant fromJson(Stream& stream);
+
+ friend void swap(Variant& variant1, Variant& variant2);
+
+private:
+ using VariantType = std::variant;
+
+ VariantType v_;
+};
+
+} // namespace particle