diff --git a/gso/db/models.py b/gso/db/models.py
index 02d8c59ce72e6e1ecb47bab5c513853a9f59551b..8b29d4e772136bd29ec180e7e783a13c9ddf1e84 100644
--- a/gso/db/models.py
+++ b/gso/db/models.py
@@ -33,7 +33,7 @@ class PartnerTable(BaseModel):
     __tablename__ = "partners"
 
     partner_id = mapped_column(String, server_default=text("uuid_generate_v4"), primary_key=True)
-    name = mapped_column(String, unique=True, nullable=True)
+    name = mapped_column(String, unique=True, nullable=False)
     email = mapped_column(String, unique=True, nullable=False)
     partner_type = mapped_column(Enum(PartnerType), nullable=False)
 
diff --git a/gso/migrations/versions/2024-07-29_41fd1ae225aq_create_partner_task.py b/gso/migrations/versions/2024-07-29_41fd1ae225aq_create_partner_task.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb7ed8792f4df2531aefb62acff92d93ba3be32a
--- /dev/null
+++ b/gso/migrations/versions/2024-07-29_41fd1ae225aq_create_partner_task.py
@@ -0,0 +1,43 @@
+"""Add task_validate_geant_products.
+
+Revision ID: 41fd1ae225aq
+Revises: 31fd1ae8d5bb
+Create Date: 2024-07-29 17:11:00.00000
+
+"""
+
+from uuid import uuid4
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "41fd1ae225aq"
+down_revision = "31fd1ae8d5bb"
+branch_labels = None
+depends_on = None
+
+workflows = [
+    {"name": "task_create_partners", "description": "Create partners", "workflow_id": uuid4(), "target": "SYSTEM"},
+]
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    for workflow in workflows:
+        conn.execute(
+            sa.text(
+                "INSERT INTO workflows VALUES (:workflow_id, :name, :target, :description, now()) ON CONFLICT DO NOTHING"
+            ),
+            workflow,
+        )
+
+    op.alter_column('partners', 'email',
+                    existing_type=sa.String(),
+                    nullable=False)
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    for workflow in workflows:
+        conn.execute(sa.text("DELETE FROM workflows WHERE name = :name"), {"name": workflow["name"]})
diff --git a/gso/schema/__init__.py b/gso/schema/__init__.py
deleted file mode 100644
index 20b21e2c5736e9d2890482561fd61bc06a84e3c9..0000000000000000000000000000000000000000
--- a/gso/schema/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""It is used to group the schema files together as a package."""
diff --git a/gso/schema/partner.py b/gso/schema/partner.py
deleted file mode 100644
index b1c58c2cf91bf544501f6b2e316117b8b83a70c9..0000000000000000000000000000000000000000
--- a/gso/schema/partner.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""Partner schema module."""
-
-from datetime import datetime
-from uuid import uuid4
-
-from pydantic import BaseModel, ConfigDict, EmailStr, Field
-
-from gso.db.models import PartnerType
-
-
-class PartnerCreate(BaseModel):
-    """Partner create schema."""
-
-    partner_id: str = Field(default_factory=lambda: str(uuid4()))
-    name: str
-    email: EmailStr | None = None
-    as_number: str | None = None
-    as_set: str | None = None
-    route_set: str | None = None
-    black_listed_as_sets: list[str] | None = None
-    additional_routers: list[str] | None = None
-    additional_bgp_speakers: list[str] | None = None
-    partner_type: PartnerType
-    created_at: datetime = Field(default_factory=lambda: datetime.now().astimezone())
-    updated_at: datetime = Field(default_factory=lambda: datetime.now().astimezone())
-    model_config = ConfigDict(from_attributes=True)
diff --git a/gso/services/partners.py b/gso/services/partners.py
index 6c425bdad2432634d27780c85541bd9a02b94b7f..8221d66dba837518a4bbdd6df3b6deb5c6bd1fcd 100644
--- a/gso/services/partners.py
+++ b/gso/services/partners.py
@@ -1,12 +1,51 @@
 """A module that returns the partners available in :term:`GSO`."""
 
