diff --git a/robot-server/robot_server/protocols/protocol_store.py b/robot-server/robot_server/protocols/protocol_store.py
index d2d3574856a..a080276594b 100644
--- a/robot-server/robot_server/protocols/protocol_store.py
+++ b/robot-server/robot_server/protocols/protocol_store.py
@@ -188,7 +188,10 @@ def get(self, protocol_id: str) -> ProtocolResource:
 
     @lru_cache(maxsize=_CACHE_ENTRIES)
     def get_all(self) -> List[ProtocolResource]:
-        """Get all protocols currently saved in this store."""
+        """Get all protocols currently saved in this store.
+
+        Results are ordered from first-added to last-added.
+        """
         all_sql_resources = self._sql_get_all()
         return [
             ProtocolResource(
@@ -297,17 +300,16 @@ def get_referencing_run_ids(self, protocol_id: str) -> List[str]:
 
         See the `runs` module for information about runs.
 
-        Results are ordered with the oldest-added (NOT created) run first.
+        Results are ordered with the oldest run first.
         """
-        select_referencing_run_ids = sqlalchemy.select(run_table.c.id).where(
-            run_table.c.protocol_id == protocol_id
+        select_referencing_run_ids = (
+            sqlalchemy.select(run_table.c.id)
+            .where(run_table.c.protocol_id == protocol_id)
+            .order_by(sqlite_rowid)
         )
 
         with self._sql_engine.begin() as transaction:
-            referencing_run_ids = (
-                transaction.execute(select_referencing_run_ids).scalars().all()
-            )
-        return referencing_run_ids
+            return transaction.execute(select_referencing_run_ids).scalars().all()
 
     def _sql_insert(self, resource: _DBProtocolResource) -> None:
         statement = sqlalchemy.insert(protocol_table).values(
@@ -334,7 +336,7 @@ def _sql_get_all(self) -> List[_DBProtocolResource]:
     def _sql_get_all_from_engine(
         sql_engine: sqlalchemy.engine.Engine,
     ) -> List[_DBProtocolResource]:
-        statement = sqlalchemy.select(protocol_table)
+        statement = sqlalchemy.select(protocol_table).order_by(sqlite_rowid)
         with sql_engine.begin() as transaction:
             all_rows = transaction.execute(statement).all()
         return [_convert_sql_row_to_dataclass(sql_row=row) for row in all_rows]
diff --git a/robot-server/robot_server/protocols/router.py b/robot-server/robot_server/protocols/router.py
index 09eaedea1f9..1b046fcc2b3 100644
--- a/robot-server/robot_server/protocols/router.py
+++ b/robot-server/robot_server/protocols/router.py
@@ -108,7 +108,10 @@ class ProtocolLinks(BaseModel):
 
     referencingRuns: List[RunLink] = Field(
         ...,
-        description="Links to runs that reference the protocol.",
+        description=(
+            "Links to runs that reference the protocol,"
+            " in order from the oldest run to the newest run."
+        ),
     )
 
 
@@ -129,11 +132,18 @@ class ProtocolLinks(BaseModel):
         When too many protocols already exist, old ones will be automatically deleted
         to make room for the new one.
         A protocol will never be automatically deleted if there's a run
-        referring to it, though.
+        referring to it, though. (See the `/runs/` endpoints.)
+
+        If you upload the exact same set of files multiple times, the first protocol
+        resource will be returned instead of creating duplicate ones.
+
+        When a new protocol resource is created, an analysis is started for it.
+        See the `/protocols/{id}/analyses/` endpoints.
         """
     ),
     status_code=status.HTTP_201_CREATED,
     responses={
+        status.HTTP_200_OK: {"model": SimpleBody[Protocol]},
         status.HTTP_201_CREATED: {"model": SimpleBody[Protocol]},
         status.HTTP_422_UNPROCESSABLE_ENTITY: {
             "model": ErrorBody[Union[ProtocolFilesInvalid, ProtocolRobotTypeMismatch]]
@@ -280,6 +290,7 @@ async def create_protocol(
     protocols_router.get,
     path="/protocols",
     summary="Get uploaded protocols",
+    description="Return all stored protocols, in order from first-uploaded to last-uploaded.",
     responses={status.HTTP_200_OK: {"model": SimpleMultiBody[Protocol]}},
 )
 async def get_protocols(
diff --git a/robot-server/robot_server/runs/router/base_router.py b/robot-server/robot_server/runs/router/base_router.py
index 3edd9a342ba..b44dba2a17a 100644
--- a/robot-server/robot_server/runs/router/base_router.py
+++ b/robot-server/robot_server/runs/router/base_router.py
@@ -202,7 +202,9 @@ async def create_run(
     base_router.get,
     path="/runs",
     summary="Get all runs",
-    description="Get a list of all active and inactive runs.",
+    description=(
+        "Get a list of all active and inactive runs, in order from oldest to newest."
+    ),
     responses={
         status.HTTP_200_OK: {"model": MultiBody[Run, AllRunsLinks]},
     },
diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py
index a8767ca5482..093c6ec925b 100644
--- a/robot-server/robot_server/runs/router/commands_router.py
+++ b/robot-server/robot_server/runs/router/commands_router.py
@@ -235,6 +235,9 @@ async def create_run_command(
     summary="Get a list of all protocol commands in the run",
     description=(
         "Get a list of all commands in the run and their statuses. "
+        "\n\n"
+        "The commands are returned in order from oldest to newest."
+        "\n\n"
         "This endpoint returns command summaries. Use "
         "`GET /runs/{runId}/commands/{commandId}` to get all "
         "information available for a given command."
diff --git a/robot-server/robot_server/runs/run_data_manager.py b/robot-server/robot_server/runs/run_data_manager.py
index 74f1d8a4db9..0fc6ee2b731 100644
--- a/robot-server/robot_server/runs/run_data_manager.py
+++ b/robot-server/robot_server/runs/run_data_manager.py
@@ -195,8 +195,10 @@ def get_run_loaded_labware_definitions(
     def get_all(self, length: Optional[int]) -> List[Run]:
         """Get current and stored run resources.
 
-        Returns:
-            All run resources.
+        Results are ordered from oldest to newest.
+
+        Params:
+            length: If `None`, return all runs. Otherwise, return the newest n runs.
         """
         return [
             _build_run(
diff --git a/robot-server/robot_server/runs/run_models.py b/robot-server/robot_server/runs/run_models.py
index 7f435e054f0..ee85902440a 100644
--- a/robot-server/robot_server/runs/run_models.py
+++ b/robot-server/robot_server/runs/run_models.py
@@ -73,7 +73,7 @@ class Run(ResourceModel):
     )
     actions: List[RunAction] = Field(
         ...,
-        description="Client-initiated run control actions.",
+        description="Client-initiated run control actions, ordered oldest to newest.",
     )
     errors: List[ErrorOccurrence] = Field(
         ...,
diff --git a/robot-server/robot_server/runs/run_store.py b/robot-server/robot_server/runs/run_store.py
index 319e9340943..849b82dafea 100644
--- a/robot-server/robot_server/runs/run_store.py
+++ b/robot-server/robot_server/runs/run_store.py
@@ -104,8 +104,10 @@ def update_run_state(
         select_run_resource = sqlalchemy.select(*_run_columns).where(
             run_table.c.id == run_id
         )
-        select_actions = sqlalchemy.select(action_table).where(
-            action_table.c.run_id == run_id
+        select_actions = (
+            sqlalchemy.select(action_table)
+            .where(action_table.c.run_id == run_id)
+            .order_by(sqlite_rowid)
         )
 
         with self._sql_engine.begin() as transaction:
@@ -220,8 +222,10 @@ def get(self, run_id: str) -> RunResource:
             run_table.c.id == run_id
         )
 
-        select_actions = sqlalchemy.select(action_table).where(
-            action_table.c.run_id == run_id
+        select_actions = (
+            sqlalchemy.select(action_table)
+            .where(action_table.c.run_id == run_id)
+            .order_by(sqlite_rowid)
         )
 
         with self._sql_engine.begin() as transaction:
@@ -237,26 +241,28 @@ def get(self, run_id: str) -> RunResource:
     def get_all(self, length: Optional[int] = None) -> List[RunResource]:
         """Get all known run resources.
 
-        Returns:
-            All stored run entries.
+        Results are ordered from oldest to newest.
+
+        Params:
+            length: If `None`, return all runs. Otherwise, return the newest n runs.
         """
-        select_runs = sqlalchemy.select(*_run_columns)
         select_actions = sqlalchemy.select(action_table).order_by(sqlite_rowid.asc())
         actions_by_run_id = defaultdict(list)
 
         with self._sql_engine.begin() as transaction:
             if length is not None:
                 select_runs = (
-                    select_runs.limit(length)
+                    sqlalchemy.select(*_run_columns)
                     .order_by(sqlite_rowid.desc())
                     .limit(length)
                 )
                 # need to select the last inserted runs and return by asc order
                 runs = list(reversed(transaction.execute(select_runs).all()))
             else:
-                runs = transaction.execute(
-                    select_runs.order_by(sqlite_rowid.asc())
-                ).all()
+                select_runs = sqlalchemy.select(*_run_columns).order_by(
+                    sqlite_rowid.asc()
+                )
+                runs = transaction.execute(select_runs).all()
 
             actions = transaction.execute(select_actions).all()