diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -60,12 +60,12 @@
     runGitLab ::
        => GitLabServerConfig
        -> GitLab a
-       -> IO a
+       -> IO (Either GitLabError a)
 
 For example the following project returns all GitLab projects for the
 user "joe".
 
-    myProjects <-
+    myProjects <- fromRight <$>
       runGitLab
         (defaultGitLabServer
            { url = "https://gitlab.example.com"
diff --git a/data/system-hooks/access-token1.json b/data/system-hooks/access-token1.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/access-token1.json
@@ -0,0 +1,30 @@
+{
+  "object_kind": "access_token",
+  "project": {
+    "id": 45653,
+    "name": "proj-name",
+    "description": "",
+    "web_url": "https://example.com/aaa1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:aaa1/proj-name.git",
+    "git_http_url": "https://example.com/aaa1/proj-name.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "aaa1/proj-name",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/aaa1/proj-name",
+    "url": "git@example.com:aaa1/proj-name.git",
+    "ssh_url": "git@example.com:aaa1/proj-name.git",
+    "http_url": "https://example.com/aaa1/proj-name.git"
+  },
+  "object_attributes": {
+    "user_id": 4642,
+    "created_at": "2025-11-20 03:49:49 UTC",
+    "id": 2458,
+    "name": "the_token",
+    "expires_at": "2025-12-20"
+  },
+  "event_name": "expiring_access_token",
+  "interval": "thirty_days"
+}
diff --git a/data/system-hooks/access-token2.json b/data/system-hooks/access-token2.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/access-token2.json
@@ -0,0 +1,30 @@
+{
+  "object_kind": "access_token",
+  "project": {
+    "id": 42526,
+    "name": "project-name",
+    "description": null,
+    "web_url": "https://example.com/abc1/example",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:abc1/example.git",
+    "git_http_url": "https://example.com/abc1/example.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "abc1/example",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/abc1/example",
+    "url": "git@example.com:abc1/example.git",
+    "ssh_url": "git@example.com:abc1/example.git",
+    "http_url": "https://example.com/abc1/example.git"
+  },
+  "object_attributes": {
+    "user_id": 4560,
+    "created_at": "2025-10-29 22:41:27 UTC",
+    "id": 2258,
+    "name": "abc",
+    "expires_at": "2025-11-28"
+  },
+  "event_name": "expiring_access_token",
+  "interval": "seven_days"
+}
diff --git a/data/system-hooks/access-token3.json b/data/system-hooks/access-token3.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/access-token3.json
@@ -0,0 +1,30 @@
+{
+  "object_kind": "access_token",
+  "project": {
+    "id": 28491,
+    "name": "The project",
+    "description": null,
+    "web_url": "https://example.com/aaa1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:aaa1/proj-name.git",
+    "git_http_url": "https://example.com/aaa1/proj-name.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "aaa1/proj-name",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/aaa1/proj-name",
+    "url": "git@example.com:aaa1/proj-name.git",
+    "ssh_url": "git@example.com:aaa1/proj-name.git",
+    "http_url": "https://example.com/aaa1/proj-name.git"
+  },
+  "object_attributes": {
+    "user_id": 3406,
+    "created_at": "2024-11-26 11:05:31 UTC",
+    "id": 1489,
+    "name": "some name",
+    "expires_at": "2025-11-26"
+  },
+  "event_name": "expiring_access_token",
+  "interval": "seven_days"
+}
diff --git a/data/system-hooks/access-token4.json b/data/system-hooks/access-token4.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/access-token4.json
@@ -0,0 +1,30 @@
+{
+  "object_kind": "access_token",
+  "project": {
+    "id": 28294,
+    "name": "proj-name",
+    "description": null,
+    "web_url": "https://example.com/aaa1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:aaa1/proj-name.git",
+    "git_http_url": "https://example.com/aaa1/proj-name.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "aaa1/proj-name",
+    "default_branch": "main",
+    "ci_config_path": null,
+    "homepage": "https://example.com/aaa1/proj-name",
+    "url": "git@example.com:aaa1/proj-name.git",
+    "ssh_url": "git@example.com:aaa1/proj-name.git",
+    "http_url": "https://example.com/aaa1/proj-name.git"
+  },
+  "object_attributes": {
+    "user_id": 3401,
+    "created_at": "2024-11-25 08:12:01 UTC",
+    "id": 1470,
+    "name": "aaa1",
+    "expires_at": "2025-11-25"
+  },
+  "event_name": "expiring_access_token",
+  "interval": "seven_days"
+}
diff --git a/data/system-hooks/build3.json b/data/system-hooks/build3.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/build3.json
@@ -0,0 +1,75 @@
+{
+  "object_kind": "build",
+  "ref": "main",
+  "tag": false,
+  "before_sha": "abc1",
+  "sha": "abbbccc1",
+  "retries_count": 0,
+  "build_id": 103999,
+  "build_name": "deploy-job",
+  "build_stage": "deploy",
+  "build_status": "created",
+  "build_created_at": "2026-02-07 02:47:56 UTC",
+  "build_started_at": null,
+  "build_finished_at": null,
+  "build_created_at_iso": "2026-02-07T02:47:56Z",
+  "build_started_at_iso": null,
+  "build_finished_at_iso": null,
+  "build_duration": null,
+  "build_queued_duration": null,
+  "build_allow_failure": false,
+  "build_failure_reason": "unknown_failure",
+  "pipeline_id": 74974,
+  "runner": null,
+  "project_id": 47461,
+  "project_name": "proj-name",
+  "user": {
+    "id": 414,
+    "name": "Joe",
+    "username": "hpc2000",
+    "avatar_url": "https://secure.gravatar.com/avatar/abc1?s=80&d=identicon",
+    "email": "[REDACTED]"
+  },
+  "commit": {
+    "id": 74974,
+    "name": null,
+    "sha": "abc1",
+    "message": "update message",
+    "author_name": "Joe",
+    "author_email": "joe@example.com",
+    "author_url": "https://example.com/hpc2000",
+    "status": "created",
+    "duration": null,
+    "started_at": null,
+    "finished_at": null,
+    "started_at_iso": null,
+    "finished_at_iso": null
+  },
+  "repository": {
+    "name": "repo-name",
+    "url": "git@example.com:joe1/proj-name.git",
+    "description": null,
+    "homepage": "https://example.com/joe1/proj-name",
+    "git_http_url": "https://example.com/joe1/proj-name.git",
+    "git_ssh_url": "git@example.com:joe1/proj-name.git",
+    "visibility_level": 0
+  },
+  "project": {
+    "id": 47461,
+    "name": "proj-name",
+    "description": null,
+    "web_url": "https://example.com/joe1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:joe1/proj-name.git",
+    "git_http_url": "https://example.com/joe1/proj-name.git",
+    "namespace": "Joe",
+    "visibility_level": 0,
+    "path_with_namespace": "joe1/proj-name",
+    "default_branch": "main",
+    "ci_config_path": ""
+  },
+  "environment": {
+    "name": "production",
+    "action": "start"
+  }
+}
diff --git a/data/system-hooks/emoji1.json b/data/system-hooks/emoji1.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/emoji1.json
@@ -0,0 +1,106 @@
+{
+  "object_kind": "emoji",
+  "event_type": "award",
+  "user": {
+    "id": 3892,
+    "name": "Bloggs, Joe",
+    "username": "abc1",
+    "avatar_url": "https://secure.gravatar.com/avatar/abc",
+    "email": "[REDACTED]"
+  },
+  "project_id": 44159,
+  "project": {
+    "id": 44159,
+    "name": "proj_name ",
+    "description": "",
+    "web_url": "https://example.com/abc1/proj_name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:abc1/proj_name.git",
+    "git_http_url": "https://example.com/abc1/proj_name.git",
+    "namespace": "Bloggs, Joe",
+    "visibility_level": 0,
+    "path_with_namespace": "abc1/proj_name",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/abc1/proj_name",
+    "url": "git@example.com:abc1/proj_name.git",
+    "ssh_url": "git@example.com:abc1/proj_name.git",
+    "http_url": "https://example.com/abc1/proj_name.git"
+  },
+  "object_attributes": {
+    "user_id": 3892,
+    "created_at": "2025-11-07 19:21:17 UTC",
+    "id": 107,
+    "name": "thumbsup",
+    "awardable_type": "Note",
+    "awardable_id": 26062,
+    "updated_at": "2025-11-07 19:21:17 UTC",
+    "awarded_on_url": "https://example.com/abc1/proj_name/-/commit/abc1234567889#note_26062"
+  },
+  "note": {
+    "author_id": 1424,
+    "change_position": {
+      "base_sha": null,
+      "start_sha": null,
+      "head_sha": null,
+      "old_path": null,
+      "new_path": null,
+      "position_type": "text",
+      "old_line": null,
+      "new_line": null,
+      "line_range": null
+    },
+    "commit_id": "abc1234567889",
+    "created_at": "2025-11-07 19:19:58 UTC",
+    "discussion_id": "1a5f45c5c3272a75442dfe28eb49a6272e3c5f6d",
+    "id": 26062,
+    "line_code": "4b6fcb2d521ef0fd442a5301e7932d16cc9f375a_0_1",
+    "note": "@abc1 This is a function",
+    "noteable_id": null,
+    "noteable_type": "Commit",
+    "original_position": {
+      "base_sha": "abcde123456789",
+      "start_sha": "abcde123456789",
+      "head_sha": "abc1234567889",
+      "old_path": "test.txt",
+      "new_path": "test.txt",
+      "position_type": "text",
+      "old_line": null,
+      "new_line": 1,
+      "line_range": null
+    },
+    "position": {
+      "base_sha": "abcde123456789",
+      "start_sha": "abcde123456789",
+      "head_sha": "abc1234567889",
+      "old_path": "test.txt",
+      "new_path": "test.txt",
+      "position_type": "text",
+      "old_line": null,
+      "new_line": 1,
+      "line_range": null
+    },
+    "project_id": 44159,
+    "resolved_at": null,
+    "resolved_by_id": null,
+    "resolved_by_push": null,
+    "st_diff": null,
+    "system": false,
+    "type": "DiffNote",
+    "updated_at": "2025-11-07 19:21:17 UTC",
+    "updated_by_id": null,
+    "description": "@abc1 This is a note.",
+    "url": "https://example.com/abc1/proj_name/-/commit/abc1234567889#note_26062"
+  },
+  "commit": {
+    "id": "abc1234567889",
+    "message": "this is a message",
+    "title": "The title",
+    "timestamp": "2025-11-07T19:00:05+00:00",
+    "url": "https://example.com/abc1/proj_name/-/commit/abc1234567889",
+    "author": {
+      "name": "Bloggs, Joe",
+      "email": "abc1@example.com"
+    }
+  }
+}
diff --git a/data/system-hooks/emoji2.json b/data/system-hooks/emoji2.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/emoji2.json
@@ -0,0 +1,135 @@
+{
+  "object_kind": "emoji",
+  "event_type": "award",
+  "user": {
+    "id": 4101,
+    "name": "Joe Bloggs",
+    "username": "abc123",
+    "avatar_url": "https://secure.gravatar.com/avatar/abc123?s=80&d=identicon",
+    "email": "[REDACTED]"
+  },
+  "project_id": 44549,
+  "project": {
+    "id": 44549,
+    "name": "The project name",
+    "description": null,
+    "web_url": "https://example.com/abc123/the-project",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:abc123/the-project.git",
+    "git_http_url": "https://example.com/abc123/the-project.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "abc123/the-project",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/abc123/the-project",
+    "url": "git@example.com:abc123/the-project.git",
+    "ssh_url": "git@example.com:abc123/the-project.git",
+    "http_url": "https://example.com/abc123/the-project.git"
+  },
+  "object_attributes": {
+    "user_id": 4101,
+    "created_at": "2025-11-12 13:32:11 UTC",
+    "id": 108,
+    "name": "thumbsup",
+    "awardable_type": "MergeRequest",
+    "awardable_id": 2586,
+    "updated_at": "2025-11-12 13:32:11 UTC",
+    "awarded_on_url": "https://example.com/abc123/the-project/-/merge_requests/1"
+  },
+  "merge_request": {
+    "assignee_id": 3789,
+    "author_id": 3783,
+    "created_at": "2025-11-12 13:12:56 UTC",
+    "description": "The description",
+    "draft": false,
+    "head_pipeline_id": null,
+    "id": 2586,
+    "iid": 1,
+    "last_edited_at": null,
+    "last_edited_by_id": null,
+    "merge_commit_sha": null,
+    "merge_error": null,
+    "merge_params": {},
+    "merge_status": "can_be_merged",
+    "merge_user_id": null,
+    "merge_when_pipeline_succeeds": false,
+    "milestone_id": null,
+    "source_branch": "main",
+    "source_project_id": 44754,
+    "state_id": 1,
+    "target_branch": "main",
+    "target_project_id": 44549,
+    "time_estimate": 0,
+    "title": "The title",
+    "updated_at": "2025-11-12 13:31:28 UTC",
+    "updated_by_id": 3783,
+    "prepared_at": "2025-11-12 13:12:59 UTC",
+    "assignee_ids": [
+      3789
+    ],
+    "blocking_discussions_resolved": true,
+    "detailed_merge_status": "mergeable",
+    "first_contribution": true,
+    "human_time_change": null,
+    "human_time_estimate": null,
+    "human_total_time_spent": null,
+    "labels": [],
+    "last_commit": {
+      "id": "xyz111",
+      "message": "commit message",
+      "title": "commit title",
+      "timestamp": "2025-11-12T13:05:23+00:00",
+      "url": "https://example.com/abc123/the-project/-/commit/xyz111",
+      "author": {
+        "name": "Author name",
+        "email": "author@example.com"
+      }
+    },
+    "reviewer_ids": [
+      3791
+    ],
+    "source": {
+      "id": 44754,
+      "name": "The project name",
+      "description": null,
+      "web_url": "https://example.com/aaa111/the-project",
+      "avatar_url": null,
+      "git_ssh_url": "git@example.com:aaa111/the-project.git",
+      "git_http_url": "https://example.com/aaa111/the-project.git",
+      "namespace": "Author name",
+      "visibility_level": 0,
+      "path_with_namespace": "aaa111/the-project",
+      "default_branch": "main",
+      "ci_config_path": "",
+      "homepage": "https://example.com/aaa111/the-project",
+      "url": "git@example.com:aaa111/the-project.git",
+      "ssh_url": "git@example.com:aaa111/the-project.git",
+      "http_url": "https://example.com/aaa111/the-project.git"
+    },
+    "state": "opened",
+    "system": false,
+    "target": {
+      "id": 44549,
+      "name": "The project name",
+      "description": null,
+      "web_url": "https://example.com/abc123/the-project",
+      "avatar_url": null,
+      "git_ssh_url": "git@example.com:abc123/the-project.git",
+      "git_http_url": "https://example.com/abc123/the-project.git",
+      "namespace": "Joe Bloggs",
+      "visibility_level": 0,
+      "path_with_namespace": "abc123/the-project",
+      "default_branch": "main",
+      "ci_config_path": "",
+      "homepage": "https://example.com/abc123/the-project",
+      "url": "git@example.com:abc123/the-project.git",
+      "ssh_url": "git@example.com:abc123/the-project.git",
+      "http_url": "https://example.com/abc123/the-project.git"
+    },
+    "time_change": 0,
+    "total_time_spent": 0,
+    "url": "https://example.com/abc123/the-project/-/merge_requests/1",
+    "work_in_progress": false
+  }
+}
diff --git a/data/system-hooks/emoji3.json b/data/system-hooks/emoji3.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/emoji3.json
@@ -0,0 +1,76 @@
+{
+  "object_kind": "emoji",
+  "event_type": "award",
+  "user": {
+    "id": 4012,
+    "name": "Joe Bloggs",
+    "username": "aaa1",
+    "avatar_url": "https://secure.gravatar.com/avatar/abc?s=80&d=identicon",
+    "email": "[REDACTED]"
+  },
+  "project_id": 44276,
+  "project": {
+    "id": 44276,
+    "name": "proj-name",
+    "description": null,
+    "web_url": "https://example.com/aaa1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:aaa1/proj-name.git",
+    "git_http_url": "https://example.com/aaa1/proj-name.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "aaa1/proj-name",
+    "default_branch": "master",
+    "ci_config_path": null,
+    "homepage": "https://example.com/aaa1/proj-name",
+    "url": "git@example.com:aaa1/proj-name.git",
+    "ssh_url": "git@example.com:aaa1/proj-name.git",
+    "http_url": "https://example.com/aaa1/proj-name.git"
+  },
+  "object_attributes": {
+    "user_id": 4012,
+    "created_at": "2025-11-20 13:31:14 UTC",
+    "id": 110,
+    "name": "thumbsup",
+    "awardable_type": "Issue",
+    "awardable_id": 3918,
+    "updated_at": "2025-11-20 13:31:14 UTC",
+    "awarded_on_url": "https://example.com/aaa1/proj-name/-/issues/1"
+  },
+  "work_item": {
+    "author_id": 386,
+    "closed_at": null,
+    "confidential": false,
+    "created_at": "2025-11-20 13:30:07 UTC",
+    "description": "@aaa1 a message.",
+    "discussion_locked": null,
+    "due_date": null,
+    "id": 3918,
+    "iid": 1,
+    "last_edited_at": null,
+    "last_edited_by_id": null,
+    "milestone_id": null,
+    "moved_to_id": null,
+    "duplicated_to_id": null,
+    "project_id": 44276,
+    "relative_position": 513,
+    "state_id": 1,
+    "time_estimate": 0,
+    "title": "a message title",
+    "updated_at": "2025-11-20 13:30:07 UTC",
+    "updated_by_id": null,
+    "type": "Issue",
+    "url": "https://example.com/aaa1/proj-name/-/issues/1",
+    "total_time_spent": 0,
+    "time_change": 0,
+    "human_total_time_spent": null,
+    "human_time_change": null,
+    "human_time_estimate": null,
+    "assignee_ids": [],
+    "assignee_id": null,
+    "labels": [],
+    "state": "opened",
+    "severity": "unknown",
+    "customer_relations_contacts": []
+  }
+}
diff --git a/data/system-hooks/emoji4.json b/data/system-hooks/emoji4.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/emoji4.json
@@ -0,0 +1,106 @@
+{
+  "object_kind": "emoji",
+  "event_type": "award",
+  "user": {
+    "id": 3095,
+    "name": "Joe Bloggs",
+    "username": "aaa1",
+    "avatar_url": "https://example.com/uploads/-/system/user/avatar/3095/avatar.png",
+    "email": "[REDACTED]"
+  },
+  "project_id": 45173,
+  "project": {
+    "id": 45173,
+    "name": "proj-name",
+    "description": null,
+    "web_url": "https://example.com/aaa1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:aaa1/proj-name.git",
+    "git_http_url": "https://example.com/aaa1/proj-name.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "aaa1/proj-name",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/aaa1/proj-name",
+    "url": "git@example.com:aaa1/proj-name.git",
+    "ssh_url": "git@example.com:aaa1/proj-name.git",
+    "http_url": "https://example.com/aaa1/proj-name.git"
+  },
+  "object_attributes": {
+    "user_id": 3095,
+    "created_at": "2025-11-18 08:46:40 UTC",
+    "id": 109,
+    "name": "heart",
+    "awardable_type": "Note",
+    "awardable_id": 26147,
+    "updated_at": "2025-11-18 08:46:40 UTC",
+    "awarded_on_url": "https://example.com/aaa1/proj-name/-/commit/abc123#note_26147"
+  },
+  "note": {
+    "author_id": 224,
+    "change_position": {
+      "base_sha": null,
+      "start_sha": null,
+      "head_sha": null,
+      "old_path": null,
+      "new_path": null,
+      "position_type": "text",
+      "old_line": null,
+      "new_line": null,
+      "line_range": null
+    },
+    "commit_id": "abc123",
+    "created_at": "2025-11-18 08:07:26 UTC",
+    "discussion_id": "abc123",
+    "id": 26147,
+    "line_code": "abc123",
+    "note": "the note",
+    "noteable_id": null,
+    "noteable_type": "Commit",
+    "original_position": {
+      "base_sha": "abc123",
+      "start_sha": "abc123",
+      "head_sha": "abc123",
+      "old_path": "src/Example.java",
+      "new_path": "src/Example.java",
+      "position_type": "text",
+      "old_line": 162,
+      "new_line": 166,
+      "line_range": null
+    },
+    "position": {
+      "base_sha": "abc123",
+      "start_sha": "abc123",
+      "head_sha": "abc123",
+      "old_path": "src/Example.java",
+      "new_path": "src/Example.java",
+      "position_type": "text",
+      "old_line": 162,
+      "new_line": 166,
+      "line_range": null
+    },
+    "project_id": 45173,
+    "resolved_at": null,
+    "resolved_by_id": null,
+    "resolved_by_push": null,
+    "st_diff": null,
+    "system": false,
+    "type": "DiffNote",
+    "updated_at": "2025-11-18 08:46:40 UTC",
+    "updated_by_id": null,
+    "description": "the note",
+    "url": "https://example.com/aaa1/proj-name/-/commit/abc123#note_26147"
+  },
+  "commit": {
+    "id": "abc123",
+    "message": "commit message",
+    "title": "commit message",
+    "timestamp": "2025-11-17T03:11:00+08:00",
+    "url": "https://example.com/aaa1/proj-name/-/commit/abc123",
+    "author": {
+      "name": "Joe",
+      "email": "joe@example.com"
+    }
+  }
+}
diff --git a/data/system-hooks/group-destroy1.json b/data/system-hooks/group-destroy1.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/group-destroy1.json
@@ -0,0 +1,9 @@
+{
+  "created_at": "2026-02-03T12:11:25Z",
+  "updated_at": "2026-02-11T21:51:07Z",
+  "event_name": "group_destroy",
+  "name": "Dataguru-deletion_scheduled-54084",
+  "path": "dataguru-deletion_scheduled-54084",
+  "full_path": "dataguru-deletion_scheduled-54084",
+  "group_id": 54084
+}
diff --git a/data/system-hooks/group-renamed.json b/data/system-hooks/group-renamed.json
deleted file mode 100644
--- a/data/system-hooks/group-renamed.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-     "event_name": "group_rename",
-     "created_at": "2017-10-30T15:09:00Z",
-     "updated_at": "2017-11-01T10:23:52Z",
-           "name": "Better Name",
-           "path": "better-name",
-      "full_path": "parent-group/better-name",
-       "group_id": 64,
-     "owner_name": null,
-    "owner_email": null,
-       "old_path": "old-name",
-  "old_full_path": "parent-group/old-name"
-}
diff --git a/data/system-hooks/group-renamed1.json b/data/system-hooks/group-renamed1.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/group-renamed1.json
@@ -0,0 +1,13 @@
+{
+     "event_name": "group_rename",
+     "created_at": "2017-10-30T15:09:00Z",
+     "updated_at": "2017-11-01T10:23:52Z",
+           "name": "Better Name",
+           "path": "better-name",
+      "full_path": "parent-group/better-name",
+       "group_id": 64,
+     "owner_name": null,
+    "owner_email": null,
+       "old_path": "old-name",
+  "old_full_path": "parent-group/old-name"
+}
diff --git a/data/system-hooks/group-renamed2.json b/data/system-hooks/group-renamed2.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/group-renamed2.json
@@ -0,0 +1,11 @@
+{
+  "created_at": "2026-01-22T09:28:55Z",
+  "updated_at": "2026-01-22T10:50:25Z",
+  "event_name": "group_rename",
+  "name": "proj-name",
+  "path": "proj-path",
+  "full_path": "full-path",
+  "group_id": 12345,
+  "old_path": "the-old-path",
+  "old_full_path": "the-new-path"
+}
diff --git a/data/system-hooks/milestone1.json b/data/system-hooks/milestone1.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/milestone1.json
@@ -0,0 +1,36 @@
+{
+  "object_kind": "milestone",
+  "event_type": "milestone",
+  "project": {
+    "id": 48355,
+    "name": "Coursework 1",
+    "description": null,
+    "web_url": "https://gitlab-student.macs.hw.ac.uk/f27wd-group-23/coursework-1",
+    "avatar_url": null,
+    "git_ssh_url": "git@gitlab-student.macs.hw.ac.uk:f27wd-group-23/coursework-1.git",
+    "git_http_url": "https://gitlab-student.macs.hw.ac.uk/f27wd-group-23/coursework-1.git",
+    "namespace": "F27WD Group 23",
+    "visibility_level": 0,
+    "path_with_namespace": "f27wd-group-23/coursework-1",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://gitlab-student.macs.hw.ac.uk/f27wd-group-23/coursework-1",
+    "url": "git@gitlab-student.macs.hw.ac.uk:f27wd-group-23/coursework-1.git",
+    "ssh_url": "git@gitlab-student.macs.hw.ac.uk:f27wd-group-23/coursework-1.git",
+    "http_url": "https://gitlab-student.macs.hw.ac.uk/f27wd-group-23/coursework-1.git"
+  },
+  "group": null,
+  "object_attributes": {
+    "id": 115,
+    "iid": 1,
+    "title": "Redesign and Implement Vortex",
+    "description": "This is to track the completion of CW1 against the marking scheme.\r\n\r\n| Criterion | Description | Marks |\r\n|-----------|-------------|-------|\r\n|  |  |  |\r\n|  |  |  |\r\n|  |  |  |\r\n\r\n",
+    "state": "active",
+    "created_at": "2026-02-12 12:35:59 UTC",
+    "updated_at": "2026-02-12 12:35:59 UTC",
+    "due_date": "2026-02-23",
+    "start_date": "2026-01-26",
+    "project_id": 48355
+  },
+  "action": "create"
+}
diff --git a/data/system-hooks/pipeline6.json b/data/system-hooks/pipeline6.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/pipeline6.json
@@ -0,0 +1,178 @@
+{
+  "object_kind": "pipeline",
+  "object_attributes": {
+    "id": 74974,
+    "iid": 1,
+    "name": null,
+    "ref": "main",
+    "tag": false,
+    "sha": "abc1",
+    "before_sha": "aaa1",
+    "source": "push",
+    "status": "pending",
+    "detailed_status": "pending",
+    "stages": [
+      "build",
+      "test",
+      "deploy"
+    ],
+    "created_at": "2026-02-07 02:47:56 UTC",
+    "finished_at": null,
+    "duration": null,
+    "queued_duration": null,
+    "protected_ref": true,
+    "variables": [],
+    "url": "https://example.com/joe1/proj1/-/pipelines/74974"
+  },
+  "merge_request": null,
+  "user": {
+    "id": 414,
+    "name": "Joe Bloggs",
+    "username": "joe1",
+    "avatar_url": "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon",
+    "email": "[REDACTED]"
+  },
+  "project": {
+    "id": 47461,
+    "name": "proj1",
+    "description": null,
+    "web_url": "https://example.com/joe1/proj1",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:joe1/proj1.git",
+    "git_http_url": "https://example.com/joe1/proj1.git",
+    "namespace": "Joe Bloggs",
+    "visibility_level": 0,
+    "path_with_namespace": "joe1/proj1",
+    "default_branch": "main",
+    "ci_config_path": ""
+  },
+  "commit": {
+    "id": "abc1",
+    "message": "commit message",
+    "title": "commit message",
+    "timestamp": "2026-02-07T02:47:55+00:00",
+    "url": "https://example.com/joe1/proj1/-/commit/abc1",
+    "author": {
+      "name": "Joe Bloggs",
+      "email": "joe1@example.com"
+    }
+  },
+  "builds": [
+    {
+      "id": 103996,
+      "stage": "build",
+      "name": "build-job",
+      "status": "pending",
+      "created_at": "2026-02-07 02:47:56 UTC",
+      "started_at": null,
+      "finished_at": null,
+      "duration": null,
+      "queued_duration": 0.486474635,
+      "failure_reason": null,
+      "when": "on_success",
+      "manual": false,
+      "allow_failure": false,
+      "user": {
+        "id": 414,
+        "name": "Joe Bloggs",
+        "username": "joe1",
+        "avatar_url": "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon",
+        "email": "[REDACTED]"
+      },
+      "runner": null,
+      "artifacts_file": {
+        "filename": null,
+        "size": null
+      },
+      "environment": null
+    },
+    {
+      "id": 103997,
+      "stage": "test",
+      "name": "test",
+      "status": "created",
+      "created_at": "2026-02-07 02:47:56 UTC",
+      "started_at": null,
+      "finished_at": null,
+      "duration": null,
+      "queued_duration": null,
+      "failure_reason": null,
+      "when": "on_success",
+      "manual": false,
+      "allow_failure": false,
+      "user": {
+        "id": 414,
+        "name": "Joe Bloggs",
+        "username": "joe1",
+        "avatar_url": "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon",
+        "email": "[REDACTED]"
+      },
+      "runner": null,
+      "artifacts_file": {
+        "filename": null,
+        "size": null
+      },
+      "environment": null
+    },
+    {
+      "id": 103998,
+      "stage": "test",
+      "name": "test job",
+      "status": "created",
+      "created_at": "2026-02-07 02:47:56 UTC",
+      "started_at": null,
+      "finished_at": null,
+      "duration": null,
+      "queued_duration": null,
+      "failure_reason": null,
+      "when": "on_success",
+      "manual": false,
+      "allow_failure": false,
+      "user": {
+        "id": 414,
+        "name": "Joe Bloggs",
+        "username": "joe1",
+        "avatar_url": "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon",
+        "email": "[REDACTED]"
+      },
+      "runner": null,
+      "artifacts_file": {
+        "filename": null,
+        "size": null
+      },
+      "environment": null
+    },
+    {
+      "id": 103999,
+      "stage": "deploy",
+      "name": "deploy-job",
+      "status": "created",
+      "created_at": "2026-02-07 02:47:56 UTC",
+      "started_at": null,
+      "finished_at": null,
+      "duration": null,
+      "queued_duration": null,
+      "failure_reason": null,
+      "when": "on_success",
+      "manual": false,
+      "allow_failure": false,
+      "user": {
+        "id": 414,
+        "name": "Joe Bloggs",
+        "username": "joe1",
+        "avatar_url": "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon",
+        "email": "[REDACTED]"
+      },
+      "runner": null,
+      "artifacts_file": {
+        "filename": null,
+        "size": null
+      },
+      "environment": {
+        "name": "production",
+        "action": "start",
+        "deployment_tier": "production"
+      }
+    }
+  ]
+}
diff --git a/data/system-hooks/tag-push.json b/data/system-hooks/tag-push.json
deleted file mode 100644
--- a/data/system-hooks/tag-push.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "event_name": "tag_push",
-  "before": "0000000000000000000000000000000000000000",
-  "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
-  "ref": "refs/tags/v1.0.0",
-  "checkout_sha": "5937ac0a7beb003549fc5fd26fc247adbce4a52e",
-  "user_id": 1,
-  "user_name": "John Smith",
-  "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
-  "project_id": 1,
-  "project":{
-    "name":"Example",
-    "description":"",
-    "web_url":"http://example.com/jsmith/example",
-    "avatar_url":null,
-    "git_ssh_url":"git@example.com:jsmith/example.git",
-    "git_http_url":"http://example.com/jsmith/example.git",
-    "namespace":"Jsmith",
-    "visibility_level":0,
-    "path_with_namespace":"jsmith/example",
-    "default_branch":"master",
-    "homepage":"http://example.com/jsmith/example",
-    "url":"git@example.com:jsmith/example.git",
-    "ssh_url":"git@example.com:jsmith/example.git",
-    "http_url":"http://example.com/jsmith/example.git"
-  },
-  "repository":{
-    "name": "Example",
-    "url": "ssh://git@example.com/jsmith/example.git",
-    "description": "",
-    "homepage": "http://example.com/jsmith/example",
-    "git_http_url":"http://example.com/jsmith/example.git",
-    "git_ssh_url":"git@example.com:jsmith/example.git",
-    "visibility_level":0
-  },
-  "commits": [],
-  "total_commits_count": 0
-}
diff --git a/data/system-hooks/tag-push1.json b/data/system-hooks/tag-push1.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/tag-push1.json
@@ -0,0 +1,38 @@
+{
+  "event_name": "tag_push",
+  "before": "0000000000000000000000000000000000000000",
+  "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
+  "ref": "refs/tags/v1.0.0",
+  "checkout_sha": "5937ac0a7beb003549fc5fd26fc247adbce4a52e",
+  "user_id": 1,
+  "user_name": "John Smith",
+  "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
+  "project_id": 1,
+  "project":{
+    "name":"Example",
+    "description":"",
+    "web_url":"http://example.com/jsmith/example",
+    "avatar_url":null,
+    "git_ssh_url":"git@example.com:jsmith/example.git",
+    "git_http_url":"http://example.com/jsmith/example.git",
+    "namespace":"Jsmith",
+    "visibility_level":0,
+    "path_with_namespace":"jsmith/example",
+    "default_branch":"master",
+    "homepage":"http://example.com/jsmith/example",
+    "url":"git@example.com:jsmith/example.git",
+    "ssh_url":"git@example.com:jsmith/example.git",
+    "http_url":"http://example.com/jsmith/example.git"
+  },
+  "repository":{
+    "name": "Example",
+    "url": "ssh://git@example.com/jsmith/example.git",
+    "description": "",
+    "homepage": "http://example.com/jsmith/example",
+    "git_http_url":"http://example.com/jsmith/example.git",
+    "git_ssh_url":"git@example.com:jsmith/example.git",
+    "visibility_level":0
+  },
+  "commits": [],
+  "total_commits_count": 0
+}
diff --git a/data/system-hooks/tag-push2.json b/data/system-hooks/tag-push2.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/tag-push2.json
@@ -0,0 +1,46 @@
+{
+  "object_kind": "tag_push",
+  "event_name": "tag_push",
+  "before": "abc123",
+  "after": "0000000000000000000000000000000000000000",
+  "ref": "refs/tags/tag1",
+  "ref_protected": false,
+  "checkout_sha": null,
+  "message": null,
+  "user_id": 123,
+  "user_name": "Joe",
+  "user_username": "rfa4000",
+  "user_email": null,
+  "user_avatar": "https://secure.gravatar.com/avatar/abc123?s=80&d=identicon",
+  "project_id": 48363,
+  "project": {
+    "id": 48363,
+    "name": "proj-name",
+    "description": null,
+    "web_url": "https://example.com/joe1/proj-name",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:joe1/proj-name.git",
+    "git_http_url": "https://example.com/joe1/proj-name.git",
+    "namespace": "Joe",
+    "visibility_level": 0,
+    "path_with_namespace": "joe1/proj-name",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://example.com/joe1/proj-name",
+    "url": "git@example.com:joe1/proj-name.git",
+    "ssh_url": "git@example.com:joe1/proj-name.git",
+    "http_url": "https://example.com/joe1/proj-name.git"
+  },
+  "commits": [],
+  "total_commits_count": 0,
+  "push_options": {},
+  "repository": {
+    "name": "proj-name",
+    "url": "git@example.com:joe1/proj-name.git",
+    "description": null,
+    "homepage": "https://example.com/joe1/proj-name",
+    "git_http_url": "https://example.com/joe1/proj-name.git",
+    "git_ssh_url": "git@example.com:joe1/proj-name.git",
+    "visibility_level": 0
+  }
+}
diff --git a/data/system-hooks/tag-push3.json b/data/system-hooks/tag-push3.json
new file mode 100644
--- /dev/null
+++ b/data/system-hooks/tag-push3.json
@@ -0,0 +1,46 @@
+{
+  "object_kind": "tag_push",
+  "event_name": "tag_push",
+  "before": "6860fc562566df969ed0e59d5b71232b0bf201fb",
+  "after": "0000000000000000000000000000000000000000",
+  "ref": "refs/tags/v1",
+  "ref_protected": false,
+  "checkout_sha": null,
+  "message": null,
+  "user_id": 3887,
+  "user_name": "Wing To Wong",
+  "user_username": "wtw4000",
+  "user_email": "",
+  "user_avatar": "https://secure.gravatar.com/avatar/7aaf2666b1f7488c41ff5f9c0dad237c3d5abbc5cd205f800b80053027274e6b?s=80&d=identicon",
+  "project_id": 48175,
+  "project": {
+    "id": 48175,
+    "name": "f28hs-cw1",
+    "description": null,
+    "web_url": "https://gitlab-student.macs.hw.ac.uk/wtw4000/f28hs-cw1",
+    "avatar_url": null,
+    "git_ssh_url": "git@gitlab-student.macs.hw.ac.uk:wtw4000/f28hs-cw1.git",
+    "git_http_url": "https://gitlab-student.macs.hw.ac.uk/wtw4000/f28hs-cw1.git",
+    "namespace": "Wing To Wong",
+    "visibility_level": 0,
+    "path_with_namespace": "wtw4000/f28hs-cw1",
+    "default_branch": "master",
+    "ci_config_path": "",
+    "homepage": "https://gitlab-student.macs.hw.ac.uk/wtw4000/f28hs-cw1",
+    "url": "git@gitlab-student.macs.hw.ac.uk:wtw4000/f28hs-cw1.git",
+    "ssh_url": "git@gitlab-student.macs.hw.ac.uk:wtw4000/f28hs-cw1.git",
+    "http_url": "https://gitlab-student.macs.hw.ac.uk/wtw4000/f28hs-cw1.git"
+  },
+  "commits": [],
+  "total_commits_count": 0,
+  "push_options": {},
+  "repository": {
+    "name": "f28hs-cw1",
+    "url": "git@gitlab-student.macs.hw.ac.uk:wtw4000/f28hs-cw1.git",
+    "description": null,
+    "homepage": "https://gitlab-student.macs.hw.ac.uk/wtw4000/f28hs-cw1",
+    "git_http_url": "https://gitlab-student.macs.hw.ac.uk/wtw4000/f28hs-cw1.git",
+    "git_ssh_url": "git@gitlab-student.macs.hw.ac.uk:wtw4000/f28hs-cw1.git",
+    "visibility_level": 0
+  }
+}
diff --git a/gitlab-haskell.cabal b/gitlab-haskell.cabal
--- a/gitlab-haskell.cabal
+++ b/gitlab-haskell.cabal
@@ -1,7 +1,7 @@
-cabal-version: 2.4
+cabal-version: 3.0
 name:           gitlab-haskell
 category:       Git
