diff --git a/public/openapi.yaml b/public/openapi.yaml
index e9258467ed57f91f554ea1d34df74b422560c590..bbe630767b8a9eed22fb503b2439f3338b5785d3 100644
--- a/public/openapi.yaml
+++ b/public/openapi.yaml
@@ -1,4 +1,4 @@
-openapi: 3.0.1
+openapi: 3.1.0
 info:
   title: Lager's OpenAPI
   description: warehouse management system
@@ -71,8 +71,18 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/Product'
+        400:
+          description: Bad Request
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
         500:
-          description: something went wrong (in a better version error should be refined)
+          description: Internal error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
 
   /products:
     post:
@@ -144,6 +154,55 @@ paths:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
 
+  /products/{productId}/sell:
+    post:
+      tags:
+        - products
+      summary: Sell a product
+      description: Sell a given amount of one product
+      parameters:
+        - in: path
+          name: productId
+          schema:
+            type: integer
+          required: true
+          description: Numeric ID of the product to get
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              required:
+                - amount
+              properties:
+                amount:
+                  type: integer
+      responses:
+        200:
+          description: The updated product
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Product'
+        400:
+          description: Bad Request
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+        404:
+          description: Parts or Product Not found
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+        500:
+          description: Internal error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+
 components:
   schemas:
     HellWorld:
@@ -213,7 +272,7 @@ components:
               amount_of:
                 type: integer
               stock:
-                type: integer,
+                type: integer
               art_name:
                 type: string
 
diff --git a/src/codecs/product.ts b/src/codecs/product.ts
index f287e176829aa10c453aee7351e47d5f45a7af53..a3a50e6cdcbfff5296d81e9354738258522b13b1 100644
--- a/src/codecs/product.ts
+++ b/src/codecs/product.ts
@@ -15,11 +15,16 @@ export const productParamsC = t.type({
   productId: tt.IntFromString
 })
 
