diff --git a/registrar/backend/abc.py b/registrar/backend/abc.py
index 631c032282ef874ee05a88ef76c098c5c06675de..f1a42f700df8a3871bb3d51550e4d75c6ff65bad 100644
--- a/registrar/backend/abc.py
+++ b/registrar/backend/abc.py
@@ -1,18 +1,39 @@
 from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Optional
 
 if TYPE_CHECKING:
     from pystac import Item
 
 
-class Backend(ABC):
+class ItemBackend(ABC):
     def __repr__(self) -> str:
         return f"<{self.__class__.__name__}>"
 
     @abstractmethod
-    def register(self, item: 'Item'):
+    def item_exists(self, item: 'Item') -> bool:
         ...
 
     @abstractmethod
-    def deregister(self, item: 'Item') -> str:
+    def register_item(self, item: 'Item'):
+        ...
+
+    @abstractmethod
+    def deregister_identifier(self, identifier: str) -> Optional[str]:
+        ...
+
+
+class PathBackend(ABC):
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}>"
+
+    @abstractmethod
+    def path_exists(self, path: str) -> bool:
+        ...
+
+    @abstractmethod
+    def register_path(self, path: str):
+        ...
+
+    @abstractmethod
+    def deregister_path(self, path: str) -> Optional[str]:
         ...
diff --git a/registrar/backend/eoxserver.py b/registrar/backend/eoxserver.py
index 573e3d842c115e860a99647c5fb4ca3d7840f95a..b757aef03ac9e173c0a4b6162b66ded249068a59 100644
--- a/registrar/backend/eoxserver.py
+++ b/registrar/backend/eoxserver.py
@@ -8,7 +8,7 @@ Contains implementations for different backends where data may be registered
 import os
 import sys
 import logging
-from typing import List, TYPE_CHECKING, TypedDict
+from typing import List, TYPE_CHECKING, Optional, TypedDict
 import json
 from urllib.parse import urlparse
 
@@ -86,12 +86,12 @@ class EOxServerBackend(Backend):
             f"<{self.__class__.__name__} instance_name ={self.instance_name}>"
         )
 
-    def exists(self, source: Source, item: 'Item'):
+    def item_exists(self, source: Source, item: 'Item') -> bool:
         """Checks whether the item exists in the given source
 
         Args:
             source (Source): source of the data
-            item (Context): item to be checked
+            item (Item): item to be checked
 
         Returns:
             (bool): true if exists, false otherwise
@@ -253,7 +253,7 @@ class EOxServerBackend(Backend):
         return product
 
     @transaction.atomic
-    def register(self, source: Source, item: 'Item', replace: bool):
+    def register_item(self, source: Source, item: 'Item', replace: bool):
         """Registers the item to the endpoint
 
         Args:
@@ -308,17 +308,17 @@ class EOxServerBackend(Backend):
             models.collection_insert_eo_object(collection, product)
 
     @transaction.atomic
-    def deregister(self, item: 'Item') -> str:
+    def deregister_identifier(self, identifier: str) -> Optional[str]:
         """Attempts to deregister item
 
         Args:
-            item (Item): item to be deregistered
+            identifier (str): identifier to be deregistered
         """
         # ugly, ugly hack
         from eoxserver.resources.coverages import models
         try:
-            logger.info(f"Deregistering product '{item.id}'")
-            product = models.Product.objects.get(identifier=item.ideidntifier)
+            logger.info(f"Deregistering product '{identifier}'")
+            product = models.Product.objects.get(identifier=identifier)
             grids = list(models.Grid.objects.filter(
                 coverage__parent_product=product
             ))
@@ -335,14 +335,14 @@ class EOxServerBackend(Backend):
                     grid.delete()
         except models.Product.DoesNotExist:
             logger.info(
-                f"No product with identifier '{item.id}' found"
+                f"No product with identifier '{identifier}' found"
             )
             # no product found with that id
             # return empty list
-            return []
+            return None
 
         logger.info(
-            f"Deregistered product with identifier '{item.id}'"
+            f"Deregistered product with identifier '{identifier}'"
         )
         # return the deleted identifier