+from datetime import datetime
 from typing import Any
+from uuid import uuid4
 
 from orchestrator.db import db
-from sqlalchemy.exc import NoResultFound, SQLAlchemyError
+from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
+from sqlalchemy.exc import NoResultFound
 
-from gso.db.models import PartnerTable
-from gso.schema.partner import PartnerCreate
+from gso.db.models import PartnerTable, PartnerType
+
+
+class PartnerCreate(BaseModel):
+    """Partner create schema."""
+
+    partner_id: str = Field(default_factory=lambda: str(uuid4()))
+    name: str
+    email: EmailStr
+    partner_type: PartnerType
+    as_number: str | None = None
+    as_set: str | None = None
+    route_set: str | None = None
+    black_listed_as_sets: list[str] | None = None
+    additional_routers: list[str] | None = None
+    additional_bgp_speakers: list[str] | None = None
+
+    created_at: datetime = Field(default_factory=lambda: datetime.now().astimezone())
+    updated_at: datetime = Field(default_factory=lambda: datetime.now().astimezone())
+    model_config = ConfigDict(from_attributes=True)
+
+    @field_validator("name")
+    def validate_name(cls, name: str) -> str:
+        """Validate name for duplication."""
+        if filter_partners_by_name(name=name):
+            msg = "Partner with this name already exists."
+            raise ValueError(msg)
+
+        return name
+
+    @field_validator("email")
+    def validate_email(cls, email: str) -> EmailStr:
+        """Validate email input."""
+        if filter_partners_by_email(email=email):
+            msg = "Partner with this email already exists."
+            raise ValueError(msg)
+
+        return email
 
 
 class PartnerNotFoundError(Exception):
@@ -22,13 +61,25 @@ def get_all_partners() -> list[dict]:
 def get_partner_by_name(name: str) -> dict[str, Any]:
     """Try to get a partner by their name."""
     try:
-        partner = PartnerTable.query.filter(PartnerTable.name == name).one()
+        partner = db.session.query(PartnerTable).filter(PartnerTable.name == name).one()
         return partner.__json__()
     except NoResultFound as e:
         msg = f"partner {name} not found"
         raise PartnerNotFoundError(msg) from e
 
 
+def filter_partners_by_name(name: str) -> list[dict[str, Any]] | None:
+    """Filter the list of partners by name."""
+    partners = db.session.query(PartnerTable).filter(PartnerTable.name == name).all()
+    return [partner.__json__() for partner in partners] if partners else None
+
+
+def filter_partners_by_email(email: str) -> list[dict[str, Any]] | None:
+    """Filter the list of partners by email."""
+    partners = db.session.query(PartnerTable).filter(PartnerTable.email == email).all()
+    return [partner.__json__() for partner in partners] if partners else None
+
+
 def create_partner(
     partner_data: PartnerCreate,
 ) -> dict:
