diff --git a/app/api/items.py b/app/api/items.py
index 7caa5cfa0599cb16fbf934ea48266958c9ea95a1..1eb8471a3cdafc538f849ad6e2ab27ec4d9bce51 100644
--- a/app/api/items.py
+++ b/app/api/items.py
@@ -9,11 +9,13 @@ This module implements the items API.
 :license: BSD 2-Clause, see LICENSE for more details.
 
 """
+import sqlalchemy as sa
 from flask import (current_app, Blueprint, jsonify, request)
 from flask_jwt_extended import create_access_token, jwt_required
 from flask_ldap3_login import AuthenticationResponseStatus
 from ..extensions import ldap_manager, db
-from ..models import User
+from ..models import User, Item
+from .. import utils
 
 bp = Blueprint('api', __name__)
 
@@ -42,13 +44,29 @@ def login():
 @bp.route('/items')
 @jwt_required
 def get_items():
-    sql = """SELECT item.id, item.name, manufacturer.name, model.name, location.name, status.name
-            FROM item
-            INNER JOIN manufacturer ON manufacturer.id = item.manufacturer_id
-            INNER JOIN model ON model.id = item.model_id
-            INNER JOIN location ON location.id = item.location_id
-            INNER JOIN status ON status.id = item.status_id
-            """
-    result = db.engine.execute(sql)
-    data = [[item for item in row] for row in result]
-    return jsonify(data=data)
+    # TODO: add pagination
+    items = Item.query.order_by(Item._created)
+    data = [item.to_dict() for item in items]
+    return jsonify({'items': data})
+
+
+@bp.route('/items', methods=['POST'])
+@jwt_required
+def create_item():
+    data = request.get_json()
+    if data is None:
+        raise utils.InventoryError('Body should be a JSON object')
+    if 'serial_number' not in data:
+        raise utils.InventoryError('serial_number is mandatory')
+    try:
+        item = Item(**data)
+    except TypeError as e:
+        message = str(e).replace('__init__() got an ', '')
+        raise utils.InventoryError(message)
+    db.session.add(item)
+    try:
+        db.session.commit()
+    except sa.exc.IntegrityError as e:
+        db.session.rollback()
+        raise utils.InventoryError('IntegrityError', status_code=409)
+    return jsonify(item.to_dict()), 201
diff --git a/app/models.py b/app/models.py
index 02f31736690e674698c8c569549e6cc1b529d123..055de442b51dafcaea00f14e3487e4d3424a8d44 100644
--- a/app/models.py
+++ b/app/models.py
@@ -184,11 +184,25 @@ class Item(db.Model):
         # All arguments must be optional for this class to work with flask-admin!
         self.serial_number = serial_number
         self.name = name
-        self.manufacturer = manufacturer
-        self.model = model
-        self.location = location
-        self.status = status
+        self.manufacturer = utils.convert_to_model(manufacturer, Manufacturer)
+        self.model = utils.convert_to_model(model, Model)
+        self.location = utils.convert_to_model(location, Location)
+        self.status = utils.convert_to_model(status, Status)
         self.hash = utils.compute_hash(serial_number)
 
     def __str__(self):
         return self.serial_number
+
+    def to_dict(self):
+        return {
+            'id': self.id,
+            'serial_number': self.serial_number,
+            'name': self.name,
+            'hash': self.hash,
+            'manufacturer': utils.format_field(self.manufacturer),
+            'model': utils.format_field(self.model),
+            'location': utils.format_field(self.location),
+            'status': utils.format_field(self.status),
+            'updated': utils.format_field(self._updated),
+            'created': utils.format_field(self._created),
+        }
diff --git a/app/utils.py b/app/utils.py
index dddef7b632a53dd158d71c91d7343fc71c32d8c9..f67893de7e450167f71fb7de8100ef88001a4249 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -65,3 +65,21 @@ def format_field(field):
     if isinstance(field, datetime.datetime):
         return field.strftime('%Y-%m-%d %H:%M')
     return str(field)
+
+
+def convert_to_model(item, model):
+    """Convert item to an instance of model
+
+    Allow to convert a string to an instance of model
+    Raise an exception if the given name is not found
+
+    :returns: an instance of model
+    """
+    if item is None:
+        return None
+    if not isinstance(item, model):
+        instance = model.query.filter_by(name=item).first()
+        if instance is None:
+            raise InventoryError(f'{item} is not a valid {model.__name__.lower()}')
+        return instance
+    return item