-        return item.id
+        return identifier
diff --git a/registrar/cli.py b/registrar/cli.py
index 9fba4bbdf40b3096bd74631b4c1ad3b345bb8c43..456fe357530fbacd2daec5a526a3ba25057bf40a 100644
--- a/registrar/cli.py
+++ b/registrar/cli.py
@@ -5,9 +5,13 @@ import click
 import yaml
 import jsonschema
 
-from .registrar import register_file, deregister_file, deregister_identifier
+from .registrar import (
+    deregister_path, register_item, register_path, deregister_item,
+    deregister_identifier
+)
 from .daemon import run_daemon
 from .config import load_config
+from .utils import import_by_path
 
 
 def setup_logging(debug=False):
@@ -47,8 +51,8 @@ def cli():
 
 @cli.command(help='Run the registrar daemon, attaching to a Redis queue')
 @click.option('--config-file', type=click.File('r'))
-@click.option('--validate/--no-validate', default=False)
-@click.option('--replace/--no-replace', default=False)
+@click.option('--validate', is_flag=True)
+@click.option('--replace', is_flag=True)
 @click.option('--host', type=str)
 @click.option('--port', type=int)
 @click.option('--listen-queue', type=str)
@@ -56,17 +60,49 @@ def cli():
 @click.option('--progress-set', type=str)
 @click.option('--failure-set', type=str)
 @click.option('--success-set', type=str)
-@click.option('--debug/--no-debug', default=False)
+@click.option('--extra/-e', type=str, multiple=True, default=[])
+@click.option('--debug', is_flag=True)
 def daemon(config_file=None, validate=False, replace=False, host=None,
            port=None, listen_queue=None, progress_set=None,
            failure_set=None, success_set=None,
-           deregister_queue=None, debug=False):
+           deregister_queue=None, extra=None, debug=False):
+    """ Run the registrar daemon to listen on the given queues
+        and execute the (de-)registrations commands.
+
+        Examples:
+
+        \b
+            registrar daemon --config-file config.yaml \
+                --validate --replace \
+                --host redis --port 6379 \
+                --listen-queue register \
+                --deregister-queue deregister \
+                --progress-set register_progress \
+                --failure-set register_failure \
+                --success-set register_success \
+                --extra some_queue=path.to.handler \
+                --extra some_other_queue=path.to.another.handler \
+                --debug
+    """
     setup_logging(debug)
     config = load_config(config_file)
     if validate:
         validate_config(config)
+
+    handlers = {}
+    if listen_queue:
+        handlers[listen_queue] = register_item
+
+    if deregister_queue:
+        handlers[deregister_queue] = deregister_item
+
+    for extra_handler in extra:
+        queue, _, path = extra_handler.partition('=')
+        handler = import_by_path(path.strip())
+        handlers[queue.strip()] = handler
+
     run_daemon(
-        config, replace, host, port, listen_queue, progress_set,
+        config, replace, host, port, handlers, progress_set,
         failure_set, success_set, deregister_queue
     )
 
