diff --git a/NEWS.rst b/NEWS.rst
index 85bcb83808e879520a789197b9a0a39195fa7af1_TkVXUy5yc3Q=..ac6f9091291b083a954e43851c04ba164ddedae4_TkVXUy5yc3Q= 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,6 +1,11 @@
 Changelog
 =========
 
+18.0.6.1.0
+----------
+
+Field converter on a binary field use data uri.
+
 18.0.6.0.0
 ----------
 
diff --git a/__init__.py b/__init__.py
index 85bcb83808e879520a789197b9a0a39195fa7af1_X19pbml0X18ucHk=..ac6f9091291b083a954e43851c04ba164ddedae4_X19pbml0X18ucHk= 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
 #    Converter Odoo module
-#    Copyright © 2020 XCG Consulting <https://xcg-consulting.fr>
+#    Copyright © 2020, 2025 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
diff --git a/__manifest__.py b/__manifest__.py
index 85bcb83808e879520a789197b9a0a39195fa7af1_X19tYW5pZmVzdF9fLnB5..ac6f9091291b083a954e43851c04ba164ddedae4_X19tYW5pZmVzdF9fLnB5 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -21,7 +21,7 @@
     "name": "Converter",
     "license": "AGPL-3",
     "summary": "Convert odoo records to/from plain data structures.",
-    "version": "18.0.6.0.0",
+    "version": "18.0.6.1.0",
     "category": "Hidden",
     "author": "XCG Consulting",
     "website": "https://orbeet.io/",
diff --git a/field.py b/field.py
index 85bcb83808e879520a789197b9a0a39195fa7af1_ZmllbGQucHk=..ac6f9091291b083a954e43851c04ba164ddedae4_ZmllbGQucHk= 100644
--- a/field.py
+++ b/field.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
 #    Converter Odoo module
-#    Copyright © 2020 XCG Consulting <https://xcg-consulting.fr>
+#    Copyright © 2020, 2025 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -17,7 +17,7 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
-
+import base64
 import datetime
 from collections.abc import Callable
 from typing import Any, Literal
@@ -21,5 +21,7 @@
 import datetime
 from collections.abc import Callable
 from typing import Any, Literal
+from urllib.parse import urlsplit
+from urllib.request import urlopen
 
 import pytz
@@ -24,4 +26,4 @@
 
 import pytz
-from odoo import api, models  # type: ignore[import-untyped]
+from odoo import api, fields, models  # type: ignore[import-untyped]
 
@@ -27,4 +29,11 @@
 
