diff --git a/changelog.md b/changelog.md
index 118dbb93..8543ea85 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,11 @@
# Changelog
+## v1.15.0
+
+### Oct 21, 2026
+
+- Feat: Added the opt-in `Contentstack.Builder.setSerializeNulls(boolean)` option. When enabled, `null` values in request bodies are sent to the API instead of being dropped, so a field's value (for example a Number field on a localized entry) can be cleared. Off by default; existing behavior is unchanged.
+
## v1.14.1
### Aug 26, 2026
diff --git a/pom.xml b/pom.xml
index 056f6622..da74f14a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
+ * By default {@code null} values are dropped before the request is sent, so
+ * {@code "field": null} behaves exactly like leaving the field out. Enable this
+ * to send {@code null} explicitly, for example to clear a Number field's value
+ * on an entry. It applies to every request made through this client.
+ *
+ * @param serializeNulls {@code true} to send {@code null} values, {@code false} (default) to drop them
+ * @return the Builder
+ */
+ public Builder setSerializeNulls(boolean serializeNulls) {
+ this.serializeNulls = serializeNulls;
+ return this;
+ }
+
/**
* Set host for client instance
*
@@ -1015,8 +1033,12 @@ private void validateClient(Contentstack contentstack) {
// of how the hostname was set (setHost, region resolution, or default).
String validatedHost = validateHostname(this.hostname);
String baseUrl = Util.PROTOCOL + "://" + validatedHost + "/" + version + "/";
+ GsonBuilder gson = new GsonBuilder();
+ if (serializeNulls) {
+ gson.serializeNulls();
+ }
this.instance = new Retrofit.Builder().baseUrl(baseUrl)
- .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(GsonConverterFactory.create(gson.create()))
.client(httpClient(contentstack, this.retry)).build();
contentstack.instance = this.instance;
diff --git a/src/test/java/com/contentstack/cms/ContentstackUnitTest.java b/src/test/java/com/contentstack/cms/ContentstackUnitTest.java
index 1da49474..881913ee 100644
--- a/src/test/java/com/contentstack/cms/ContentstackUnitTest.java
+++ b/src/test/java/com/contentstack/cms/ContentstackUnitTest.java
@@ -2,7 +2,10 @@
import com.contentstack.cms.core.AuthInterceptor;
import com.contentstack.cms.organization.Organization;
+import com.contentstack.cms.stack.Entry;
import com.contentstack.cms.stack.Stack;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
@@ -12,7 +15,10 @@
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.Assertions;
+import okio.Buffer;
+import org.json.simple.JSONObject;
import org.junit.jupiter.api.Test;
+import retrofit2.Call;
import retrofit2.Response;
import java.io.IOException;
@@ -412,4 +418,71 @@ public void testEarlyAccessHeaderEmpty() {
Assertions.assertEquals(0, client.earlyAccess.length);
}
+ @Test
+ void localizeBodyKeepsNullFieldWhenSerializeNullsEnabled() throws IOException {
+ Contentstack client = new Contentstack.Builder().setSerializeNulls(true).build();
+
+ JsonObject entry = sentEntry(entryOf(client).localize(entryBody(fieldsWith("position", null)), "de-de"));
+
+ Assertions.assertTrue(entry.has("position"));
+ Assertions.assertTrue(entry.get("position").isJsonNull());
+ }
+
+ @Test
+ void localizeBodyDropsNullFieldByDefault() throws IOException {
+ Contentstack client = new Contentstack.Builder().build();
+
+ JsonObject entry = sentEntry(entryOf(client).localize(entryBody(fieldsWith("position", null)), "de-de"));
+
+ Assertions.assertFalse(entry.has("position"));
+ Assertions.assertEquals("example", entry.get("title").getAsString());
+ }
+
+ @Test
+ void localizeBodyKeepsNullInsideGroupFieldWhenSerializeNullsEnabled() throws IOException {
+ Contentstack client = new Contentstack.Builder().setSerializeNulls(true).build();
+ Map