diff --git a/genologics/entities.py b/genologics/entities.py
index 3cdef28..89c4fd9 100644
--- a/genologics/entities.py
+++ b/genologics/entities.py
@@ -297,10 +297,12 @@ class Entity(object):
         self.lims.post(self.uri, data)
 
     @classmethod
-    def create(cls, lims, **kwargs):
-        """Create an instance from attributes then post it to the LIMS"""
+    def _create(cls, lims, creation_tag=None, **kwargs):
+        """Create an instance from attributes and return it"""
         instance = cls(lims, _create_new=True)
-        if cls._TAG:
+        if creation_tag:
+            instance.root = ElementTree.Element(nsmap(cls._PREFIX + ':' + creation_tag))
+        elif cls._TAG:
             instance.root = ElementTree.Element(nsmap(cls._PREFIX + ':' + cls._TAG))
         else:
             instance.root = ElementTree.Element(nsmap(cls._PREFIX + ':' + cls.__name__.lower()))
@@ -309,6 +311,13 @@ class Entity(object):
                 setattr(instance, attribute, kwargs.get(attribute))
             else:
                 raise TypeError("%s create: got an unexpected keyword argument '%s'" % (cls.__name__, attribute))
+
+        return instance
+
+    @classmethod
+    def create(cls, lims, creation_tag=None, **kwargs):
+        """Create an instance from attributes then post it to the LIMS"""
+        instance = cls._create(lims, creation_tag=None, **kwargs)
         data = lims.tostring(ElementTree.ElementTree(instance.root))
         instance.root = lims.post(uri=lims.get_uri(cls._URI), data=data)
         instance._uri = instance.root.attrib['uri']
@@ -412,6 +421,23 @@ class Sample(Entity):
     # biosource XXX
 
 
+    @classmethod
+    def create(cls, lims, container, position, **kwargs):
+        """Create an instance of Sample from attributes then post it to the LIMS"""
+        if not isinstance(container, Container):
+            raise TypeError('%s is not of type Container'%container)
+        instance = super(Sample, cls)._create(lims, creation_tag='samplecreation', **kwargs)
+
+        location = ElementTree.SubElement(instance.root, 'location')
+        ElementTree.SubElement(location, 'container', dict(uri=container.uri))
+        position_element = ElementTree.SubElement(location, 'value')
+        position_element.text = position
+        data = lims.tostring(ElementTree.ElementTree(instance.root))
+        instance.root = lims.post(uri=lims.get_uri(cls._URI), data=data)
+        instance._uri = instance.root.attrib['uri']
+        return instance
+
+
 class Containertype(Entity):
     "Type of container for analyte artifacts."
 
diff --git a/genologics/version.py b/genologics/version.py
index 613e29a..58caa73 100644
--- a/genologics/version.py
+++ b/genologics/version.py
@@ -1,1 +1,1 @@
-__version__="0.3.8"
+__version__="0.3.9"

Create new samples
Hi guys!

I was wondering how it would be possible to create new samples using the REST API. I got it working using this package but it required some hacking. Perhaps I'm missing something.. Are you using the API to do something like that?

This is briefly my approach:

1. The API needs a "location" reference with a container on sample level so I borrow some code from `create` and add the extra stuff in the XML code:

```python
lims_sample = Sample(lims=lims, _create_new=True)
uhmmm = "{}:{}".format(Sample._PREFIX, Sample.__name__.lower())
lims_sample.root = ElementTree.Element(nsmap(uhmmm))

lims_sample.name = name
lims_sample.project = lims_project
location = ElementTree.SubElement(lims_sample.root, 'location')
ElementTree.SubElement(location, 'container', dict(uri=lims_container.uri))
position_element = ElementTree.SubElement(location, 'value')
position_element.text = position
```