-from .base import PHASE_POSTCREATE, Context, Converter, Newinstance, Skip, SkipType
+from .base import (
+    PHASE_POSTCREATE,
+    Context,
+    Newinstance,
+    PostHookConverter,
+    Skip,
+    SkipType,
+)
 
 
@@ -29,6 +38,6 @@
 
 
-class Field(Converter):
+class Field(PostHookConverter):
     """Converter linked to a single field"""
 
     def __init__(
@@ -111,6 +120,20 @@
         if isinstance(value, datetime.date):
             if not self._message_formatter:
                 value = value.isoformat()
+        field = instance._fields[self.field_name]
+        if isinstance(field, fields.Binary):
+            if field.attachment:
+                domain = [
+                    ("res_model", "=", instance._name),
+                    ("res_field", "=", self.field_name),
+                    ("res_id", "in", instance.ids),
+                ]
+                # sudo needed, no right to read those attachments
+                attachment = instance.env["ir.attachment"].sudo().search(domain)
+                mimetype = attachment.mimetype.encode("UTF-8")
+            else:
+                mimetype = b"application/octet-stream"
+            value = b"data:" + mimetype + b";base64," + value
         if self._message_formatter:
             value = self._message_formatter(value, False)
         return value
@@ -135,8 +158,20 @@
             value = self.odoo_to_message(instance)
             if isinstance(value, SkipType) or value == message_value:
                 return {}
+        field = instance._fields[self.field_name]
+        if isinstance(field, fields.Binary):
+            if field.attachment:
+                return {}
+            else:
+                url = urlsplit(message_value)
+                if url.scheme != "data":
+                    # urlsplit raises ValueError too
+                    raise ValueError("Not a Data URI (%s scheme)", url.scheme)
+                with urlopen(message_value) as response:
+                    message_value = base64.b64encode(response.read())
+
         if self._odoo_formatter:
             message_value = self._odoo_formatter(message_value)
 
         return {self.field_name: message_value}
 
@@ -138,8 +173,36 @@
         if self._odoo_formatter:
             message_value = self._odoo_formatter(message_value)
 
         return {self.field_name: message_value}
 
+    def post_hook(self, instance: models.BaseModel, message_data):
+        field = instance._fields[self.field_name]
+        if isinstance(field, fields.Binary):
+            if field.attachment:
+                # create ir.attachment directly
+                url = urlsplit(message_data)
+                # sudo needed, no right to read those attachments
+                if url.scheme != "data":
+                    # urlsplit raises ValueError too
+                    raise ValueError("Not a Data URI (%s scheme)", url.scheme)
+                domain = [
+                    ("res_model", "=", instance._name),
+                    ("res_field", "=", self.field_name),
+                    ("res_id", "in", instance.ids),
+                ]
+                # sudo needed, no right to read those attachments
+                attachment = instance.env["ir.attachment"].sudo().search(domain)
+                with urlopen(message_data) as response:
+                    datas = base64.b64encode(response.read())
+                if self._odoo_formatter:
+                    datas = self._odoo_formatter(datas)
+                # TODO does not update the data. Need to use _set_attachment_data
+                #  directly maybe?
+                attachment.write(
+                    {"datas": datas, "mimetype": url.path.split(";", 1)[0]}
+                )
+                instance.invalidate_recordset([self.field_name])
+
 
 class TranslatedSelection(Field):
     """Converter that uses a translation value of a selection field rather
diff --git a/tests/test_field.py b/tests/test_field.py
index 85bcb83808e879520a789197b9a0a39195fa7af1_dGVzdHMvdGVzdF9maWVsZC5weQ==..ac6f9091291b083a954e43851c04ba164ddedae4_dGVzdHMvdGVzdF9maWVsZC5weQ== 100644
--- a/tests/test_field.py
+++ b/tests/test_field.py
@@ -1,7 +1,7 @@
 ##############################################################################
 #
 #    Converter Odoo module
-#    Copyright © 2021 XCG Consulting <https://xcg-consulting.fr>
+#    Copyright © 2021, 2025 XCG Consulting <https://xcg-consulting.fr>
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
@@ -110,3 +110,84 @@
                 )
             ),
         )
+
+    def test_binary_data_uri(self):
+        converter = Field("avatar_1920")
+        partner = self.env.ref("base.partner_admin")
+        old_image = partner.avatar_1920
+        value = converter.odoo_to_message(partner)
+        # admin partner avatar is stored with application/octet-stream
+        self.assertTrue(value.startswith(b"data:application/octet-stream;base64,"))
+        # white 1920x1920 image
+        data_uri = """data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAB4AAAAeAAQAAAAAH2XdrAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAd2KE6QAAAAHdElN
+RQfpAhURBCXSFMwjAAAIE0lEQVR42u3PAQ0AMAjAMPyb/mWQjE7BOu9Ysz0ADAwMDAwMfCfgesD1
+gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOu
+B1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5w
+PeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA
+6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64H
+XA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA9
+4HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDr
+AdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdc
+D7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3g
+esD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB
+1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wP
+uB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6
+wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHX
+A64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4
+HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA
+9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcD
+rgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7ge
+cD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1
+gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOu
+B1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5w
+PeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA
+6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64H
+XA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA9
+4HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDr
+AdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdc
+D7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3g
+esD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB
+1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wP
+uB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6
+wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHX
+A64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4
+HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA
+9YDrAdcDrgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcD
+rgdcD7gecD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7ge
+cD3gesD1gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1
+gOsB1wOuB1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrAdcDrgdcD7gecD3gesD1gOsB1wOu
+B1wPuB5wPeB6wPWA6wHXA64HXA+4HnA94HrA9YDrfdjxYRCgcj/RAAAAJXRFWHRkYXRlOmNyZWF0
+ZQAyMDI1LTAyLTIxVDE3OjA0OjM3KzAwOjAw60rorAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNS0w
+Mi0yMVQxNzowNDozNyswMDowMJoXUBAAAAAASUVORK5CYII="""
+        values = converter.message_to_odoo(self.env, "update", data_uri, partner, True)
+        partner.write(values)
+        self.assertNotEqual(old_image, partner.avatar_1920)
+        value = converter.odoo_to_message(partner)
+        # not an attachment, mimetype is lost
+        self.assertTrue(value.startswith(b"data:application/octet-stream;base64,"))
+        converter = Field("image_1920")
+        main_company = self.env.ref("base.main_company").partner_id
+        values = converter.message_to_odoo(self.env, "update", data_uri, partner, True)
+        main_company.write(values)
+        value = converter.odoo_to_message(main_company)
+        self.assertTrue(value.startswith(b"data:image/png;base64,"))
+        # test on an attachment field
+        data_uri = (
+            "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElE"
+            "QVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
+        )
+        menu = self.env["ir.ui.menu"].search([], limit=1)
+        old_image = menu.web_icon_data
+        converter = Field("web_icon_data")
+        values = converter.message_to_odoo(self.env, "update", data_uri, menu, True)
+        menu.write(values)
+        self.assertEqual(old_image, menu.web_icon_data)
+        converter.post_hook(menu, data_uri)
+        # TODO fix that, post_hook is not working
+        self.assertNotEqual(old_image, menu.web_icon_data)
+        value = converter.odoo_to_message(menu)
+        self.assertTrue(value.startswith(b"data:image/png;base64,"))
+        # TODO test with a image/svg and a non admin user
+        # TODO add more tests like text/plain