@@ -74,25 +110,29 @@ def daemon(config_file=None, validate=False, replace=False, host=None,
 @cli.command(help='Run a single, one-off registration')
 @click.argument('item', type=str)
 @click.option('--config-file', type=click.File('r'))
-@click.option('--validate/--no-validate', default=False)
-@click.option('--replace/--no-replace', default=False)
-@click.option('--debug/--no-debug', default=False)
-def register(item, config_file=None, validate=False, replace=False,
-             debug=False):
+@click.option('--is-path', is_flag=True)
+@click.option('--validate', is_flag=True)
+@click.option('--replace', is_flag=True)
+@click.option('--debug', is_flag=True)
+def register(item, is_path=False, config_file=None, validate=False,
+             replace=False, debug=False):
     setup_logging(debug)
     config = load_config(config_file)
     if validate:
         validate_config(config)
 
-    register_file(config, item, replace)
+    if is_path:
+        register_item(config, item, replace)
+    else:
+        register_path(config, item, replace)
 
 
 @cli.command(help='Run a single, one-off de-registration')
 @click.argument('--path', type=str)
 @click.argument('--identifier', type=str)
 @click.option('--config-file', type=click.File('r'))
-@click.option('--validate/--no-validate', default=False)
-@click.option('--debug/--no-debug', default=False)
+@click.option('--validate', is_flag=True)
+@click.option('--debug', is_flag=True)
 def deregister(file_path=None, identifier=None, config_file=None,
                validate=False, debug=False):
     setup_logging(debug)
@@ -101,7 +141,7 @@ def deregister(file_path=None, identifier=None, config_file=None,
         validate_config(config)
 
     if file_path:
-        deregister_file(config, file_path)
+        deregister_path(config, file_path)
     elif identifier:
         deregister_identifier(config, identifier)
 
diff --git a/registrar/registrar.py b/registrar/registrar.py
index ad27fe3b03c5a231aa544ed5f8477995dcbf2e5e..cf466aa78330521a66e6b6627ecc89b288d33dbd 100644
--- a/registrar/registrar.py
+++ b/registrar/registrar.py
@@ -6,35 +6,33 @@ Contains all functions relevant for registration
 
 """
 import logging
-from typing import List
+from typing import Optional
 import json
 
-from .source import get_source
-from .backend import get_backends
+from pystac import Item
+
+from .source import get_source, get_source_from_path
+from .backend import get_backends, get_path_backends
 from .exceptions import RegistrationError
 from .utils import import_by_path
-from pystac import Item
 
 logger = logging.getLogger(__name__)
 
 
-def register_file(config: dict, item: str, replace: bool = False):
+def register_item(config: dict, value: str, replace: bool = False):
     """Handle registration of a stac item
 
     Args:
         config (dict): configuration of the registrar
-        item (str): STAC item to register
+        value (str): encoded STAC item to register
         replace (bool, optional): replace the file in the endpoint or not.
                                   Defaults to False.
 
     Raises:
-        RegistrationError: Raised if file is already registered and replace is
+        RegistrationError: Raised if item is already registered and replace is
                            not turned on
-
-    Returns:
-        list: List of contexts
     """
-    item = Item.from_dict(json.loads(item))
+    item = Item.from_dict(json.loads(value))
     source = get_source(config, item)
     logger.info(f"Handling '{item!r}'.")
 
@@ -43,7 +41,7 @@ def register_file(config: dict, item: str, replace: bool = False):
 
     try:
         for backend in get_backends(config):
-            if backend.exists(source, item):
+            if backend.item_exists(source, item):
                 if replace:
                     logger.info(f"Replacing '{item!r}'.")
                     backend.register(source, item, replace=True)
@@ -67,49 +65,102 @@ def register_file(config: dict, item: str, replace: bool = False):
             f"'{item!r}'"
         )
 
-    return item
 
+def register_path(config: dict, path: str, replace: bool = False):
+    """Handle registration of a path
+
+    Args:
+        config (dict): configuration of the registrar
+        path (str): path to register
+        replace (bool, optional): replace the file in the endpoint or not.
+                                  Defaults to False.
+
+    Raises:
+        RegistrationError: Raised if file is already registered and replace is
+                           not turned on
+    """
 
-def deregister_identifier(config, identifier: str):
+    source = get_source_from_path(config, path)
+    logger.info(f"Handling '{path!r}'.")
+
+    for pre_handler in get_path_pre_handlers(config):
+        pre_handler(config, path)
+
+    try:
+        for backend in get_path_backends(config):
+            if backend.path_exists(source, path):
+                if replace:
+                    logger.info(f"Replacing '{path!r}'.")
+                    backend.register_path(source, path, replace=True)
+                else:
+                    raise RegistrationError(
+                        f'Path {path!r} is already registered'
+                    )
+            else:
+                logger.info(f"Registering '{path!r}'.")
+                backend.register_path(source, path, replace=False)
+    except Exception as e:
+        for error_handler in get_path_error_handlers(config):
+            error_handler(config, path, e)
+        raise
+    else:
+        for post_handler in get_path_post_handlers(config):
+            post_handler(config, path)
+
+        logger.info(
+            f"Successfully {'replaced' if replace else 'registered'} "
+            f"'{path!r}'"
+        )
+
+
+def deregister_item(config: dict, value: str, _: bool = False):
+    item = Item.from_dict(json.loads(value))
+    logger.info(f"Handling deregistration for '{item!r}'.")
+    return _deregister_identifier(config, item.id, item)
+
+
+def deregister_identifier(config, identifier: str, _: bool = False):
     logger.info(f"Handling deregistration of identifier '{identifier}'.")
-    contexts = [Context(
-        identifier=identifier,
-        path=None,
-        scheme=None
-    )]
-    return _deregister_contexts(config, contexts)
+    return _deregister_identifier(config, identifier)
+
+
+def _deregister_identifier(config, identifier: str,
+                           item: Optional[dict] = None):
 
+    for pre_handler in get_deregister_pre_handlers(config):
+        pre_handler(config, identifier, item)
 
-def deregister_file(config: dict, path: str):
-    logger.info(f"Handling deregistration of path '{path}'.")
-    source = get_source(config, path)
-    scheme = get_scheme(config, path)
-    contexts = scheme.get_context(source, path)
-    return _deregister_contexts(config, contexts)
+    try:
+        for backend in get_backends(config):
+            backend.deregister_identifier(identifier)
+    except Exception as e:
+        for error_handler in get_deregister_error_handlers(config):
+            error_handler(config, identifier, item, e)
+        raise
+    else:
+        for post_handler in get_deregister_post_handlers(config):
+            post_handler(config, identifier, item)
 
+        logger.info(f"Successfully deregistered '{identifier}'")
 
-def _deregister_contexts(config, items: List[Item]):
-    if isinstance(items, Item):
-        items = [items]
 
-    for item in items:
-        for pre_handler in get_deregister_pre_handlers(config):
-            pre_handler(config, context, context)
+def deregister_path(config, path: str, _: bool = False):
 
-        try:
-            for backend in get_backends(config, context):
-                backend.deregister(context)
-        except Exception as e:
-            for error_handler in get_deregister_error_handlers(config):
-                error_handler(config, context, context, e)
-            raise
-        else:
-            for post_handler in get_deregister_post_handlers(config):
-                post_handler(config, context, context)
+    for pre_handler in get_deregister_path_pre_handlers(config):
+        pre_handler(config, path)
 
-            logger.info(f"Successfully deregistered '{context.identifier}'")
+    try:
+        for backend in get_path_backends(config):
+            backend.deregister_path(path)
+    except Exception as e:
+        for error_handler in get_deregister_path_error_handlers(config):
+            error_handler(config, path, e)
+        raise
+    else:
+        for post_handler in get_deregister_path_post_handlers(config):
+            post_handler(config, path)
 
-    return items
+        logger.info(f"Successfully deregistered '{path}'")
 
 
 def _get_handlers(config, name):
@@ -138,6 +189,18 @@ def get_error_handlers(config):
     return _get_handlers(config, 'error_handlers')
 
 
+def get_path_pre_handlers(config):
+    return _get_handlers(config, 'path_pre_handlers')
+
+
+def get_path_post_handlers(config):
+    return _get_handlers(config, 'path_post_handlers')
+
+
+def get_path_error_handlers(config):
+    return _get_handlers(config, 'path_error_handlers')
+
+
 def get_deregister_pre_handlers(config):
     return _get_handlers(config, 'deregister_pre_handlers')
 
@@ -148,3 +211,15 @@ def get_deregister_post_handlers(config):
 
 def get_deregister_error_handlers(config):
     return _get_handlers(config, 'deregister_error_handlers')
+
+
+def get_deregister_path_pre_handlers(config):
+    return _get_handlers(config, 'deregister_path_pre_handlers')
+
+
+def get_deregister_path_post_handlers(config):
+    return _get_handlers(config, 'deregister_path_post_handlers')
+
+
+def get_deregister_path_error_handlers(config):
+    return _get_handlers(config, 'deregister_path_error_handlers')
diff --git a/registrar/source.py b/registrar/source.py
index 7cf274f7e5f555a3e2dfcf31824c63e82276993f..74bf74f38eefee5c8339e1aa49022a49006b69f2 100644
--- a/registrar/source.py
+++ b/registrar/source.py
@@ -335,7 +335,7 @@ def get_source(config: dict, item: 'Item') -> Source:
             break
     else:
         # no source found
-        raise RegistrationError(f'Could not find a suitable source for {item!r}')
+        cfg_source['name'],
 
     return SOURCE_TYPES[cfg_source['type']](
         cfg_source['name'],