2. To save a sample you need to use the "smp:samplecreation" tag (or whatever it's called) so I have to modify the data string before posting:

```python
data = lims.tostring(ElementTree.ElementTree(lims_sample.root))
data = data.decode('utf-8').replace('smp:sample', 'smp:samplecreation')
lims_sample.root = lims.post(uri=lims.get_uri(Sample._URI), data=data)
lims_sample._uri = lims_sample.root.attrib['uri']
```

Just wanted to share the research I did to make this work. If it's something you are interested in adding support for I'd be happy to work with you. Right now I don't feel like I understand the package well enough to suggest how best to add it tho.
diff --git a/tests/test_entities.py b/tests/test_entities.py
index 91dcbbd..933f708 100644
--- a/tests/test_entities.py
+++ b/tests/test_entities.py
@@ -1,9 +1,10 @@
+import operator
 from sys import version_info
 from unittest import TestCase
 from xml.etree import ElementTree
 
 from genologics.entities import StepActions, Researcher, Artifact, \
-    Step, StepPlacements, Container, Stage, ReagentKit, ReagentLot
+    Step, StepPlacements, Container, Stage, ReagentKit, ReagentLot, Sample, Project
 from genologics.lims import Lims
 
 if version_info[0] == 2:
@@ -126,6 +127,20 @@ generic_step_actions_no_escalation_xml = """<stp:actions xmlns:stp="http://genol
   </next-actions>
 </stp:actions>"""
 
+generic_sample_creation_xml = """
+<smp:samplecreation xmlns:smp="http://genologics.com/ri/sample" limsid="s1" uri="{url}/api/v2/samples/s1">
+  <location>
+    <container limsid="cont1" uri="{url}/api/v2/containers/cont1">
+    </container>
+    <value>1:1</value>
+  </location>
+  <name>
+    sample1
+  </name>
+  <project uri="{url}/api/v2/projects/p1" limsid="p1">
+  </project>
+</smp:samplecreation>
+"""
 
 class TestEntities(TestCase):
     def test_pass(self):
@@ -133,12 +148,22 @@ class TestEntities(TestCase):
 
 
 def elements_equal(e1, e2):
-    if e1.tag != e2.tag: return False
-    if e1.text and e2.text and e1.text.strip() != e2.text.strip(): return False
-    if e1.tail and e2.tail and e1.tail.strip() != e2.tail.strip(): return False
-    if e1.attrib != e2.attrib: return False
-    if len(e1) != len(e2): return False
-    return all(elements_equal(c1, c2) for c1, c2 in zip(e1, e2))
+    if e1.tag != e2.tag:
+        print('Tag: %s != %s'%(e1.tag, e2.tag))
+        return False
+    if e1.text and e2.text and e1.text.strip() != e2.text.strip():
+        print('Text: %s != %s' % (e1.text.strip(), e2.text.strip()))
+        return False
+    if e1.tail and e2.tail and e1.tail.strip() != e2.tail.strip():
+        print('Tail: %s != %s' % (e1.tail.strip(), e2.tail.strip()))
+        return False
+    if e1.attrib != e2.attrib:
+        print('Attrib: %s != %s' % (e1.attrib, e2.attrib))
+        return False
+    if len(e1) != len(e2):
+        print('length %s (%s) != length (%s) ' % (e1.tag, len(e1), e2.tag, len(e2)))
+        return False
+    return all(elements_equal(c1, c2) for c1, c2 in zip(sorted(e1, key=lambda x: x.tag), sorted(e2, key=lambda x: x.tag)))
 
 
 class TestEntities(TestCase):
@@ -289,3 +314,30 @@ class TestReagentLots(TestEntities):
             assert l.uri
             assert l.name == 'kitname'
             assert l.lot_number == '100'
+
+
+class TestSample(TestEntities):
+    sample_creation = generic_sample_creation_xml.format(url=url)
+
+    def test_create_entity(self):
+        with patch('genologics.lims.requests.post',
+                   return_value=Mock(content=self.sample_creation, status_code=201)) as patch_post:
+            l = Sample.create(
+                self.lims,
+                project=Project(self.lims, uri='project'),
+                container=Container(self.lims, uri='container'),
+                position='1:1',
+                name='s1',
+            )
+            data = '''<?xml version=\'1.0\' encoding=\'utf-8\'?>
+            <smp:samplecreation xmlns:smp="http://genologics.com/ri/sample">
+            <name>s1</name>
+            <project uri="project" />
+            <location>
+              <container uri="container" />
+              <value>1:1</value>
+            </location>
+            </smp:samplecreation>'''
+            assert elements_equal(ElementTree.fromstring(patch_post.call_args_list[0][1]['data']), ElementTree.fromstring(data))
+
+

pip install -e .
parse_log_pytest
requirements.txt
3.5
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
charset-normalizer==2.0.12
-e git+https://github.com/SciLifeLab/genologics.git@dc79dc55dc1218582f30a5e76e9a0fff428cbf30#egg=genologics
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
mock==5.2.0
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
requests==2.27.1
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: genologics
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - charset-normalizer==2.0.12
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - mock==5.2.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - requests==2.27.1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/genologics

tests/test_entities.py::TestSample::test_create_entity
tests/test_entities.py::TestStepActions::test_escalation tests/test_entities.py::TestStepActions::test_next_actions tests/test_entities.py::TestStepPlacements::test_get_placements_list tests/test_entities.py::TestStepPlacements::test_set_placements_list tests/test_entities.py::TestStepPlacements::test_set_placements_list_fail tests/test_entities.py::TestArtifacts::test_input_artifact_list tests/test_entities.py::TestArtifacts::test_workflow_stages_and_statuses tests/test_entities.py::TestReagentKits::test_create_entity tests/test_entities.py::TestReagentKits::test_parse_entity tests/test_entities.py::TestReagentLots::test_create_entity tests/test_entities.py::TestReagentLots::test_parse_entity
genologics/version.py genologics/entities.py
genologics/version.py genologics/entities.py