-version:        1.1.0.0
+version:        1.2.1.0
 synopsis:       A Haskell library for the GitLab web API
 description:
             This library lifts the GitLab REST API into Haskell: <https://docs.gitlab.com/ee/api/>
@@ -179,6 +179,7 @@
                 , GitLab.API.Pipelines
                 , GitLab.API.Branches
                 , GitLab.API.Jobs
+                , GitLab.API.JobArtifacts
                 , GitLab.API.Repositories
                 , GitLab.API.MergeRequests
                 , GitLab.API.RepositoryFiles
@@ -188,6 +189,7 @@
                 , GitLab.API.Notes
                 , GitLab.API.Discussions
                 , GitLab.API.Boards
+                , GitLab.API.Events
                 , GitLab.SystemHooks.Types
                 , GitLab.SystemHooks.GitLabSystemHooks
                 , GitLab.SystemHooks.Rules
@@ -201,14 +203,14 @@
               , crypton-connection >= 0.4.1
               , aeson >= 2.0.0.0
               , bytestring
-              , text
+              , text >= 2.1
               , http-client
               , http-types
               , transformers
               , time >= 1.9
               , temporary
               , unix-compat
-              , mtl
+              , mtl >= 2.2.2
               , data-default
   default-language: Haskell2010
   ghc-options: -Wall
@@ -249,7 +251,7 @@
                , bytestring
                , aeson >= 2.0.0.0
                , tree-diff
-               , ansi-wl-pprint
+               , prettyprinter
                , vector
                , unordered-containers
                , text
diff --git a/src/GitLab.hs b/src/GitLab.hs
--- a/src/GitLab.hs
+++ b/src/GitLab.hs
@@ -22,6 +22,7 @@
     module GitLab.API.Issues,
     module GitLab.API.Branches,
     module GitLab.API.Jobs,
+    module GitLab.API.JobArtifacts,
     module GitLab.API.MergeRequests,
     module GitLab.API.Repositories,
     module GitLab.API.RepositoryFiles,
@@ -31,22 +32,26 @@
     module GitLab.API.Notes,
     module GitLab.API.Boards,
     module GitLab.API.Discussions,
+    module GitLab.API.Events,
     module GitLab.SystemHooks.GitLabSystemHooks,
     module GitLab.SystemHooks.Types,
     module GitLab.SystemHooks.Rules,
   )
 where
 
+import Control.Monad.Except
 import Control.Monad.IO.Class
 import Control.Monad.Trans.Reader
 import Data.Default
 import qualified Data.Text as T
 import GitLab.API.Boards
 import GitLab.API.Branches
+import GitLab.API.Events
 import GitLab.API.Commits
 import GitLab.API.Discussions
 import GitLab.API.Groups
 import GitLab.API.Issues
+import GitLab.API.JobArtifacts
 import GitLab.API.Jobs
 import GitLab.API.Members
 import GitLab.API.MergeRequests
@@ -81,10 +86,10 @@
 -- >   where
 -- >     myConfig = defaultGitLabServer
 -- >         { url = "https://gitlab.example.com"
--- >         , token = "my_access_token" }
+-- >         , token = AuthMethodToken "my_access_token" }
 -- >     issueEnabled Nothing = False
 -- >     issueEnabled (Just b) = b
-runGitLab :: GitLabServerConfig -> GitLab a -> IO a
+runGitLab :: GitLabServerConfig -> GitLab a -> IO (Either GitLabError a)
 runGitLab cfg action = do
   liftIO $ hSetBuffering stdout LineBuffering
   let settings = mkManagerSettings def Nothing
@@ -99,7 +104,7 @@
 -- be asked for at runtime:
 --
 -- > runGitLabPassPrompt defaultGitLabServer myGitLabProgram
-runGitLabPassPrompt :: GitLabServerConfig -> GitLab a -> IO a
+runGitLabPassPrompt :: GitLabServerConfig -> GitLab a -> IO (Either GitLabError a)
 runGitLabPassPrompt cfg action = do
   liftIO $ hSetBuffering stdout NoBuffering
   liftIO (putStr "Enter GitLab server URL\n> ")
@@ -110,26 +115,31 @@
 
 -- | The same as 'runGitLab', except that it also takes a connection
 -- manager as an argument.
-runGitLabWithManager :: Manager -> GitLabServerConfig -> GitLab a -> IO a
+runGitLabWithManager :: Manager -> GitLabServerConfig -> GitLab a -> IO (Either GitLabError a)
 runGitLabWithManager manager cfg (GitLabT action) = do
-  -- test the token access
-  let (GitLabT versionCheck) = gitlabVersion
-  tokenTest <- runReaderT versionCheck (GitLabState cfg manager)
-  case tokenTest of
-    Left response ->
-      case responseStatus response of
-        (Status 401 "Unauthorized") -> error "access token not accepted."
-        st -> error ("unexpected HTTP status: " <> show st)
-    Right _versionInfo ->
-      -- it worked, run the user code.
-      runReaderT action (GitLabState cfg manager)
+  let withVersionCheck func = do
+        -- test the token access
+        let (GitLabT versionCheck) = gitlabVersion
+        tokenTest <- runExceptT (runReaderT versionCheck (GitLabState cfg manager))
+        case tokenTest of
+          Left (GitLabError t) -> return (Left (GitLabError t))
+          Right (Left response) ->
+            case responseStatus response of
+              (Status 401 "Unauthorized") -> return $ Left (GitLabError "access token not accepted.")
+              st -> return $ Left (GitLabError ("unexpected HTTP status: " <> T.pack (show st)))
+          Right (Right _versionInfo) -> func
+      goAhead = runExceptT (runReaderT action (GitLabState cfg manager))
+  -- No version check without authentication because /version is not public.
+  case token cfg of
+    AuthMethodNone -> goAhead
+    _ -> withVersionCheck  goAhead
 
 -- | Only useful for testing GitLab actions that lift IO actions with
 -- liftIO. Cannot speak to a GitLab server. Only useful for the
 -- gitlab-haskell tests.
-runGitLabDbg :: GitLab a -> IO a
+runGitLabDbg :: GitLab a -> IO (Either GitLabError a)
 runGitLabDbg (GitLabT action) = do
   liftIO $ hSetBuffering stdout LineBuffering
   manager <- liftIO $ newManager (mkManagerSettings def Nothing)
   let cfg = GitLabServerConfig {url = "", token = AuthMethodToken "", retries = 1, debugSystemHooks = Nothing}
-  runReaderT action (GitLabState cfg manager)
+  runExceptT (runReaderT action (GitLabState cfg manager))
diff --git a/src/GitLab/API/Branches.hs b/src/GitLab/API/Branches.hs
--- a/src/GitLab/API/Branches.hs
+++ b/src/GitLab/API/Branches.hs
@@ -25,6 +25,7 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
 import Data.Either
 import Data.Text (Text)
@@ -36,6 +37,7 @@
     gitlabGetMany,
     gitlabGetOne,
     gitlabPost,
+    responseErrorText,
   )
 import Network.HTTP.Client
 
@@ -44,7 +46,9 @@
 branches :: Project -> GitLab [Branch]
 branches project = do
   result <- branches' (project_id project)