+export const productSellC = t.type({
+  amount: t.number
+})
+
 export type Product = {
     id: number,
     name: string,
     availability: number,
     contain_articles: {
+        amount_of: number,
         art_id: number,
         stock: number,
         art_name: string
diff --git a/src/server/controllers/inventory.ts b/src/server/controllers/inventory.ts
index 2111801e413ad0253690f99c3caa72dbaea1ea0c..555e8ca369e675b67e4b1b0467f30a5e3d2eaa47 100644
--- a/src/server/controllers/inventory.ts
+++ b/src/server/controllers/inventory.ts
@@ -5,6 +5,7 @@ import * as T from 'fp-ts/Task'
 import { pipe } from 'fp-ts/function'
 import { inventoryPartRequestC } from '@/codecs/inventory'
 import { decodeWith } from '@/utils'
+import { handleError } from '@/server/utils'
 
 export interface InventoryControllers {
     getAllInventory: Handler,
@@ -23,10 +24,8 @@ export default (repo: InventoryRepo): InventoryControllers => ({
     decodeWith(inventoryPartRequestC)(req.body),
     TE.fromEither,
     TE.map(part => ({ ...part, stock: part.stock || 0 })),
-    TE.chain(part => repo.create(part)),
-    TE.fold(
-      errors => T.of(res.status(500).send(errors)),
-      partId => T.of(res.status(200).json(partId))
-    )
+    TE.chainW(part => repo.create(part)),
+    TE.map(partId => res.status(200).json(partId)),
+    handleError(res)
   )()
 })
diff --git a/src/server/controllers/product.ts b/src/server/controllers/product.ts
index 9048a5fd07c5d332fd00c3728cb1483244108cac..18eb36f69f773ca0c9e49567a28e648dc4bebb48 100644
--- a/src/server/controllers/product.ts
+++ b/src/server/controllers/product.ts
@@ -1,14 +1,16 @@
 import { ProductRepo } from '@/storage/productRepo'
 import { Handler } from 'express'
 import { pipe } from 'fp-ts/function'
-import { productParamsC, productCreateC } from '@/codecs/product'
+import { productParamsC, productCreateC, productSellC } from '@/codecs/product'
 import * as TE from 'fp-ts/TaskEither'
+import * as E from 'fp-ts/Either'
 import { decodeWith } from '@/utils'
 import { handleError } from '@/server/utils'
 
 export interface ProductControllers {
     getProduct: Handler,
-    postProduct: Handler
+    postProduct: Handler,
+    sellProduct: Handler
 }
 
 export default (repo: ProductRepo): ProductControllers => ({
@@ -25,5 +27,16 @@ export default (repo: ProductRepo): ProductControllers => ({
     TE.chainW(repo.create),
     TE.map(product => res.status(201).json(product)),
     handleError(res)
+  )(),
+  sellProduct: (req, res) => pipe(
+    decodeWith(productSellC)(req.body),
+    E.chain((body) => pipe(
+      decodeWith(productParamsC)(req.params),
+      E.map((params) => ([params, body] as [{ productId: number }, { amount: number }]))
+    )),
+    TE.fromEither,
+    TE.chainW(([params, body]) => repo.sell(params.productId, body.amount)),
+    TE.map(product => res.status(200).json(product)),
+    handleError(res)
   )()
 })
diff --git a/src/server/errors.ts b/src/server/errors.ts
index 51cc04cfcd5e91b0bf4db8b832e1f3a752a8db98..206172f0237ee4636eef6edcab6812bd24b7f80c 100644
--- a/src/server/errors.ts
+++ b/src/server/errors.ts
@@ -9,6 +9,14 @@ export class DecodeError extends Error {
   }
 }
 
+export class DBDecodeError extends Error {
+  constructor (
+        readonly errors: Errors
+  ) {
+    super()
+  }
+}
+
 export class NotFound extends Error {
   constructor (
         readonly entityType: string,
@@ -27,12 +35,14 @@ export class ApiError extends Error {
   }
 }
 
-export type HandlerErrors = NotFound | DecodeError | DatabaseError
+export type HandlerErrors = NotFound | DecodeError | DatabaseError | DBDecodeError
 
 const refineDbError = (dbError: DatabaseError) => {
   switch (dbError.constraint) {
     case 'product_to_inventory_amount_of_check':
       return new ApiError(400, 'amount of a piece can\'t be 0')
+    case 'inventory_stock_check':
+      return new ApiError(400, 'the stock can\'t be below 0')
     case 'inventory_unique_article_id':
       return new ApiError(400, 'that part already exist')
     case 'pti_product_id_exist':
@@ -46,6 +56,8 @@ const refineDbError = (dbError: DatabaseError) => {
 
 export const toApiError = (error: Error): ApiError => {
   switch (true) {
+    case error.constructor === DBDecodeError.prototype.constructor:
+      return new ApiError(500, 'wrong database format')
     case error.constructor === DecodeError.prototype.constructor:
       return new ApiError(400, 'wrong body format')
     case error.constructor === NotFound.prototype.constructor:
diff --git a/src/server/index.ts b/src/server/index.ts
index a5b57cbc9cd0a0c8aa2c7d27e6a97645de6116da..480769539e718d1289ddb4997755a197ea548940 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -10,23 +10,25 @@ export type AppDeps = {
     dbClient: Client
 }
 
-export default (deps: AppDeps): Application => express()
-  .use(expressWinston.logger({
-    transports: [
-      new winston.transports.Console()
-    ],
-    format: winston.format.combine(
-      winston.format.colorize(),
-      winston.format.json()
-    ),
-    meta: true,
-    msg: 'HTTP {{req.method}} {{req.url}}',
-    expressFormat: true,
-    colorize: false
-  }))
-  .use(express.json())
-  .use(express.static('public'))
-  .use('/', makeRoutes({
-    inventoryRepo: makeInventoryRepo(deps.dbClient),
-    productRepo: makeProductRepo(deps.dbClient)
-  }))
+export default (deps: AppDeps): Application => {
+  const inventoryRepo = makeInventoryRepo(deps.dbClient)
+  const productRepo = makeProductRepo(deps.dbClient, inventoryRepo)
+
+  return express()
+    .use(expressWinston.logger({
+      transports: [
+        new winston.transports.Console()
+      ],
+      format: winston.format.combine(
+        winston.format.colorize(),
+        winston.format.json()
+      ),
+      meta: true,
+      msg: 'HTTP {{req.method}} {{req.url}}',
+      expressFormat: true,
+      colorize: false
+    }))
+    .use(express.json())
+    .use(express.static('public'))
+    .use('/', makeRoutes({ inventoryRepo, productRepo }))
+}
diff --git a/src/server/routes/product.ts b/src/server/routes/product.ts
index e2949066050c4f7b1c9f430e5c612e4c1927488a..d8c3768f69d273f65a5e529c2b14d324be8cb959 100644
--- a/src/server/routes/product.ts
+++ b/src/server/routes/product.ts
@@ -6,5 +6,6 @@ export default (repo: ProductRepo): Router => {
   const controllers = makeControllers(repo)
   return Router()
     .get('/:productId(\\d+)', controllers.getProduct)
+    .post('/:productId(\\d+)/sell', controllers.sellProduct)
     .post('/', controllers.postProduct)
 }
diff --git a/src/storage/inventoryRepo.ts b/src/storage/inventoryRepo.ts
index e775362b18ab745c04b577e886d83feb1fd5b4d6..acbf87a8ec7a7119f1e8dfdd0ef94e799beb925e 100644
--- a/src/storage/inventoryRepo.ts
+++ b/src/storage/inventoryRepo.ts
@@ -1,29 +1,56 @@
-import { Client, QueryResult } from 'pg'
+import { Client, DatabaseError } from 'pg'
 import * as TE from 'fp-ts/TaskEither'
-import * as E from 'fp-ts/Either'
-import { InventoryPart, inventoryPartsC } from '@/codecs/inventory'
+import { InventoryPart, inventoryPartC, inventoryPartsC } from '@/codecs/inventory'
 import { pipe } from 'fp-ts/function'
-import { toError } from 'fp-ts/Either'
+import { decodeWithDb, tryCatchDb } from '@/utils'
+import { DBDecodeError, NotFound } from '@/server/errors'
+
+export const sql = {
+  selectAll: 'SELECT * FROM inventory',
+  selectArt: 'SELECT * FROM inventory WHERE art_id = $1',
+  insertIntoInventory: 'INSERT INTO inventory(art_id, name, stock) VALUES($1, $2, $3)',
+  updateInventory: 'UPDATE inventory SET stock = $1 WHERE art_id = $2'
+}
 
 export interface InventoryRepo {
-    getAll(): TE.TaskEither<Error, InventoryPart[]>,
+    getAll(): TE.TaskEither<DatabaseError | DBDecodeError, InventoryPart[]>,
+
+    get(partId: number): TE.TaskEither<DatabaseError | DBDecodeError | NotFound, InventoryPart>
+
+    update(partId: number, newStock: number): TE.TaskEither<DatabaseError | DBDecodeError | NotFound, InventoryPart>
 
-    create(part: InventoryPart): TE.TaskEither<Error, number>
+    create(part: InventoryPart): TE.TaskEither<DatabaseError, number>,
 }
 
+const getAll = (dbClient: Client) => () => pipe(
+  tryCatchDb(() => dbClient.query(sql.selectAll)),
+  TE.chainEitherKW((res) => decodeWithDb(inventoryPartsC)(res.rows))
+)
+
+const get = (dbClient: Client) => (partId) => pipe(
+  tryCatchDb(() => dbClient.query(sql.selectArt, [partId])),
+  TE.chainW(result => result.rowCount === 0
+    ? TE.left(new NotFound('article', partId))
+    : TE.right(result.rows[0])),
+  TE.chainEitherKW(decodeWithDb(inventoryPartC))
+)
+
+const update = (dbClient: Client) => (partId, newStock) => pipe(
+  tryCatchDb(() => dbClient.query(sql.updateInventory, [newStock, partId])),
+  TE.chain(() => get(dbClient)(partId))
+)
+
+const create = (dbClient: Client) => (part) => pipe(
+  tryCatchDb(() => dbClient.query(
+    sql.insertIntoInventory,
+    [part.art_id, part.name, part.stock]
+  )),
+  TE.map(() => part.art_id)
+)
+
 export default (dbClient: Client): InventoryRepo => ({
-  getAll: () => pipe(
-    TE.tryCatch(() => dbClient.query<unknown>('SELECT * FROM inventory'), toError),
-    TE.chainEitherK((res: QueryResult<unknown>) => pipe(
-      inventoryPartsC.decode(res.rows),
-      E.mapLeft(errors => new Error(errors.join(',')))
-    ))
-  ),
-  create: (part) => pipe(
-    TE.tryCatch(() => dbClient.query(
-      'INSERT INTO inventory(art_id, name, stock) VALUES($1, $2, $3)',
-      [part.art_id, part.name, part.stock]
-    ), toError),
-    TE.map(() => part.art_id)
-  )
+  getAll: getAll(dbClient),
+  get: get(dbClient),
+  update: update(dbClient),
+  create: create(dbClient)
 })
diff --git a/src/storage/productRepo.ts b/src/storage/productRepo.ts
index fd76d53df4ad9e00a65ee7fd5c52905329a3a572..cbaaf6117e44664dcb5c92711cfd680626a122a4 100644
--- a/src/storage/productRepo.ts
+++ b/src/storage/productRepo.ts
@@ -4,7 +4,8 @@ import * as TE from 'fp-ts/TaskEither'
 import { Product, ProductCreate } from '@/codecs/product'
 import { pipe } from 'fp-ts/function'
 import { tryCatchDb } from '@/utils'
-import { NotFound } from '@/server/errors'
+import { DBDecodeError, NotFound } from '@/server/errors'
+import { InventoryRepo } from '@/storage/inventoryRepo'
 
 export const sql = {
   getProduct: 'SELECT product_id, p.name, amount_of, i.art_id, i.stock as art_stock, i.name as art_name, div(i.stock, amount_of) AS availability' +
@@ -21,6 +22,8 @@ export interface ProductRepo {
     get(productId: number): TE.TaskEither<DatabaseError | NotFound, Product>
 
     create(product: ProductCreate): TE.TaskEither<DatabaseError | NotFound, Product>
+
+    sell(productId: number, amount: number): TE.TaskEither<DatabaseError | NotFound | DBDecodeError, Product>
 }
 
 const getProduct = (dbClient: Client) => (productId) => pipe(
@@ -66,7 +69,22 @@ const createProduct = (dbClient: Client) => (product) => pipe(
   TE.chainFirst(() => tryCatchDb(() => dbClient.query('COMMIT')))
 )
 
-export default (dbClient: Client): ProductRepo => ({
+const sell = (dbClient: Client, inventoryRepo: InventoryRepo) => (productId, amount) => pipe(
+  tryCatchDb(() => dbClient.query('BEGIN')),
+  TE.chain(() => getProduct(dbClient)(productId)),
+  TE.chain((product: Product) => TE.sequenceArray(
+    product
+      .contain_articles
+      .map(article =>
+        inventoryRepo.update(article.art_id, article.stock - article.amount_of * amount))
+  )),
+  TE.chain(() => getProduct(dbClient)(productId)),
+  TE.orElseFirstW(() => tryCatchDb(() => dbClient.query('ROLLBACK'))),
+  TE.chainFirst(() => tryCatchDb(() => dbClient.query('COMMIT')))
+)
+
+export default (dbClient: Client, inventoryRepo: InventoryRepo): ProductRepo => ({
   get: getProduct(dbClient),
-  create: createProduct(dbClient)
+  create: createProduct(dbClient),
+  sell: sell(dbClient, inventoryRepo)
 })
diff --git a/src/utils.ts b/src/utils.ts
index 66398de8f89afb93d1468e67d81e02343008825d..097e48315f25eb69e8cc860f3d593342c77698fa 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,6 +1,6 @@
 import { Decoder } from 'io-ts'
 import * as E from 'fp-ts/Either'
-import { DecodeError } from '@/server/errors'
+import { DBDecodeError, DecodeError } from '@/server/errors'
 import { Lazy, pipe } from 'fp-ts/function'
 import * as TE from 'fp-ts/TaskEither'
 import { DatabaseError } from 'pg'
@@ -11,5 +11,11 @@ export const decodeWith = <A, B>(decoder: Decoder<A, B>) => (val: A): E.Either<D
     E.mapLeft(errors => new DecodeError(errors))
   )
 
+export const decodeWithDb = <A, B>(decoder: Decoder<A, B>) => (val: A): E.Either<DBDecodeError, B> =>
+  pipe(
+    decoder.decode(val),
+    E.mapLeft(errors => new DBDecodeError(errors))
+  )
+
 export const tryCatchDb = <A>(fa: Lazy<Promise<A>>): TE.TaskEither<DatabaseError, A> =>
   TE.tryCatch(fa, err => err as DatabaseError)
diff --git a/tests/server/product.spec.ts b/tests/server/product.spec.ts
index 78c003c5e1c8b4ba48eb210cd8b571a8ad99e3b3..1cb43a224501b28b4a5bdc7c2df15d6849353444 100644
--- a/tests/server/product.spec.ts
+++ b/tests/server/product.spec.ts
@@ -6,6 +6,7 @@ import request from 'supertest'
 import PgMock2 from 'pgmock2'
 
 import { sql } from '@/storage/productRepo'
+import { sql as inventorySql } from '@/storage/inventoryRepo'
 import { Client } from 'pg'
 import makeServer from '@/server'
 import { expectedProductApiAnswer, getProductSqlAnswer, product } from '@tests/fixtures/products'
@@ -102,5 +103,42 @@ describe('product routes', () => {
             expect(response.body).to.eql(expectedProductApiAnswer(productId)))
       })
     })
+
+    describe('POST sell', () => {
+      it('should return the updated product on sell', async () => {
+        const pg = new PgMock2()
+        const productId = 1
+
+        pg.add('BEGIN', [], {})
+        pg.add('COMMIT', [], {})
+        pg.add('ROLLBACK', [], {})
+
+        pg.add(inventorySql.updateInventory, ['number', 'number'], {
+          rowCount: 1
+        })
+
+        pg.add(inventorySql.selectArt, ['number'], {
+          rowCount: 1,
+          rows: [{
+            stock: 20,
+            name: 'an article',
+            art_id: 1
+          }]
+        })
+
+        pg.add(sql.getProduct, ['number'], {
+          rowCount: 4,
+          rows: getProductSqlAnswer(productId)
+        })
+
+        const server = await serveWithPG(pg)
+        return request(server)
+          .post(`/products/${productId}/sell`)
+          .send({ amount: 2 })
+          .expect(200)
+          .expect((response) =>
+            expect(response.body).to.eql(expectedProductApiAnswer(productId)))
+      })
+    })
   })
 })