@@ -37,31 +88,8 @@ def create_partner(
     :param partner_data: Partner data validated by Pydantic schema.
     :return: JSON representation of the created partner.
     """
-    try:
-        new_partner = PartnerTable(**partner_data.model_dump())
-
-        db.session.add(new_partner)
-        db.session.commit()
-
-        return new_partner.__json__()
-    except SQLAlchemyError:
-        db.session.rollback()
-        raise
-    finally:
-        db.session.close()
+    new_partner = PartnerTable(**partner_data.model_dump())
+    db.session.add(new_partner)
+    db.session.commit()
 
-
-def delete_partner_by_name(name: str) -> None:
-    """Delete a partner by their name."""
-    try:
-        partner = PartnerTable.query.filter(PartnerTable.name == name).one()
-        db.session.delete(partner)
-        db.session.commit()
-    except NoResultFound as e:
-        msg = f"partner {name} not found"
-        raise PartnerNotFoundError(msg) from e
-    except SQLAlchemyError:
-        db.session.rollback()
-        raise
-    finally:
-        db.session.close()
+    return new_partner.__json__()
diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json
index 81af4e6b786d2b92c132ce9aaf894a65b5e58de9..257be4de1df2e0c81cb0b5ebbaed56539d217cb5 100644
--- a/gso/translations/en-GB.json
+++ b/gso/translations/en-GB.json
@@ -67,6 +67,7 @@
         "import_opengear": "NOT FOR HUMANS -- Finalize import into an OpenGear",
         "validate_iptrunk": "Validate IP Trunk configuration",
         "validate_router": "Validate router configuration",
-        "task_validate_geant_products": "Validation task for GEANT products"
+        "task_validate_geant_products": "Validation task for GEANT products",
+        "task_create_partners": "Create partner task"
     }
 }
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index b6072c1c6bae54d2d6af93abdba533655902957c..9e3cfc4a169075f8999d48d5a5ceeabd6907c7d5 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -68,3 +68,4 @@ LazyWorkflowInstance("gso.workflows.opengear.import_opengear", "import_opengear"
 
 #  Tasks
 LazyWorkflowInstance("gso.workflows.tasks.validate_geant_products", "task_validate_geant_products")
+LazyWorkflowInstance("gso.workflows.tasks.create_partners", "task_create_partners")
diff --git a/gso/workflows/tasks/create_partners.py b/gso/workflows/tasks/create_partners.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2363fcb51aeeffe74b369d1391161b90d3873a4
--- /dev/null
+++ b/gso/workflows/tasks/create_partners.py
@@ -0,0 +1,95 @@
+"""A creation workflow that create a partner."""
+
+from typing import Annotated
+
+from orchestrator.forms import FormPage
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, State
+from orchestrator.workflow import StepList, begin, done, step, workflow
+from pydantic import AfterValidator, ConfigDict, EmailStr, field_validator
+from pydantic_forms.validators import validate_unique_list
+
+from gso.db.models import PartnerType
+from gso.services.partners import PartnerCreate, create_partner, filter_partners_by_email, filter_partners_by_name
+
+UniqueStringList = Annotated[
+    list[str],
+    AfterValidator(validate_unique_list),
+]
+
+
+def initial_input_form_generator() -> FormGenerator:
+    """Gather input from the user needed for creating a partner."""
+
+    class CreatePartnerForm(FormPage):
+        model_config = ConfigDict(title="Create a Partner")
+
+        name: str
+        email: EmailStr
+        partner_type: PartnerType
+        as_number: str | None = None
+        as_set: str | None = None
+        route_set: str | None = None
+        black_listed_as_sets: UniqueStringList
+        additional_routers: UniqueStringList
+        additional_bgp_speakers: UniqueStringList
+
+        @field_validator("name")
+        def validate_name(cls, name: str) -> str:
+            if filter_partners_by_name(name=name):
+                msg = "Partner with this name already exists."
+                raise ValueError(msg)
+
+            return name
+
+        @field_validator("email")
+        def validate_email(cls, email: str) -> EmailStr:
+            if filter_partners_by_email(email=email):
+                msg = "Partner with this email already exists."
+                raise ValueError(msg)
+
+            return email
+
+    initial_user_input = yield CreatePartnerForm
+
+    return initial_user_input.model_dump()
+
+
+@step("Save partner information to database")
+def save_partner_to_database(
+    name: str,
+    partner_type: PartnerType,
+    email: EmailStr,
+    as_number: str | None,
+    as_set: str | None,
+    route_set: str | None,
+    black_listed_as_sets: UniqueStringList | None,
+    additional_routers: UniqueStringList | None,
+    additional_bgp_speakers: UniqueStringList | None,
+) -> State:
+    """Save user input as a new partner in database."""
+    partner = create_partner(
+        partner_data=PartnerCreate(
+            name=name,
+            email=email,
+            partner_type=partner_type,
+            as_number=as_number,
+            as_set=as_set,
+            route_set=route_set,
+            black_listed_as_sets=black_listed_as_sets,
+            additional_routers=additional_routers,
+            additional_bgp_speakers=additional_bgp_speakers,
+        )
+    )
+
+    return {"created_partner": partner}
+
+
+@workflow(
+    "Create partners",
+    initial_input_form=initial_input_form_generator,
+    target=Target.SYSTEM,
+)
+def task_create_partners() -> StepList:
+    """Create a new Partner."""
+    return begin >> save_partner_to_database >> done
diff --git a/test/conftest.py b/test/conftest.py
index d450ab43e176bc08a6b5e5658b8814b30801316f..2b8317adaf3e44b59eecfe9ca92c5b8a5dec6faf 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -33,8 +33,7 @@ from starlette.testclient import TestClient
 
 from gso.db.models import PartnerType
 from gso.main import init_gso_app
-from gso.schema.partner import PartnerCreate
-from gso.services.partners import create_partner
+from gso.services.partners import PartnerCreate, create_partner
 from gso.utils.helpers import LAGMember
 from test.fixtures import (  # noqa: F401
     iptrunk_side_subscription_factory,
@@ -271,6 +270,36 @@ def geant_partner():
     return create_partner(PartnerCreate(name="GEANT-TEST", partner_type=PartnerType.GEANT, email="goat-test@geant.org"))
 
 
+@pytest.fixture(scope="session")
+def partner_factory():
+    def _create_partner(
+        name: str,
+        email: str,
+        partner_type: PartnerType,
+        as_number: str | None = None,
+        as_set: str | None = None,
+        rout_set: str | None = None,
+        black_listed_as_set: list[str] | None = None,
+        additional_routers: list[str] | None = None,
+        additional_bgp_speakers: list[str] | None = None,
+    ) -> dict:
+        return create_partner(
+            PartnerCreate(
+                name=name,
+                email=email,
+                partner_type=partner_type,
+                as_number=as_number,
+                as_set=as_set,
+                rout_set=rout_set,
+                black_listed_as_set=black_listed_as_set,
+                additional_routers=additional_routers,
+                additional_bgp_speakers=additional_bgp_speakers,
+            )
+        )
+
+    return _create_partner
+
+
 @pytest.fixture()
 def generic_resource_type_1():
     rt = ResourceTypeTable(description="Resource Type one", resource_type="rt_1")
diff --git a/test/workflows/tasks/test_create_partners.py b/test/workflows/tasks/test_create_partners.py
new file mode 100644
index 0000000000000000000000000000000000000000..7944ebf5ed2d07579ef974c3940c23ec0430c699
--- /dev/null
+++ b/test/workflows/tasks/test_create_partners.py
@@ -0,0 +1,146 @@
+import pytest
+from pydantic_forms.exceptions import FormValidationError
+
+from gso.db.models import PartnerType
+from gso.services.partners import get_partner_by_name
+from test.workflows import assert_complete, extract_state, run_workflow
+
+
+@pytest.mark.workflow()
+def test_create_partner_with_minimal_input(responses):
+    result, _, _ = run_workflow(
+        "task_create_partners",
+        [
+            {
+                "name": "GEANT-TEST-CREATION",
+                "email": "goat-test-creation@geant.org",
+                "partner_type": PartnerType.GEANT,
+                "black_listed_as_sets": [],
+                "additional_routers": [],
+                "additional_bgp_speakers": [],
+            }
+        ],
+    )
+    assert_complete(result)
+    state = extract_state(result)
+
+    partner = get_partner_by_name(state["name"])
+    assert partner["name"] == "GEANT-TEST-CREATION"
+    assert partner["email"] == "goat-test-creation@geant.org"
+    assert partner["partner_type"] == PartnerType.GEANT
+    assert partner["as_number"] is None
+    assert partner["as_set"] is None
+    assert partner["route_set"] is None
+    assert partner["black_listed_as_sets"] == []
+    assert partner["additional_routers"] == []
+    assert partner["additional_bgp_speakers"] == []
+
+
+@pytest.mark.workflow()
+def test_create_partner_with_full_input(responses):
+    result, _, _ = run_workflow(
+        "task_create_partners",
+        [
+            {
+                "name": "GEANT-TEST-CREATION",
+                "email": "goat-test-creation@geant.org",
+                "partner_type": PartnerType.GEANT,
+                "as_number": "1212",
+                "as_set": "1212",
+                "route_set": "1212",
+                "black_listed_as_sets": ["a"],
+                "additional_routers": ["b"],
+                "additional_bgp_speakers": ["c"],
+            }
+        ],
+    )
+
+    assert_complete(result)
+    state = extract_state(result)
+
+    partner = get_partner_by_name(state["name"])
+    assert partner["name"] == "GEANT-TEST-CREATION"
+    assert partner["email"] == "goat-test-creation@geant.org"
+    assert partner["partner_type"] == PartnerType.GEANT
+    assert partner["as_number"] == "1212"
+    assert partner["as_set"] == "1212"
+    assert partner["route_set"] == "1212"
+    assert partner["black_listed_as_sets"] == ["a"]
+    assert partner["additional_routers"] == ["b"]
+    assert partner["additional_bgp_speakers"] == ["c"]
+
+
+@pytest.mark.workflow()
+def test_create_partner_with_invalid_input_fails(responses, faker):
+    with pytest.raises(FormValidationError) as error:
+        run_workflow(
+            "task_create_partners",
+            [
+                {
+                    "name": "Kenneth Boyle",
+                    "email": "invalid_email",
+                    "black_listed_as_sets": ["a", "a"],
+                    "additional_routers": ["b", "b"],
+                    "additional_bgp_speakers": ["c", "c", "c"],
+                }
+            ],
+        )
+
+    assert error.value.errors == [
+        {
+            "type": "value_error",
+            "loc": ("email",),
+            "msg": (
+                "value is not a valid email address: The email address is not valid. "
+                "It must have exactly one @-sign."
+            ),
+            "input": "invalid_email",
+            "ctx": {"reason": "The email address is not valid. It must have exactly one @-sign."},
+        },
+        {
+            "type": "missing",
+            "loc": ("partner_type",),
+            "msg": "Field required",
+            "input": {
+                "name": "Kenneth Boyle",
+                "email": "invalid_email",
+                "black_listed_as_sets": ["a", "a"],
+                "additional_routers": ["b", "b"],
+                "additional_bgp_speakers": ["c", "c", "c"],
+            },
+            "url": "https://errors.pydantic.dev/2.7/v/missing",
+        },
+        {"type": "unique_list", "loc": ("black_listed_as_sets",), "msg": "List must be unique", "input": ["a", "a"]},
+        {"type": "unique_list", "loc": ("additional_routers",), "msg": "List must be unique", "input": ["b", "b"]},
+        {
+            "type": "unique_list",
+            "loc": ("additional_bgp_speakers",),
+            "msg": "List must be unique",
+            "input": ["c", "c", "c"],
+        },
+    ]
+
+
+def test_create_partner_with_duplicate_name_or_email_fails(partner_factory):
+    partner_factory(
+        name="new_name",
+        email="myemail@gmail.com",
+        partner_type=PartnerType.PRIVATE_PEER,
+    )
+
+    with pytest.raises(FormValidationError) as error:
+        run_workflow(
+            "task_create_partners",
+            [
+                {
+                    "name": "new_name",
+                    "email": "myemail@gmail.com",
+                    "black_listed_as_sets": [],
+                    "additional_routers": [],
+                    "additional_bgp_speakers": [],
+                }
+            ],
+        )
+
+    assert error.value.errors[0]["msg"] == "Partner with this name already exists."
+    assert error.value.errors[1]["msg"] == "Partner with this email already exists."
diff --git a/test/workflows/tasks/test_task_validate_products.py b/test/workflows/tasks/test_task_validate_products.py
index 0dba0817344e426db93a3603815bc669ff7fc510..b12b3a4a6b0190f7d150faf54d9c61d466f4f8de 100644
--- a/test/workflows/tasks/test_task_validate_products.py
+++ b/test/workflows/tasks/test_task_validate_products.py
@@ -4,7 +4,7 @@ from test.workflows import assert_complete, extract_state, run_workflow
 
 
 @pytest.mark.workflow()
-def test_task_validate_geant_products(responses, faker):
+def test_task_validate_geant_products(responses):
     result, _, _ = run_workflow("task_validate_geant_products", [{}])
     assert_complete(result)
     state = extract_state(result)