-  return (fromRight (error "branches error") result)
+  case result of
+    Left er -> throwError (GitLabError ("branches error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of repository branches from a project given its
 -- project ID, sorted by name alphabetically.
@@ -70,7 +74,9 @@
   GitLab (Maybe Branch)
 branch project branchName = do
   result <- branch' (project_id project) branchName
-  return (fromRight (error "branch error") result)
+  case result of
+    Left er -> throwError (GitLabError ("branch error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a single project repository branch.
 branch' ::
diff --git a/src/GitLab/API/Commits.hs b/src/GitLab/API/Commits.hs
--- a/src/GitLab/API/Commits.hs
+++ b/src/GitLab/API/Commits.hs
@@ -57,6 +57,7 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
 import Data.Either
 import Data.Text (Text)
@@ -103,7 +104,7 @@
         ("actions", Just (T.encodeUtf8 (T.pack (show actions))))
       ]
   case result of
-    Left resp -> error ("createCommitMultipleFilesActions: " <> show resp)
+    Left resp -> throwError (GitLabError ("createCommitMultipleFilesActions: " <> responseErrorText resp))
     Right x -> return x
   where
     commitsAddr :: Int -> Text
diff --git a/src/GitLab/API/Events.hs b/src/GitLab/API/Events.hs
new file mode 100644
--- /dev/null
+++ b/src/GitLab/API/Events.hs
@@ -0,0 +1,237 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StrictData #-}
+
+-- |
+-- Module      : GitLab.API.Events
+-- Description : Queries for GitLab Events API
+-- Copyright   : (c) Rob Stewart, Heriot-Watt University, 2019
+-- License     : BSD3
+-- Maintainer  : robstewart57@gmail.com
+-- Stability   : stable
+--
+-- See <https://docs.gitlab.com/ee/api/events.html>
+module GitLab.API.Events
+  ( -- * Authenticated user events
+    currentUserEvents,
+
+    -- * User events
+    userEvents,
+    userEvents',
+
+    -- * Project events
+    projectEvents,
+    projectEvents',
+
+    -- * Group events
+    groupEvents,
+    groupEvents',
+
+    -- * Filter attributes
+    EventFilterAttrs (..),
+    defaultEventFilters,
+  )
+where
+
+import Control.Monad.Except
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import Data.Maybe
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import Data.Time.Clock
+import Data.Time.Format.ISO8601
+import GitLab.Types
+import GitLab.WebRequests.GitLabWebCalls
+import Network.HTTP.Client
+
+-- | Filter attributes for event queries.
+--
+-- Use 'defaultEventFilters' as a starting point and override fields as needed.
+data EventFilterAttrs = EventFilterAttrs
+  { -- | Filter by action name (e.g. 'ANOpened', 'ANMerged', 'ANCommentedOn').
+    eventFilter_action :: Maybe EventActionName,
+    -- | Filter by target type (e.g. 'ETTIssue', 'ETTMergeRequest').
+    eventFilter_target_type :: Maybe EventTargetType,
+    -- | Return events created before this date (ISO 8601).
+    eventFilter_before :: Maybe UTCTime,
+    -- | Return events created after this date (ISO 8601).
+    eventFilter_after :: Maybe UTCTime,
+    -- | Sort order: 'Asc' or 'Desc'.
+    eventFilter_sort :: Maybe SortBy
+  }
+
+-- | Default event filters: no filtering applied.
+defaultEventFilters :: EventFilterAttrs
+defaultEventFilters =
+  EventFilterAttrs
+    { eventFilter_action = Nothing,
+      eventFilter_target_type = Nothing,
+      eventFilter_before = Nothing,
+      eventFilter_after = Nothing,
+      eventFilter_sort = Nothing
+    }
+
+-- | Get events for the currently authenticated user.
+--
+-- Calls @GET \/events@
+currentUserEvents ::
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab [Event]
+currentUserEvents attrs = do
+  result <- gitlabGetMany "/events" (eventFilters attrs)
+  case result of
+    Left er -> throwError (GitLabError ("currentUserEvents error: " <> responseErrorText er))
+    Right xs -> return xs
+
+-- | Get events for a specific user.
+--
+-- Calls @GET \/users\/:id\/events@
+userEvents ::
+  -- | the user
+  User ->
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab [Event]
+userEvents usr attrs = do
+  result <- userEvents' (user_id usr) attrs
+  case result of
+    Left er -> throwError (GitLabError ("userEvents error: " <> responseErrorText er))
+    Right xs -> return xs
+
+-- | Get events for a specific user by ID.
+--
+-- Calls @GET \/users\/:id\/events@
+userEvents' ::
+  -- | user ID
+  Int ->
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab (Either (Response BSL.ByteString) [Event])
+userEvents' userId attrs =
+  gitlabGetMany urlPath (eventFilters attrs)
+  where
+    urlPath =
+      T.pack $
+        "/users/"
+          <> show userId
+          <> "/events"
+
+-- | Get events for a project.
+--
+-- Calls @GET \/projects\/:id\/events@
+projectEvents ::
+  -- | the project
+  Project ->
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab [Event]
+projectEvents proj attrs = do
+  result <- projectEvents' (project_id proj) attrs
+  case result of
+    Left er -> throwError (GitLabError ("projectEvents error: " <> responseErrorText er))
+    Right xs -> return xs
+
+-- | Get events for a project by ID.
+--
+-- Calls @GET \/projects\/:id\/events@
+projectEvents' ::
+  -- | project ID
+  Int ->
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab (Either (Response BSL.ByteString) [Event])
+projectEvents' projectId attrs =
+  gitlabGetMany urlPath (eventFilters attrs)
+  where
+    urlPath =
+      T.pack $
+        "/projects/"
+          <> show projectId
+          <> "/events"
+
+-- | Get events for a group.
+--
+-- Calls @GET \/groups\/:id\/events@
+groupEvents ::
+  -- | the group
+  Group ->
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab [Event]
+groupEvents grp attrs = do
+  result <- groupEvents' (group_id grp) attrs
+  case result of
+    Left er -> throwError (GitLabError ("groupEvents error: " <> responseErrorText er))
+    Right xs -> return xs
+
+-- | Get events for a group by ID.
+--
+-- Calls @GET \/groups\/:id\/events@
+groupEvents' ::
+  -- | group ID
+  Int ->
+  -- | filter attributes
+  EventFilterAttrs ->
+  GitLab (Either (Response BSL.ByteString) [Event])
+groupEvents' groupId attrs =
+  gitlabGetMany urlPath (eventFilters attrs)
+  where
+    urlPath =
+      T.pack $
+        "/groups/"
+          <> show groupId
+          <> "/events"
+
+-- Internal helpers
+
+eventFilters :: EventFilterAttrs -> [GitLabParam]
+eventFilters attrs =
+  catMaybes
+    [ (\a -> Just ("action", encodeText (actionParam a))) =<< eventFilter_action attrs,
+      (\t -> Just ("target_type", encodeText (targetTypeParam t))) =<< eventFilter_target_type attrs,
+      (\d -> Just ("before", encodeText (showTime d))) =<< eventFilter_before attrs,
+      (\d -> Just ("after", encodeText (showTime d))) =<< eventFilter_after attrs,
+      (\s -> Just ("sort", encodeText (T.pack (show s)))) =<< eventFilter_sort attrs
+    ]
+  where
+    encodeText :: Text -> Maybe BS.ByteString
+    encodeText = Just . T.encodeUtf8
+    showTime :: UTCTime -> Text
+    showTime = T.pack . iso8601Show
+
+-- | Convert 'EventActionName' to the string value expected by the
+-- GitLab filter query parameter.
+actionParam :: EventActionName -> Text
+actionParam ANOpened = "opened"
+actionParam ANClosed = "closed"
+actionParam ANPushed = "pushed"
+actionParam ANCommentedOn = "commented"
+actionParam ANMerged = "merged"
+actionParam ANCreated = "created"
+actionParam ANUpdated = "updated"
+actionParam ANApproved = "approved"
+actionParam ANReopened = "reopened"
+actionParam ANLeft = "left"
+actionParam ANJoined = "joined"
+actionParam ANDestroyed = "destroyed"
+actionParam ANExpired = "expired"
+actionParam ANDeleted = "deleted"
+actionParam ANAccepted = "accepted"
+actionParam (ANOther t) = t
+
+-- | Convert 'EventTargetType' to the lowercase string value expected
+-- by the GitLab filter query parameter.
+targetTypeParam :: EventTargetType -> Text
+targetTypeParam ETTIssue = "issue"
+targetTypeParam ETTMilestone = "milestone"
+targetTypeParam ETTMergeRequest = "merge_request"
+targetTypeParam ETTNote = "note"
+targetTypeParam ETTProject = "project"
+targetTypeParam ETTSnippet = "snippet"
+targetTypeParam ETTUser = "user"
+targetTypeParam ETTWikiPage = "wiki_page"
+targetTypeParam ETTDesign = "design"
+targetTypeParam (ETTOther t) = t
diff --git a/src/GitLab/API/Groups.hs b/src/GitLab/API/Groups.hs
--- a/src/GitLab/API/Groups.hs
+++ b/src/GitLab/API/Groups.hs
@@ -56,8 +56,8 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import Data.Maybe
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -68,53 +68,67 @@
 
 -- | Get a list of visible groups for the authenticated user.
 groups :: ListGroupsAttrs -> GitLab [Group]
-groups attrs =
-  fromRight (error "groups error")
-    <$> gitlabGetMany "/groups" (listGroupsAttrs attrs)
+groups attrs = do
+  result <- gitlabGetMany "/groups" (listGroupsAttrs attrs)
+  case result of
+    Left er -> throwError (GitLabError ("groups error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of visible direct subgroups in this group.
 subGroups :: Group -> ListGroupsAttrs -> GitLab [Group]
-subGroups parentGrp attrs =
-  fromRight (error "subGroups error")
-    <$> gitlabGetMany
+subGroups parentGrp attrs = do
+  result <-
+    gitlabGetMany
       ( "/groups/"
           <> T.pack (show (group_id parentGrp))
           <> "/subgroups"
       )
       (listGroupsAttrs attrs)
+  case result of
+    Left er -> throwError (GitLabError ("subGroups error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of visible descendant groups of this group.
 descendantGroups :: Group -> ListGroupsAttrs -> GitLab [Group]
-descendantGroups parentGrp attrs =
-  fromRight (error "subGroups error")
-    <$> gitlabGetMany
+descendantGroups parentGrp attrs = do
+  result <-
+    gitlabGetMany
       ( "/groups/"
           <> T.pack (show (group_id parentGrp))
           <> "/descendant_groups"
       )
       (listGroupsAttrs attrs)
+  case result of
+    Left er -> throwError (GitLabError ("descendantGroups error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of projects in this group.
 groupProjects :: Group -> GroupProjectAttrs -> GitLab [Project]
-groupProjects parentGrp attrs =
-  fromRight (error "groupProjects error")
-    <$> gitlabGetMany
+groupProjects parentGrp attrs = do
+  result <-
+    gitlabGetMany
       ( "/groups/"
           <> T.pack (show (group_id parentGrp))
           <> "/projects"
       )
       (groupProjectAttrs attrs)
+  case result of
+    Left er -> throwError (GitLabError ("groupProjects error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of projects in this group.
 groupSharedProjects :: Group -> GroupProjectAttrs -> GitLab [Project]
-groupSharedProjects parentGrp attrs =
-  fromRight (error "groupSharedProjects error")
-    <$> gitlabGetMany
+groupSharedProjects parentGrp attrs = do
+  result <-
+    gitlabGetMany
       ( "/groups/"
           <> T.pack (show (group_id parentGrp))
           <> "/projects/shared"
       )
       (groupProjectAttrs attrs)
+  case result of
+    Left er -> throwError (GitLabError ("groupSharedProjects error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get all details of a group.
 group ::
@@ -202,9 +216,11 @@
   -- | String or path to search for.
   Text ->
   GitLab [Group]
-searchGroup searchTxt =
-  fromRight (error "searchGroups error")
-    <$> gitlabGetMany "/groups" [("search", Just (T.encodeUtf8 searchTxt))]
+searchGroup searchTxt = do
+  result <- gitlabGetMany "/groups" [("search", Just (T.encodeUtf8 searchTxt))]
+  case result of
+    Left er -> throwError (GitLabError ("searchGroups error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Attributes related to a group
 data GroupProjectAttrs = GroupProjectAttrs
diff --git a/src/GitLab/API/Issues.hs b/src/GitLab/API/Issues.hs
--- a/src/GitLab/API/Issues.hs
+++ b/src/GitLab/API/Issues.hs
@@ -80,14 +80,15 @@
     defaultIssueFilters,
     defaultIssueAttrs,
     IssueAttrs (..),
+    IssueFilterAttrs (..),
     DueDate (..),
     IssueState (..),
   )
 where
 
+import Control.Monad.Except
 import Data.Aeson.TH
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import Data.Maybe
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -108,7 +109,9 @@
   GitLab [Issue]
 groupIssues grp attrs = do
   result <- gitlabGetMany urlPath (issueFilters attrs)
-  return (fromRight (error "groupsIssues error") result)
+  case result of
+    Left er -> throwError (GitLabError ("groupIssues error: " <> responseErrorText er))
+    Right x -> return x
   where
     urlPath =
       T.pack $
@@ -116,9 +119,6 @@
           <> show (group_id grp)
           <> "/issues"
 
--- result <- projectIssues' (project_id p) filters
--- return (fromRight (error "projectIssues error") result)
-
 -- | Get a list of a project’s issues
 projectIssues ::
   -- | the project
@@ -129,7 +129,9 @@
   GitLab [Issue]
 projectIssues p filters = do
   result <- projectIssues' (project_id p) filters
-  return (fromRight (error "projectIssues error") result)
+  case result of
+    Left er -> throwError (GitLabError ("projectIssues error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of a project’s issues
 projectIssues' ::
@@ -166,8 +168,11 @@
   -- | the user
   User ->
   GitLab [Issue]
-userIssues usr =
-  fromRight (error "userIssues error") <$> gitlabGetMany addr params
+userIssues usr = do
+  result <- gitlabGetMany addr params
+  case result of
+    Left er -> throwError (GitLabError ("userIssues error: " <> responseErrorText er))
+    Right x -> return x
   where
     addr = "/issues"
     params :: [GitLabParam]
@@ -254,7 +259,7 @@
       (issueAttrs (project_id prj) editIssueReq)
   case result of
     Left resp -> return (Left resp)
-    Right Nothing -> error "editIssue error"
+    Right Nothing -> throwError (GitLabError "editIssue error")
     Right (Just iss) -> return (Right iss)
 
 -- | deletes an issue. see <https://docs.gitlab.com/ee/api/issues.html#delete-an-issue>
@@ -300,7 +305,7 @@
       ]
   case result of
     Left resp -> return (Left resp)
-    Right Nothing -> error "reorderIssue error"
+    Right Nothing -> throwError (GitLabError "reorderIssue error")
     Right (Just iss) -> return (Right iss)
 
 -- | Moves an issue to a different project. If a given label or
@@ -493,8 +498,8 @@
 issueStatisticsGroup group filters = do
   result <- issueStatisticsGroup' (group_id group) filters
   case result of
-    Left _s -> error "issueStatisticsGroup error"
-    Right Nothing -> error "issueStatisticsGroup error"
+    Left er -> throwError (GitLabError ("issueStatisticsGroup error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError "issueStatisticsGroup error")
     Right (Just stats) -> return stats
 
 -- | Gets issues count statistics for a given group.
@@ -525,8 +530,8 @@
 issueStatisticsProject proj filters = do
   result <- issueStatisticsProject' (project_id proj) filters
   case result of
-    Left _s -> error "issueStatisticsProject error"
-    Right Nothing -> error "issueStatisticsProject error"
+    Left er -> throwError (GitLabError ("issueStatisticsProject error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError "issueStatisticsProject error")
     Right (Just stats) -> return stats
 
 -- | Gets issues count statistics for a given project.
@@ -576,7 +581,7 @@
     issueFilter_created_after :: Maybe UTCTime,
     issueFilter_created_before :: Maybe UTCTime,
     issueFilter_due_date :: Maybe DueDate,
-    issueFilter_iids :: Maybe Int,
+    issueFilter_iids :: Maybe [Int],
     issueFilter_in :: Maybe SearchIn,
     issueFilter_iteration_id :: Maybe Int,
     issueFilter_iteration_title :: Maybe String,
@@ -639,7 +644,6 @@
       (\t -> Just ("created_after", textToBS (showTime t))) =<< issueFilter_created_after filters,
       (\t -> Just ("created_before", textToBS (showTime t))) =<< issueFilter_created_before filters,
       (\due -> Just ("due_date", textToBS (T.pack (show due)))) =<< issueFilter_due_date filters,
-      (\iids -> Just ("iids[]", textToBS (T.pack (show iids)))) =<< issueFilter_iids filters,
       (\issueIn -> Just ("assignee_id", textToBS (T.pack (show issueIn)))) =<< issueFilter_in filters,
       (\i -> Just ("iteration_id", textToBS (T.pack (show i)))) =<< issueFilter_iteration_id filters,
       (\s -> Just ("iteration_title", textToBS (T.pack s))) =<< issueFilter_iteration_title filters,
@@ -656,6 +660,9 @@
       (\t -> Just ("updated_before", textToBS (showTime t))) =<< issueFilter_updated_before filters,
       (\b -> Just ("with_labels_details", textToBS (showBool b))) =<< issueFilter_with_labels_details filters
     ]
+    <> case issueFilter_iids filters of
+         Nothing   -> []
+         Just iids -> map (\i -> ("iids[]", textToBS (T.pack (show i)))) iids
   where
     textToBS = Just . T.encodeUtf8
     showBool :: Bool -> Text
diff --git a/src/GitLab/API/JobArtifacts.hs b/src/GitLab/API/JobArtifacts.hs
new file mode 100644
--- /dev/null
+++ b/src/GitLab/API/JobArtifacts.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- |
+-- Module      : Job artifacts
+-- Description : Queries about and deletions of job artifacts
+-- Copyright   : (c) Rob Stewart, Heriot-Watt University, 2025
+-- License     : BSD3
+-- Maintainer  : robstewart57@gmail.com
+-- Stability   : stable
+module GitLab.API.JobArtifacts
+  ( -- * Delete all project artifacts
+    deleteProjectArtifacts,
+  )
+where
+
+import qualified Data.ByteString.Lazy as BSL
+import Data.Text (Text)
+import qualified Data.Text as T
+import GitLab.Types
+import GitLab.WebRequests.GitLabWebCalls
+import Network.HTTP.Client
+
+-- | Delete all job artifacts in a project.
+--
+-- Note from the documentation: "Requests to this endpoint set the expiry of all
+-- job artifacts that can be deleted to the current time. The files are then
+-- deleted from the system as part of the regular cleanup of expired job
+-- artifacts. Job logs are never deleted. The regular cleanup occurs
+-- asynchronously on a schedule, so there might be a short delay before
+-- artifacts are deleted."
+deleteProjectArtifacts ::
+  -- | the project
+  Project ->
+  GitLab (Either (Response BSL.ByteString) (Maybe ()))
+deleteProjectArtifacts prj = do
+  gitlabDelete projAddr []
+  where
+    projAddr :: Text
+    projAddr =
+      "/projects/"
+        <> T.pack (show (project_id prj))
+        <> "/artifacts"
diff --git a/src/GitLab/API/Jobs.hs b/src/GitLab/API/Jobs.hs
--- a/src/GitLab/API/Jobs.hs
+++ b/src/GitLab/API/Jobs.hs
@@ -34,8 +34,8 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import qualified Data.Text as T
 import GitLab.Types
 import GitLab.WebRequests.GitLabWebCalls
@@ -48,7 +48,9 @@
   GitLab [Job]
 jobs project = do
   result <- jobs' (project_id project)
-  return (fromRight (error "jobs error") result)
+  case result of
+    Left er -> throwError (GitLabError ("jobs error: " <> responseErrorText er))
+    Right answer -> return answer
 
 -- | Get a list of jobs in a project. Jobs are sorted in descending
 -- order of their IDs.
@@ -148,7 +150,7 @@
         <> T.pack (show jobId)
         <> "/cancel"
 
--- | Retry a single job of a project.
+-- | Erase a single job of a project.
 eraseJob ::
   -- | the project
   Project ->
diff --git a/src/GitLab/API/Members.hs b/src/GitLab/API/Members.hs
--- a/src/GitLab/API/Members.hs
+++ b/src/GitLab/API/Members.hs
@@ -55,8 +55,8 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import GitLab.API.Users
@@ -73,9 +73,11 @@
 -- user. Returns only direct members and not inherited members through
 -- ancestors groups.
 membersOfProject :: Project -> GitLab [Member]
-membersOfProject prj =
-  fromRight (error "membersOfProject error")
-    <$> gitlabGetMany addr []
+membersOfProject prj = do
+  result <- gitlabGetMany addr []
+  case result of
+    Left er -> throwError (GitLabError ("membersOfProject error: " <> responseErrorText er))
+    Right x -> return x
   where
     addr =
       "/projects/"
diff --git a/src/GitLab/API/MergeRequests.hs b/src/GitLab/API/MergeRequests.hs
--- a/src/GitLab/API/MergeRequests.hs
+++ b/src/GitLab/API/MergeRequests.hs
@@ -26,8 +26,12 @@
 
     --     -- * Get single MR reviewers
 
-    --     -- * Get single MR commits
+    -- * Get single MR commits
+    mergeRequestCommits,
 
+    -- * Get issues that would be closed by merging an MR
+    mergeRequestClosesIssues,
+
     --     -- * Get single MR changes
 
     --     -- * List MR pipelines
@@ -52,9 +56,9 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import Data.Maybe
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -100,7 +104,9 @@
   GitLab [MergeRequest]
 mergeRequests p = do
   result <- mergeRequests' (project_id p)
-  return (fromRight (error "mergeRequests error") result)
+  case result of
+    Left er -> throwError (GitLabError ("mergeRequests error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | returns the merge requests for a project given its project ID.
 mergeRequests' ::
@@ -131,7 +137,9 @@
   GitLab [MergeRequest]
 mergeRequestsWith p attrs = do
   result <- mergeRequestsWith' (project_id p) attrs
-  return (fromRight (error "mergeRequests error") result)
+  case result of
+    Left er -> throwError (GitLabError ("mergeRequests error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | returns the merge requests for a project given its project ID and
 -- a set of search attributes as 'Just' values in 'MergeProjectAttrs'.
@@ -156,6 +164,63 @@
         <> T.pack (show projectId)
         <> "/merge_requests"
 
+mergeRequestCommits ::
+  -- | project ID
+  Project ->
+  -- | merge request ID
+  MergeRequest ->
+  GitLab [Commit]
+mergeRequestCommits p mr = do
+  result <- mergeRequestCommits' (project_id p) (merge_request_iid mr)
+  case result of
+    Left er -> throwError (GitLabError ("mergeRequestCommits error: " <> responseErrorText er))
+    Right x -> return x
+
+mergeRequestCommits' ::
+  -- | project ID
+  Int ->
+  -- | merge request IID
+  Int ->
+  GitLab (Either (Response BSL.ByteString) [Commit])
+mergeRequestCommits' projectId mrId =
+  gitlabGetMany addr []
+  where
+    addr =
+      "/projects/"
+        <> T.pack (show projectId)
+        <> "/merge_requests/"
+        <> T.pack (show mrId)
+        <> "/commits"
+
+-- | Get all issues that would be closed by merging the given merge request.
+--
+-- Calls @GET \/projects\/:id\/merge_requests\/:merge_request_iid\/closes_issues@
+--
+-- NB: this information is not always accurate, for example in the following
+-- scenario:
+--
+--  - a previous iteration of the MR contained commits that closed certain issues
+--  - after a rebase of the MR (with a force push), these commits are no longer present
+--
+-- In this case, 'closes_issues' reports a larger set of closed issues than
+-- expected, as it includes issues closed by commits that are no longer part of
+-- the MR.
+mergeRequestClosesIssues ::
+  -- | project
+  Project ->
+  -- | merge request IID
+  Int ->
+  GitLab (Either (Response BSL.ByteString) [Issue])
+mergeRequestClosesIssues project mrIID =
+  gitlabGetMany addr []
+  where
+    addr =
+      "/projects/"
+        <> T.pack (show (project_id project))
+        <> "/merge_requests/"
+        <> T.pack (show mrIID)
+        <> "/closes_issues"
+
 -- | Creates a merge request.
 createMergeRequest ::
   -- | project
@@ -352,7 +417,9 @@
     -- | Return merge requests deployed before the given date/time.
     mr_attr_deployed_before :: Maybe UTCTime,
     -- | Return merge requests deployed after the given date/time.
-    mr_attr_deployed_after :: Maybe UTCTime
+    mr_attr_deployed_after :: Maybe UTCTime,
+    -- | Return only merge requests with the given internal IDs.
+    mr_attr_iids :: Maybe [Int]
   }
 
 -- TODO create types for merge_request_my_reaction_emoji
@@ -388,7 +455,7 @@
 -- | No merge request search filters, thereby returning all merge requests. Default scope is "all".
 mrAttrs :: MergeProjectAttrs
 mrAttrs =
-  MergeProjectAttrs Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
+  MergeProjectAttrs Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
 
 mrAttrsParams :: MergeProjectAttrs -> [GitLabParam]
 mrAttrsParams attrs =
@@ -422,6 +489,9 @@
       (\t -> Just ("deployed_before", showTime t)) =<< mr_attr_deployed_before attrs,
       (\t -> Just ("deployed_after", showTime t)) =<< mr_attr_deployed_after attrs
     ]
+    <> case mr_attr_iids attrs of
+         Nothing   -> []
+         Just iids -> map (\i -> ("iids[]", showAttr i)) iids
   where
     showAttr :: (Show a) => a -> Maybe BS.ByteString
     showAttr = Just . T.encodeUtf8 . T.pack . show
diff --git a/src/GitLab/API/Pipelines.hs b/src/GitLab/API/Pipelines.hs
--- a/src/GitLab/API/Pipelines.hs
+++ b/src/GitLab/API/Pipelines.hs
@@ -52,8 +52,6 @@
     Right ps -> return (Just ps)
     Left _err -> return Nothing
 
--- return (fromRight (error "pipelines error") result)
-
 -- | returns the pipelines for a project given its project ID.
 pipelines' ::
   -- | the project ID
diff --git a/src/GitLab/API/Projects.hs b/src/GitLab/API/Projects.hs
--- a/src/GitLab/API/Projects.hs
+++ b/src/GitLab/API/Projects.hs
@@ -101,8 +101,8 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import Data.List
 import Data.Maybe
 import Data.Text (Text)
@@ -126,11 +126,11 @@
   -- | project filters
   ProjectSearchAttrs ->
   GitLab [Project]
-projects attrs =
-  fromRight (error "projects error")
-    <$> gitlabGetMany
-      "/projects"
-      (projectSearchAttrsParams attrs)
+projects attrs = do
+  result <- gitlabGetMany "/projects" (projectSearchAttrsParams attrs)
+  case result of
+    Left er -> throwError (GitLabError ("projects error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a specific project. This endpoint can be accessed without
 -- authentication if the project is publicly accessible.
@@ -138,7 +138,7 @@
   Int ->
   GitLab (Either (Response BSL.ByteString) (Maybe Project))
 project pId = do
-  gitlabGetOne urlPath []
+  gitlabGetOne urlPath [("statistics", Just (T.encodeUtf8 "true"))]
   where
     urlPath =
       "/projects/"
@@ -177,7 +177,7 @@
           (urlPath (user_id usr))
           (projectSearchAttrsParams attrs)
       case result of
-        Left _ -> error "userProjects' error"
+        Left er -> throwError (GitLabError ("userProjects' error: " <> responseErrorText er))
         Right projs -> return (Just projs)
   where
     urlPath usrId = "/users/" <> T.pack (show usrId) <> "/projects"
@@ -195,13 +195,16 @@
 -- > userProjects myUser
 starredProjects :: User -> ProjectSearchAttrs -> GitLab [Project]
 starredProjects usr attrs = do
-  fromRight (error "starredProjects error")
-    <$> gitlabGetMany
+  result <-
+    gitlabGetMany
       ( "/users/"
           <> T.pack (show (user_id usr))
           <> "/starred_projects"
       )
       (projectSearchAttrsParams attrs)
+  case result of
+    Left er -> throwError (GitLabError ("starredProjects error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of ancestor groups for this project.
 projectGroups ::
@@ -290,7 +293,7 @@
       (projectAttrsParams attrs)
   case result of
     Left resp -> return (Left resp)
-    Right Nothing -> error "editProject error"
+    Right Nothing -> throwError (GitLabError "editProject error")
     Right (Just proj) -> return (Right proj)
 
 -- | Forks a project into the user namespace of the authenticated user
@@ -507,13 +510,12 @@
   Text ->
   GitLab [Project]
 projectsWithName projectName = do
-  foundProjects <-
-    fromRight (error "projectsWithName error")
-      <$> gitlabGetMany
-        "/projects"
-        [("search", Just (T.encodeUtf8 projectName))]
-  return $
-    filter (\prj -> projectName == project_path prj) foundProjects
+  result <- gitlabGetMany "/projects" [("search", Just (T.encodeUtf8 projectName))]
+  case result of
+    Left er -> throwError (GitLabError ("projectsWithName error: " <> responseErrorText er))
+    Right foundProjects -> do
+      return $
+        filter (\prj -> projectName == project_path prj) foundProjects
 
 -- | gets a project with the given name for the given full path of the
 --   namespace. E.g.
@@ -581,7 +583,7 @@
       ]
   case result of
     Left resp -> return (Left resp)
-    Right Nothing -> error "transferProject error"
+    Right Nothing -> throwError (GitLabError "transferProject error")
     Right (Just proj) -> return (Right proj)
 
 --------------------
@@ -609,11 +611,14 @@
 -- was created.
 projectOfIssue :: Issue -> GitLab Project
 projectOfIssue iss = do
-  let prId = fromMaybe (error "projectOfIssue error") (issue_project_id iss)
-  result <- project prId
-  case fromRight (error "projectOfIssue error") result of
-    Nothing -> error "projectOfIssue error"
-    Just proj -> return proj
+  case issue_project_id iss of
+    Nothing -> throwError (GitLabError "projectOfIssue error")
+    Just prId -> do
+      result <- project prId
+      case result of
+        Left er -> throwError (GitLabError ("projectOfIssue error: " <> responseErrorText er))
+        Right Nothing -> throwError (GitLabError "projectOfIssue error")
+        Right (Just proj) -> return proj
 
 -- | gets all diffs in a project for a given commit SHA.
 projectDiffs :: Project -> Text -> GitLab (Either (Response BSL.ByteString) [Diff])
@@ -936,7 +941,7 @@
 defaultProjectSearchAttrs =
   ProjectSearchAttrs Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
 
--- | Attributes related to a group
+-- | Attributes related to a project
 data ProjectSearchAttrs = ProjectSearchAttrs
   { -- | Limit by archived status.
     projectSearchFilter_archived :: Maybe Bool,
@@ -960,7 +965,7 @@
     -- | Limit by current user minimal access level.
     projectSearchFilter_min_access_level :: Maybe AccessLevel,
     -- | Return projects ordered by a given criteria.
-    projectSearchFilter_order_by :: Maybe OrderBy,
+    projectSearchFilter_order_by :: Maybe ProjectOrderBy,
     -- | Limit by projects explicitly owned by the current user.
     projectSearchFilter_owned :: Maybe Bool,
     -- | Limit projects where the repository checksum calculation has
diff --git a/src/GitLab/API/Repositories.hs b/src/GitLab/API/Repositories.hs
--- a/src/GitLab/API/Repositories.hs
+++ b/src/GitLab/API/Repositories.hs
@@ -23,10 +23,10 @@
   )
 where
 
+import Control.Monad.Except
 import Control.Monad.IO.Class
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import Data.Maybe
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -41,8 +41,11 @@
   -- | the project
   Project ->
   GitLab [Repository]
-repositoryTree project =
-  fromRight (error "repositories error") <$> repositories' (project_id project)
+repositoryTree project = do
+  result <- repositories' (project_id project)
+  case result of
+    Left er -> throwError (GitLabError ("repositories error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | returns a list of repository files and directories in a project
 -- given its project ID.
@@ -86,7 +89,7 @@
   result <- getFileArchiveBS' (project_id project) format
   case result of
     Left resp -> return (Left resp)
-    Right Nothing -> error "could not download file"
+    Right Nothing -> throwError (GitLabError "fileArchiveBS could not download file")
     Right (Just bs) -> return (Right bs)
 
 -- | get a file archive of the repository files using the project's
@@ -106,7 +109,7 @@
   case attempt of
     Left st -> return (Left st)
     Right Nothing ->
-      Right <$> error "cannot download file"
+      Right <$> throwError (GitLabError "getFileArchive' could not download file")
     Right (Just archiveData) ->
       Right <$> liftIO (BSL.writeFile fPath archiveData)
 
@@ -145,9 +148,11 @@
   -- asc.
   Maybe SortBy ->
   GitLab [Contributor]
-contributors prj order sort =
-  fromRight (error "contributors error")
-    <$> gitlabGetMany addr params
+contributors prj order sort = do
+  result <- gitlabGetMany addr params
+  case result of
+    Left er -> throwError (GitLabError ("contributors error: " <> responseErrorText er))
+    Right x -> return x
   where
     addr =
       "/projects/"
diff --git a/src/GitLab/API/Users.hs b/src/GitLab/API/Users.hs
--- a/src/GitLab/API/Users.hs
+++ b/src/GitLab/API/Users.hs
@@ -95,8 +95,8 @@
   )
 where
 
+import Control.Monad.Except
 import qualified Data.ByteString.Lazy as BSL
-import Data.Either
 import Data.Maybe
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -109,7 +109,10 @@
 users :: GitLab [User]
 users = do
   let pathUser = "/users"
-  fromRight (error "allUsers error") <$> gitlabGetMany pathUser []
+  result <- gitlabGetMany pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("allUsers error: " <> responseErrorText er))
+    Right usrs -> return usrs
 
 -- | Get a single user.
 user ::
@@ -242,16 +245,24 @@
 
 -- | Get current user.
 currentUser :: GitLab User
-currentUser =
-  fromMaybe (error "currentUser") . fromRight (error "currentUser error") <$> gitlabGetOne pathUser []
+currentUser = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("currentUser error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError "currentUser error")
+    Right (Just usr) -> return usr
   where
     pathUser =
       "/user"
 
 -- | Get current user status.
 currentUserStatus :: GitLab UserStatus
-currentUserStatus =
-  fromMaybe (error "currentUserStatus") . fromRight (error "currentUserStatus error") <$> gitlabGetOne pathUser []
+currentUserStatus = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("currentUserStatus error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError "currentUserStatus error")
+    Right (Just x) -> return x
   where
     pathUser =
       "/user/status"
@@ -261,8 +272,12 @@
   -- | user
   User ->
   GitLab UserStatus
-userStatus usr =
-  fromMaybe (error "userStatus") . fromRight (error "userStatus error") <$> gitlabGetOne pathUser []
+userStatus usr = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("userStatus error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError ("userStatus error"))
+    Right (Just st) -> return st
   where
     pathUser =
       "/users/"
@@ -272,8 +287,12 @@
 -- | Get the status of the current user.
 userPreferences ::
   GitLab UserPrefs
-userPreferences =
-  fromMaybe (error "userPreferences") . fromRight (error "userPreferences error") <$> gitlabGetOne pathUser []
+userPreferences = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("userPreferences error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError ("userPreferences error"))
+    Right (Just prefs) -> return prefs
   where
     pathUser =
       "/user/preferences"
@@ -312,16 +331,24 @@
 
 -- | Get the counts of the currently signed in user.
 currentUserCounts :: GitLab UserCount
-currentUserCounts =
-  fromMaybe (error "currentUserCounts") . fromRight (error "currentUserCounts error") <$> gitlabGetOne pathUser []
+currentUserCounts = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("currentUserCounts error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError ("currentUserCounts error"))
+    Right (Just counts) -> return counts
   where
     pathUser =
       "/user_counts"
 
 -- | Get a list of currently authenticated user’s SSH keys.
 currentUserSshKeys :: GitLab Key
-currentUserSshKeys =
-  fromMaybe (error "currentUserSshKeys") . fromRight (error "currentUserSshKeys error") <$> gitlabGetOne pathUser []
+currentUserSshKeys = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("currentUserSshKeys error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError ("currentUserSshKeys error"))
+    Right (Just k) -> return k
   where
     pathUser =
       "/user/keys"
@@ -331,8 +358,12 @@
   -- | user
   User ->
   GitLab Key
-userSshKeys usr =
-  fromMaybe (error "userSshKeys") . fromRight (error "userSshKeys error") <$> gitlabGetOne pathUser []
+userSshKeys usr = do
+  result <- gitlabGetOne pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("userSshKeys error: " <> responseErrorText er))
+    Right Nothing -> throwError (GitLabError ("userSshKeys error"))
+    Right (Just k) -> return k
   where
     pathUser =
       "/user/"
@@ -415,7 +446,10 @@
 emails :: GitLab [Email]
 emails = do
   let pathUser = "/user/emails/"
-  fromRight (error "emails error") <$> gitlabGetMany pathUser []
+  result <- gitlabGetMany pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("emails error: " <> responseErrorText er))
+    Right x -> return x
 
 -- | Get a list of currently authenticated user’s emails.
 emailsCurrentUser ::
@@ -427,19 +461,25 @@
         "/user/"
           <> T.pack (show (user_email usr))
           <> "/emails/"
-  fromRight (error "emails error") <$> gitlabGetMany pathUser []
+  result <- gitlabGetMany pathUser []
+  case result of
+    Left er -> throwError (GitLabError ("emailsCurrentUser error: " <> responseErrorText er))
+    Right emls -> return emls
 
 -- | Used internally by the following functions
 userAction ::
   -- | user action
   Text ->
-  -- | function name for the error
+  -- | function name
   Text ->
   -- | user
   User ->
   GitLab (Maybe User)
-userAction action funcName usr =
-  fromRight (error (T.unpack funcName <> " error")) <$> gitlabPost userAddr []
+userAction action _funcName usr = do
+  result <- gitlabPost userAddr []
+  case result of
+    Left er -> throwError (GitLabError ("userAction error: " <> responseErrorText er))
+    Right x -> return x
   where
     userAddr :: Text
     userAddr =
diff --git a/src/GitLab/SystemHooks/GitLabSystemHooks.hs b/src/GitLab/SystemHooks/GitLabSystemHooks.hs
--- a/src/GitLab/SystemHooks/GitLabSystemHooks.hs
+++ b/src/GitLab/SystemHooks/GitLabSystemHooks.hs
@@ -19,11 +19,12 @@
 
 import qualified Control.Exception as E
 import Control.Monad
+import Control.Monad.Except
 import Control.Monad.IO.Class
 import qualified Control.Monad.Reader as MR
 import Data.Text (Text)
 import qualified Data.Text as T
-import qualified Data.Text.IO as TIO
+import qualified Data.Text.IO.Utf8 as TIO
 import Data.Typeable
 import GitLab.SystemHooks.Types
 import GitLab.Types
@@ -41,20 +42,27 @@
 -- received from a function argument.
 receiveString :: Text -> [Rule] -> GitLab ()
 receiveString eventContent rules = do
-  -- maybe log the JSON received
+  -- log all JSON received if AllJSON stated
   traceSystemHook eventContent
   -- fire the rules
-  didFire <- mapM (fire eventContent) rules
+  didFire <-
+    mapM (fire eventContent) rules
+      `catchError` \(GitLabError e) -> do
+        liftIO $ do
+          fpath <- writeSystemTempFile "gitlab-system-hook-exception-" (T.unpack e)
+          void $ setFileMode fpath otherReadMode
+          return []
+
   -- if nothing fired
-  when (not (or didFire)) $ do
+  unless (or didFire) $ do
     cfg <- MR.asks serverCfg
-    -- maybe log the JSON if it was not parsed
+    -- log the JSON if it was not parsed and NonParsedJSON was stated
     when (debugSystemHooks cfg == Just NonParsedJSON) $ liftIO $ do
       -- no rules fired, was it because the JSON was not parsed?
-      when (not (attemptGitLabEventParse eventContent)) $ do
+      unless (attemptGitLabEventParse eventContent) $ do
         fpath <- writeSystemTempFile "gitlab-system-hook-nonparsed-" (T.unpack eventContent)
         void $ setFileMode fpath otherReadMode
-    -- maybe log the JSON if no rules were fired for it
+    -- log the JSON if no rules were fired for it and UnprocessedEvents was states
     when (debugSystemHooks cfg == Just UnprocessedEvents) $ liftIO $ do
       fpath <- writeSystemTempFile "gitlab-system-hook-unprocessed-" (T.unpack eventContent)
       void $ setFileMode fpath otherReadMode
@@ -81,11 +89,13 @@
 fire :: Text -> Rule -> GitLab Bool
 fire contents rule = do
   result <- tryFire contents rule
-  case result of
-    True -> do
-      liftIO (putStrLn ("fired: " <> labelOf rule))
-      return True
-    False -> return False
+  if result
+    then
+      ( do
+          liftIO (putStrLn ("fired: " <> labelOf rule))
+          return True
+      )
+    else return False
   where
     labelOf :: Rule -> String
     labelOf (Match lbl _) = lbl
@@ -165,8 +175,8 @@
       (parseEvent contents :: Maybe GroupRemove)
     `orElse` fireIf'
       (Just (\_ -> return True))
-      (cast f :: Maybe (GroupRename -> GitLab ()))
-      (parseEvent contents :: Maybe GroupRename)
+      (cast f :: Maybe (GroupRenameEvent -> GitLab ()))
+      (parseEvent contents :: Maybe GroupRenameEvent)
     `orElse` fireIf'
       (Just (\_ -> return True))
       (cast f :: Maybe (NewGroupMember -> GitLab ()))
@@ -219,6 +229,14 @@
       (Just (\_ -> return True))
       (cast f :: Maybe (WorkItemEvent -> GitLab ()))
       (parseEvent contents :: Maybe WorkItemEvent)
+    `orElse` fireIf'
+      (Just (\_ -> return True))
+      (cast f :: Maybe (EmojiEvent -> GitLab ()))
+      (parseEvent contents :: Maybe EmojiEvent)
+    `orElse` fireIf'
+      (Just (\_ -> return True))
+      (cast f :: Maybe (AccessTokenEvent -> GitLab ()))
+      (parseEvent contents :: Maybe AccessTokenEvent)
 tryFire contents (MatchIf _ predF f) = do
   fireIf'
     (cast predF :: Maybe (ProjectCreate -> GitLab Bool))
@@ -289,9 +307,9 @@
       (cast f :: Maybe (GroupRemove -> GitLab ()))
       (parseEvent contents :: Maybe GroupRemove)
     `orElse` fireIf'
-      (cast predF :: Maybe (GroupRename -> GitLab Bool))
-      (cast f :: Maybe (GroupRename -> GitLab ()))
-      (parseEvent contents :: Maybe GroupRename)
+      (cast predF :: Maybe (GroupRenameEvent -> GitLab Bool))
+      (cast f :: Maybe (GroupRenameEvent -> GitLab ()))
+      (parseEvent contents :: Maybe GroupRenameEvent)
     `orElse` fireIf'
       (cast predF :: Maybe (NewGroupMember -> GitLab Bool))
       (cast f :: Maybe (NewGroupMember -> GitLab ()))
@@ -344,6 +362,14 @@
       (cast predF :: Maybe (WorkItemEvent -> GitLab Bool))
       (cast f :: Maybe (WorkItemEvent -> GitLab ()))
       (parseEvent contents :: Maybe WorkItemEvent)
+    `orElse` fireIf'
+      (cast predF :: Maybe (EmojiEvent -> GitLab Bool))
+      (cast f :: Maybe (EmojiEvent -> GitLab ()))
+      (parseEvent contents :: Maybe EmojiEvent)
+    `orElse` fireIf'
+      (cast predF :: Maybe (AccessTokenEvent -> GitLab Bool))
+      (cast f :: Maybe (AccessTokenEvent -> GitLab ()))
+      (parseEvent contents :: Maybe AccessTokenEvent)
 
 fireIf' :: (Typeable a, Show a) => Maybe (a -> GitLab Bool) -> Maybe (a -> GitLab ()) -> Maybe a -> GitLab Bool
 fireIf' castPred castF parsed = do
@@ -418,7 +444,7 @@
                                                                   case parseEvent contents :: Maybe GroupRemove of
                                                                     Just _ -> True
                                                                     Nothing ->
-                                                                      case parseEvent contents :: Maybe GroupRename of
+                                                                      case parseEvent contents :: Maybe GroupRenameEvent of
                                                                         Just _ -> True
                                                                         Nothing ->
                                                                           case parseEvent contents :: Maybe NewGroupMember of
@@ -456,4 +482,10 @@
                                                                                                                     Nothing ->
                                                                                                                       case parseEvent contents :: Maybe WorkItemEvent of
                                                                                                                         Just _ -> True
-                                                                                                                        Nothing -> False
+                                                                                                                        Nothing ->
+                                                                                                                          case parseEvent contents :: Maybe EmojiEvent of
+                                                                                                                            Just _ -> True
+                                                                                                                            Nothing ->
+                                                                                                                              case parseEvent contents :: Maybe AccessTokenEvent of
+                                                                                                                                Just _ -> True
+                                                                                                                                Nothing -> False
diff --git a/src/GitLab/SystemHooks/Types.hs b/src/GitLab/SystemHooks/Types.hs
--- a/src/GitLab/SystemHooks/Types.hs
+++ b/src/GitLab/SystemHooks/Types.hs
@@ -5,7 +5,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE StrictData #-}
-{-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeFamilies #-}
 
 -- |
@@ -35,7 +34,7 @@
     KeyRemove (..),
     GroupCreate (..),
     GroupRemove (..),
-    GroupRename (..),
+    GroupRenameEvent (..),
     NewGroupMember (..),
     GroupMemberRemove (..),
     GroupMemberUpdate (..),
@@ -55,6 +54,7 @@
     MergeParams (..),
     UserEvent (..),
     BuildEvent (..),
+    Environment (..),
     BuildCommit (..),
     BuildProject (..),
     PipelineEvent (..),
@@ -77,11 +77,17 @@
     ArtifactsFile (..),
     NoteEvent (..),
     NoteObjectAttributes (..),
+    ChangePosition (..),
     WikiPageEvent (..),
     Wiki (..),
     WikiPageObjectAttributes (..),
     WorkItemEvent (..),
     WorkItemObjectAttributes (..),
+    EmojiEvent (..),
+    EmojiProject (..),
+    EmojiObjectAttributes (..),
+    AccessTokenEvent (..),
+    AccessTokenObjectAttributes (..),
     parseEvent,
   )
 where
@@ -107,7 +113,7 @@
 parseEvent :: (FromJSON a) => Text -> Maybe a
 parseEvent eventText =
   case eitherDecodeStrict (T.encodeUtf8 eventText) of
-    Left _error -> Nothing
+    Left _er -> Nothing
     Right event -> Just event
 
 instance SystemHook ProjectCreate where
@@ -404,12 +410,12 @@
   }
   deriving (Typeable, Show, Eq)
 
-instance SystemHook GroupRename where
+instance SystemHook GroupRenameEvent where
   match = Match
   matchIf = MatchIf
 
 -- | A group has been renamed.
-data GroupRename = GroupRename
+data GroupRenameEvent = GroupRenameEvent
   { groupRename_event_name :: Text,
     groupRename_created_at :: Text, -- todo improve: date
     groupRename_updated_at :: Text, -- todo improve: date
@@ -518,7 +524,7 @@
     tagPush_before :: Text,
     tagPush_after :: Text,
     tagPush_ref :: Text,
-    tagPush_checkout_sha :: Text,
+    tagPush_checkout_sha :: Maybe Text,
     tagPush_user_id :: Int,
     tagPush_user_name :: Text,
     tagPush_user_avatar :: Text,
@@ -757,6 +763,8 @@
   | Noted
   | WikiPaged
   | WorkItemed
+  | Emojid
+  | AccessTokened
   deriving (Show, Eq)
 
 -- | CI build
@@ -786,7 +794,7 @@
     build_event_commit :: BuildCommit,
     build_event_repository :: Repository,
     build_event_project :: BuildProject,
-    build_event_environment :: Maybe Text
+    build_event_environment :: Maybe Environment
   }
   deriving (Typeable, Show, Eq, Generic)
 
@@ -794,6 +802,13 @@
   match = Match
   matchIf = MatchIf
 
+data Environment = Environment
+  { environment_name :: Text, -- "production"
+    environment_action :: Text, -- "start"
+    environment_deployment_tier :: Maybe Text -- "production"
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
 -- | CI build commit
 data BuildCommit = BuildCommit
   { build_commit_id :: Int,
@@ -875,7 +890,7 @@
     pipeline_build_started_at :: Maybe Text,
     pipeline_build_finished_at :: Maybe Text,
     pipeline_build_duration :: Maybe Double,
-    pipeline_build_queued_duration :: Double,
+    pipeline_build_queued_duration :: Maybe Double,
     pipeline_build_failure_reason :: Maybe Text,
     pipeline_build_when :: Text, -- "on_success"
     pipeline_build_manual :: Bool,
@@ -883,7 +898,7 @@
     pipeline_build_user :: UserEvent,
     pipeline_build_runner :: Maybe Runner,
     pipeline_build_artifacts_file :: ArtifactsFile,
-    pipeline_build_environment :: Maybe Text
+    pipeline_build_environment :: Maybe Environment
   }
   deriving (Typeable, Show, Eq, Generic)
 
@@ -1068,17 +1083,17 @@
 data NoteObjectAttributes = NoteObjectAttributes
   { note_object_attributes_attachment :: Maybe Text, -- ?
     note_object_attributes_author_id :: Int,
-    note_object_attributes_change_position :: Maybe Text, -- ?
+    note_object_attributes_change_position :: Maybe ChangePosition,
     note_object_attributes_commit_id :: Maybe String,
     note_object_attributes_created_at :: Text, -- change to date
     note_object_attributes_discussion_id :: Text,
     note_object_attributes_id :: Int,
-    note_object_attributes_line_code :: Maybe Int, -- ?
+    note_object_attributes_line_code :: Maybe Text,
     note_object_attributes_note :: Text,
     note_object_attributes_noteable_id :: Maybe Int,
     note_object_attributes_noteable_type :: Text, -- "Issue"
-    note_object_attributes_original_position :: Maybe Int, -- ?
-    note_object_attributes_position :: Maybe Int, -- ?
+    note_object_attributes_original_position :: Maybe ChangePosition,
+    note_object_attributes_position :: Maybe ChangePosition,
     note_object_attributes_project_id :: Int,
     note_object_attributes_resolved_at :: Maybe Text, -- date?
     note_object_attributes_resolved_by_id :: Maybe Int,
@@ -1090,10 +1105,23 @@
     note_object_attributes_updated_by_id :: Maybe Int, -- ?
     note_object_attributes_description :: Text,
     note_object_attributes_url :: Text,
-    note_object_attributes_action :: Text -- "create"
+    note_object_attributes_action :: Maybe Text -- "create"
   }
   deriving (Typeable, Show, Eq, Generic)
 
+data ChangePosition = ChangePosition
+  { change_position_base_sha :: Maybe Text,
+    change_position_start_sha :: Maybe Text,
+    change_position_head_sha :: Maybe Text,
+    change_position_old_path :: Maybe Text,
+    change_position_new_path :: Maybe Text,
+    change_position_position_type :: Maybe Text, -- change to data type, currently e.g. "text"
+    change_position_old_line :: Maybe Int,
+    change_position_new_line :: Maybe Int,
+    change_position_line_range :: Maybe Text
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
 -- | Note event
 data WikiPageEvent = WikiPageEvent
   { wiki_page_event_object_kind :: Text,
@@ -1184,6 +1212,72 @@
   }
   deriving (Typeable, Show, Eq, Generic)
 
+data EmojiEvent = EmojiEvent
+  { emoji_event_user :: User, -- use URL related type in future for these
+    emoji_event_project_id :: Maybe Int,
+    emoji_event_project :: Maybe EmojiProject,
+    emoji_event_object_attributes :: Maybe EmojiObjectAttributes,
+    emoji_event_note :: Maybe NoteObjectAttributes,
+    emoji_event_commit :: Maybe CommitEvent
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
+data EmojiProject = EmojiProject
+  { emoji_project_id :: Int,
+    emoji_project_name :: Text,
+    emoji_project_description :: Maybe Text,
+    emoji_project_web_url :: Text,
+    emoji_project_avatar_url :: Maybe Text,
+    emoji_project_git_ssh_url :: Text,
+    emoji_project_git_http_url :: Text,
+    emoji_project_namespace :: Text,
+    emoji_project_visibility_level :: Int, -- change with a data type, currently e.g. 0
+    emoji_project_path_with_namespace :: Text,
+    emoji_project_default_branch :: Text,
+    emoji_project_ci_config_path :: Maybe Text,
+    emoji_project_homepage :: Text,
+    emoji_project_url :: Text,
+    emoji_project_ssh_url :: Text,
+    emoji_project_http_url :: Text
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
+instance SystemHook EmojiEvent where
+  match = Match
+  matchIf = MatchIf
+
+data EmojiObjectAttributes = EmojiObjectAttributes
+  { emoji_object_user_id :: Int,
+    emoji_object_created_at :: Text, -- change to date type
+    emoji_object_id :: Int,
+    emoji_object_name :: Text, -- change to its own type, currently "thumbsup"
+    emoji_object_awardable_type :: Text, -- change to its own type, currently "Note"
+    emoji_object_updated_at :: Maybe Text, -- change to date type
+    emoji_object_awarded_on_url :: Maybe Text -- change to URL type
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
+data AccessTokenEvent = AccessTokenEvent
+  { access_token_event_project :: EmojiProject, -- make this project type more generic for multiple events rather than named for one project type
+    access_token_event_object_attributes :: AccessTokenObjectAttributes,
+    access_token_event_event_name :: Text,
+    access_token_event_interval :: Text
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
+data AccessTokenObjectAttributes = AccessTokenObjectAttributes
+  { access_token_object_user_id :: Int,
+    access_token_object_created_at :: Text, -- change to date type
+    access_token_object_id :: Int,
+    access_token_object_name :: Text,
+    access_token_object_expires_at :: Maybe Text -- change to date type
+  }
+  deriving (Typeable, Show, Eq, Generic)
+
+instance SystemHook AccessTokenEvent where
+  match = Match
+  matchIf = MatchIf
+
 instance FromJSON ProjectCreate where
   parseJSON =
     withObject "ProjectCreate" $ \v -> do
@@ -1525,7 +1619,7 @@
             _unexpected -> fail "group_remove parsing failed"
         _unexpected -> fail "group_remove parsing failed"
 
-instance FromJSON GroupRename where
+instance FromJSON GroupRenameEvent where
   parseJSON =
     withObject "GroupRename" $ \v -> do
       isProjectEvent <- v .:? "event_name"
@@ -1533,7 +1627,7 @@
         Just theEvent ->
           case theEvent of
             GroupRenamed ->
-              GroupRename
+              GroupRenameEvent
                 <$> v .: "event_name"
                 <*> v .: "created_at"
                 <*> v .: "updated_at"
@@ -1541,8 +1635,8 @@
                 <*> v .: "path"
                 <*> v .: "full_path"
                 <*> v .: "group_id"
-                <*> v .: "owner_name"
-                <*> v .: "owner_email"
+                <*> v .:? "owner_name"
+                <*> v .:? "owner_email"
                 <*> v .: "old_path"
                 <*> v .: "old_full_path"
             _unexpected -> fail "group_rename parsing failed"
@@ -1827,6 +1921,24 @@
             _unexpected -> fail "work item parsing failed"
         _unexpected -> fail "work item parsing failed"
 
+instance FromJSON EmojiEvent where
+  parseJSON =
+    withObject "EmojiEvent" $ \v -> do
+      isWorkItemEvent <- v .:? "object_kind"
+      case isWorkItemEvent of
+        Just theEvent ->
+          case theEvent of
+            Emojid ->
+              EmojiEvent
+                <$> v .: "user"
+                <*> v .: "project_id"
+                <*> v .: "project"
+                <*> v .: "object_attributes"
+                <*> v .:? "note"
+                <*> v .:? "commit"
+            _unexpected -> fail "emoji parsing failed"
+        _unexpected -> fail "emoji parsing failed"
+
 -- TODO: replace bodyNoPrefix with a template Haskell based approach in src/GitLab/Types.hs
 -- e.g.
 
@@ -2130,6 +2242,50 @@
 bodyNoPrefix "work_item_object_severity" = "severity"
 bodyNoPrefix "work_item_object_customer_relations_contacts" = "customer_relations_contacts"
 bodyNoPrefix "work_item_object_action" = "action"
+bodyNoPrefix "emoji_object_user_id" = "user_id"
+bodyNoPrefix "emoji_object_created_at" = "created_at"
+bodyNoPrefix "emoji_object_id" = "id"
+bodyNoPrefix "emoji_object_name" = "name"
+bodyNoPrefix "emoji_object_awardable_type" = "awardable_type"
+bodyNoPrefix "emoji_object_updated_at" = "updated_at"
+bodyNoPrefix "emoji_object_awarded_on_url" = "awarded_on_url"
+bodyNoPrefix "emoji_project_id" = "id"
+bodyNoPrefix "emoji_project_name" = "name"
+bodyNoPrefix "emoji_project_description" = "description"
+bodyNoPrefix "emoji_project_web_url" = "web_url"
+bodyNoPrefix "emoji_project_avatar_url" = "avatar_url"
+bodyNoPrefix "emoji_project_git_ssh_url" = "git_ssh_url"
+bodyNoPrefix "emoji_project_git_http_url" = "git_http_url"
+bodyNoPrefix "emoji_project_namespace" = "namespace"
+bodyNoPrefix "emoji_project_visibility_level" = "visibility_level"
+bodyNoPrefix "emoji_project_path_with_namespace" = "path_with_namespace"
+bodyNoPrefix "emoji_project_default_branch" = "default_branch"
+bodyNoPrefix "emoji_project_ci_config_path" = "ci_config_path"
+bodyNoPrefix "emoji_project_homepage" = "homepage"
+bodyNoPrefix "emoji_project_url" = "url"
+bodyNoPrefix "emoji_project_ssh_url" = "ssh_url"
+bodyNoPrefix "emoji_project_http_url" = "http_url"
+bodyNoPrefix "change_position_base_sha" = "base_sha"
+bodyNoPrefix "change_position_start_sha" = "start_sha"
+bodyNoPrefix "change_position_head_sha" = "head_sha"
+bodyNoPrefix "change_position_old_path" = "old_path"
+bodyNoPrefix "change_position_new_path" = "new_path"
+bodyNoPrefix "change_position_position_type" = "position_type"
+bodyNoPrefix "change_position_old_line" = "old_line"
+bodyNoPrefix "change_position_new_line" = "new_line"
+bodyNoPrefix "change_position_line_range" = "line_range"
+bodyNoPrefix "access_token_event_project" = "event_project"
+bodyNoPrefix "access_token_event_object_attributes" = "object_attributes"
+bodyNoPrefix "access_token_event_event_name" = "event_name"
+bodyNoPrefix "access_token_event_interval" = "interval"
+bodyNoPrefix "access_token_object_user_id" = "user_id"
+bodyNoPrefix "access_token_object_created_at" = "created_at"
+bodyNoPrefix "access_token_object_id" = "id"
+bodyNoPrefix "access_token_object_name" = "name"
+bodyNoPrefix "access_token_object_expires_at" = "expires_at"
+bodyNoPrefix "environment_name" = "name"
+bodyNoPrefix "environment_action" = "action"
+bodyNoPrefix "environment_deployment_tier" = "deployment_tier"
 bodyNoPrefix s = fail ("uexpected JSON field prefix: " <> s)
 
 instance FromJSON ProjectEvent where
@@ -2204,6 +2360,14 @@
           }
       )
 
+instance FromJSON Environment where
+  parseJSON =
+    genericParseJSON
+      ( defaultOptions
+          { fieldLabelModifier = bodyNoPrefix
+          }
+      )
+
 instance FromJSON BuildCommit where
   parseJSON =
     genericParseJSON
@@ -2396,6 +2560,38 @@
           }
       )
 
+instance FromJSON EmojiObjectAttributes where
+  parseJSON =
+    genericParseJSON
+      ( defaultOptions
+          { fieldLabelModifier = bodyNoPrefix
+          }
+      )
+
+instance FromJSON EmojiProject where
+  parseJSON =
+    genericParseJSON
+      ( defaultOptions
+          { fieldLabelModifier = bodyNoPrefix
+          }
+      )
+
+instance FromJSON ChangePosition where
+  parseJSON =
+    genericParseJSON
+      ( defaultOptions
+          { fieldLabelModifier = bodyNoPrefix
+          }
+      )
+
+instance FromJSON AccessTokenObjectAttributes where
+  parseJSON =
+    genericParseJSON
+      ( defaultOptions
+          { fieldLabelModifier = bodyNoPrefix
+          }
+      )
+
 instance FromJSON Label where
   parseJSON = withObject "Label" $ \obj -> do
     labelId <- obj .:? "id"
@@ -2422,6 +2618,22 @@
           label_group_id = labelGroupId
         }
 
+instance FromJSON AccessTokenEvent where
+  parseJSON =
+    withObject "AccessTokenEvent" $ \v -> do
+      isAccessTokenEvent <- v .:? "object_kind"
+      case isAccessTokenEvent of
+        Just theEvent ->
+          case theEvent of
+            AccessTokened ->
+              AccessTokenEvent
+                <$> v .: "project"
+                <*> v .: "object_attributes"
+                <*> v .: "event_name"
+                <*> v .: "interval"
+            _unexpected -> fail "acess_token parsing failed"
+        _unexpected -> fail "acess_token parsing failed"
+
 instance FromJSON ProjectAction where
   parseJSON (String "project_create") = return ProjectCreated
   parseJSON (String "project_destroy") = return ProjectDestroyed
@@ -2452,4 +2664,6 @@
   parseJSON (String "note") = return Noted
   parseJSON (String "wiki_page") = return WikiPaged
   parseJSON (String "work_item") = return WorkItemed
+  parseJSON (String "emoji") = return Emojid
+  parseJSON (String "access_token") = return AccessTokened
   parseJSON s = fail ("unexpected system hook event: " <> show s)
diff --git a/src/GitLab/Types.hs b/src/GitLab/Types.hs
--- a/src/GitLab/Types.hs
+++ b/src/GitLab/Types.hs
@@ -18,6 +18,7 @@
     GitLabT (..),
     GitLabState (..),
     GitLabServerConfig (..),
+    GitLabError (..),
     AuthMethod (..),
     defaultGitLabServer,
     ArchiveFormat (..),
@@ -26,6 +27,7 @@
     Scope (..),
     SortBy (..),
     OrderBy (..),
+    ProjectOrderBy (..),
     Member (..),
     SamlIdentity (..),
     Identity (..),
@@ -109,6 +111,7 @@
   )
 where
 
+import Control.Monad.Except
 import Control.Monad.IO.Class
 import qualified Control.Monad.IO.Class as MIO
 import qualified Control.Monad.Reader as MR
@@ -126,7 +129,7 @@
 --
 -- Run it with 'runGitLab'
 newtype GitLabT m a = GitLabT (MR.ReaderT GitLabState m a)
-  deriving (Functor, Applicative, Monad, MonadFail, MR.MonadReader GitLabState)
+  deriving (Functor, Applicative, Monad, MonadFail, MR.MonadReader GitLabState, MonadError e)
 
 instance MT.MonadTrans GitLabT where
   lift = GitLabT . MT.lift
@@ -134,8 +137,11 @@
 instance (MIO.MonadIO m) => MIO.MonadIO (GitLabT m) where
   liftIO = GitLabT . MIO.liftIO
 
+data GitLabError = GitLabError Text
+  deriving (Show)
+
 -- | Utility type which uses 'IO' as underlying monad
-type GitLab a = GitLabT IO a
+type GitLab a = GitLabT (ExceptT GitLabError IO) a
 
 -- | state used by GitLab actions, used internally.
 data GitLabState = GitLabState
@@ -162,19 +168,20 @@
     AllJSON
   deriving (Eq)
 
--- | default settings, the 'url' and 'token' values will need to be overwritten.
+-- | default settings, the 'url' and 'authMethod' values may need to be overwritten.
 defaultGitLabServer :: GitLabServerConfig
 defaultGitLabServer =
   GitLabServerConfig
     { url = "https://gitlab.com",
-      token = AuthMethodToken "",
+      token = AuthMethodNone,
       retries = 5,
       debugSystemHooks = Nothing
     }
 
 -- | personal access token, see <https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html>
 data AuthMethod
-  = AuthMethodToken Text
+  = AuthMethodNone
+  | AuthMethodToken Text
   | AuthMethodOAuth Text
 
 -- https://docs.gitlab.com/ee/api/repositories.html#get-file-archive
@@ -280,6 +287,39 @@
   show Popularity = "popularity"
   show Weight = "weight"
 
+-- | Ordering search results
+data ProjectOrderBy
+  = ProjectId
+  | ProjectName
+  | ProjectPath
+  | ProjectCreatedAt
+  | ProjectUpdatedAt
+  | ProjectStarCount
+  | ProjectLastActivity
+  | ProjectSimilarity
+  | -- | only available to admin users
+    ProjectRepositorySize
+  | -- | only available to admin users
+    ProjectStorageSize
+  | -- | only available to admin users
+    ProjectPackageSize
+  | -- | only available to admin users
+    ProjectWikiSize
+
+instance Show ProjectOrderBy where
+  show ProjectId = "id"
+  show ProjectName = "name"
+  show ProjectPath = "path"
+  show ProjectCreatedAt = "created_at"
+  show ProjectUpdatedAt = "updated_at"
+  show ProjectStarCount = "star_count"
+  show ProjectLastActivity = "last_activity"
+  show ProjectSimilarity = "similarity"
+  show ProjectRepositorySize = "repository_size"
+  show ProjectStorageSize = "storage_size"
+  show ProjectPackageSize = "package_size"
+  show ProjectWikiSize = "wiki_size"
+
 -- | member of a project.
 data Member = Member
   { member_id :: Int,
@@ -637,7 +677,7 @@
     issue_web_url :: Text,
     issue_references :: Maybe References,
     issue_confidential :: Maybe Bool,
-    issue_weight :: Maybe Text, -- Int?
+    issue_weight :: Maybe Int,
     issue_epic :: Maybe Epic, -- Int?
     issue_discussion_locked :: Maybe Bool,
     issue_time_stats :: Maybe TimeStats,
@@ -892,7 +932,7 @@
     merge_request_iid :: Int,
     merge_request_project_id :: Int,
     merge_request_title :: Text,
-    merge_request_description :: Text,
+    merge_request_description :: Maybe Text,
     merge_request_state :: Text, -- TODO make a type e.g. 'reopened'
     merge_request_created_at :: UTCTime,
     merge_request_updated_at :: UTCTime,
@@ -1177,7 +1217,7 @@
 
 -- | has a change been promoted to an epic.
 newtype CommandsChanges = CommanandsChanges
-  { commands_changes_promote_to_epic :: Bool
+  { commands_changes_promote_to_epic :: Maybe Bool
   }
   deriving (Show, Eq)
 
@@ -1372,20 +1412,32 @@
 data PushData = PushData
   { push_data_commit_count :: Int,
     push_data_action :: EventActionName,
-    push_data_ref_type :: Text, -- TODO type for "branch"
-    push_data_commit_from :: Text, -- sha hash
-    push_data_commit_to :: Text, -- sha hash
-    push_data_ref :: Text,
-    push_data_commit_title :: Text
+    push_data_ref_type :: Maybe Text, -- TODO type for "branch"
+    push_data_commit_from :: Maybe Text, -- sha hash
+    push_data_commit_to :: Maybe Text, -- sha hash
+    push_data_ref :: Maybe Text,
+    push_data_commit_title :: Maybe Text
   }
   deriving (Show, Eq)
 
--- | Tracks whether an action is open, closed, pushed or commented on.
+-- | Tracks the action name on a GitLab event.
 data EventActionName
   = ANOpened
   | ANClosed
   | ANPushed
   | ANCommentedOn
+  | ANMerged
+  | ANCreated
+  | ANUpdated
+  | ANApproved
+  | ANReopened
+  | ANLeft
+  | ANJoined
+  | ANDestroyed
+  | ANExpired
+  | ANDeleted
+  | ANAccepted
+  | ANOther Text
   deriving (Show, Eq)
 
 instance ToJSON EventActionName where
@@ -1393,12 +1445,36 @@
   toJSON ANClosed = String "closed"
   toJSON ANPushed = String "pushed"
   toJSON ANCommentedOn = String "commented on"
+  toJSON ANMerged = String "merged"
+  toJSON ANCreated = String "created"
+  toJSON ANUpdated = String "updated"
+  toJSON ANApproved = String "approved"
+  toJSON ANReopened = String "reopened"
+  toJSON ANLeft = String "left"
+  toJSON ANJoined = String "joined"
+  toJSON ANDestroyed = String "destroyed"
+  toJSON ANExpired = String "expired"
+  toJSON ANDeleted = String "deleted"
+  toJSON ANAccepted = String "accepted"
+  toJSON (ANOther t) = String t
 
 instance FromJSON EventActionName where
   parseJSON (String "opened") = return ANOpened
   parseJSON (String "closed") = return ANClosed
   parseJSON (String "pushed") = return ANPushed
   parseJSON (String "commented on") = return ANCommentedOn
+  parseJSON (String "merged") = return ANMerged
+  parseJSON (String "created") = return ANCreated
+  parseJSON (String "updated") = return ANUpdated
+  parseJSON (String "approved") = return ANApproved
+  parseJSON (String "reopened") = return ANReopened
+  parseJSON (String "left") = return ANLeft
+  parseJSON (String "joined") = return ANJoined
+  parseJSON (String "destroyed") = return ANDestroyed
+  parseJSON (String "expired") = return ANExpired
+  parseJSON (String "deleted") = return ANDeleted
+  parseJSON (String "accepted") = return ANAccepted
+  parseJSON (String t) = return (ANOther t)
   parseJSON x = unexpected x
 
 -- | Associates an event with a particular target.
@@ -1410,6 +1486,9 @@
   | ETTProject
   | ETTSnippet
   | ETTUser
+  | ETTWikiPage
+  | ETTDesign
+  | ETTOther Text
   deriving (Show, Eq)
 
 instance ToJSON EventTargetType where
@@ -1420,6 +1499,9 @@
   toJSON ETTProject = String "Project"
   toJSON ETTSnippet = String "Snippet"
   toJSON ETTUser = String "User"
+  toJSON ETTWikiPage = String "WikiPage"
+  toJSON ETTDesign = String "Design"
+  toJSON (ETTOther t) = String t
 
 instance FromJSON EventTargetType where
   parseJSON (String "Issue") = return ETTIssue
@@ -1429,6 +1511,9 @@
   parseJSON (String "Project") = return ETTProject
   parseJSON (String "Snippet") = return ETTSnippet
   parseJSON (String "User") = return ETTUser
+  parseJSON (String "WikiPage") = return ETTWikiPage
+  parseJSON (String "Design") = return ETTDesign
+  parseJSON (String t) = return (ETTOther t)
   parseJSON x = unexpected x
 
 -- | Events https://docs.gitlab.com/ee/api/events.html
diff --git a/src/GitLab/WebRequests/GitLabWebCalls.hs b/src/GitLab/WebRequests/GitLabWebCalls.hs
--- a/src/GitLab/WebRequests/GitLabWebCalls.hs
+++ b/src/GitLab/WebRequests/GitLabWebCalls.hs
@@ -11,10 +11,12 @@
     gitlabDelete,
     gitlabUnsafe,
     gitlabGetByteStringResponse,
+    responseErrorText,
   )
 where
 
 import qualified Control.Exception as Exception
+import Control.Monad.Except
 import Control.Monad.IO.Class
 import qualified Control.Monad.Reader as MR
 import Data.Aeson
@@ -28,6 +30,7 @@
 import Network.HTTP.Conduit
 import Network.HTTP.Types.Status
 import Network.HTTP.Types.URI
+import System.IO (hPutStrLn, stderr)
 import Text.Read
 
 newtype GitLabException = GitLabException String
@@ -89,7 +92,7 @@
         params
 
 gitlabPut ::
-  FromJSON a =>
+  (FromJSON a) =>
   -- | the URL to post to
   Text ->
   -- | the data to post
@@ -107,7 +110,7 @@
         params
 
 gitlabDelete ::
-  FromJSON a =>
+  (FromJSON a) =>
   -- | the URL to post to
   Text ->
   -- | the data to post
@@ -130,8 +133,8 @@
 gitlabUnsafe query = do
   result <- query
   case result of
-    Left _err -> error "gitlabUnsafe error"
-    Right Nothing -> error "gitlabUnsafe error"
+    Left _err -> throwError (GitLabError "gitlabUnsafe error")
+    Right Nothing -> throwError (GitLabError "gitlabUnsafe error")
     Right (Just x) -> return x
 
 -- | Lower level query that returns the raw bytestring response from a
@@ -153,6 +156,14 @@
         params
         []
 
+-- | Format a failed HTTP response into a human-readable error message.
+responseErrorText :: Response payload -> T.Text
+responseErrorText resp =
+  "HTTP "
+    <> T.pack (show (statusCode (responseStatus resp)))
+    <> " "
+    <> T.decodeUtf8 (statusMessage (responseStatus resp))
+
 ---------------------
 -- internal functions
 
@@ -172,23 +183,23 @@
   cfg <- MR.asks serverCfg
   manager <- MR.asks httpManager
   let url' = url cfg <> "/api/v4" <> urlPath <> T.decodeUtf8 (renderQuery True urlParams)
-  let authHeader = case token cfg of
-        AuthMethodToken t -> ("PRIVATE-TOKEN", T.encodeUtf8 t)
-        AuthMethodOAuth t -> ("Authorization", "Bearer " <> T.encodeUtf8 t)
+  let authHeaders = case token cfg of
+        AuthMethodNone -> []
+        AuthMethodToken t -> [("PRIVATE-TOKEN", T.encodeUtf8 t)]
+        AuthMethodOAuth t -> [("Authorization", "Bearer " <> T.encodeUtf8 t)]
   let request' = parseRequest_ (T.unpack url')
       request =
         request'
           { method = httpMethod,
-            requestHeaders =
-              [ authHeader,
-                ("content-type", contentType)
+            requestHeaders = authHeaders <>
+              [ ("content-type", contentType)
               ],
             requestBody = RequestBodyBS (renderQuery False contentParams)
           }
   liftIO $ tryGitLab 0 request (retries cfg) manager Nothing
 
 gitlabHTTPOne ::
-  FromJSON a =>
+  (FromJSON a) =>
   -- | HTTP method (PUT, POST, DELETE, GET)
   BS.ByteString ->
   -- | Content type (content-type)
@@ -230,7 +241,7 @@
 gitlabHTTPMany httpMethod contentType urlPath urlParams contentParams = do
   go 1 []
   where
-    go :: FromJSON a => Int -> [a] -> GitLab (Either (Response BSL.ByteString) [a])
+    go :: (FromJSON a) => Int -> [a] -> GitLab (Either (Response BSL.ByteString) [a])
     go pageNum accum = do
       response <-
         gitlabHTTP
@@ -241,9 +252,16 @@
           contentParams
       if successStatus (responseStatus response)
         then do
-          case parseMany (responseBody response) of
-            Nothing -> return (Right accum)
-            Just moreResults -> do
+          case eitherDecode (responseBody response) of
+            Left decodeErr -> do
+              liftIO $ hPutStrLn stderr $
+                "[gitlab-haskell] JSON decode error for " <> T.unpack urlPath
+                  <> " (page " <> show pageNum <> "): " <> decodeErr
+              liftIO $ hPutStrLn stderr $
+                "[gitlab-haskell] Raw response body: "
+                  <> show (BSL.take 2000 (responseBody response))
+              return (Right accum)
+            Right moreResults -> do
               let accum' = accum <> moreResults
               if hasNextPage response
                 then go (pageNum + 1) accum'
@@ -283,14 +301,8 @@
       httpLbs request manager
         `Exception.catch` \ex -> tryGitLab (i + 1) request maxRetries manager (Just ex)
 
-parseOne :: FromJSON a => BSL.ByteString -> Maybe a
+parseOne :: (FromJSON a) => BSL.ByteString -> Maybe a
 parseOne bs =
   case eitherDecode bs of
     Left _err -> Nothing
     Right x -> Just x
-
-parseMany :: FromJSON a => BSL.ByteString -> Maybe [a]
-parseMany bs =
-  case eitherDecode bs of
-    Left _err -> Nothing
-    Right xs -> Just xs
diff --git a/tests/API/Common.hs b/tests/API/Common.hs
--- a/tests/API/Common.hs
+++ b/tests/API/Common.hs
@@ -92,9 +92,9 @@
     Version (Version),
     Visibility (..),
   )
+import Prettyprinter
 import Test.Tasty
 import Test.Tasty.HUnit
-import qualified Text.PrettyPrint.ANSI.Leijen as WL
 
 removeNulls :: Value -> Maybe Value
 removeNulls Null = Nothing
@@ -125,9 +125,11 @@
 jsonValuesEqual jsonFilename decodedCustomTypeF = do
   jsonValueFromFile <- parseValuesFromFile jsonFilename
   decodedCustomType <- decodedCustomTypeF
-  let (Just jsonFromCustomType) = decode (encode decodedCustomType) :: Maybe Value
-  (jsonValueFromFile == jsonFromCustomType)
-    @? showWL (ansiWlEditExprCompact (ediff jsonValueFromFile jsonFromCustomType))
+  case decode (encode decodedCustomType) :: Maybe Value of
+    Nothing -> False @? ("Unable able to parse " <> show decodedCustomType)
+    Just jsonFromCustomType ->
+      (jsonValueFromFile == jsonFromCustomType)
+        @? showWL (ansiWlEditExprCompact (ediff jsonValueFromFile jsonFromCustomType))
 
 parseValuesFromFile :: String -> IO Value
 parseValuesFromFile fname =
@@ -140,8 +142,8 @@
   (expectedHaskellValue == result)
     @? showWL (ansiWlEditExprCompact (ediff expectedHaskellValue result))
 
-showWL :: WL.Doc -> String
-showWL doc = WL.displayS (WL.renderSmart 0.4 80 doc) ""
+showWL :: Doc ann -> String
+showWL = show
 
 gitlabParseTestMany :: (ToExpr a, FromJSON a, Eq a, Show a) => [a] -> String -> Assertion
 gitlabParseTestMany expectedHaskellValue filename = do
@@ -150,13 +152,13 @@
   (expectedHaskellValue == result)
     @? showWL (ansiWlEditExprCompact (ediff expectedHaskellValue result))
 
-parseOne :: FromJSON a => BSL.ByteString -> IO a
+parseOne :: (FromJSON a) => BSL.ByteString -> IO a
 parseOne bs =
   case eitherDecode bs of
     Left err -> assertFailure err
     Right xs -> return xs
 
-parseMany :: FromJSON a => BSL.ByteString -> IO [a]
+parseMany :: (FromJSON a) => BSL.ByteString -> IO [a]
 parseMany bs =
   case eitherDecode bs of
     Left err -> assertFailure err
diff --git a/tests/SystemHookTests.hs b/tests/SystemHookTests.hs
--- a/tests/SystemHookTests.hs
+++ b/tests/SystemHookTests.hs
@@ -3,7 +3,9 @@
 
 module SystemHookTests (systemHookTests) where
 
+import Control.Monad
 import Control.Monad.IO.Class
+import Data.Either
 import Data.Maybe
 import qualified Data.Text.IO as TIO
 import GitLab
@@ -109,11 +111,16 @@
             >>= \eventJson -> parseEvent eventJson @?= Just groupRemovedHaskell
         ),
       testCase
-        "group-renamed-event"
-        ( TIO.readFile "data/system-hooks/group-renamed.json"
-            >>= \eventJson -> parseEvent eventJson @?= Just groupRenamedHaskell
+        "group-renamed1"
+        ( TIO.readFile "data/system-hooks/group-renamed1.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just groupRenamed1Haskell
         ),
       testCase
+        "group-renamed2"
+        ( TIO.readFile "data/system-hooks/group-renamed2.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just groupRenamed2Haskell
+        ),
+      testCase
         "new-group-member-event"
         ( TIO.readFile "data/system-hooks/new-group-member.json"
             >>= \eventJson -> parseEvent eventJson @?= Just newGroupMemberHaskell
@@ -139,11 +146,16 @@
             >>= \eventJson -> parseEvent eventJson @?= Just push2Haskell
         ),
       testCase
-        "tag-push-event"
-        ( TIO.readFile "data/system-hooks/tag-push.json"
-            >>= \eventJson -> parseEvent eventJson @?= Just tagPushHaskell
+        "tag-push-event1"
+        ( TIO.readFile "data/system-hooks/tag-push1.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just tagPush1Haskell
         ),
       testCase
+        "tag-push-event2"
+        ( TIO.readFile "data/system-hooks/tag-push2.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just tagPush2Haskell
+        ),
+      testCase
         "merge-request-event"
         ( TIO.readFile "data/system-hooks/merge-request.json"
             >>= \eventJson -> parseEvent eventJson @?= Just mergeRequestHaskell
@@ -166,7 +178,7 @@
       testCase
         "build1"
         ( TIO.readFile "data/system-hooks/build.json"
-            >>= \eventJson -> parseEvent eventJson @?= Just buildHaskell
+            >>= \eventJson -> parseEvent eventJson @?= Just build1Haskell
         ),
       testCase
         "build2"
@@ -174,9 +186,14 @@
             >>= \eventJson -> parseEvent eventJson @?= Just build2Haskell
         ),
       testCase
+        "build3"
+        ( TIO.readFile "data/system-hooks/build3.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just build3Haskell
+        ),
+      testCase
         "pipeline1"
         ( TIO.readFile "data/system-hooks/pipeline.json"
-            >>= \eventJson -> parseEvent eventJson @?= Just pipelineHaskell
+            >>= \eventJson -> parseEvent eventJson @?= Just pipeline1Haskell
         ),
       testCase
         "pipeline2"
@@ -199,6 +216,11 @@
             >>= \eventJson -> parseEvent eventJson @?= Just pipeline5Haskell
         ),
       testCase
+        "pipeline6"
+        ( TIO.readFile "data/system-hooks/pipeline6.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just pipeline6Haskell
+        ),
+      testCase
         "issue1"
         ( TIO.readFile "data/system-hooks/issue1.json"
             >>= \eventJson -> parseEvent eventJson @?= Just issue1Haskell
@@ -242,26 +264,67 @@
         "work-item1"
         ( TIO.readFile "data/system-hooks/work-item1.json"
             >>= \eventJson -> parseEvent eventJson @?= Just workItem1Haskell
+        ),
+      testCase
+        "emoji1"
+        ( TIO.readFile "data/system-hooks/emoji1.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just emoji1Haskell
+        ),
+      testCase
+        "emoji2"
+        ( TIO.readFile "data/system-hooks/emoji2.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just emoji2Haskell
+        ),
+      testCase
+        "emoji3"
+        ( TIO.readFile "data/system-hooks/emoji3.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just emoji3Haskell
+        ),
+      testCase
+        "emoji4"
+        ( TIO.readFile "data/system-hooks/emoji4.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just emoji4Haskell
+        ),
+      testCase
+        "access-token1"
+        ( TIO.readFile "data/system-hooks/access-token1.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just accessToken1Haskell
+        ),
+      testCase
+        "access-token2"
+        ( TIO.readFile "data/system-hooks/access-token2.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just accessToken2Haskell
+        ),
+      testCase
+        "access-token3"
+        ( TIO.readFile "data/system-hooks/access-token3.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just accessToken3Haskell
+        ),
+      testCase
+        "access-token4"
+        ( TIO.readFile "data/system-hooks/access-token4.json"
+            >>= \eventJson -> parseEvent eventJson @?= Just accessToken4Haskell
         )
     ]
 
 matchTest :: String -> String -> Rule -> String -> Rule -> [TestTree]
 matchTest lbl jsonFilename rule wrongJson wrongRule =
   [ testCase lbl $
-      runGitLabDbg
-        ( liftIO (TIO.readFile ("data/system-hooks/" <> jsonFilename))
-            >>= \eventJson -> tryFire eventJson rule
-        )
+      (fromRight False)
+        <$> runGitLabDbg
+          ( liftIO (TIO.readFile ("data/system-hooks/" <> jsonFilename))
+              >>= \eventJson -> tryFire eventJson rule
+          )
         @? (lbl <> " failed"),
     testCase (lbl <> "-wrong-json") $
-      not
+      (not . fromRight False)
         <$> runGitLabDbg
           ( liftIO (TIO.readFile ("data/system-hooks/" <> wrongJson))
               >>= \eventJson -> tryFire eventJson rule
           )
         @? (lbl <> "-wrong-json failed"),
     testCase (lbl <> "-wrong-rule") $
-      not
+      (not . fromRight False)
         <$> runGitLabDbg
           ( liftIO (TIO.readFile ("data/system-hooks/" <> jsonFilename))
               >>= \eventJson -> tryFire eventJson wrongRule
@@ -272,13 +335,14 @@
 matchIfTest :: String -> String -> Rule -> Rule -> [TestTree]
 matchIfTest lbl jsonFilename yesFire noFire =
   [ testCase (lbl <> "-yes") $
-      runGitLabDbg
-        ( liftIO (TIO.readFile ("data/system-hooks/" <> jsonFilename))
-            >>= \eventJson -> tryFire eventJson yesFire
-        )
+      (fromRight False)
+        <$> runGitLabDbg
+          ( liftIO (TIO.readFile ("data/system-hooks/" <> jsonFilename))
+              >>= \eventJson -> tryFire eventJson yesFire
+          )
         @? (lbl <> "-fireIf-yes failed"),
     testCase (lbl <> "-no") $
-      not
+      (not . fromRight False)
         <$> runGitLabDbg
           ( liftIO (TIO.readFile ("data/system-hooks/" <> jsonFilename))
               >>= \eventJson -> tryFire eventJson noFire
@@ -327,20 +391,22 @@
       <> matchTest "key-remove" "key-removed.json" keyRemoveRule "project-created.json" projectCreateRule
       <> matchTest "group-create" "group-created.json" groupCreateRule "project-created.json" projectCreateRule
       <> matchTest "group-remove" "group-removed.json" groupRemoveRule "project-created.json" projectCreateRule
-      <> matchTest "group-rename" "group-renamed.json" groupRenameRule "project-created.json" projectCreateRule
+      <> matchTest "group-rename" "group-renamed1.json" groupRenameRule "project-created.json" projectCreateRule
       <> matchTest "new-group-member" "new-group-member.json" newGroupMemberRule "project-created.json" projectCreateRule
       <> matchTest "group-member-remove" "group-member-removed.json" groupMemberRemoveRule "project-created.json" projectCreateRule
       <> matchTest "group-member-update" "group-member-updated.json" groupMemberUpdateRule "project-created.json" projectCreateRule
       <> matchTest "push" "push.json" pushRule "project-created.json" projectCreateRule
-      <> matchTest "tag-push" "tag-push.json" tagPushRule "project-created.json" projectCreateRule
+      <> matchTest "tag-push" "tag-push1.json" tagPushRule "project-created.json" projectCreateRule
       <> matchTest "repository-update" "repository-update.json" repositoryUpdateRule "project-created.json" projectCreateRule
       <> matchTest "merge-request" "merge-request.json" mergeRequestRule "project-created.json" projectCreateRule
       <> matchTest "build" "build.json" buildRule "project-created.json" projectCreateRule
       <> matchTest "pipeline" "pipeline.json" pipelineRule "project-created.json" projectCreateRule
       <> matchTest "issue" "issue1.json" issueRule "project-created.json" projectCreateRule
       <> matchTest "note" "note1.json" noteRule "project-created.json" projectCreateRule
-      <> matchTest "note" "wiki-page1.json" wikiPageRule "project-created.json" projectCreateRule
-      <> matchTest "note" "work-item1.json" workItemRule "project-created.json" projectCreateRule
+      <> matchTest "wiki-page" "wiki-page1.json" wikiPageRule "project-created.json" projectCreateRule
+      <> matchTest "work-item" "work-item1.json" workItemRule "project-created.json" projectCreateRule
+      <> matchTest "emoji" "emoji1.json" emojiRule "project-created.json" projectCreateRule
+      <> matchTest "access-token" "access-token1.json" accessTokenRule "project-created.json" projectCreateRule
 
 matchIfTests :: TestTree
 matchIfTests =
@@ -361,41 +427,46 @@
       <> matchIfTest "key-remove" "key-removed.json" keyRemoveIfRuleYes keyRemoveIfRuleNo
       <> matchIfTest "group-create" "group-created.json" groupCreateIfRuleYes groupCreateIfRuleNo
       <> matchIfTest "group-remove" "group-removed.json" groupRemoveIfRuleYes groupRemoveIfRuleNo
-      <> matchIfTest "group-rename" "group-renamed.json" groupRenameIfRuleYes groupRenameIfRuleNo
+      <> matchIfTest "group-rename" "group-renamed1.json" groupRenameIfRuleYes groupRenameIfRuleNo
       <> matchIfTest "new-group-member" "new-group-member.json" newGroupMemberIfRuleYes newGroupMemberIfRuleNo
       <> matchIfTest "group-member-remove" "group-member-removed.json" groupMemberRemoveIfRuleYes groupMemberRemoveIfRuleNo
       <> matchIfTest "group-member-update" "group-member-updated.json" groupMemberUpdateIfRuleYes groupMemberUpdateIfRuleNo
       <> matchIfTest "push" "push.json" pushIfRuleYes pushIfRuleNo
-      <> matchIfTest "tag-push" "tag-push.json" tagPushIfRuleYes tagPushIfRuleNo
+      <> matchIfTest "tag-push" "tag-push1.json" tagPushIfRuleYes tagPushIfRuleNo
       <> matchIfTest "repository-update" "repository-update.json" repositoryUpdateIfRuleYes repositoryUpdateIfRuleNo
       <> matchIfTest "merge-request" "merge-request.json" mergeRequestIfRuleYes mergeRequestIfRuleNo
       <> matchIfTest "build" "build.json" buildIfRuleYes buildIfRuleNo
       <> matchIfTest "pipeline" "pipeline.json" pipelineIfRuleYes pipelineIfRuleNo
       <> matchIfTest "issue" "issue1.json" issueIfRuleYes issueIfRuleNo
       <> matchIfTest "note" "note1.json" noteIfRuleYes noteIfRuleNo
-      <> matchIfTest "note" "wiki-page1.json" wikiPageIfRuleYes wikiPageIfRuleNo
-      <> matchIfTest "note" "work-item1.json" workItemIfRuleYes workItemIfRuleNo
+      <> matchIfTest "wiki-page" "wiki-page1.json" wikiPageIfRuleYes wikiPageIfRuleNo
+      <> matchIfTest "work-item" "work-item1.json" workItemIfRuleYes workItemIfRuleNo
+      <> matchIfTest "emoji" "emoji1.json" emojiIfRuleYes emojiIfRuleNo
+      <> matchIfTest "access-token" "access-token1.json" accessTokenIfRuleYes accessTokenIfRuleNo
 
 receiveTests :: TestTree
 receiveTests =
   testGroup
     "GitLab system hooks receive"
     [ testCase "1-rule-match" $
-        runGitLabDbg $
-          liftIO (TIO.readFile "data/system-hooks/project-created.json")
-            >>= \eventJson ->
-              receiveString
-                eventJson
-                [projectCreateRule],
+        void $
+          runGitLabDbg $
+            liftIO (TIO.readFile "data/system-hooks/project-created.json")
+              >>= \eventJson ->
+                receiveString
+                  eventJson
+                  [projectCreateRule],
       testCase "1-rule-no-match" $
-        runGitLabDbg $
-          liftIO (TIO.readFile "data/system-hooks/project-created.json")
-            >>= \eventJson ->
-              receiveString
-                eventJson
-                [projectRenameRule],
+        void $
+          runGitLabDbg $
+            liftIO (TIO.readFile "data/system-hooks/project-created.json")
+              >>= \eventJson ->
+                receiveString
+                  eventJson
+                  [projectRenameRule],
       testCase
         "2-rules"
+        $ void
         $ runGitLabDbg
         $ liftIO (TIO.readFile "data/system-hooks/project-created.json")
           >>= \eventJson ->
@@ -1016,7 +1087,7 @@
 groupRenameRule =
   match
     "groupRename rule"
-    ( \GroupRename {} -> do
+    ( \GroupRenameEvent {} -> do
         return ()
     )
 
@@ -1024,13 +1095,13 @@
 groupRenameIfRuleYes =
   matchIf
     "groupRename rule-if yes"
-    ( \event@GroupRename {} -> do
+    ( \event@GroupRenameEvent {} -> do
         return
           ( groupRename_full_path event
               == "parent-group/better-name"
           )
     )
-    ( \GroupRename {} -> do
+    ( \GroupRenameEvent {} -> do
         return ()
     )
 
@@ -1038,13 +1109,13 @@
 groupRenameIfRuleNo =
   matchIf
     "groupRename rule-if no"
-    ( \event@GroupRename {} -> do
+    ( \event@GroupRenameEvent {} -> do
         return
           ( groupRename_full_path event
               == "parent-group/wrong-name"
           )
     )
-    ( \GroupRename {} -> do
+    ( \GroupRenameEvent {} -> do
         return ()
     )
 
@@ -1420,6 +1491,66 @@
         return ()
     )
 
+emojiRule :: Rule
+emojiRule =
+  match
+    "emoji rule"
+    ( \EmojiEvent {} -> do
+        return ()
+    )
+
+emojiIfRuleYes :: Rule
+emojiIfRuleYes =
+  matchIf
+    "emoji rule-if yes"
+    ( \event@EmojiEvent {} -> do
+        return (user_username (emoji_event_user event) == "abc1")
+    )
+    ( \EmojiEvent {} -> do
+        return ()
+    )
+
+emojiIfRuleNo :: Rule
+emojiIfRuleNo =
+  matchIf
+    "emoji rule-if no"
+    ( \event@EmojiEvent {} -> do
+        return (user_username (emoji_event_user event) == "not abc1")
+    )
+    ( \EmojiEvent {} -> do
+        return ()
+    )
+
+accessTokenRule :: Rule
+accessTokenRule =
+  match
+    "access token rule"
+    ( \AccessTokenEvent {} -> do
+        return ()
+    )
+
+accessTokenIfRuleYes :: Rule
+accessTokenIfRuleYes =
+  matchIf
+    "access token rule-if yes"
+    ( \event@AccessTokenEvent {} -> do
+        return (access_token_event_event_name event == "expiring_access_token")
+    )
+    ( \AccessTokenEvent {} -> do
+        return ()
+    )
+
+accessTokenIfRuleNo :: Rule
+accessTokenIfRuleNo =
+  matchIf
+    "access token rule-if no"
+    ( \event@AccessTokenEvent {} -> do
+        return (access_token_event_event_name event == "not expiring_access_token")
+    )
+    ( \AccessTokenEvent {} -> do
+        return ()
+    )
+
 projectCreatedHaskell :: ProjectCreate
 projectCreatedHaskell =
   ProjectCreate
@@ -1668,9 +1799,9 @@
       groupRemove_group_id = 78
     }
 
-groupRenamedHaskell :: GroupRename
-groupRenamedHaskell =
-  GroupRename
+groupRenamed1Haskell :: GroupRenameEvent
+groupRenamed1Haskell =
+  GroupRenameEvent
     { groupRename_event_name = "group_rename",
       groupRename_created_at = "2017-10-30T15:09:00Z",
       groupRename_updated_at = "2017-11-01T10:23:52Z",
@@ -1684,6 +1815,22 @@
       groupRename_old_full_path = "parent-group/old-name"
     }
 
+groupRenamed2Haskell :: GroupRenameEvent
+groupRenamed2Haskell =
+  GroupRenameEvent
+    { groupRename_event_name = "group_rename",
+      groupRename_created_at = "2026-01-22T09:28:55Z",
+      groupRename_updated_at = "2026-01-22T10:50:25Z",
+      groupRename_name = "proj-name",
+      groupRename_path = "proj-path",
+      groupRename_full_path = "full-path",
+      groupRename_group_id = 12345,
+      groupRename_owner_name = Nothing,
+      groupRename_owner_email = Nothing,
+      groupRename_old_path = "the-old-path",
+      groupRename_old_full_path = "the-new-path"
+    }
+
 newGroupMemberHaskell :: NewGroupMember
 newGroupMemberHaskell =
   NewGroupMember
@@ -1740,10 +1887,14 @@
 push2Haskell =
   Push {push_event_name = "push", push_before = "edcba", push_after = "0000000000000000000000000000000000000000", push_ref = "refs/heads/Rohan", push_checkout_sha = Nothing, push_user_id = 2583, push_user_name = "Joe", push_user_username = Just "joe", push_user_email = Nothing, push_user_avatar = "https://secure.gravatar.com/avatar/abc", push_project_id = 25811, push_project = ProjectEvent {projectEvent_id = Just 25811, projectEvent_name = "The project name", projectEvent_description = Nothing, projectEvent_web_url = "https://example.com/joe/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_git_http_url = "https://example.com/joe/proj-name.git", projectEvent_namespace = "The namespace", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/proj-name", projectEvent_default_branch = "main", projectEvent_homepage = Just "https://example.com/joe/proj-name", projectEvent_url = "git@example.com:joe/proj-name.git", projectEvent_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_http_url = "https://example.com/joe/proj-name.git"}, push_repository = RepositoryEvent {repositoryEvent_name = "The project name", repositoryEvent_url = "git@example.com:joe/proj-name.git", repositoryEvent_description = Nothing, repositoryEvent_homepage = Just "https://example.com/joe/proj-name", repositoryEvent_git_http_url = Just "https://example.com/joe/proj-name.git", repositoryEvent_git_ssh_url = Just "git@example.com:joe/proj-name.git", repositoryEvent_visibility_level = Just Private}, push_commits = [], push_total_commits_count = 0}
 
-tagPushHaskell :: TagPush
-tagPushHaskell =
-  TagPush {tagPush_event_name = "tag_push", tagPush_before = "0000000000000000000000000000000000000000", tagPush_after = "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", tagPush_ref = "refs/tags/v1.0.0", tagPush_checkout_sha = "5937ac0a7beb003549fc5fd26fc247adbce4a52e", tagPush_user_id = 1, tagPush_user_name = "John Smith", tagPush_user_avatar = "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", tagPush_project_id = 1, tagPush_project = ProjectEvent {projectEvent_id = Nothing, projectEvent_name = "Example", projectEvent_description = Just "", projectEvent_web_url = "http://example.com/jsmith/example", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:jsmith/example.git", projectEvent_git_http_url = "http://example.com/jsmith/example.git", projectEvent_namespace = "Jsmith", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "jsmith/example", projectEvent_default_branch = "master", projectEvent_homepage = Just "http://example.com/jsmith/example", projectEvent_url = "git@example.com:jsmith/example.git", projectEvent_ssh_url = "git@example.com:jsmith/example.git", projectEvent_http_url = "http://example.com/jsmith/example.git"}, tagPush_repository = RepositoryEvent {repositoryEvent_name = "Example", repositoryEvent_url = "ssh://git@example.com/jsmith/example.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "http://example.com/jsmith/example", repositoryEvent_git_http_url = Just "http://example.com/jsmith/example.git", repositoryEvent_git_ssh_url = Just "git@example.com:jsmith/example.git", repositoryEvent_visibility_level = Just Private}, tagPush_commits = [], tagPush_total_commits_count = 0}
+tagPush1Haskell :: TagPush
+tagPush1Haskell =
+  TagPush {tagPush_event_name = "tag_push", tagPush_before = "0000000000000000000000000000000000000000", tagPush_after = "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", tagPush_ref = "refs/tags/v1.0.0", tagPush_checkout_sha = Just "5937ac0a7beb003549fc5fd26fc247adbce4a52e", tagPush_user_id = 1, tagPush_user_name = "John Smith", tagPush_user_avatar = "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", tagPush_project_id = 1, tagPush_project = ProjectEvent {projectEvent_id = Nothing, projectEvent_name = "Example", projectEvent_description = Just "", projectEvent_web_url = "http://example.com/jsmith/example", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:jsmith/example.git", projectEvent_git_http_url = "http://example.com/jsmith/example.git", projectEvent_namespace = "Jsmith", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "jsmith/example", projectEvent_default_branch = "master", projectEvent_homepage = Just "http://example.com/jsmith/example", projectEvent_url = "git@example.com:jsmith/example.git", projectEvent_ssh_url = "git@example.com:jsmith/example.git", projectEvent_http_url = "http://example.com/jsmith/example.git"}, tagPush_repository = RepositoryEvent {repositoryEvent_name = "Example", repositoryEvent_url = "ssh://git@example.com/jsmith/example.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "http://example.com/jsmith/example", repositoryEvent_git_http_url = Just "http://example.com/jsmith/example.git", repositoryEvent_git_ssh_url = Just "git@example.com:jsmith/example.git", repositoryEvent_visibility_level = Just Private}, tagPush_commits = [], tagPush_total_commits_count = 0}
 
+tagPush2Haskell :: TagPush
+tagPush2Haskell =
+  TagPush {tagPush_event_name = "tag_push", tagPush_before = "abc123", tagPush_after = "0000000000000000000000000000000000000000", tagPush_ref = "refs/tags/tag1", tagPush_checkout_sha = Nothing, tagPush_user_id = 123, tagPush_user_name = "Joe", tagPush_user_avatar = "https://secure.gravatar.com/avatar/abc123?s=80&d=identicon", tagPush_project_id = 48363, tagPush_project = ProjectEvent {projectEvent_id = Just 48363, projectEvent_name = "proj-name", projectEvent_description = Nothing, projectEvent_web_url = "https://example.com/joe1/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe1/proj-name.git", projectEvent_git_http_url = "https://example.com/joe1/proj-name.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe1/proj-name", projectEvent_default_branch = "main", projectEvent_homepage = Just "https://example.com/joe1/proj-name", projectEvent_url = "git@example.com:joe1/proj-name.git", projectEvent_ssh_url = "git@example.com:joe1/proj-name.git", projectEvent_http_url = "https://example.com/joe1/proj-name.git"}, tagPush_repository = RepositoryEvent {repositoryEvent_name = "proj-name", repositoryEvent_url = "git@example.com:joe1/proj-name.git", repositoryEvent_description = Nothing, repositoryEvent_homepage = Just "https://example.com/joe1/proj-name", repositoryEvent_git_http_url = Just "https://example.com/joe1/proj-name.git", repositoryEvent_git_ssh_url = Just "git@example.com:joe1/proj-name.git", repositoryEvent_visibility_level = Just Private}, tagPush_commits = [], tagPush_total_commits_count = 0}
+
 repositoryUpdateHaskell :: RepositoryUpdate
 repositoryUpdateHaskell =
   RepositoryUpdate {repositoryUpdate_event_name = "repository_update", repositoryUpdate_user_id = 1, repositoryUpdate_user_name = "John Smith", repositoryUpdate_user_email = "admin@example.com", repositoryUpdate_user_avatar = "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", repositoryUpdate_project_id = 1, repositoryUpdate_project = ProjectEvent {projectEvent_id = Nothing, projectEvent_name = "Example", projectEvent_description = Just "", projectEvent_web_url = "http://example.com/jsmith/example", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:jsmith/example.git", projectEvent_git_http_url = "http://example.com/jsmith/example.git", projectEvent_namespace = "Jsmith", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "jsmith/example", projectEvent_default_branch = "master", projectEvent_homepage = Just "http://example.com/jsmith/example", projectEvent_url = "git@example.com:jsmith/example.git", projectEvent_ssh_url = "git@example.com:jsmith/example.git", projectEvent_http_url = "http://example.com/jsmith/example.git"}, repositoryUpdate_changes = [ProjectChanges {projectChanges_before = "8205ea8d81ce0c6b90fbe8280d118cc9fdad6130", projectChanges_after = "4045ea7a3df38697b3730a20fb73c8bed8a3e69e", projectChanges_ref = "refs/heads/master"}], repositoryUpdate_refs = ["refs/heads/master"]}
@@ -1762,34 +1913,42 @@
 mergeRequestGitLab_15_5_0_maybe_descriptions_Haskell =
   MergeRequestEvent {mergeRequest_object_kind = "merge_request", mergeRequest_event_type = "merge_request", mergeRequest_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Bloggs, Joe", userEvent_username = "joe123", userEvent_avatar_url = "https://secure.gravatar.com/avatar/df1b70b1fd7d7eaaa5f9a41f5f5093c0?s=80&d=identicon", userEvent_email = Nothing}, mergeRequest_project = ProjectEvent {projectEvent_id = Just 43593, projectEvent_name = "proj1", projectEvent_description = Nothing, projectEvent_web_url = "https://gitlab.example.com/grp1/grp2/proj1", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@gitlab.example.com:grp1/grp2/proj1.git", projectEvent_git_http_url = "https://gitlab.example.com/grp1/grp2/proj1.git", projectEvent_namespace = "grp2", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "grp1/grp2/proj1", projectEvent_default_branch = "main", projectEvent_homepage = Just "https://gitlab.example.com/grp1/grp2/proj1", projectEvent_url = "git@gitlab.example.com:grp1/grp2/proj1.git", projectEvent_ssh_url = "git@gitlab.example.com:grp1/grp2/proj1.git", projectEvent_http_url = "https://gitlab.example.com/grp1/grp2/proj1.git"}, mergeRequest_object_attributes = MergeRequestObjectAttributes {objectAttributes_id = 3461, objectAttributes_target_branch = "main", objectAttributes_source_branch = "main", objectAttributes_source_project_id = 49668, objectAttributes_author_id = Just 2312, objectAttributes_assignee_id = Nothing, objectAttributes_assignee_ids = Just [], objectAttributes_title = "the MR title", objectAttributes_created_at = "2022-10-25 18:51:48 UTC", objectAttributes_updated_at = "2022-10-25 18:51:48 UTC", objectAttributes_milestone_id = Nothing, objectAttributes_state = "opened", objectAttributes_state_id = Just 1, objectAttributes_merge_status = "preparing", objectAttributes_target_project_id = 43593, objectAttributes_iid = 14, objectAttributes_description = "", objectAttributes_updated_by_id = Nothing, objectAttributes_merge_error = Nothing, objectAttributes_merge_params = Just (MergeParams {mergeParams_force_remove_source_branch = Nothing}), objectAttributes_merge_when_pipeline_succeeds = Just False, objectAttributes_merge_user_id = Nothing, objectAttributes_merge_commit_sha = Nothing, objectAttributes_deleted_at = Nothing, objectAttributes_in_progress_merge_commit_sha = Nothing, objectAttributes_lock_version = Nothing, objectAttributes_time_estimate = Just 0, objectAttributes_last_edited_at = Nothing, objectAttributes_last_edited_by_id = Nothing, objectAttributes_head_pipeline_id = Nothing, objectAttributes_ref_fetched = Nothing, objectAttributes_merge_jid = Nothing, objectAttributes_source = ProjectEvent {projectEvent_id = Just 49668, projectEvent_name = "proj1", projectEvent_description = Nothing, projectEvent_web_url = "https://gitlab.example.com/joe123/proj1", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@gitlab.example.com:joe123/proj1.git", projectEvent_git_http_url = "https://gitlab.example.com/joe123/proj1.git", projectEvent_namespace = "Bloggs, Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe123/proj1", projectEvent_default_branch = "main", projectEvent_homepage = Just "https://gitlab.example.com/joe123/proj1", projectEvent_url = "git@gitlab.example.com:joe123/proj1.git", projectEvent_ssh_url = "git@gitlab.example.com:joe123/proj1.git", projectEvent_http_url = "https://gitlab.example.com/joe123/proj1.git"}, objectAttributes_target = ProjectEvent {projectEvent_id = Just 43593, projectEvent_name = "proj1", projectEvent_description = Nothing, projectEvent_web_url = "https://gitlab.example.com/grp1/grp2/proj1", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@gitlab.example.com:grp1/grp2/proj1.git", projectEvent_git_http_url = "https://gitlab.example.com/grp1/grp2/proj1.git", projectEvent_namespace = "grp2", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "grp1/grp2/proj1", projectEvent_default_branch = "main", projectEvent_homepage = Just "https://gitlab.example.com/grp1/grp2/proj1", projectEvent_url = "git@gitlab.example.com:grp1/grp2/proj1.git", projectEvent_ssh_url = "git@gitlab.example.com:grp1/grp2/proj1.git", projectEvent_http_url = "https://gitlab.example.com/grp1/grp2/proj1.git"}, objectAttributes_last_commit = CommitEvent {commitEvent_id = "249d94fdc92825a2ca22c4fa74c511ec5f03e086", commitEvent_message = "Updated README\n", commitEvent_timestamp = "2022-10-08T10:37:28+01:00", commitEvent_url = "https://gitlab.example.com/grp1/grp2/proj1/-/commit/249d94fdc92825a2ca22c4fa74c511ec5f03e086", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Hill, Harry", commitAuthorEvent_email = "harry@gmail.com"}}, objectAttributes_work_in_progress = True, objectAttributes_total_time_spent = Just 0, objectAttributes_human_total_time_spent = Nothing, objectAttributes_human_time_estimate = Nothing, objectAttributes_action = Just "open"}, mergeRequest_labels = Just [], mergeRequest_changes = MergeRequestChanges {mergeRequestChanges_author_id = Nothing, mergeRequestChanges_created_at = Nothing, mergeRequestChanges_description = Nothing, mergeRequestChanges_id = Nothing, mergeRequestChanges_iid = Nothing, mergeRequestChanges_source_branch = Nothing, mergeRequestChanges_source_project_id = Nothing, mergeRequestChanges_target_branch = Nothing, mergeRequestChanges_target_project_id = Nothing, mergeRequestChanges_title = Nothing, mergeRequestChanges_updated_at = Nothing}, mergeRequest_repository = RepositoryEvent {repositoryEvent_name = "proj1", repositoryEvent_url = "git@gitlab.example.com:grp1/grp2/proj1.git", repositoryEvent_description = Nothing, repositoryEvent_homepage = Just "https://gitlab.example.com/grp1/grp2/proj1", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}}
 
-buildHaskell :: BuildEvent
-buildHaskell =
+build1Haskell :: BuildEvent
+build1Haskell =
   BuildEvent {build_event_object_kind = "build", build_event_ref = "master", build_event_tag = False, build_event_before_sha = "19d1543c2f5ea888e3c69019bd7e3daff228fcbc", build_event_sha = "12fc8bb3a8b3f52c5bffa020e6d362027c724359", build_event_retries_count = 0, build_event_build_event_id = 47423, build_event_build_event_name = "junit", build_event_build_event_stage = "test", build_event_build_event_status = "created", build_event_created_at = "2024-11-01 20:15:34 UTC", build_event_started_at = Nothing, build_event_finished_at = Nothing, build_event_duration = Nothing, build_event_queued_duration = Nothing, build_event_allow_failure = False, build_event_failure_reason = Just "unknown_failure", build_event_pipeline_id = 32611, build_event_runner = Nothing, build_event_project_id = 25039, build_event_project_name = "Joe / repo-name", build_event_user = User {user_id = 2682, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abcde", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, build_event_commit = BuildCommit {build_commit_id = 32611, build_commit_name = Nothing, build_commit_sha = "12fc8bb3a8b3f52c5bffa020e6d362027c724359", build_commit_message = "the message", build_commit_author_name = "Joe", build_commit_author_email = "joe@gmail.com", build_commit_author_url = "mailto:joe@gmail.com", build_commit_status = "created", build_commit_duration = Nothing, build_commit_started_at = Nothing, build_commit_finished_at = Nothing}, build_event_repository = Repository {repository_id = Nothing, repository_name = "repo-name", repository_type = Nothing, repository_path = Nothing, repository_mode = Nothing}, build_event_project = BuildProject {build_project_project_id = 25039, build_project_project_name = "repo-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/repo-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/repo-name.git", build_project_git_http_url = "https://example.com/joe/repo-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/repo-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, build_event_environment = Nothing}
 
 build2Haskell :: BuildEvent
 build2Haskell =
   BuildEvent {build_event_object_kind = "build", build_event_ref = "master", build_event_tag = False, build_event_before_sha = "edcba", build_event_sha = "abcde", build_event_retries_count = 0, build_event_build_event_id = 47500, build_event_build_event_name = "junit", build_event_build_event_stage = "test", build_event_build_event_status = "failed", build_event_created_at = "2024-11-02 18:29:00 UTC", build_event_started_at = Just "2024-11-02 18:29:01 UTC", build_event_finished_at = Just "2024-11-02 18:29:06 UTC", build_event_duration = Just 4.543246, build_event_queued_duration = Just 1.08065, build_event_allow_failure = False, build_event_failure_reason = Just "script_failure", build_event_pipeline_id = 32685, build_event_runner = Just (Runner {runner_id = 17, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), build_event_project_id = 25078, build_event_project_name = "joe/proj-name", build_event_user = User {user_id = 742, user_username = "jg2078", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Goddard, Jamie", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, build_event_commit = BuildCommit {build_commit_id = 32685, build_commit_name = Nothing, build_commit_sha = "abcde", build_commit_message = "message name", build_commit_author_name = "joe", build_commit_author_email = "joe@gmail.com", build_commit_author_url = "mailto:joe@gmail.com", build_commit_status = "failed", build_commit_duration = Just 4, build_commit_started_at = Just "2024-11-02 18:29:01 UTC", build_commit_finished_at = Just "2024-11-02 18:29:06 UTC"}, build_event_repository = Repository {repository_id = Nothing, repository_name = "proj-name", repository_type = Nothing, repository_path = Nothing, repository_mode = Nothing}, build_event_project = BuildProject {build_project_project_id = 25078, build_project_project_name = "proj-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, build_event_environment = Nothing}
 
-pipelineHaskell :: PipelineEvent
-pipelineHaskell =
-  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 32617, pipeline_object_attributes_iid = 4, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "failed", pipeline_object_attributes_detailed_status = "failed", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-01 21:58:31 UTC", pipeline_object_attributes_finished_at = Just "2024-11-01 21:58:35 UTC", pipeline_object_attributes_duration = Just 3.0, pipeline_object_attributes_queued_duration = Nothing, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/32617"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 899, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 24472, build_project_project_name = "proj-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the commit message", commitEvent_timestamp = "2024-11-01T21:58:31+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "12345+Joe@users.noreply.github.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 47429, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "failed", pipeline_build_created_at = "2024-11-01 21:58:31 UTC", pipeline_build_started_at = Just "2024-11-01 21:58:31 UTC", pipeline_build_finished_at = Just "2024-11-01 21:58:35 UTC", pipeline_build_duration = Just 3.889573, pipeline_build_queued_duration = 0.19131, pipeline_build_failure_reason = Just "script_failure", pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 15, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
+build3Haskell :: BuildEvent
+build3Haskell =
+  BuildEvent {build_event_object_kind = "build", build_event_ref = "main", build_event_tag = False, build_event_before_sha = "abc1", build_event_sha = "abbbccc1", build_event_retries_count = 0, build_event_build_event_id = 103999, build_event_build_event_name = "deploy-job", build_event_build_event_stage = "deploy", build_event_build_event_status = "created", build_event_created_at = "2026-02-07 02:47:56 UTC", build_event_started_at = Nothing, build_event_finished_at = Nothing, build_event_duration = Nothing, build_event_queued_duration = Nothing, build_event_allow_failure = False, build_event_failure_reason = Just "unknown_failure", build_event_pipeline_id = 74974, build_event_runner = Nothing, build_event_project_id = 47461, build_event_project_name = "proj-name", build_event_user = User {user_id = 414, user_username = "hpc2000", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc1?s=80&d=identicon", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, build_event_commit = BuildCommit {build_commit_id = 74974, build_commit_name = Nothing, build_commit_sha = "abc1", build_commit_message = "update message", build_commit_author_name = "Joe", build_commit_author_email = "joe@example.com", build_commit_author_url = "https://example.com/hpc2000", build_commit_status = "created", build_commit_duration = Nothing, build_commit_started_at = Nothing, build_commit_finished_at = Nothing}, build_event_repository = Repository {repository_id = Nothing, repository_name = "repo-name", repository_type = Nothing, repository_path = Nothing, repository_mode = Nothing}, build_event_project = BuildProject {build_project_project_id = 47461, build_project_project_name = "proj-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe1/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe1/proj-name.git", build_project_git_http_url = "https://example.com/joe1/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe1/proj-name", build_project_default_branch = "main", build_project_ci_config_path = Just ""}, build_event_environment = Just (Environment {environment_name = "production", environment_action = "start", environment_deployment_tier = Nothing})}
 
+pipeline1Haskell :: PipelineEvent
+pipeline1Haskell =
+  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 32617, pipeline_object_attributes_iid = 4, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "failed", pipeline_object_attributes_detailed_status = "failed", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-01 21:58:31 UTC", pipeline_object_attributes_finished_at = Just "2024-11-01 21:58:35 UTC", pipeline_object_attributes_duration = Just 3.0, pipeline_object_attributes_queued_duration = Nothing, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/32617"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 899, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 24472, build_project_project_name = "proj-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the commit message", commitEvent_timestamp = "2024-11-01T21:58:31+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "12345+Joe@users.noreply.github.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 47429, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "failed", pipeline_build_created_at = "2024-11-01 21:58:31 UTC", pipeline_build_started_at = Just "2024-11-01 21:58:31 UTC", pipeline_build_finished_at = Just "2024-11-01 21:58:35 UTC", pipeline_build_duration = Just 3.889573, pipeline_build_queued_duration = Just 0.19131, pipeline_build_failure_reason = Just "script_failure", pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 15, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
+
 pipeline2Haskell :: PipelineEvent
 pipeline2Haskell =
-  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 32686, pipeline_object_attributes_iid = 2, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "failed", pipeline_object_attributes_detailed_status = "failed", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-02 18:32:17 UTC", pipeline_object_attributes_finished_at = Just "2024-11-02 18:32:23 UTC", pipeline_object_attributes_duration = Just 4.0, pipeline_object_attributes_queued_duration = Just 1.0, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/32686"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 742, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 25078, build_project_project_name = "proj-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the commit message", commitEvent_timestamp = "2024-11-02T18:32:16+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 47501, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "failed", pipeline_build_created_at = "2024-11-02 18:32:18 UTC", pipeline_build_started_at = Just "2024-11-02 18:32:18 UTC", pipeline_build_finished_at = Just "2024-11-02 18:32:23 UTC", pipeline_build_duration = Just 4.428372, pipeline_build_queued_duration = 0.561693, pipeline_build_failure_reason = Just "script_failure", pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 15, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
+  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 32686, pipeline_object_attributes_iid = 2, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "failed", pipeline_object_attributes_detailed_status = "failed", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-02 18:32:17 UTC", pipeline_object_attributes_finished_at = Just "2024-11-02 18:32:23 UTC", pipeline_object_attributes_duration = Just 4.0, pipeline_object_attributes_queued_duration = Just 1.0, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/32686"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 742, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 25078, build_project_project_name = "proj-name", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the commit message", commitEvent_timestamp = "2024-11-02T18:32:16+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 47501, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "failed", pipeline_build_created_at = "2024-11-02 18:32:18 UTC", pipeline_build_started_at = Just "2024-11-02 18:32:18 UTC", pipeline_build_finished_at = Just "2024-11-02 18:32:23 UTC", pipeline_build_duration = Just 4.428372, pipeline_build_queued_duration = Just 0.561693, pipeline_build_failure_reason = Just "script_failure", pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 15, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
 
 pipeline3Haskell :: PipelineEvent
 pipeline3Haskell =
-  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 32701, pipeline_object_attributes_iid = 5, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "failed", pipeline_object_attributes_detailed_status = "failed", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-02 19:31:48 UTC", pipeline_object_attributes_finished_at = Just "2024-11-02 19:31:53 UTC", pipeline_object_attributes_duration = Just 3.0, pipeline_object_attributes_queued_duration = Just 1.0, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/32701"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 3041, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 25084, build_project_project_name = "proj-name", build_project_description = Just "", build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the commit message", commitEvent_timestamp = "2024-11-02T19:31:48+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 47516, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "failed", pipeline_build_created_at = "2024-11-02 19:31:48 UTC", pipeline_build_started_at = Just "2024-11-02 19:31:50 UTC", pipeline_build_finished_at = Just "2024-11-02 19:31:53 UTC", pipeline_build_duration = Just 3.734497, pipeline_build_queued_duration = 0.672819, pipeline_build_failure_reason = Just "script_failure", pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 14, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
+  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 32701, pipeline_object_attributes_iid = 5, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "failed", pipeline_object_attributes_detailed_status = "failed", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-02 19:31:48 UTC", pipeline_object_attributes_finished_at = Just "2024-11-02 19:31:53 UTC", pipeline_object_attributes_duration = Just 3.0, pipeline_object_attributes_queued_duration = Just 1.0, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/32701"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 3041, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 25084, build_project_project_name = "proj-name", build_project_description = Just "", build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the commit message", commitEvent_timestamp = "2024-11-02T19:31:48+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 47516, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "failed", pipeline_build_created_at = "2024-11-02 19:31:48 UTC", pipeline_build_started_at = Just "2024-11-02 19:31:50 UTC", pipeline_build_finished_at = Just "2024-11-02 19:31:53 UTC", pipeline_build_duration = Just 3.734497, pipeline_build_queued_duration = Just 0.672819, pipeline_build_failure_reason = Just "script_failure", pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 14, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
 
 pipeline4Haskell :: PipelineEvent
 pipeline4Haskell =
-  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 36096, pipeline_object_attributes_iid = 1, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "pending", pipeline_object_attributes_detailed_status = "pending", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-08 21:26:15 UTC", pipeline_object_attributes_finished_at = Nothing, pipeline_object_attributes_duration = Nothing, pipeline_object_attributes_queued_duration = Nothing, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/36096"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 2659, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Lad, Krishan", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://example.com/uploads/-/system/user/avatar/2659/avatar.png", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 25415, build_project_project_name = "F28SG - Lab 7", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "commit message", commitEvent_timestamp = "2024-11-08T21:26:13+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 51146, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "pending", pipeline_build_created_at = "2024-11-08 21:26:15 UTC", pipeline_build_started_at = Nothing, pipeline_build_finished_at = Nothing, pipeline_build_duration = Nothing, pipeline_build_queued_duration = 0.328087189, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://example.com/uploads/-/system/user/avatar/2659/avatar.png", userEvent_email = Nothing}, pipeline_build_runner = Nothing, pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
+  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 36096, pipeline_object_attributes_iid = 1, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "pending", pipeline_object_attributes_detailed_status = "pending", pipeline_object_attributes_stages = ["test"], pipeline_object_attributes_created_at = "2024-11-08 21:26:15 UTC", pipeline_object_attributes_finished_at = Nothing, pipeline_object_attributes_duration = Nothing, pipeline_object_attributes_queued_duration = Nothing, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe/proj-name/-/pipelines/36096"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 2659, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Lad, Krishan", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://example.com/uploads/-/system/user/avatar/2659/avatar.png", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 25415, build_project_project_name = "F28SG - Lab 7", build_project_description = Nothing, build_project_web_url = "https://example.com/joe/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe/proj-name.git", build_project_git_http_url = "https://example.com/joe/proj-name.git", build_project_namespace = "Joe", build_project_visibility_level = 0, build_project_path_with_namespace = "joe/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "commit message", commitEvent_timestamp = "2024-11-08T21:26:13+00:00", commitEvent_url = "https://example.com/joe/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 51146, pipeline_build_stage = "test", pipeline_build_name = "junit", pipeline_build_status = "pending", pipeline_build_created_at = "2024-11-08 21:26:15 UTC", pipeline_build_started_at = Nothing, pipeline_build_finished_at = Nothing, pipeline_build_duration = Nothing, pipeline_build_queued_duration = Just 0.328087189, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe", userEvent_avatar_url = "https://example.com/uploads/-/system/user/avatar/2659/avatar.png", userEvent_email = Nothing}, pipeline_build_runner = Nothing, pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}]}
 
 pipeline5Haskell :: PipelineEvent
 pipeline5Haskell =
-  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 38727, pipeline_object_attributes_iid = 53, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "success", pipeline_object_attributes_detailed_status = "passed", pipeline_object_attributes_stages = ["deploy"], pipeline_object_attributes_created_at = "2024-11-17 16:17:11 UTC", pipeline_object_attributes_finished_at = Just "2024-11-17 16:17:15 UTC", pipeline_object_attributes_duration = Just 2.0, pipeline_object_attributes_queued_duration = Just 1.0, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/group-name/proj-name/-/pipelines/38727"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 2638, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 20385, build_project_project_name = "Cinema Website", build_project_description = Nothing, build_project_web_url = "https://example.com/group-name/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:group-name/proj-name.git", build_project_git_http_url = "https://example.com/group-name/proj-name.git", build_project_namespace = "Group name", build_project_visibility_level = 0, build_project_path_with_namespace = "group-name/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the message", commitEvent_timestamp = "2024-11-17T16:17:13+00:00", commitEvent_url = "https://example.com/group-name/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 54510, pipeline_build_stage = "deploy", pipeline_build_name = "pages", pipeline_build_status = "success", pipeline_build_created_at = "2024-11-17 16:17:11 UTC", pipeline_build_started_at = Just "2024-11-17 16:17:12 UTC", pipeline_build_finished_at = Just "2024-11-17 16:17:15 UTC", pipeline_build_duration = Just 2.767007, pipeline_build_queued_duration = 0.868802, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe Bloggs", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 15, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Just "artifacts.zip", artifacts_file_size = Just 8136559}, pipeline_build_environment = Nothing}]}
+  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 38727, pipeline_object_attributes_iid = 53, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "master", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abcde", pipeline_object_attributes_before_sha = "edcba", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "success", pipeline_object_attributes_detailed_status = "passed", pipeline_object_attributes_stages = ["deploy"], pipeline_object_attributes_created_at = "2024-11-17 16:17:11 UTC", pipeline_object_attributes_finished_at = Just "2024-11-17 16:17:15 UTC", pipeline_object_attributes_duration = Just 2.0, pipeline_object_attributes_queued_duration = Just 1.0, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/group-name/proj-name/-/pipelines/38727"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 2638, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 20385, build_project_project_name = "Cinema Website", build_project_description = Nothing, build_project_web_url = "https://example.com/group-name/proj-name", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:group-name/proj-name.git", build_project_git_http_url = "https://example.com/group-name/proj-name.git", build_project_namespace = "Group name", build_project_visibility_level = 0, build_project_path_with_namespace = "group-name/proj-name", build_project_default_branch = "master", build_project_ci_config_path = Nothing}, pipeline_event_commit = CommitEvent {commitEvent_id = "abcde", commitEvent_message = "the message", commitEvent_timestamp = "2024-11-17T16:17:13+00:00", commitEvent_url = "https://example.com/group-name/proj-name/-/commit/abcde", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@gmail.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 54510, pipeline_build_stage = "deploy", pipeline_build_name = "pages", pipeline_build_status = "success", pipeline_build_created_at = "2024-11-17 16:17:11 UTC", pipeline_build_started_at = Just "2024-11-17 16:17:12 UTC", pipeline_build_finished_at = Just "2024-11-17 16:17:15 UTC", pipeline_build_duration = Just 2.767007, pipeline_build_queued_duration = Just 0.868802, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe Bloggs", userEvent_username = "joe", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abc", userEvent_email = Nothing}, pipeline_build_runner = Just (Runner {runner_id = 15, runner_description = "", runner_runner_type = "instance_type", runner_active = True, runner_is_shared = True, runner_tags = []}), pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Just "artifacts.zip", artifacts_file_size = Just 8136559}, pipeline_build_environment = Nothing}]}
 
+pipeline6Haskell :: PipelineEvent
+pipeline6Haskell =
+  PipelineEvent {pipeline_event_object_kind = "pipeline", pipeline_event_object_attributes = PipelineObjectAttributes {pipeline_object_attributes_id = 74974, pipeline_object_attributes_iid = 1, pipeline_object_attributes_name = Nothing, pipeline_object_attributes_ref = "main", pipeline_object_attributes_tag = False, pipeline_object_attributes_sha = "abc1", pipeline_object_attributes_before_sha = "aaa1", pipeline_object_attributes_source = "push", pipeline_object_attributes_status = "pending", pipeline_object_attributes_detailed_status = "pending", pipeline_object_attributes_stages = ["build", "test", "deploy"], pipeline_object_attributes_created_at = "2026-02-07 02:47:56 UTC", pipeline_object_attributes_finished_at = Nothing, pipeline_object_attributes_duration = Nothing, pipeline_object_attributes_queued_duration = Nothing, pipeline_object_attributes_variables = [], pipeline_object_attributes_url = "https://example.com/joe1/proj1/-/pipelines/74974"}, pipeline_event_merge_request = Nothing, pipeline_event_user = User {user_id = 414, user_username = "joe1", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, pipeline_event_project = BuildProject {build_project_project_id = 47461, build_project_project_name = "proj1", build_project_description = Nothing, build_project_web_url = "https://example.com/joe1/proj1", build_project_avatar_url = Nothing, build_project_git_ssh_url = "git@example.com:joe1/proj1.git", build_project_git_http_url = "https://example.com/joe1/proj1.git", build_project_namespace = "Joe Bloggs", build_project_visibility_level = 0, build_project_path_with_namespace = "joe1/proj1", build_project_default_branch = "main", build_project_ci_config_path = Just ""}, pipeline_event_commit = CommitEvent {commitEvent_id = "abc1", commitEvent_message = "commit message", commitEvent_timestamp = "2026-02-07T02:47:55+00:00", commitEvent_url = "https://example.com/joe1/proj1/-/commit/abc1", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe Bloggs", commitAuthorEvent_email = "joe1@example.com"}}, pipeline_event_builds = [PipelineBuild {pipeline_build_id = 103996, pipeline_build_stage = "build", pipeline_build_name = "build-job", pipeline_build_status = "pending", pipeline_build_created_at = "2026-02-07 02:47:56 UTC", pipeline_build_started_at = Nothing, pipeline_build_finished_at = Nothing, pipeline_build_duration = Nothing, pipeline_build_queued_duration = Just 0.486474635, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe Bloggs", userEvent_username = "joe1", userEvent_avatar_url = "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon", userEvent_email = Nothing}, pipeline_build_runner = Nothing, pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}, PipelineBuild {pipeline_build_id = 103997, pipeline_build_stage = "test", pipeline_build_name = "test", pipeline_build_status = "created", pipeline_build_created_at = "2026-02-07 02:47:56 UTC", pipeline_build_started_at = Nothing, pipeline_build_finished_at = Nothing, pipeline_build_duration = Nothing, pipeline_build_queued_duration = Nothing, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe Bloggs", userEvent_username = "joe1", userEvent_avatar_url = "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon", userEvent_email = Nothing}, pipeline_build_runner = Nothing, pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}, PipelineBuild {pipeline_build_id = 103998, pipeline_build_stage = "test", pipeline_build_name = "test job", pipeline_build_status = "created", pipeline_build_created_at = "2026-02-07 02:47:56 UTC", pipeline_build_started_at = Nothing, pipeline_build_finished_at = Nothing, pipeline_build_duration = Nothing, pipeline_build_queued_duration = Nothing, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe Bloggs", userEvent_username = "joe1", userEvent_avatar_url = "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon", userEvent_email = Nothing}, pipeline_build_runner = Nothing, pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Nothing}, PipelineBuild {pipeline_build_id = 103999, pipeline_build_stage = "deploy", pipeline_build_name = "deploy-job", pipeline_build_status = "created", pipeline_build_created_at = "2026-02-07 02:47:56 UTC", pipeline_build_started_at = Nothing, pipeline_build_finished_at = Nothing, pipeline_build_duration = Nothing, pipeline_build_queued_duration = Nothing, pipeline_build_failure_reason = Nothing, pipeline_build_when = "on_success", pipeline_build_manual = False, pipeline_build_allow_failure = False, pipeline_build_user = UserEvent {userEvent_id = Nothing, userEvent_name = "Joe Bloggs", userEvent_username = "joe1", userEvent_avatar_url = "https://secure.gravatar.com/avatar/aaa1?s=80&d=identicon", userEvent_email = Nothing}, pipeline_build_runner = Nothing, pipeline_build_artifacts_file = ArtifactsFile {artifacts_file_filename = Nothing, artifacts_file_size = Nothing}, pipeline_build_environment = Just (Environment {environment_name = "production", environment_action = "start", environment_deployment_tier = Just "production"})}]}
+
 issue1Haskell :: IssueEvent
 issue1Haskell =
   IssueEvent {issue_event_event_type = "issue", issue_event_user = Just (UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "ma2305", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abcde", userEvent_email = Nothing}), issue_event_project = Just (ProjectEvent {projectEvent_id = Just 19102, projectEvent_name = "proj-name", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe123/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe123/proj-name.git", projectEvent_git_http_url = "https://example.com/joe123/proj-name.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe123/proj-name", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe123/proj-name", projectEvent_url = "git@example.com:joe123/proj-name.git", projectEvent_ssh_url = "git@example.com:joe123/proj-name.git", projectEvent_http_url = "https://example.com/joe123/proj-name.git"}), issue_event_object_attributes = Just (IssueEventObjectAttributes {issue_event_object_attributes_author_id = 2052, issue_event_object_attributes_closed_at = Just "2024-11-02 19:47:16 UTC", issue_event_object_attributes_confidential = False, issue_event_object_attributes_created_at = "2024-11-02 05:45:09 UTC", issue_event_object_attributes_description = Just "", issue_event_object_attributes_discussion_locked = Nothing, issue_event_object_attributes_due_date = Nothing, issue_event_object_attributes_id = 2183, issue_event_object_attributes_iid = 40, issue_event_object_attributes_last_edited_at = Nothing, issue_event_object_attributes_last_edited_by_id = Nothing, issue_event_object_attributes_milestone_id = Nothing, issue_event_object_attributes_move_to_id = Nothing, issue_event_object_attributes_duplicated_to_id = Nothing, issue_event_object_attributes_project_id = 19102, issue_event_object_attributes_relative_position = Just 20520, issue_event_object_attributes_state_id = 2, issue_event_object_attributes_time_estimate = 0, issue_event_object_attributes_title = "Issue title", issue_event_object_attributes_updated_at = "2024-11-02 19:47:17 UTC", issue_event_object_attributes_updated_by_id = Nothing, issue_event_object_attributes_type = "Issue", issue_event_object_attributes_url = "https://example.com/joe123/proj-name/-/issues/40", issue_event_object_attributes_total_time_spent = 0, issue_event_object_attributes_time_change = 0, issue_event_object_attributes_human_total_time_spent = Nothing, issue_event_object_attributes_human_time_change = Nothing, issue_event_object_attributes_human_time_estimate = Nothing}), issue_event_labels = Just [Label {label_id = Just 385, label_title = Just "Stage 3", label_color = Just "#cd5b45", label_project_id = Just 19102, label_created_at = Just "2024-11-02 05:42:30 UTC", label_updated_at = Just "2024-11-02 05:42:30 UTC", label_template = Just False, label_description = Nothing, label_type = Just "ProjectLabel", label_group_id = Nothing}], issue_event_changes = Just (IssueEventChanges {issue_event_changes_author_id = Nothing, issue_event_changes_created_at = Nothing, issue_event_changes_description = Nothing, issue_event_changes_id = Nothing, issue_event_changes_iid = Nothing, issue_event_changes_project_id = Nothing, issue_event_changes_title = Nothing, issue_event_changes_closed_at = Just (IssueChangesClosedAt {issue_event_closed_at_previous = Nothing, issue_event_closed_at_current = Just "2024-11-02 19:47:16 UTC"}), issue_event_changes_state_id = Just (IssueChangesStateId {issue_event_state_id_previous = Just 1, issue_event_state_id_current = 2}), issue_event_changes_updated_at = Just (IssueChangesUpdatedAt {issue_event_updated_at_previous = Just "2024-11-02 05:45:09 UTC", issue_event_updated_at_current = "2024-11-02 19:47:17 UTC"})}), issue_event_repository = Just (RepositoryEvent {repositoryEvent_name = "proj-name", repositoryEvent_url = "git@example.com:joe123/proj-name.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe123/proj-name", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}), issue_event_assignees = Just [UserEvent {userEvent_id = Nothing, userEvent_name = "Joe", userEvent_username = "joe123", userEvent_avatar_url = "https://secure.gravatar.com/avatar/abcde", userEvent_email = Nothing}]}
@@ -1808,15 +1967,15 @@
 
 note1Haskell :: NoteEvent
 note1Haskell =
-  NoteEvent {note_event_object_kind = "note", note_event_event_type = "note", note_event_user = User {user_id = 2950, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, note_event_project_id = 21219, note_event_project = ProjectEvent {projectEvent_id = Just 21219, projectEvent_name = "proj-name", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_git_http_url = "https://example.com/joe/proj-name.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/proj-name", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe/proj-name", projectEvent_url = "git@example.com:joe/proj-name.git", projectEvent_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_http_url = "https://example.com/joe/proj-name.git"}, note_event_object_attributes = NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 2950, note_object_attributes_change_position = Nothing, note_object_attributes_commit_id = Nothing, note_object_attributes_created_at = "2024-11-16 16:16:10 UTC", note_object_attributes_discussion_id = "4f0df21318e0110d71de2114a6c34385bd81b713", note_object_attributes_id = 16684, note_object_attributes_line_code = Nothing, note_object_attributes_note = "the note text", note_object_attributes_noteable_id = Just 2797, note_object_attributes_noteable_type = "Issue", note_object_attributes_original_position = Nothing, note_object_attributes_position = Nothing, note_object_attributes_project_id = 21219, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Nothing, note_object_attributes_updated_at = Just "2024-11-16 16:16:10 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "the note text", note_object_attributes_url = "https://example.com/joe/proj-name/-/issues/19#note_16684", note_object_attributes_action = "create"}, note_event_repository = RepositoryEvent {repositoryEvent_name = "proj-name", repositoryEvent_url = "git@example.com:joe/proj-name.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe/proj-name", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}, note_event_issue = Just (IssueEventObjectAttributes {issue_event_object_attributes_author_id = 2950, issue_event_object_attributes_closed_at = Nothing, issue_event_object_attributes_confidential = False, issue_event_object_attributes_created_at = "2024-11-16 12:47:16 UTC", issue_event_object_attributes_description = Just "", issue_event_object_attributes_discussion_locked = Nothing, issue_event_object_attributes_due_date = Nothing, issue_event_object_attributes_id = 2797, issue_event_object_attributes_iid = 19, issue_event_object_attributes_last_edited_at = Nothing, issue_event_object_attributes_last_edited_by_id = Nothing, issue_event_object_attributes_milestone_id = Nothing, issue_event_object_attributes_move_to_id = Nothing, issue_event_object_attributes_duplicated_to_id = Nothing, issue_event_object_attributes_project_id = 21219, issue_event_object_attributes_relative_position = Just 9747, issue_event_object_attributes_state_id = 1, issue_event_object_attributes_time_estimate = 0, issue_event_object_attributes_title = "the issue title", issue_event_object_attributes_updated_at = "2024-11-16 16:16:10 UTC", issue_event_object_attributes_updated_by_id = Just 2950, issue_event_object_attributes_type = "Issue", issue_event_object_attributes_url = "https://example.com/joe/proj-name/-/issues/19", issue_event_object_attributes_total_time_spent = 0, issue_event_object_attributes_time_change = 0, issue_event_object_attributes_human_total_time_spent = Nothing, issue_event_object_attributes_human_time_change = Nothing, issue_event_object_attributes_human_time_estimate = Nothing})}
+  NoteEvent {note_event_object_kind = "note", note_event_event_type = "note", note_event_user = User {user_id = 2950, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, note_event_project_id = 21219, note_event_project = ProjectEvent {projectEvent_id = Just 21219, projectEvent_name = "proj-name", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_git_http_url = "https://example.com/joe/proj-name.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/proj-name", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe/proj-name", projectEvent_url = "git@example.com:joe/proj-name.git", projectEvent_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_http_url = "https://example.com/joe/proj-name.git"}, note_event_object_attributes = NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 2950, note_object_attributes_change_position = Nothing, note_object_attributes_commit_id = Nothing, note_object_attributes_created_at = "2024-11-16 16:16:10 UTC", note_object_attributes_discussion_id = "4f0df21318e0110d71de2114a6c34385bd81b713", note_object_attributes_id = 16684, note_object_attributes_line_code = Nothing, note_object_attributes_note = "the note text", note_object_attributes_noteable_id = Just 2797, note_object_attributes_noteable_type = "Issue", note_object_attributes_original_position = Nothing, note_object_attributes_position = Nothing, note_object_attributes_project_id = 21219, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Nothing, note_object_attributes_updated_at = Just "2024-11-16 16:16:10 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "the note text", note_object_attributes_url = "https://example.com/joe/proj-name/-/issues/19#note_16684", note_object_attributes_action = Just "create"}, note_event_repository = RepositoryEvent {repositoryEvent_name = "proj-name", repositoryEvent_url = "git@example.com:joe/proj-name.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe/proj-name", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}, note_event_issue = Just (IssueEventObjectAttributes {issue_event_object_attributes_author_id = 2950, issue_event_object_attributes_closed_at = Nothing, issue_event_object_attributes_confidential = False, issue_event_object_attributes_created_at = "2024-11-16 12:47:16 UTC", issue_event_object_attributes_description = Just "", issue_event_object_attributes_discussion_locked = Nothing, issue_event_object_attributes_due_date = Nothing, issue_event_object_attributes_id = 2797, issue_event_object_attributes_iid = 19, issue_event_object_attributes_last_edited_at = Nothing, issue_event_object_attributes_last_edited_by_id = Nothing, issue_event_object_attributes_milestone_id = Nothing, issue_event_object_attributes_move_to_id = Nothing, issue_event_object_attributes_duplicated_to_id = Nothing, issue_event_object_attributes_project_id = 21219, issue_event_object_attributes_relative_position = Just 9747, issue_event_object_attributes_state_id = 1, issue_event_object_attributes_time_estimate = 0, issue_event_object_attributes_title = "the issue title", issue_event_object_attributes_updated_at = "2024-11-16 16:16:10 UTC", issue_event_object_attributes_updated_by_id = Just 2950, issue_event_object_attributes_type = "Issue", issue_event_object_attributes_url = "https://example.com/joe/proj-name/-/issues/19", issue_event_object_attributes_total_time_spent = 0, issue_event_object_attributes_time_change = 0, issue_event_object_attributes_human_total_time_spent = Nothing, issue_event_object_attributes_human_time_change = Nothing, issue_event_object_attributes_human_time_estimate = Nothing})}
 
 note2Haskell :: NoteEvent
 note2Haskell =
-  NoteEvent {note_event_object_kind = "note", note_event_event_type = "note", note_event_user = User {user_id = 2477, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://example.com/uploads/-/system/user/avatar/2477/avatar.png", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, note_event_project_id = 19102, note_event_project = ProjectEvent {projectEvent_id = Just 19102, projectEvent_name = "proj-name", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_git_http_url = "https://example.com/joe/proj-name.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/proj-name", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe/proj-name", projectEvent_url = "git@example.com:joe/proj-name.git", projectEvent_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_http_url = "https://example.com/joe/proj-name.git"}, note_event_object_attributes = NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 2477, note_object_attributes_change_position = Nothing, note_object_attributes_commit_id = Just "8dd4c91c0efac607bf6d164ea7e5fd92e6fbe7c6", note_object_attributes_created_at = "2024-11-20 05:48:59 UTC", note_object_attributes_discussion_id = "163e23673e00ebb477a030944824cf25109bdf3e", note_object_attributes_id = 17030, note_object_attributes_line_code = Nothing, note_object_attributes_note = "The note.", note_object_attributes_noteable_id = Nothing, note_object_attributes_noteable_type = "Commit", note_object_attributes_original_position = Nothing, note_object_attributes_position = Nothing, note_object_attributes_project_id = 19102, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Nothing, note_object_attributes_updated_at = Just "2024-11-20 05:48:59 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "The note.", note_object_attributes_url = "https://example.com/joe/proj-name/-/commit/abcde#note_17030", note_object_attributes_action = "create"}, note_event_repository = RepositoryEvent {repositoryEvent_name = "proj-name", repositoryEvent_url = "git@example.com:joe/proj-name.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe/proj-name", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}, note_event_issue = Nothing}
+  NoteEvent {note_event_object_kind = "note", note_event_event_type = "note", note_event_user = User {user_id = 2477, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://example.com/uploads/-/system/user/avatar/2477/avatar.png", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, note_event_project_id = 19102, note_event_project = ProjectEvent {projectEvent_id = Just 19102, projectEvent_name = "proj-name", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe/proj-name", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_git_http_url = "https://example.com/joe/proj-name.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/proj-name", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe/proj-name", projectEvent_url = "git@example.com:joe/proj-name.git", projectEvent_ssh_url = "git@example.com:joe/proj-name.git", projectEvent_http_url = "https://example.com/joe/proj-name.git"}, note_event_object_attributes = NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 2477, note_object_attributes_change_position = Nothing, note_object_attributes_commit_id = Just "8dd4c91c0efac607bf6d164ea7e5fd92e6fbe7c6", note_object_attributes_created_at = "2024-11-20 05:48:59 UTC", note_object_attributes_discussion_id = "163e23673e00ebb477a030944824cf25109bdf3e", note_object_attributes_id = 17030, note_object_attributes_line_code = Nothing, note_object_attributes_note = "The note.", note_object_attributes_noteable_id = Nothing, note_object_attributes_noteable_type = "Commit", note_object_attributes_original_position = Nothing, note_object_attributes_position = Nothing, note_object_attributes_project_id = 19102, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Nothing, note_object_attributes_updated_at = Just "2024-11-20 05:48:59 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "The note.", note_object_attributes_url = "https://example.com/joe/proj-name/-/commit/abcde#note_17030", note_object_attributes_action = Just "create"}, note_event_repository = RepositoryEvent {repositoryEvent_name = "proj-name", repositoryEvent_url = "git@example.com:joe/proj-name.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe/proj-name", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}, note_event_issue = Nothing}
 
 note3Haskell :: NoteEvent
 note3Haskell =
-  NoteEvent {note_event_object_kind = "note", note_event_event_type = "note", note_event_user = User {user_id = 1853, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, note_event_project_id = 19127, note_event_project = ProjectEvent {projectEvent_id = Just 19127, projectEvent_name = "f21sf-24-the-proj-name,", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe/the-proj-name,", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/the-proj-name,.git", projectEvent_git_http_url = "https://example.com/joe/the-proj-name,.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/the-proj-name,", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe/the-proj-name,", projectEvent_url = "git@example.com:joe/the-proj-name,.git", projectEvent_ssh_url = "git@example.com:joe/the-proj-name,.git", projectEvent_http_url = "https://example.com/joe/the-proj-name,.git"}, note_event_object_attributes = NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 1853, note_object_attributes_change_position = Nothing, note_object_attributes_commit_id = Nothing, note_object_attributes_created_at = "2024-11-23 15:44:37 UTC", note_object_attributes_discussion_id = "db1ba73e93464583ae259a6ea79f95a7e38c7e3c", note_object_attributes_id = 17524, note_object_attributes_line_code = Nothing, note_object_attributes_note = "The description", note_object_attributes_noteable_id = Just 2449, note_object_attributes_noteable_type = "Issue", note_object_attributes_original_position = Nothing, note_object_attributes_position = Nothing, note_object_attributes_project_id = 19127, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Nothing, note_object_attributes_updated_at = Just "2024-11-23 15:44:37 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "The description", note_object_attributes_url = "https://example.com/joe/the-proj-name,/-/work_items/76#note_17524", note_object_attributes_action = "create"}, note_event_repository = RepositoryEvent {repositoryEvent_name = "the repo name", repositoryEvent_url = "git@example.com:joe/the-proj-name,.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe/the-proj-name,", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}, note_event_issue = Just (IssueEventObjectAttributes {issue_event_object_attributes_author_id = 1853, issue_event_object_attributes_closed_at = Nothing, issue_event_object_attributes_confidential = False, issue_event_object_attributes_created_at = "2024-11-11 14:23:52 UTC", issue_event_object_attributes_description = Nothing, issue_event_object_attributes_discussion_locked = Nothing, issue_event_object_attributes_due_date = Nothing, issue_event_object_attributes_id = 2449, issue_event_object_attributes_iid = 76, issue_event_object_attributes_last_edited_at = Nothing, issue_event_object_attributes_last_edited_by_id = Nothing, issue_event_object_attributes_milestone_id = Just 63, issue_event_object_attributes_move_to_id = Nothing, issue_event_object_attributes_duplicated_to_id = Nothing, issue_event_object_attributes_project_id = 19127, issue_event_object_attributes_relative_position = Just 38988, issue_event_object_attributes_state_id = 1, issue_event_object_attributes_time_estimate = 0, issue_event_object_attributes_title = "issue title", issue_event_object_attributes_updated_at = "2024-11-23 15:44:37 UTC", issue_event_object_attributes_updated_by_id = Just 1853, issue_event_object_attributes_type = "Task", issue_event_object_attributes_url = "https://example.com/joe/the-proj-name,/-/work_items/76", issue_event_object_attributes_total_time_spent = 0, issue_event_object_attributes_time_change = 0, issue_event_object_attributes_human_total_time_spent = Nothing, issue_event_object_attributes_human_time_change = Nothing, issue_event_object_attributes_human_time_estimate = Nothing})}
+  NoteEvent {note_event_object_kind = "note", note_event_event_type = "note", note_event_user = User {user_id = 1853, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, note_event_project_id = 19127, note_event_project = ProjectEvent {projectEvent_id = Just 19127, projectEvent_name = "f21sf-24-the-proj-name,", projectEvent_description = Just "", projectEvent_web_url = "https://example.com/joe/the-proj-name,", projectEvent_avatar_url = Nothing, projectEvent_git_ssh_url = "git@example.com:joe/the-proj-name,.git", projectEvent_git_http_url = "https://example.com/joe/the-proj-name,.git", projectEvent_namespace = "Joe", projectEvent_visibility_level = Private, projectEvent_path_with_namespace = "joe/the-proj-name,", projectEvent_default_branch = "master", projectEvent_homepage = Just "https://example.com/joe/the-proj-name,", projectEvent_url = "git@example.com:joe/the-proj-name,.git", projectEvent_ssh_url = "git@example.com:joe/the-proj-name,.git", projectEvent_http_url = "https://example.com/joe/the-proj-name,.git"}, note_event_object_attributes = NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 1853, note_object_attributes_change_position = Nothing, note_object_attributes_commit_id = Nothing, note_object_attributes_created_at = "2024-11-23 15:44:37 UTC", note_object_attributes_discussion_id = "db1ba73e93464583ae259a6ea79f95a7e38c7e3c", note_object_attributes_id = 17524, note_object_attributes_line_code = Nothing, note_object_attributes_note = "The description", note_object_attributes_noteable_id = Just 2449, note_object_attributes_noteable_type = "Issue", note_object_attributes_original_position = Nothing, note_object_attributes_position = Nothing, note_object_attributes_project_id = 19127, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Nothing, note_object_attributes_updated_at = Just "2024-11-23 15:44:37 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "The description", note_object_attributes_url = "https://example.com/joe/the-proj-name,/-/work_items/76#note_17524", note_object_attributes_action = Just "create"}, note_event_repository = RepositoryEvent {repositoryEvent_name = "the repo name", repositoryEvent_url = "git@example.com:joe/the-proj-name,.git", repositoryEvent_description = Just "", repositoryEvent_homepage = Just "https://example.com/joe/the-proj-name,", repositoryEvent_git_http_url = Nothing, repositoryEvent_git_ssh_url = Nothing, repositoryEvent_visibility_level = Nothing}, note_event_issue = Just (IssueEventObjectAttributes {issue_event_object_attributes_author_id = 1853, issue_event_object_attributes_closed_at = Nothing, issue_event_object_attributes_confidential = False, issue_event_object_attributes_created_at = "2024-11-11 14:23:52 UTC", issue_event_object_attributes_description = Nothing, issue_event_object_attributes_discussion_locked = Nothing, issue_event_object_attributes_due_date = Nothing, issue_event_object_attributes_id = 2449, issue_event_object_attributes_iid = 76, issue_event_object_attributes_last_edited_at = Nothing, issue_event_object_attributes_last_edited_by_id = Nothing, issue_event_object_attributes_milestone_id = Just 63, issue_event_object_attributes_move_to_id = Nothing, issue_event_object_attributes_duplicated_to_id = Nothing, issue_event_object_attributes_project_id = 19127, issue_event_object_attributes_relative_position = Just 38988, issue_event_object_attributes_state_id = 1, issue_event_object_attributes_time_estimate = 0, issue_event_object_attributes_title = "issue title", issue_event_object_attributes_updated_at = "2024-11-23 15:44:37 UTC", issue_event_object_attributes_updated_by_id = Just 1853, issue_event_object_attributes_type = "Task", issue_event_object_attributes_url = "https://example.com/joe/the-proj-name,/-/work_items/76", issue_event_object_attributes_total_time_spent = 0, issue_event_object_attributes_time_change = 0, issue_event_object_attributes_human_total_time_spent = Nothing, issue_event_object_attributes_human_time_change = Nothing, issue_event_object_attributes_human_time_estimate = Nothing})}
 
 wikiPage1Haskell :: WikiPageEvent
 wikiPage1Haskell =
@@ -1825,3 +1984,35 @@
 workItem1Haskell :: WorkItemEvent
 workItem1Haskell =
   WorkItemEvent {work_item_event_user = User {user_id = 1853, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, work_item_event_object_attributes = WorkItemObjectAttributes {work_item_object_author_id = 1853, work_item_object_closed_at = "2024-11-23 15:56:50 UTC", work_item_object_confidential = False, work_item_object_created_at = "2024-11-11 14:23:52 UTC", work_item_object_description = Nothing, work_item_object_discussion_locked = Nothing, work_item_object_due_date = Nothing, work_item_object_id = 2449, work_item_object_iid = 76, work_item_object_last_edited_at = Nothing, work_item_object_last_edited_by_id = Nothing, work_item_object_milestone_id = Just 63, work_item_object_moved_to_id = Nothing, work_item_object_duplicated_to_id = Nothing, work_item_object_project_id = 19127, work_item_object_relative_position = 38988, work_item_object_state_id = 2, work_item_object_time_estimate = 0, work_item_object_title = "The title", work_item_object_updated_at = "2024-11-23 15:56:50 UTC", work_item_object_updated_by_id = 1853, work_item_object_type = "Task", work_item_object_url = "http://example.com/joe/project/-/work_items/76", work_item_object_total_time_spent = 0, work_item_object_time_change = 0, work_item_object_human_total_time_spent = Nothing, work_item_object_human_time_change = Nothing, work_item_object_human_time_estimate = Nothing, work_item_object_assignee_ids = [1853], work_item_object_assignee_id = 1853, work_item_object_labels = [Label {label_id = Just 391, label_title = Just "Stage 6", label_color = Just "#330066", label_project_id = Just 19127, label_created_at = Just "2024-11-11 13:54:59 UTC", label_updated_at = Just "2024-11-14 09:04:36 UTC", label_template = Just False, label_description = Just "", label_type = Just "ProjectLabel", label_group_id = Nothing}, Label {label_id = Just 337, label_title = Just "documentation", label_color = Just "#f0ad4e", label_project_id = Just 19127, label_created_at = Just "2024-10-07 07:46:00 UTC", label_updated_at = Just "2024-10-07 07:46:00 UTC", label_template = Just False, label_description = Nothing, label_type = Just "ProjectLabel", label_group_id = Nothing}], work_item_object_state = "closed", work_item_object_severity = "unknown", work_item_object_customer_relations_contacts = [], work_item_object_action = "close"}, work_item_event_labels = [Label {label_id = Just 391, label_title = Just "Stage 6", label_color = Just "#330066", label_project_id = Just 19127, label_created_at = Just "2024-11-11 13:54:59 UTC", label_updated_at = Just "2024-11-14 09:04:36 UTC", label_template = Just False, label_description = Just "", label_type = Just "ProjectLabel", label_group_id = Nothing}, Label {label_id = Just 337, label_title = Just "documentation", label_color = Just "#f0ad4e", label_project_id = Just 19127, label_created_at = Just "2024-10-07 07:46:00 UTC", label_updated_at = Just "2024-10-07 07:46:00 UTC", label_template = Just False, label_description = Nothing, label_type = Just "ProjectLabel", label_group_id = Nothing}], work_item_event_repository = Repository {repository_id = Nothing, repository_name = "proj-name", repository_type = Nothing, repository_path = Nothing, repository_mode = Nothing}, work_item_event_assignees = [User {user_id = 1853, user_username = "joe", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}]}
+
+emoji1Haskell :: EmojiEvent
+emoji1Haskell =
+  EmojiEvent {emoji_event_user = User {user_id = 3892, user_username = "abc1", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Bloggs, Joe", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, emoji_event_project_id = Just 44159, emoji_event_project = Just (EmojiProject {emoji_project_id = 44159, emoji_project_name = "proj_name ", emoji_project_description = Just "", emoji_project_web_url = "https://example.com/abc1/proj_name", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:abc1/proj_name.git", emoji_project_git_http_url = "https://example.com/abc1/proj_name.git", emoji_project_namespace = "Bloggs, Joe", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "abc1/proj_name", emoji_project_default_branch = "main", emoji_project_ci_config_path = Just "", emoji_project_homepage = "https://example.com/abc1/proj_name", emoji_project_url = "git@example.com:abc1/proj_name.git", emoji_project_ssh_url = "git@example.com:abc1/proj_name.git", emoji_project_http_url = "https://example.com/abc1/proj_name.git"}), emoji_event_object_attributes = Just (EmojiObjectAttributes {emoji_object_user_id = 3892, emoji_object_created_at = "2025-11-07 19:21:17 UTC", emoji_object_id = 107, emoji_object_name = "thumbsup", emoji_object_awardable_type = "Note", emoji_object_updated_at = Just "2025-11-07 19:21:17 UTC", emoji_object_awarded_on_url = Just "https://example.com/abc1/proj_name/-/commit/abc1234567889#note_26062"}), emoji_event_note = Just (NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 1424, note_object_attributes_change_position = Just (ChangePosition {change_position_base_sha = Nothing, change_position_start_sha = Nothing, change_position_head_sha = Nothing, change_position_old_path = Nothing, change_position_new_path = Nothing, change_position_position_type = Just "text", change_position_old_line = Nothing, change_position_new_line = Nothing, change_position_line_range = Nothing}), note_object_attributes_commit_id = Just "abc1234567889", note_object_attributes_created_at = "2025-11-07 19:19:58 UTC", note_object_attributes_discussion_id = "1a5f45c5c3272a75442dfe28eb49a6272e3c5f6d", note_object_attributes_id = 26062, note_object_attributes_line_code = Just "4b6fcb2d521ef0fd442a5301e7932d16cc9f375a_0_1", note_object_attributes_note = "@abc1 This is a function", note_object_attributes_noteable_id = Nothing, note_object_attributes_noteable_type = "Commit", note_object_attributes_original_position = Just (ChangePosition {change_position_base_sha = Just "abcde123456789", change_position_start_sha = Just "abcde123456789", change_position_head_sha = Just "abc1234567889", change_position_old_path = Just "test.txt", change_position_new_path = Just "test.txt", change_position_position_type = Just "text", change_position_old_line = Nothing, change_position_new_line = Just 1, change_position_line_range = Nothing}), note_object_attributes_position = Just (ChangePosition {change_position_base_sha = Just "abcde123456789", change_position_start_sha = Just "abcde123456789", change_position_head_sha = Just "abc1234567889", change_position_old_path = Just "test.txt", change_position_new_path = Just "test.txt", change_position_position_type = Just "text", change_position_old_line = Nothing, change_position_new_line = Just 1, change_position_line_range = Nothing}), note_object_attributes_project_id = 44159, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Just "DiffNote", note_object_attributes_updated_at = Just "2025-11-07 19:21:17 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "@abc1 This is a note.", note_object_attributes_url = "https://example.com/abc1/proj_name/-/commit/abc1234567889#note_26062", note_object_attributes_action = Nothing}), emoji_event_commit = Just (CommitEvent {commitEvent_id = "abc1234567889", commitEvent_message = "this is a message", commitEvent_timestamp = "2025-11-07T19:00:05+00:00", commitEvent_url = "https://example.com/abc1/proj_name/-/commit/abc1234567889", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Bloggs, Joe", commitAuthorEvent_email = "abc1@example.com"}})}
+
+emoji2Haskell :: EmojiEvent
+emoji2Haskell =
+  EmojiEvent {emoji_event_user = User {user_id = 4101, user_username = "abc123", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc123?s=80&d=identicon", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, emoji_event_project_id = Just 44549, emoji_event_project = Just (EmojiProject {emoji_project_id = 44549, emoji_project_name = "The project name", emoji_project_description = Nothing, emoji_project_web_url = "https://example.com/abc123/the-project", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:abc123/the-project.git", emoji_project_git_http_url = "https://example.com/abc123/the-project.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "abc123/the-project", emoji_project_default_branch = "main", emoji_project_ci_config_path = Just "", emoji_project_homepage = "https://example.com/abc123/the-project", emoji_project_url = "git@example.com:abc123/the-project.git", emoji_project_ssh_url = "git@example.com:abc123/the-project.git", emoji_project_http_url = "https://example.com/abc123/the-project.git"}), emoji_event_object_attributes = Just (EmojiObjectAttributes {emoji_object_user_id = 4101, emoji_object_created_at = "2025-11-12 13:32:11 UTC", emoji_object_id = 108, emoji_object_name = "thumbsup", emoji_object_awardable_type = "MergeRequest", emoji_object_updated_at = Just "2025-11-12 13:32:11 UTC", emoji_object_awarded_on_url = Just "https://example.com/abc123/the-project/-/merge_requests/1"}), emoji_event_note = Nothing, emoji_event_commit = Nothing}
+
+emoji3Haskell :: EmojiEvent
+emoji3Haskell =
+  EmojiEvent {emoji_event_user = User {user_id = 4012, user_username = "aaa1", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://secure.gravatar.com/avatar/abc?s=80&d=identicon", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, emoji_event_project_id = Just 44276, emoji_event_project = Just (EmojiProject {emoji_project_id = 44276, emoji_project_name = "proj-name", emoji_project_description = Nothing, emoji_project_web_url = "https://example.com/aaa1/proj-name", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_git_http_url = "https://example.com/aaa1/proj-name.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "aaa1/proj-name", emoji_project_default_branch = "master", emoji_project_ci_config_path = Nothing, emoji_project_homepage = "https://example.com/aaa1/proj-name", emoji_project_url = "git@example.com:aaa1/proj-name.git", emoji_project_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_http_url = "https://example.com/aaa1/proj-name.git"}), emoji_event_object_attributes = Just (EmojiObjectAttributes {emoji_object_user_id = 4012, emoji_object_created_at = "2025-11-20 13:31:14 UTC", emoji_object_id = 110, emoji_object_name = "thumbsup", emoji_object_awardable_type = "Issue", emoji_object_updated_at = Just "2025-11-20 13:31:14 UTC", emoji_object_awarded_on_url = Just "https://example.com/aaa1/proj-name/-/issues/1"}), emoji_event_note = Nothing, emoji_event_commit = Nothing}
+
+emoji4Haskell :: EmojiEvent
+emoji4Haskell =
+  EmojiEvent {emoji_event_user = User {user_id = 3095, user_username = "aaa1", user_bio = Nothing, user_two_factor_enabled = Nothing, user_last_sign_in_at = Nothing, user_current_sign_in_at = Nothing, user_last_activity_on = Nothing, user_skype = Nothing, user_twitter = Nothing, user_website_url = Nothing, user_theme_id = Nothing, user_color_scheme_id = Nothing, user_external = Nothing, user_private_profile = Nothing, user_projects_limit = Nothing, user_can_create_group = Nothing, user_can_create_project = Nothing, user_public_email = Nothing, user_organization = Nothing, user_job_title = Nothing, user_pronouns = Nothing, user_linkedin = Nothing, user_confirmed_at = Nothing, user_identities = Nothing, user_name = "Joe Bloggs", user_email = Just "[REDACTED]", user_followers = Nothing, user_bot = Nothing, user_following = Nothing, user_state = Nothing, user_avatar_url = Just "https://example.com/uploads/-/system/user/avatar/3095/avatar.png", user_web_url = Nothing, user_location = Nothing, user_extern_uid = Nothing, user_group_id_for_saml = Nothing, user_discussion_locked = Nothing, user_created_at = Nothing, user_note = Nothing, user_password = Nothing, user_force_random_password = Nothing, user_providor = Nothing, user_reset_password = Nothing, user_skip_confirmation = Nothing, user_view_diffs_file_by_file = Nothing}, emoji_event_project_id = Just 45173, emoji_event_project = Just (EmojiProject {emoji_project_id = 45173, emoji_project_name = "proj-name", emoji_project_description = Nothing, emoji_project_web_url = "https://example.com/aaa1/proj-name", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_git_http_url = "https://example.com/aaa1/proj-name.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "aaa1/proj-name", emoji_project_default_branch = "main", emoji_project_ci_config_path = Just "", emoji_project_homepage = "https://example.com/aaa1/proj-name", emoji_project_url = "git@example.com:aaa1/proj-name.git", emoji_project_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_http_url = "https://example.com/aaa1/proj-name.git"}), emoji_event_object_attributes = Just (EmojiObjectAttributes {emoji_object_user_id = 3095, emoji_object_created_at = "2025-11-18 08:46:40 UTC", emoji_object_id = 109, emoji_object_name = "heart", emoji_object_awardable_type = "Note", emoji_object_updated_at = Just "2025-11-18 08:46:40 UTC", emoji_object_awarded_on_url = Just "https://example.com/aaa1/proj-name/-/commit/abc123#note_26147"}), emoji_event_note = Just (NoteObjectAttributes {note_object_attributes_attachment = Nothing, note_object_attributes_author_id = 224, note_object_attributes_change_position = Just (ChangePosition {change_position_base_sha = Nothing, change_position_start_sha = Nothing, change_position_head_sha = Nothing, change_position_old_path = Nothing, change_position_new_path = Nothing, change_position_position_type = Just "text", change_position_old_line = Nothing, change_position_new_line = Nothing, change_position_line_range = Nothing}), note_object_attributes_commit_id = Just "abc123", note_object_attributes_created_at = "2025-11-18 08:07:26 UTC", note_object_attributes_discussion_id = "abc123", note_object_attributes_id = 26147, note_object_attributes_line_code = Just "abc123", note_object_attributes_note = "the note", note_object_attributes_noteable_id = Nothing, note_object_attributes_noteable_type = "Commit", note_object_attributes_original_position = Just (ChangePosition {change_position_base_sha = Just "abc123", change_position_start_sha = Just "abc123", change_position_head_sha = Just "abc123", change_position_old_path = Just "src/Example.java", change_position_new_path = Just "src/Example.java", change_position_position_type = Just "text", change_position_old_line = Just 162, change_position_new_line = Just 166, change_position_line_range = Nothing}), note_object_attributes_position = Just (ChangePosition {change_position_base_sha = Just "abc123", change_position_start_sha = Just "abc123", change_position_head_sha = Just "abc123", change_position_old_path = Just "src/Example.java", change_position_new_path = Just "src/Example.java", change_position_position_type = Just "text", change_position_old_line = Just 162, change_position_new_line = Just 166, change_position_line_range = Nothing}), note_object_attributes_project_id = 45173, note_object_attributes_resolved_at = Nothing, note_object_attributes_resolved_by_id = Nothing, note_object_attributes_resolved_by_push = Nothing, note_object_attributes_st_diff = Nothing, note_object_attributes_system = False, note_object_attributes_type = Just "DiffNote", note_object_attributes_updated_at = Just "2025-11-18 08:46:40 UTC", note_object_attributes_updated_by_id = Nothing, note_object_attributes_description = "the note", note_object_attributes_url = "https://example.com/aaa1/proj-name/-/commit/abc123#note_26147", note_object_attributes_action = Nothing}), emoji_event_commit = Just (CommitEvent {commitEvent_id = "abc123", commitEvent_message = "commit message", commitEvent_timestamp = "2025-11-17T03:11:00+08:00", commitEvent_url = "https://example.com/aaa1/proj-name/-/commit/abc123", commitEvent_author = CommitAuthorEvent {commitAuthorEvent_name = "Joe", commitAuthorEvent_email = "joe@example.com"}})}
+
+accessToken1Haskell :: AccessTokenEvent
+accessToken1Haskell =
+  AccessTokenEvent {access_token_event_project = EmojiProject {emoji_project_id = 45653, emoji_project_name = "proj-name", emoji_project_description = Just "", emoji_project_web_url = "https://example.com/aaa1/proj-name", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_git_http_url = "https://example.com/aaa1/proj-name.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "aaa1/proj-name", emoji_project_default_branch = "main", emoji_project_ci_config_path = Just "", emoji_project_homepage = "https://example.com/aaa1/proj-name", emoji_project_url = "git@example.com:aaa1/proj-name.git", emoji_project_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_http_url = "https://example.com/aaa1/proj-name.git"}, access_token_event_object_attributes = AccessTokenObjectAttributes {access_token_object_user_id = 4642, access_token_object_created_at = "2025-11-20 03:49:49 UTC", access_token_object_id = 2458, access_token_object_name = "the_token", access_token_object_expires_at = Just "2025-12-20"}, access_token_event_event_name = "expiring_access_token", access_token_event_interval = "thirty_days"}
+
+accessToken2Haskell :: AccessTokenEvent
+accessToken2Haskell =
+  AccessTokenEvent {access_token_event_project = EmojiProject {emoji_project_id = 42526, emoji_project_name = "project-name", emoji_project_description = Nothing, emoji_project_web_url = "https://example.com/abc1/example", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:abc1/example.git", emoji_project_git_http_url = "https://example.com/abc1/example.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "abc1/example", emoji_project_default_branch = "main", emoji_project_ci_config_path = Just "", emoji_project_homepage = "https://example.com/abc1/example", emoji_project_url = "git@example.com:abc1/example.git", emoji_project_ssh_url = "git@example.com:abc1/example.git", emoji_project_http_url = "https://example.com/abc1/example.git"}, access_token_event_object_attributes = AccessTokenObjectAttributes {access_token_object_user_id = 4560, access_token_object_created_at = "2025-10-29 22:41:27 UTC", access_token_object_id = 2258, access_token_object_name = "abc", access_token_object_expires_at = Just "2025-11-28"}, access_token_event_event_name = "expiring_access_token", access_token_event_interval = "seven_days"}
+
+accessToken3Haskell :: AccessTokenEvent
+accessToken3Haskell =
+  AccessTokenEvent {access_token_event_project = EmojiProject {emoji_project_id = 28491, emoji_project_name = "The project", emoji_project_description = Nothing, emoji_project_web_url = "https://example.com/aaa1/proj-name", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_git_http_url = "https://example.com/aaa1/proj-name.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "aaa1/proj-name", emoji_project_default_branch = "main", emoji_project_ci_config_path = Just "", emoji_project_homepage = "https://example.com/aaa1/proj-name", emoji_project_url = "git@example.com:aaa1/proj-name.git", emoji_project_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_http_url = "https://example.com/aaa1/proj-name.git"}, access_token_event_object_attributes = AccessTokenObjectAttributes {access_token_object_user_id = 3406, access_token_object_created_at = "2024-11-26 11:05:31 UTC", access_token_object_id = 1489, access_token_object_name = "some name", access_token_object_expires_at = Just "2025-11-26"}, access_token_event_event_name = "expiring_access_token", access_token_event_interval = "seven_days"}
+
+accessToken4Haskell :: AccessTokenEvent
+accessToken4Haskell =
+  AccessTokenEvent {access_token_event_project = EmojiProject {emoji_project_id = 28294, emoji_project_name = "proj-name", emoji_project_description = Nothing, emoji_project_web_url = "https://example.com/aaa1/proj-name", emoji_project_avatar_url = Nothing, emoji_project_git_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_git_http_url = "https://example.com/aaa1/proj-name.git", emoji_project_namespace = "Joe Bloggs", emoji_project_visibility_level = 0, emoji_project_path_with_namespace = "aaa1/proj-name", emoji_project_default_branch = "main", emoji_project_ci_config_path = Nothing, emoji_project_homepage = "https://example.com/aaa1/proj-name", emoji_project_url = "git@example.com:aaa1/proj-name.git", emoji_project_ssh_url = "git@example.com:aaa1/proj-name.git", emoji_project_http_url = "https://example.com/aaa1/proj-name.git"}, access_token_event_object_attributes = AccessTokenObjectAttributes {access_token_object_user_id = 3401, access_token_object_created_at = "2024-11-25 08:12:01 UTC", access_token_object_id = 1470, access_token_object_name = "aaa1", access_token_object_expires_at = Just "2025-11-25"}, access_token_event_event_name = "expiring_access_token", access_token_event_interval = "seven_days"}
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -22,11 +22,13 @@
 import API.TodosTests
 import API.UsersTests
 import API.VersionTests
+import GHC.IO.Encoding (setLocaleEncoding, utf8)
 import SystemHookTests
 import Test.Tasty
 
 main :: IO ()
 main = do
+  setLocaleEncoding utf8
   defaultMain
     ( testGroup
         "gitlab-haskell"
