diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,422 @@
+## Changes for 0.30.0.2
+
+_2026-03-12, Andreas Abel_
+
+- Fixed issue [#531](https://github.com/haskell-github/github/issues/531)
+  (Norfairking, PR [#532](https://github.com/haskell-github/github/pull/532)).
+- Allow `http-client-tls-0.4`.
+
+Tested with GHC 8.2 - 9.14.1.
+
+## Changes for 0.30.0.1
+
+_2025-08-27, Andreas Abel_
+
+- Drop dependencies `deepseq-generics` and `transformers-compat`.
+- Remove obsolete `deriving Typeable`.
+
+Tested with GHC 8.2 - 9.14 alpha1.
+
+## Changes for 0.30
+
+_2025-05-09, Andreas Abel, Peace edition_
+
+- Organization membership endpoint (Domen Kožar, PR [#487](https://github.com/haskell-github/github/pull/487)).
+- Allow JWT as an authentication method (Tom Sydney Kerckhove, PR [#497](https://github.com/haskell-github/github/pull/497)).
+- Support pagination (Tom McLaughlin, PR [#503](https://github.com/haskell-github/github/pull/503)).
+- Initial subset of Reactions endpoints (Dan Rijks, PR [#509](https://github.com/haskell-github/github/pull/509)).
+- Fix `getNotifications` (maralorn, PR [#511](https://github.com/haskell-github/github/pull/511)).
+- Add missing `name` field to `WorkflowJobs` `Job` type (Hugh Davidson, PR [#518](https://github.com/haskell-github/github/pull/518)).
+- Add `StateReasonDuplicate` to `IssueStateReason` (PR [#523](https://github.com/haskell-github/github/pull/523)).
+- Drop support for GHC 8.0 and below.
+- Drop dependency `time-compat`.
+
+Tested with GHC 8.2 - 9.12.2.
+
+
+## Changes for 0.29
+
+_2023-06-24, Andreas Abel, Midsommar edition_
+
+- Support for the GitHub Actions API
+  (PR [#459](https://github.com/haskell-github/github/pull/459)):
+  * New endpoint modules `GitHub.EndPoints.Actions.Artifacts`, `.Cache`,
+    `.Secrets`, `.Workflows`, `.WorkflowRuns`, `.WorkflowJobs`.
+  * Matching data structure modules `GitHub.Data.Actions.*`.
+
+- Add field `issueStateReason` of type `Maybe IssueStateReason` to `Issue`
+  with possible values `completed`, `not_planned` and `reopened`
+  (PR [#496](https://github.com/haskell-github/github/pull/496)).
+
+Tested with GHC 7.8 - 9.6.2
+
+## Changes for 0.28.0.1
+
+_2022-07-23, Andreas Abel_
+
+Tested with GHC 7.8 - 9.4.1 alpha3
+
+- Drop unused dependency `vector-instances`.
+- Allow latest: `aeson-2.1`, `mtl-2.3`, `vector-0.13`, `transformers-0.6`.
+
+## Changes for 0.28
+
+_2022-04-30, Andreas Abel, Valborg edition_
+
+Tested with GHC 7.8 - 9.2.2
+
+- Add constructors to `IssueRepoMod` that allow filtering issues by
+  milestone, assignee, creator, mentioned user:
+  `GitHub.Data.Options.options{Milestone,Assignee,Creator,Mentioned}`
+  (PR [#470](https://github.com/haskell-github/github/pull/470))
+
+- Add permissions field to `Repo`.
+  This adds record `RepoPermissions` and field `Repo.repoPermissions`
+  in module `GitHub.Data.Repos`.
+  (PR [#476](https://github.com/haskell-github/github/pull/476))
+
+- Add unwatch request `GitHub.Endpoints.Activity.Watching.unwatchRepoR`
+  (PR [#473](https://github.com/haskell-github/github/pull/473))
+
+Breaking change:
+
+- Make searches paginated
+  (PR [#474](https://github.com/haskell-github/github/pull/474)):
+  * Adds record `GitHub.Data.Repos.CodeSearchRepo`.
+  * Adds argument `FetchCount`
+    to `GitHub.Endpoints.Search.search{Repos,Code,Issues,Users}R`.
+
+## Changes for 0.27
+
+_2021-10-10, Oleg Grenrus_
+
+- Add vector of `SimpleTeam` in "requested_teams" field of `PullRequest`
+  [#453](https://github.com/haskell-github/github/pull/453)
+- Add endpoint to create gist
+  [#455](https://github.com/haskell-github/github/pull/455)
+- Update `RepoWebhookEvent`
+  [#461](https://github.com/haskell-github/github/pull/461)
+- `PullRequest` Reviews may not have submitted_at field
+  [#450](https://github.com/haskell-github/github/pull/450)
+
+## Changes for 0.26
+
+_2020-05-26, Oleg Grenrus_
+
+- Generalize `PagedQuery` to allow its reuse by preview github APIs
+  [#439](https://github.com/haskell-github/github/pull/439)
+- Add endpoint for listing organizations outside collaborators
+  [#445](https://github.com/haskell-github/github/pull/445)
+- Add endpoint for users search
+  [#444](https://github.com/haskell-github/github/pull/444)
+- Make `repoWebhookResponseStatus` optional
+  [#436](https://github.com/haskell-github/github/pull/436)
+- Teams improvements
+  [#417](https://github.com/haskell-github/github/pull/417)
+- Add `deleteReference` endpoint
+  [#388](https://github.com/haskell-github/github/pull/388)
+
+## Changes for 0.25
+
+_2020-02-18, Oleg Grenrus_
+
+- Add `executeRequestWithMgrAndRes`
+  [#421](https://github.com/haskell-github/github/pull/421)
+- Add `limitsFromHttpResponse`
+  [#421](https://github.com/haskell-github/github/pull/421)
+- Add label descriptions
+  [#418](https://github.com/haskell-github/github/pull/418)
+- Add "draft" option to mergeable state
+  [#431](https://github.com/haskell-github/github/pull/431)
+- Use `IssueNumber` in `editIssueR` and `issueR`
+  [#429](https://github.com/haskell-github/github/pull/429)
+- Manage orgs in GitHub Enterprise
+  [#420](https://github.com/haskell-github/github/pull/420)
+- Add support for collaborator permission endpoint
+  [#425](https://github.com/haskell-github/github/pull/425)
+- Add support for the comment reply endpoint
+  [#424](https://github.com/haskell-github/github/pull/424)
+- Organise exports in `GitHub`
+  [#430](https://github.com/haskell-github/github/pull/430)
+
+## Changes for 0.24
+
+_2019-11-27, Oleg Grenrus_
+
+**Major change**:
+Introduce `github` n-ary combinator to hoist `... -> Request rw res`
+into `... -> IO (Either Error res)` (i.e. n-ary `executeRequest`).
+With that in place drop `.. -> IO (Either Error res)` functions.
+
+This reduces symbol bloat in the library.
+[#415](https://github.com/haskell-github/github/pull/415)
+
+- Remove double `withOpenSSL`
+  [#414](https://github.com/haskell-github/github/pull/414)
+- Pull requests reviews API uses issue number
+  [#409](https://github.com/haskell-github/github/pull/409)
+- Update `Repo`, `NewRepo` and `EditRepo` data types
+  [#407](https://github.com/haskell-github/github/pull/407)
+
+## Changes for 0.23
+
+_2019-10-01, Oleg Grenrus_
+
+- Escape URI paths
+  [#404](https://github.com/haskell-github/github/pull/404)
+- Add `OwnerBot` to `OwnerType`
+  [#399](https://github.com/haskell-github/github/pull/399)
+- Make `File.fileSha` optional
+  [#392](https://github.com/haskell-github/github/pull/392)
+- Update User-Agent to contain up to date version
+  [#403](https://github.com/haskell-github/github/pull/403)
+  [#394](https://github.com/haskell-github/github/pull/394)
+
+## Changes for 0.22
+
+_2019-05-31, Oleg Grenrus_
+
+- Type-class for various auth methods
+  [#365](https://github.com/haskell-github/github/pull/365)
+- Throw on non-200 responses
+  [#350](https://github.com/haskell-github/github/pull/350)
+- Add extension point for (preview) media types
+  [#370](https://github.com/haskell-github/github/pull/370)
+- Add missing webhook event types
+  [#359](https://github.com/haskell-github/github/pull/359)
+- Add invitation endpoint
+  [#360](https://github.com/haskell-github/github/pull/360)
+- Add notifications endpoints
+  [#324](https://github.com/haskell-github/github/pull/324)
+- Add ssh keys endpoints
+  [#363](https://github.com/haskell-github/github/pull/365)
+- Case insensitive enum parsing
+  [#373](https://github.com/haskell-github/github/pull/373)
+- Don't try parse unitary responses
+  [#377](https://github.com/haskell-github/github/issues/377)
+- Update dependencies
+  [#364](https://github.com/haskell-github/github/pull/364)
+  [#368](https://github.com/haskell-github/github/pull/368)
+  [#369](https://github.com/haskell-github/github/pull/369)
+- Documentation improvements
+  [#357](https://github.com/haskell-github/github/pull/357)
+
+## Changes for 0.21
+
+_2019-02-18, Oleg Grenrus_
+
+- Refactor `Request` type.
+  [#349](https://github.com/haskell-github/github/pull/349)
+- Allow `http-client-0.6`
+  [#344](https://github.com/haskell-github/github/pull/344)
+- Change to use `cryptohash-sha1` (`cryptohash` was used before)
+- Add Create milestone endpoints
+  [#337](https://github.com/haskell-github/github/pull/337)
+- Make `fileBlobUrl` and `fileRawUrl` optional
+  [#339](https://github.com/haskell-github/github/issues/339)
+  [#340](https://github.com/haskell-github/github/pull/340)
+- Add `organizationsR` to request user organizations
+  [#345](https://github.com/haskell-github/github/pull/345)
+- Add `updateMilestoneR`, `deleteMilestoneR`
+  [#338](https://github.com/haskell-github/github/pull/338)
+- Allow multiple assignees in `NewIssue` and `EditIssue`
+  [#336](https://github.com/haskell-github/github/pull/336)
+- Add `pullRequestPatchR` and `pullRequestDiffR`
+  [#325](https://github.com/haskell-github/github/pull/325)
+
+## Changes for 0.20
+
+_2018-09-26, Oleg Grenrus_
+
+- Add ratelimit endpoint
+  [#315](https://github.com/haskell-github/github/pull/315)
+- Add some deployment endoints
+  [#330](https://github.com/haskell-github/github/pull/330)
+- Add webhook installation events
+  [#329](https://github.com/haskell-github/github/pull/330)
+- Tighten lower bounds (also remove `aeson-compat` dep)
+  [#332](https://github.com/haskell-github/github/pull/332)
+
+## Changes for 0.19
+
+_2018-02-19, Oleg Grenrus_
+
+- Fix issue event type enumeration
+  [#301](https://github.com/haskell-github/github/issues/301)
+- Include label info in `IssueEvent`
+  [#302](https://github.com/haskell-github/github/issues/302)
+- Fix `ShowRepo` example
+  [#306](https://github.com/haskell-github/github/pull/306)
+- Add "Get archive link" API
+  [#307](https://github.com/haskell-github/github/pull/307)
+- Make "repo" in `PullRequestCommit` nullable (repository can be gone)
+  [#311](https://github.com/haskell-github/github/pull/311)
+- Add read-only emails endpoint
+  [#313](https://github.com/haskell-github/github/pull/313)
+- Organisation membership API
+  [#312](https://github.com/haskell-github/github/pull/312)
+- Fix `isPullRequestMerged` and other boolean responses
+  [#312](https://github.com/haskell-github/github/pull/312)
+- Add `behind` pull request mergeable state
+  [#308](https://github.com/haskell-github/github/pull/308)
+- Add list organisation invitations endpoint
+
+## Changes for 0.18
+
+_2017-11-10, Oleg Grenrus_
+
+- Endpoints for deleting issue comments.
+  [#294](https://github.com/haskell-github/github/pull/294)
+- Endpoints for (un)starring gists.
+  [#296](https://github.com/haskell-github/github/pull/296)
+- Add `archived` field to `Repo`.
+  [#298](https://github.com/haskell-github/github/pull/298)
+- Update dependencies.
+  [#295](https://github.com/haskell-github/github/pull/295)
+- Add Statuses endpoints.
+  [#268](https://github.com/haskell-github/github/pull/268)
+- Add requested reviewers field to pull request records.
+  [#292](https://github.com/haskell-github/github/pull/292)
+
+## Changes for 0.17.0
+
+_2017-09-26, Oleg Grenrus_
+
+- Add `Ord Request` instance
+- Repository contents
+- Repository starring endpoints
+- Pull Request review endpoints
+
+## Changes for 0.16.0
+
+_2017-07-24, Oleg Grenrus_
+
+- Add support for `mergeable_state = "blocked".`
+- Fix HTTP status code of merge PR
+- Supports newest versions of dependencies
+- user events
+- release endpoints
+- `forkExistingRepo`
+
+## Changes for 0.15.0
+
+_2016-11-04, Oleg Grenrus_
+
+- Reworked `PullRequest` (notably `pullRequestsFor`)
+- Reworked PR and Issue filtering
+- GHC-8.0.1 support
+- Change `repoMasterBranch` to `repoDefaultBranch` in `Repo`
+- Add `listTeamReposR`
+- Add `myStarredAcceptStarR`
+- Add `HeaderQuery` to `Request`
+- Add `Hashable Auth` instance
+- Add `mkUserId`, `mkUserName`, `fromUserId`, `fromOrganizationId`
+- Add `userIssuesR`
+- Add `organizationIssuesR`
+- Make `teamName :: Text` amnd `teamSlug :: Name Team` in both: `Team` and `SimpleTeam`
+- Refactor `Request` structure
+- Added multiple issue assignees
+- Preliminary support for repository events: `repositoryEventsR`
+- Support for adding repository permissions to the team
+- Remove `simpleUserType`, it was always the same.
+
+See [git commit summary](https://github.com/haskell-github/github/compare/v0.14.1...v0.15.0)
+
+## Changes for 0.14.1
+
+_2016-02-02, Oleg Grenrus_
+
+- Add `membersOfWithR`, `listTeamMembersR`
+- Add related enums: `OrgMemberFilter`, `OrgMemberRole`, `TeamMemberRole`
+- Add `Enum` and `Bounded` instances to `Privacy`, `Permission`,
+  `RepoPublicity`
+- Don't require network access for search tests
+
+## Changes for 0.14.0
+
+_2016-01-25, Oleg Grenrus_
+
+Large API changes:
+
+- Use `Text` and `Vector` in place of `String` and `[]`.
+- Use `Name` and `Id` tagged types for names and identifiers.
+- Make detailed structures un-prefixed, simple ones prefixed with `Simple`. Example: `Team` and `SimpleTeam`.
+- Decouple request creation from execution (`*R` and `executeRequest*` functions).
+- Add `Binary` instances for all data
+- `GithubOwner` is a `newtype` of `Either User Organization`. There's still `SimpleOwner`.
+
+## Releases without changelog
+
+| Version | Date  | Uploader  |
+|---|---|---|
+| __0.13.2__ | _2015-04-26_ | _John Wiegley_        |
+| __0.13.1__ | _2014-12-01_ | _César López-Natarén_ |
+| __0.13__   | _2014-11-09_ | _César López-Natarén_ |
+| __0.12__   | _2014-11-09_ | _César López-Natarén_ |
+| __0.11.1__ | _2014-09-07_ | _César López-Natarén_ |
+| __0.11.0__ | _2014-08-25_ | _César López-Natarén_ |
+| __0.10.0__ | _2014-08-18_ | _César López-Natarén_ |
+| __0.9__    | _2014-07-31_ | _John Wiegley_        |
+| __0.8__    | _2014-05-02_ | _John Wiegley_        |
+| __0.7.4__  | _2014-01-22_ | _John Wiegley_        |
+| __0.7.3__  | _2013-12-21_ | _John Wiegley_        |
+| __0.7.2__  | _2013-12-02_ | _John Wiegley_        |
+| __0.7.1__  | _2013-08-08_ | _John Wiegley_        |
+| __0.7.0__  | _2013-04-26_ | _John Wiegley_        |
+| __0.6.0__  | _2013-04-12_ | _John Wiegley_        |
+
+## Changes for 0.5.0:
+
+_2013-02-05, Mike Burns_
+
+* `OAuth`.
+* New function: `Github.Repos.organizationRepo`, to get the repo for a specific organization.
+* Introduce a new `newRepoAutoInit` flag to `NewRepo`, for whether to initialize a repo while creating it.
+* Relax the `attoparsec` version requirements.
+* The above by [John Wiegley](https://github.com/jwiegley).
+
+## Changes for 0.4.1:
+
+_2013-01-14, Mike Burns_
+
+* Stop using the `uri` package.
+* Use `aeson` version 0.6.1.0.
+* Use `attoparsec` version 0.10.3.0.
+* Use `http-conduit` over 1.8.
+* Use `unordered-containers` between 0.2 and 0.3.
+
+## Changes for 0.4.0:
+
+_2012-06-26, Mike Burns_
+
+* Use `http-conduit` version 1.4.1.10.
+
+## Changes for 0.3.0:
+
+_2012-06-10, Mike Burns_
+
+* Re-instantiate the Blobs API.
+* `repoDescription1` and `repoPushedAt` are a `Maybe GithubDate`.
+* Add `deleteRepo`, `editRepo`, and `createRepo`.
+* Private gists, issues, organizations, pull requests, and users.
+* Lock down `tls` and `tls-extra` instead of keeping up with the
+  ever-changing `http-conduit` package.
+* Features by [Pavel Ryzhov](https://github.com/paulrzcz) and [Simon Hengel](https://github.com/sol).
+
+## Changes for 0.2.1:
+
+_2012-02-16, Mike Burns_
+
+* Expand the `unordered-containers` dependency to anything in 0.1.x .
+
+## Changes for 0.2.0:
+
+_2012-02-15, Mike Burns_
+
+* `milestoneDueOn` and `repoLanguage` are now `Maybe` types.
+* Introduce `GithubOwner` as the sum type for a `GithubUser` or `GithubOrganization`. Everything that once produced a `GithubUser` now produces a `GithubOwner`. All record accessors have changed their names.
+* Similar to `GithubOwner`, introduce `DetailedOwner`, which can be a `DetailedUser` or a `DetailedOrganization`. All record accessors have changed their names.
+* An `HTTPConnectionError` now composes `SomeException` instead of `IOException`. All exceptions raised by the underlying http-conduit library are encapulated there.
+* The `githubIssueClosedBy` function now produces a `Maybe GithubOwner`.
+* Remove the Blobs API, as it is broken upstream.
+* Bugs found and squashed thanks to [Joey Hess](https://github.com/joeyh) and [Simon Hengel](https://github.com/sol).
diff --git a/Github/Auth.hs b/Github/Auth.hs
deleted file mode 100644
--- a/Github/Auth.hs
+++ /dev/null
@@ -1,4 +0,0 @@
--- | The Github auth data type
-module Github.Auth (P.GithubAuth(..)) where
-
-import qualified Github.Private as P
diff --git a/Github/Data.hs b/Github/Data.hs
deleted file mode 100644
--- a/Github/Data.hs
+++ /dev/null
@@ -1,721 +0,0 @@
-{-# LANGUAGE CPP, DeriveDataTypeable, OverloadedStrings #-}
-
--- | This module re-exports the @Github.Data.Definitions@ module, adding
--- instances of @FromJSON@ to it. If you wish to use the data without the
--- instances, use the @Github.Data.Definitions@ module instead.
-
-module Github.Data (module Github.Data.Definitions) where
-
-import Control.Applicative
-import Control.Monad
-import qualified Data.Text as T
-import Data.Aeson.Types
-import qualified Data.Vector as V
-import qualified Data.HashMap.Lazy as Map
-import Data.Hashable (Hashable)
-
-#if MIN_VERSION_base(4,8,0)
-import Data.Time
-#else
-import Data.Time
-import System.Locale (defaultTimeLocale)
-#endif
-
-import Github.Data.Definitions
-
-instance FromJSON GithubDate where
-  parseJSON (String t) =
-    case parseTime defaultTimeLocale "%FT%T%Z" (T.unpack t) of
-         Just d -> pure $ GithubDate d
-         _      -> fail "could not parse Github datetime"
-  parseJSON _          = fail "Given something besides a String"
-
-instance FromJSON Commit where
-  parseJSON (Object o) =
-    Commit <$> o .: "sha"
-           <*> o .: "parents"
-           <*> o .: "url"
-           <*> o .: "commit"
-           <*> o .:? "committer"
-           <*> o .:? "author"
-           <*> o .:< "files"
-           <*> o .:? "stats"
-  parseJSON _          = fail "Could not build a Commit"
-
-instance FromJSON Tree where
-  parseJSON (Object o) =
-    Tree <$> o .: "sha"
-         <*> o .: "url"
-         <*> o .:< "tree"
-  parseJSON _          = fail "Could not build a Tree"
-
-instance FromJSON GitTree where
-  parseJSON (Object o) =
-    GitTree <$> o .: "type"
-         <*> o .: "sha"
-         <*> o .:? "url"
-         <*> o .:? "size"
-         <*> o .: "path"
-         <*> o .: "mode"
-  parseJSON _          = fail "Could not build a GitTree"
-
-instance FromJSON GitCommit where
-  parseJSON (Object o) =
-    GitCommit <$> o .: "message"
-              <*> o .: "url"
-              <*> o .: "committer"
-              <*> o .: "author"
-              <*> o .: "tree"
-              <*> o .:? "sha"
-              <*> o .:< "parents"
-  parseJSON _          = fail "Could not build a GitCommit"
-
-instance FromJSON GithubOwner where
-  parseJSON (Object o)
-    | o `at` "gravatar_id" == Nothing =
-      GithubOrganization <$> o .: "avatar_url"
-                 <*> o .: "login"
-                 <*> o .: "url"
-                 <*> o .: "id"
-    | otherwise =
-      GithubUser <$> o .: "avatar_url"
-                 <*> o .: "login"
-                 <*> o .: "url"
-                 <*> o .: "id"
-                 <*> o .: "gravatar_id"
-  parseJSON v          = fail $ "Could not build a GithubOwner out of " ++ (show v)
-
-instance FromJSON GitUser where
-  parseJSON (Object o) =
-    GitUser <$> o .: "name"
-            <*> o .: "email"
-            <*> o .: "date"
-  parseJSON _          = fail "Could not build a GitUser"
-
-instance FromJSON File where
-  parseJSON (Object o) =
-    File <$> o .: "blob_url"
-         <*> o .: "status"
-         <*> o .: "raw_url"
-         <*> o .: "additions"
-         <*> o .: "sha"
-         <*> o .: "changes"
-         <*> o .: "patch"
-         <*> o .: "filename"
-         <*> o .: "deletions"
-  parseJSON _          = fail "Could not build a File"
-
-instance FromJSON Stats where
-  parseJSON (Object o) =
-    Stats <$> o .: "additions"
-          <*> o .: "total"
-          <*> o .: "deletions"
-  parseJSON _          = fail "Could not build a Stats"
-
-instance FromJSON Comment where
-  parseJSON (Object o) =
-    Comment <$> o .:? "position"
-            <*> o .:? "line"
-            <*> o .: "body"
-            <*> o .:? "commit_id"
-            <*> o .: "updated_at"
-            <*> o .:? "html_url"
-            <*> o .: "url"
-            <*> o .: "created_at"
-            <*> o .:? "path"
-            <*> o .: "user"
-            <*> o .: "id"
-  parseJSON _          = fail "Could not build a Comment"
-
-instance ToJSON NewComment where
-  toJSON (NewComment b) = object [ "body" .= b ]
-
-instance ToJSON EditComment where
-  toJSON (EditComment b) = object [ "body" .= b ]
-
-instance FromJSON Diff where
-  parseJSON (Object o) =
-    Diff <$> o .: "status"
-         <*> o .: "behind_by"
-         <*> o .: "patch_url"
-         <*> o .: "url"
-         <*> o .: "base_commit"
-         <*> o .:< "commits"
-         <*> o .: "total_commits"
-         <*> o .: "html_url"
-         <*> o .:< "files"
-         <*> o .: "ahead_by"
-         <*> o .: "diff_url"
-         <*> o .: "permalink_url"
-  parseJSON _          = fail "Could not build a Diff"
-
-instance FromJSON Gist where
-  parseJSON (Object o) =
-    Gist <$> o .: "user"
-         <*> o .: "git_push_url"
-         <*> o .: "url"
-         <*> o .:? "description"
-         <*> o .: "created_at"
-         <*> o .: "public"
-         <*> o .: "comments"
-         <*> o .: "updated_at"
-         <*> o .: "html_url"
-         <*> o .: "id"
-         <*> o `values` "files"
-         <*> o .: "git_push_url"
-  parseJSON _          = fail "Could not build a Gist"
-
-instance FromJSON GistFile where
-  parseJSON (Object o) =
-    GistFile <$> o .: "type"
-             <*> o .: "raw_url"
-             <*> o .: "size"
-             <*> o .:? "language"
-             <*> o .: "filename"
-             <*> o .:? "content"
-  parseJSON _          = fail "Could not build a GistFile"
-
-instance FromJSON GistComment where
-  parseJSON (Object o) =
-    GistComment <$> o .: "user"
-                <*> o .: "url"
-                <*> o .: "created_at"
-                <*> o .: "body"
-                <*> o .: "updated_at"
-                <*> o .: "id"
-  parseJSON _          = fail "Could not build a GistComment"
-
-instance FromJSON Blob where
-  parseJSON (Object o) =
-    Blob <$> o .: "url"
-         <*> o .: "encoding"
-         <*> o .: "content"
-         <*> o .: "sha"
-         <*> o .: "size"
-  parseJSON _          = fail "Could not build a Blob"
-
-instance ToJSON NewGitReference where
-  toJSON (NewGitReference r s) = object [ "ref" .= r, "sha" .= s  ]
-
-instance FromJSON GitReference where
-  parseJSON (Object o) =
-    GitReference <$> o .: "object"
-                 <*> o .: "url"
-                 <*> o .: "ref"
-  parseJSON _          = fail "Could not build a GitReference"
-
-instance FromJSON GitObject where
-  parseJSON (Object o) =
-    GitObject <$> o .: "type"
-           <*> o .: "sha"
-           <*> o .: "url"
-  parseJSON _          = fail "Could not build a GitObject"
-
-instance FromJSON Issue where
-  parseJSON (Object o) =
-    Issue <$> o .:? "closed_at"
-          <*> o .: "updated_at"
-          <*> o .: "events_url"
-          <*> o .: "html_url"
-          <*> o .:? "closed_by"
-          <*> o .: "labels"
-          <*> o .: "number"
-          <*> o .:? "assignee"
-          <*> o .: "user"
-          <*> o .: "title"
-          <*> o .:? "pull_request"
-          <*> o .: "url"
-          <*> o .: "created_at"
-          <*> o .: "body"
-          <*> o .: "state"
-          <*> o .: "id"
-          <*> o .: "comments"
-          <*> o .:? "milestone"
-  parseJSON _          = fail "Could not build an Issue"
-
-instance ToJSON NewIssue where
-  toJSON (NewIssue t b a m ls) =
-    object
-    [ "title"     .= t
-    , "body"      .= b
-    , "assignee"  .= a
-    , "milestone" .= m
-    , "labels"    .= ls ]
-
-instance ToJSON EditIssue where
-  toJSON (EditIssue t b a s m ls) =
-    object $ filter notNull $ [ "title" .= t
-                              , "body" .= b
-                              , "assignee" .= a
-                              , "state" .= s
-                              , "milestone" .= m
-                              , "labels" .= ls ]
-    where notNull (_, Null) = False
-          notNull (_, _)    = True
-
-instance FromJSON Milestone where
-  parseJSON (Object o) =
-    Milestone <$> o .: "creator"
-              <*> o .: "due_on"
-              <*> o .: "open_issues"
-              <*> o .: "number"
-              <*> o .: "closed_issues"
-              <*> o .: "description"
-              <*> o .: "title"
-              <*> o .: "url"
-              <*> o .: "created_at"
-              <*> o .: "state"
-  parseJSON _          = fail "Could not build a Milestone"
-
-instance FromJSON IssueLabel where
-  parseJSON (Object o) =
-    IssueLabel <$> o .: "color"
-               <*> o .: "url"
-               <*> o .: "name"
-  parseJSON _          = fail "Could not build a Milestone"
-
-instance FromJSON PullRequestReference where
-  parseJSON (Object o) =
-    PullRequestReference <$> o .:? "html_url"
-                         <*> o .:? "patch_url"
-                         <*> o .:? "diff_url"
-  parseJSON _          = fail "Could not build a PullRequest"
-
-instance FromJSON IssueComment where
-  parseJSON (Object o) =
-    IssueComment <$> o .: "updated_at"
-                 <*> o .: "user"
-                 <*> o .: "url"
-                 <*> o .: "created_at"
-                 <*> o .: "body"
-                 <*> o .: "id"
-  parseJSON _          = fail "Could not build an IssueComment"
-
-instance FromJSON Event where
-  parseJSON (Object o) =
-    Event <$> o .: "actor"
-          <*> o .: "event"
-          <*> o .:? "commit_id"
-          <*> o .: "url"
-          <*> o .: "created_at"
-          <*> o .: "id"
-          <*> o .:? "issue"
-  parseJSON _          = fail "Could not build an Event"
-
-instance FromJSON EventType where
-  parseJSON (String "closed") = pure Closed
-  parseJSON (String "reopened") = pure Reopened
-  parseJSON (String "subscribed") = pure Subscribed
-  parseJSON (String "merged") = pure Merged
-  parseJSON (String "referenced") = pure Referenced
-  parseJSON (String "mentioned") = pure Mentioned
-  parseJSON (String "assigned") = pure Assigned
-  parseJSON (String "unsubscribed") = pure Unsubscribed
-  parseJSON (String "unassigned") = pure ActorUnassigned
-  parseJSON (String "labeled") = pure Labeled
-  parseJSON (String "unlabeled") = pure Unlabeled
-  parseJSON (String "milestoned") = pure Milestoned
-  parseJSON (String "demilestoned") = pure Demilestoned
-  parseJSON (String "renamed") = pure Renamed
-  parseJSON (String "locked") = pure Locked
-  parseJSON (String "unlocked") = pure Unlocked
-  parseJSON (String "head_ref_deleted") = pure HeadRefDeleted
-  parseJSON (String "head_ref_restored") = pure HeadRefRestored
-  parseJSON _ = fail "Could not build an EventType"
-
-instance FromJSON SimpleOrganization where
-  parseJSON (Object o) =
-    SimpleOrganization <$> o .: "url"
-                       <*> o .: "avatar_url"
-                       <*> o .: "id"
-                       <*> o .: "login"
-  parseJSON _ = fail "Could not build a SimpleOrganization"
-
-instance FromJSON Organization where
-  parseJSON (Object o) =
-    Organization <$> o .: "type"
-                 <*> o .:? "blog"
-                 <*> o .:? "location"
-                 <*> o .: "login"
-                 <*> o .: "followers"
-                 <*> o .:? "company"
-                 <*> o .: "avatar_url"
-                 <*> o .: "public_gists"
-                 <*> o .: "html_url"
-                 <*> o .:? "email"
-                 <*> o .: "following"
-                 <*> o .: "public_repos"
-                 <*> o .: "url"
-                 <*> o .: "created_at"
-                 <*> o .:? "name"
-                 <*> o .: "id"
-  parseJSON _ = fail "Could not build an Organization"
-
-instance FromJSON PullRequest where
-  parseJSON (Object o) =
-      PullRequest
-        <$> o .:? "closed_at"
-        <*> o .: "created_at"
-        <*> o .: "user"
-        <*> o .: "patch_url"
-        <*> o .: "state"
-        <*> o .: "number"
-        <*> o .: "html_url"
-        <*> o .: "updated_at"
-        <*> o .: "body"
-        <*> o .: "issue_url"
-        <*> o .: "diff_url"
-        <*> o .: "url"
-        <*> o .: "_links"
-        <*> o .:? "merged_at"
-        <*> o .: "title"
-        <*> o .: "id"
-  parseJSON _ = fail "Could not build a PullRequest"
-
-instance ToJSON EditPullRequestState where
-  toJSON (EditPullRequestStateOpen) = String "open"
-  toJSON (EditPullRequestStateClosed) = String "closed"
-
-instance ToJSON EditPullRequest where
-  toJSON (EditPullRequest t b s) =
-    object $ filter notNull [ "title" .= t, "body" .= b, "state" .= s ]
-    where notNull (_, Null) = False
-          notNull (_, _) = True
-
-instance ToJSON CreatePullRequest where
-  toJSON (CreatePullRequest t b headPR basePR) =
-    object [ "title" .= t, "body" .= b, "head" .= headPR, "base" .= basePR ]
-  toJSON (CreatePullRequestIssue issueNum headPR basePR) =
-    object [ "issue" .= issueNum, "head" .= headPR, "base" .= basePR]
-
-instance FromJSON DetailedPullRequest where
-  parseJSON (Object o) =
-      DetailedPullRequest
-        <$> o .:? "closed_at"
-        <*> o .: "created_at"
-        <*> o .: "user"
-        <*> o .: "patch_url"
-        <*> o .: "state"
-        <*> o .: "number"
-        <*> o .: "html_url"
-        <*> o .: "updated_at"
-        <*> o .: "body"
-        <*> o .: "issue_url"
-        <*> o .: "diff_url"
-        <*> o .: "url"
-        <*> o .: "_links"
-        <*> o .:? "merged_at"
-        <*> o .: "title"
-        <*> o .: "id"
-        <*> o .:? "merged_by"
-        <*> o .: "changed_files"
-        <*> o .: "head"
-        <*> o .: "comments"
-        <*> o .: "deletions"
-        <*> o .: "additions"
-        <*> o .: "review_comments"
-        <*> o .: "base"
-        <*> o .: "commits"
-        <*> o .: "merged"
-        <*> o .:? "mergeable"
-  parseJSON _ = fail "Could not build a DetailedPullRequest"
-
-instance FromJSON PullRequestLinks where
-  parseJSON (Object o) =
-    PullRequestLinks <$> o <.:> ["review_comments", "href"]
-                     <*> o <.:> ["comments", "href"]
-                     <*> o <.:> ["html", "href"]
-                     <*> o <.:> ["self", "href"]
-  parseJSON _ = fail "Could not build a PullRequestLinks"
-
-instance FromJSON PullRequestCommit where
-  parseJSON (Object o) =
-    PullRequestCommit <$> o .: "label"
-                      <*> o .: "ref"
-                      <*> o .: "sha"
-                      <*> o .: "user"
-                      <*> o .: "repo"
-  parseJSON _ = fail "Could not build a PullRequestCommit"
-
-instance FromJSON PullRequestEvent where
-  parseJSON (Object o) =
-    PullRequestEvent <$> o .: "action"
-                     <*> o .: "number"
-                     <*> o .: "pull_request"
-                     <*> o .: "repository"
-                     <*> o .: "sender"
-  parseJSON _ = fail "Could not build a PullRequestEvent"
-
-instance FromJSON PullRequestEventType where
-  parseJSON (String "opened") = pure PullRequestOpened
-  parseJSON (String "closed") = pure PullRequestClosed
-  parseJSON (String "synchronize") = pure PullRequestSynchronized
-  parseJSON (String "reopened") = pure PullRequestReopened
-  parseJSON (String "assigned") = pure PullRequestAssigned
-  parseJSON (String "unassigned") = pure PullRequestUnassigned
-  parseJSON (String "labeled") = pure PullRequestLabeled
-  parseJSON (String "unlabeled") = pure PullRequestUnlabeled
-  parseJSON _ = fail "Could not build a PullRequestEventType"
-
-instance FromJSON RepoWebhookEvent where
-  parseJSON (String "*") = pure WebhookWildcardEvent
-  parseJSON (String "commit_comment") = pure WebhookCommitCommentEvent
-  parseJSON (String "create") = pure WebhookCreateEvent
-  parseJSON (String "delete") = pure WebhookDeleteEvent
-  parseJSON (String "deployment") = pure WebhookDeploymentEvent
-  parseJSON (String "deployment_status") = pure WebhookDeploymentStatusEvent
-  parseJSON (String "fork") = pure WebhookForkEvent
-  parseJSON (String "gollum") = pure WebhookGollumEvent
-  parseJSON (String "issue_comment") = pure WebhookIssueCommentEvent
-  parseJSON (String "issues") = pure WebhookIssuesEvent
-  parseJSON (String "member") = pure WebhookMemberEvent
-  parseJSON (String "page_build") = pure WebhookPageBuildEvent
-  parseJSON (String "public") = pure WebhookPublicEvent
-  parseJSON (String "pull_request_review_comment") = pure WebhookPullRequestReviewCommentEvent
-  parseJSON (String "pull_request") = pure WebhookPullRequestEvent
-  parseJSON (String "push") = pure WebhookPushEvent
-  parseJSON (String "release") = pure WebhookReleaseEvent
-  parseJSON (String "status") = pure WebhookStatusEvent
-  parseJSON (String "team_add") = pure WebhookTeamAddEvent
-  parseJSON (String "watch") = pure WebhookWatchEvent
-  parseJSON _ = fail "Could not build a Webhook event"
-
-instance ToJSON RepoWebhookEvent where
-  toJSON (WebhookWildcardEvent) = String "*"
-  toJSON (WebhookCommitCommentEvent) = String "commit_comment"
-  toJSON (WebhookCreateEvent) = String "create"
-  toJSON (WebhookDeleteEvent) = String "delete"
-  toJSON (WebhookDeploymentEvent) = String "deployment"
-  toJSON (WebhookDeploymentStatusEvent) = String "deployment_status"
-  toJSON (WebhookForkEvent) = String "fork"
-  toJSON (WebhookGollumEvent) = String "gollum"
-  toJSON (WebhookIssueCommentEvent) = String "issue_comment"
-  toJSON (WebhookIssuesEvent) = String "issues"
-  toJSON (WebhookMemberEvent) = String "member"
-  toJSON (WebhookPageBuildEvent) = String "page_build"
-  toJSON (WebhookPublicEvent) = String "public"
-  toJSON (WebhookPullRequestReviewCommentEvent) = String "pull_request_review_comment"
-  toJSON (WebhookPullRequestEvent) = String "pull_request"
-  toJSON (WebhookPushEvent) = String "push"
-  toJSON (WebhookReleaseEvent) = String "release"
-  toJSON (WebhookStatusEvent) = String "status"
-  toJSON (WebhookTeamAddEvent) = String "team_add"
-  toJSON (WebhookWatchEvent) = String "watch"
-
-instance FromJSON PingEvent where
-  parseJSON (Object o) =
-    PingEvent <$> o .: "zen"
-              <*> o .: "hook"
-              <*> o .: "hook_id"
-  parseJSON _ = fail "Could not build a PingEvent"
-
-instance FromJSON SearchReposResult where
-  parseJSON (Object o) =
-    SearchReposResult <$> o .: "total_count"
-                      <*> o .:< "items"
-  parseJSON _ = fail "Could not build a SearchReposResult"
-
-instance FromJSON Repo where
-  parseJSON (Object o) =
-    Repo <$> o .:? "ssh_url"
-         <*> o .: "description"
-         <*> o .:? "created_at"
-         <*> o .: "html_url"
-         <*> o .:? "svn_url"
-         <*> o .:? "forks"
-         <*> o .:? "homepage"
-         <*> o .: "fork"
-         <*> o .:? "git_url"
-         <*> o .: "private"
-         <*> o .:? "clone_url"
-         <*> o .:? "size"
-         <*> o .:? "updated_at"
-         <*> o .:? "watchers"
-         <*> o .: "owner"
-         <*> o .: "name"
-         <*> o .:? "language"
-         <*> o .:? "master_branch"
-         <*> o .:? "pushed_at"
-         <*> o .: "id"
-         <*> o .: "url"
-         <*> o .:? "open_issues"
-         <*> o .:? "has_wiki"
-         <*> o .:? "has_issues"
-         <*> o .:? "has_downloads"
-	 <*> o .:? "parent"
-	 <*> o .:? "source"
-         <*> o .: "hooks_url"
-  parseJSON _ = fail "Could not build a Repo"
-
-instance FromJSON SearchCodeResult where
-  parseJSON (Object o) =
-    SearchCodeResult <$> o .: "total_count"
-                     <*> o .:< "items"
-  parseJSON _ = fail "Could not build a SearchCodeResult"
-
-instance FromJSON Code where
-  parseJSON (Object o ) =
-    Code <$> o .: "name"
-         <*> o .: "path"
-         <*> o .: "sha"
-         <*> o .: "url"
-         <*> o .: "git_url"
-         <*> o .: "html_url"
-         <*> o .: "repository"
-  parseJSON _ = fail "Could not build a Code"
-
-instance FromJSON RepoRef where
-  parseJSON (Object o) =
-    RepoRef <$> o .: "owner"
-            <*> o .: "name"
-  parseJSON _ = fail "Could not build a RepoRef"
-
-instance FromJSON Contributor where
-  parseJSON (Object o)
-    | o `at` "type" == (Just "Anonymous") =
-      AnonymousContributor <$> o .: "contributions"
-                           <*> o .: "name"
-    | otherwise =
-      KnownContributor <$> o .: "contributions"
-                       <*> o .: "avatar_url"
-                       <*> o .: "login"
-                       <*> o .: "url"
-                       <*> o .: "id"
-                       <*> o .: "gravatar_id"
-  parseJSON _ = fail "Could not build a Contributor"
-
-instance FromJSON Languages where
-  parseJSON (Object o) =
-    Languages <$>
-      mapM (\name -> Language (T.unpack name) <$> o .: name)
-           (Map.keys o)
-  parseJSON _ = fail "Could not build Languages"
-
-instance FromJSON Tag where
-  parseJSON (Object o) =
-    Tag <$> o .: "name"
-        <*> o .: "zipball_url"
-        <*> o .: "tarball_url"
-        <*> o .: "commit"
-  parseJSON _ = fail "Could not build a Tag"
-
-instance FromJSON Branch where
-  parseJSON (Object o) = Branch <$> o .: "name" <*> o .: "commit"
-  parseJSON _ = fail "Could not build a Branch"
-
-instance FromJSON BranchCommit where
-  parseJSON (Object o) = BranchCommit <$> o .: "sha" <*> o .: "url"
-  parseJSON _ = fail "Could not build a BranchCommit"
-
-instance FromJSON DetailedOwner where
-  parseJSON (Object o)
-    | o `at` "gravatar_id" == Nothing =
-      DetailedOrganization <$> o .: "created_at"
-                   <*> o .: "type"
-                   <*> o .: "public_gists"
-                   <*> o .: "avatar_url"
-                   <*> o .: "followers"
-                   <*> o .: "following"
-                   <*> o .:? "blog"
-                   <*> o .:? "bio"
-                   <*> o .: "public_repos"
-                   <*> o .:? "name"
-                   <*> o .:? "location"
-                   <*> o .:? "company"
-                   <*> o .: "url"
-                   <*> o .: "id"
-                   <*> o .: "html_url"
-                   <*> o .: "login"
-    | otherwise =
-      DetailedUser <$> o .: "created_at"
-                   <*> o .: "type"
-                   <*> o .: "public_gists"
-                   <*> o .: "avatar_url"
-                   <*> o .: "followers"
-                   <*> o .: "following"
-                   <*> o .:? "hireable"
-                   <*> o .: "gravatar_id"
-                   <*> o .:? "blog"
-                   <*> o .:? "bio"
-                   <*> o .: "public_repos"
-                   <*> o .:? "name"
-                   <*> o .:? "location"
-                   <*> o .:? "company"
-                   <*> o .:? "email"
-                   <*> o .: "url"
-                   <*> o .: "id"
-                   <*> o .: "html_url"
-                   <*> o .: "login"
-  parseJSON _ = fail "Could not build a DetailedOwner"
-
-instance FromJSON RepoWebhook where
-  parseJSON (Object o) =
-    RepoWebhook <$> o .: "url"
-                <*> o .: "test_url"
-                <*> o .: "id"
-                <*> o .: "name"
-                <*> o .: "active"
-                <*> o .: "events"
-                <*> o .: "config"
-                <*> o .: "last_response"
-                <*> o .: "updated_at"
-                <*> o .: "created_at"
-  parseJSON _          = fail "Could not build a RepoWebhook"
-
-instance FromJSON RepoWebhookResponse where
-  parseJSON (Object o) =
-    RepoWebhookResponse <$> o .: "code"
-                        <*> o .: "status"
-                        <*> o .: "message"
-  parseJSON _          = fail "Could not build a RepoWebhookResponse"
-
-instance FromJSON Content where
-  parseJSON o@(Object _) = ContentFile <$> parseJSON o
-  parseJSON (Array os) = ContentDirectory <$> (mapM parseJSON $ V.toList os)
-  parseJSON _ = fail "Could not build a Content"
-
-instance FromJSON ContentData where
-  parseJSON (Object o) =
-    ContentData <$> o .: "type"
-                <*> o .: "encoding"
-                <*> o .: "size"
-                <*> o .: "name"
-                <*> o .: "path"
-                <*> o .: "content"
-                <*> o .: "sha"
-                <*> o .: "url"
-                <*> o .: "git_url"
-                <*> o .: "html_url"
-  parseJSON _ = fail "Could not build a ContentData"
-
--- | A slightly more generic version of Aeson's @(.:?)@, using `mzero' instead
--- of `Nothing'.
-(.:<) :: (FromJSON a) => Object -> T.Text -> Parser [a]
-obj .:< key = case Map.lookup key obj of
-                   Nothing -> pure mzero
-                   Just v  -> parseJSON v
-
--- | Produce all values for the given key.
-values :: (Eq k, Hashable k, FromJSON v) => Map.HashMap k Value -> k -> Parser v
-obj `values` key =
-  let (Object children) = findWithDefault (Object Map.empty) key obj in
-    parseJSON $ Array $ V.fromList $ Map.elems children
-
--- | Produce the value for the last key by traversing.
-(<.:>) :: (FromJSON v) => Object -> [T.Text] -> Parser v
-obj <.:> [key] = obj .: key
-obj <.:> (key:keys) =
-  let (Object nextObj) = findWithDefault (Object Map.empty) key obj in
-      nextObj <.:> keys
-_ <.:> [] = fail "must have a pair"
-
--- | Produce the value for the given key, maybe.
-at :: Object -> T.Text -> Maybe Value
-obj `at` key = Map.lookup key obj
-
--- Taken from Data.Map:
-findWithDefault :: (Eq k, Hashable k) => v -> k -> Map.HashMap k v -> v
-findWithDefault def k m =
-  case Map.lookup k m of
-    Nothing -> def
-    Just x  -> x
diff --git a/Github/Data/Definitions.hs b/Github/Data/Definitions.hs
deleted file mode 100644
--- a/Github/Data/Definitions.hs
+++ /dev/null
@@ -1,624 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}
-
-module Github.Data.Definitions where
-
-import Data.Time
-import Data.Data
-import qualified Control.Exception as E
-import qualified Data.Map as M
-
--- | Errors have been tagged according to their source, so you can more easily
--- dispatch and handle them.
-data Error =
-    HTTPConnectionError E.SomeException -- ^ A HTTP error occurred. The actual caught error is included.
-  | ParseError String -- ^ An error in the parser itself.
-  | JsonError String -- ^ The JSON is malformed or unexpected.
-  | UserError String -- ^ Incorrect input.
-  deriving Show
-
--- | A date in the Github format, which is a special case of ISO-8601.
-newtype GithubDate = GithubDate { fromGithubDate :: UTCTime }
-  deriving (Show, Data, Typeable, Eq, Ord)
-
-data Commit = Commit {
-   commitSha       :: String
-  ,commitParents   :: [Tree]
-  ,commitUrl       :: String
-  ,commitGitCommit :: GitCommit
-  ,commitCommitter :: Maybe GithubOwner
-  ,commitAuthor    :: Maybe GithubOwner
-  ,commitFiles     :: [File]
-  ,commitStats     :: Maybe Stats
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Tree = Tree {
-   treeSha :: String
-  ,treeUrl :: String
-  ,treeGitTrees :: [GitTree]
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GitTree = GitTree {
-  gitTreeType :: String
-  ,gitTreeSha :: String
-  -- Can be empty for submodule
-  ,gitTreeUrl :: Maybe String
-  ,gitTreeSize :: Maybe Int
-  ,gitTreePath :: String
-  ,gitTreeMode :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GitCommit = GitCommit {
-   gitCommitMessage :: String
-  ,gitCommitUrl :: String
-  ,gitCommitCommitter :: GitUser
-  ,gitCommitAuthor :: GitUser
-  ,gitCommitTree :: Tree
-  ,gitCommitSha :: Maybe String
-  ,gitCommitParents :: [Tree]
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GithubOwner = GithubUser {
-   githubOwnerAvatarUrl :: String
-  ,githubOwnerLogin :: String
-  ,githubOwnerUrl :: String
-  ,githubOwnerId :: Int
-  ,githubOwnerGravatarId :: Maybe String
-  }
-  | GithubOrganization {
-   githubOwnerAvatarUrl :: String
-  ,githubOwnerLogin :: String
-  ,githubOwnerUrl :: String
-  ,githubOwnerId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GitUser = GitUser {
-   gitUserName  :: String
-  ,gitUserEmail :: String
-  ,gitUserDate  :: GithubDate
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data File = File {
-   fileBlobUrl :: String
-  ,fileStatus :: String
-  ,fileRawUrl :: String
-  ,fileAdditions :: Int
-  ,fileSha :: String
-  ,fileChanges :: Int
-  ,filePatch :: String
-  ,fileFilename :: String
-  ,fileDeletions :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Stats = Stats {
-   statsAdditions :: Int
-  ,statsTotal :: Int
-  ,statsDeletions :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Comment = Comment {
-   commentPosition :: Maybe Int
-  ,commentLine :: Maybe Int
-  ,commentBody :: String
-  ,commentCommitId :: Maybe String
-  ,commentUpdatedAt :: UTCTime
-  ,commentHtmlUrl :: Maybe String
-  ,commentUrl :: String
-  ,commentCreatedAt :: Maybe UTCTime
-  ,commentPath :: Maybe String
-  ,commentUser :: GithubOwner
-  ,commentId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data NewComment = NewComment {
-   newCommentBody :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data EditComment = EditComment {
-   editCommentBody :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Diff = Diff {
-   diffStatus :: String
-  ,diffBehindBy :: Int
-  ,diffPatchUrl :: String
-  ,diffUrl :: String
-  ,diffBaseCommit :: Commit
-  ,diffCommits :: [Commit]
-  ,diffTotalCommits :: Int
-  ,diffHtmlUrl :: String
-  ,diffFiles :: [File]
-  ,diffAheadBy :: Int
-  ,diffDiffUrl :: String
-  ,diffPermalinkUrl :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Gist = Gist {
-   gistUser :: GithubOwner
-  ,gistGitPushUrl :: String
-  ,gistUrl :: String
-  ,gistDescription :: Maybe String
-  ,gistCreatedAt :: GithubDate
-  ,gistPublic :: Bool
-  ,gistComments :: Int
-  ,gistUpdatedAt :: GithubDate
-  ,gistHtmlUrl :: String
-  ,gistId :: String
-  ,gistFiles :: [GistFile]
-  ,gistGitPullUrl :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GistFile = GistFile {
-   gistFileType :: String
-  ,gistFileRawUrl :: String
-  ,gistFileSize :: Int
-  ,gistFileLanguage :: Maybe String
-  ,gistFileFilename :: String
-  ,gistFileContent :: Maybe String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GistComment = GistComment {
-   gistCommentUser :: GithubOwner
-  ,gistCommentUrl :: String
-  ,gistCommentCreatedAt :: GithubDate
-  ,gistCommentBody :: String
-  ,gistCommentUpdatedAt :: GithubDate
-  ,gistCommentId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Blob = Blob {
-   blobUrl :: String
-  ,blobEncoding :: String
-  ,blobContent :: String
-  ,blobSha :: String
-  ,blobSize :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data NewGitReference = NewGitReference {
-   newGitReferenceRef :: String
-  ,newGitReferenceSha :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GitReference = GitReference {
-   gitReferenceObject :: GitObject
-  ,gitReferenceUrl :: String
-  ,gitReferenceRef :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data GitObject = GitObject {
-   gitObjectType :: String
-  ,gitObjectSha :: String
-  ,gitObjectUrl :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Issue = Issue {
-   issueClosedAt :: Maybe GithubDate
-  ,issueUpdatedAt :: GithubDate
-  ,issueEventsUrl :: String
-  ,issueHtmlUrl :: Maybe String
-  ,issueClosedBy :: Maybe GithubOwner
-  ,issueLabels :: [IssueLabel]
-  ,issueNumber :: Int
-  ,issueAssignee :: Maybe GithubOwner
-  ,issueUser :: GithubOwner
-  ,issueTitle :: String
-  ,issuePullRequest :: Maybe PullRequestReference
-  ,issueUrl :: String
-  ,issueCreatedAt :: GithubDate
-  ,issueBody :: Maybe String
-  ,issueState :: String
-  ,issueId :: Int
-  ,issueComments :: Int
-  ,issueMilestone :: Maybe Milestone
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data NewIssue = NewIssue {
-  newIssueTitle :: String
-, newIssueBody :: Maybe String
-, newIssueAssignee :: Maybe String
-, newIssueMilestone :: Maybe Int
-, newIssueLabels :: Maybe [String]
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data EditIssue = EditIssue {
-  editIssueTitle :: Maybe String
-, editIssueBody :: Maybe String
-, editIssueAssignee :: Maybe String
-, editIssueState :: Maybe String
-, editIssueMilestone :: Maybe Int
-, editIssueLabels :: Maybe [String]
-} deriving  (Show, Data, Typeable, Eq, Ord)
-
-
-data Milestone = Milestone {
-   milestoneCreator :: GithubOwner
-  ,milestoneDueOn :: Maybe GithubDate
-  ,milestoneOpenIssues :: Int
-  ,milestoneNumber :: Int
-  ,milestoneClosedIssues :: Int
-  ,milestoneDescription :: Maybe String
-  ,milestoneTitle :: String
-  ,milestoneUrl :: String
-  ,milestoneCreatedAt :: GithubDate
-  ,milestoneState :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data IssueLabel = IssueLabel {
-   labelColor :: String
-  ,labelUrl :: String
-  ,labelName :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data PullRequestReference = PullRequestReference {
-  pullRequestReferenceHtmlUrl :: Maybe String
-  ,pullRequestReferencePatchUrl :: Maybe String
-  ,pullRequestReferenceDiffUrl :: Maybe String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data IssueComment = IssueComment {
-   issueCommentUpdatedAt :: GithubDate
-  ,issueCommentUser :: GithubOwner
-  ,issueCommentUrl :: String
-  ,issueCommentCreatedAt :: GithubDate
-  ,issueCommentBody :: String
-  ,issueCommentId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
--- | Data describing an @Event@.
-data EventType =
-    Mentioned     -- ^ The actor was @mentioned in an issue body.
-  | Subscribed    -- ^ The actor subscribed to receive notifications for an issue.
-  | Unsubscribed  -- ^ The issue was unsubscribed from by the actor.
-  | Referenced    -- ^ The issue was referenced from a commit message. The commit_id attribute is the commit SHA1 of where that happened.
-  | Merged        -- ^ The issue was merged by the actor. The commit_id attribute is the SHA1 of the HEAD commit that was merged.
-  | Assigned      -- ^ The issue was assigned to the actor.
-  | Closed        -- ^ The issue was closed by the actor. When the commit_id is present, it identifies the commit that closed the issue using “closes / fixes #NN” syntax. 
-  | Reopened      -- ^ The issue was reopened by the actor.
-  | ActorUnassigned    -- ^ The issue was unassigned to the actor
-  | Labeled       -- ^ A label was added to the issue.
-  | Unlabeled     -- ^ A label was removed from the issue.
-  | Milestoned    -- ^ The issue was added to a milestone.
-  | Demilestoned  -- ^ The issue was removed from a milestone.
-  | Renamed       -- ^ The issue title was changed.
-  | Locked        -- ^ The issue was locked by the actor.
-  | Unlocked      -- ^ The issue was unlocked by the actor.
-  | HeadRefDeleted -- ^ The pull request’s branch was deleted.
-  | HeadRefRestored -- ^ The pull request’s branch was restored.
-  deriving (Show, Data, Typeable, Eq, Ord)
-
-data Event = Event {
-   eventActor :: GithubOwner
-  ,eventType :: EventType
-  ,eventCommitId :: Maybe String
-  ,eventUrl :: String
-  ,eventCreatedAt :: GithubDate
-  ,eventId :: Int
-  ,eventIssue :: Maybe Issue
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data SimpleOrganization = SimpleOrganization {
-   simpleOrganizationUrl :: String
-  ,simpleOrganizationAvatarUrl :: String
-  ,simpleOrganizationId :: Int
-  ,simpleOrganizationLogin :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Organization = Organization {
-   organizationType :: String
-  ,organizationBlog :: Maybe String
-  ,organizationLocation :: Maybe String
-  ,organizationLogin :: String
-  ,organizationFollowers :: Int
-  ,organizationCompany :: Maybe String
-  ,organizationAvatarUrl :: String
-  ,organizationPublicGists :: Int
-  ,organizationHtmlUrl :: String
-  ,organizationEmail :: Maybe String
-  ,organizationFollowing :: Int
-  ,organizationPublicRepos :: Int
-  ,organizationUrl :: String
-  ,organizationCreatedAt :: GithubDate
-  ,organizationName :: Maybe String
-  ,organizationId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data PullRequest = PullRequest {
-   pullRequestClosedAt :: Maybe GithubDate
-  ,pullRequestCreatedAt :: GithubDate
-  ,pullRequestUser :: GithubOwner
-  ,pullRequestPatchUrl :: String
-  ,pullRequestState :: String
-  ,pullRequestNumber :: Int
-  ,pullRequestHtmlUrl :: String
-  ,pullRequestUpdatedAt :: GithubDate
-  ,pullRequestBody :: String
-  ,pullRequestIssueUrl :: String
-  ,pullRequestDiffUrl :: String
-  ,pullRequestUrl :: String
-  ,pullRequestLinks :: PullRequestLinks
-  ,pullRequestMergedAt :: Maybe GithubDate
-  ,pullRequestTitle :: String
-  ,pullRequestId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data DetailedPullRequest = DetailedPullRequest {
-  -- this is a duplication of a PullRequest
-   detailedPullRequestClosedAt :: Maybe GithubDate
-  ,detailedPullRequestCreatedAt :: GithubDate
-  ,detailedPullRequestUser :: GithubOwner
-  ,detailedPullRequestPatchUrl :: String
-  ,detailedPullRequestState :: String
-  ,detailedPullRequestNumber :: Int
-  ,detailedPullRequestHtmlUrl :: String
-  ,detailedPullRequestUpdatedAt :: GithubDate
-  ,detailedPullRequestBody :: String
-  ,detailedPullRequestIssueUrl :: String
-  ,detailedPullRequestDiffUrl :: String
-  ,detailedPullRequestUrl :: String
-  ,detailedPullRequestLinks :: PullRequestLinks
-  ,detailedPullRequestMergedAt :: Maybe GithubDate
-  ,detailedPullRequestTitle :: String
-  ,detailedPullRequestId :: Int
-
-  ,detailedPullRequestMergedBy :: Maybe GithubOwner
-  ,detailedPullRequestChangedFiles :: Int
-  ,detailedPullRequestHead :: PullRequestCommit
-  ,detailedPullRequestComments :: Int
-  ,detailedPullRequestDeletions :: Int
-  ,detailedPullRequestAdditions :: Int
-  ,detailedPullRequestReviewComments :: Int
-  ,detailedPullRequestBase :: PullRequestCommit
-  ,detailedPullRequestCommits :: Int
-  ,detailedPullRequestMerged :: Bool
-  ,detailedPullRequestMergeable :: Maybe Bool
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data EditPullRequest = EditPullRequest {
-   editPullRequestTitle :: Maybe String
-  ,editPullRequestBody :: Maybe String
-  ,editPullRequestState :: Maybe EditPullRequestState
-} deriving (Show)
-
-data CreatePullRequest =
-      CreatePullRequest
-      { createPullRequestTitle :: String
-      , createPullRequestBody  :: String
-      , createPullRequestHead  :: String
-      , createPullRequestBase  :: String
-      }
-    | CreatePullRequestIssue
-      { createPullRequestIssueNum :: Int
-      , createPullRequestHead     :: String
-      , createPullRequestBase     :: String
-      }
-    deriving (Show)
-
-data PullRequestLinks = PullRequestLinks {
-   pullRequestLinksReviewComments :: String
-  ,pullRequestLinksComments :: String
-  ,pullRequestLinksHtml :: String
-  ,pullRequestLinksSelf :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data PullRequestCommit = PullRequestCommit {
-   pullRequestCommitLabel :: String
-  ,pullRequestCommitRef :: String
-  ,pullRequestCommitSha :: String
-  ,pullRequestCommitUser :: GithubOwner
-  ,pullRequestCommitRepo :: Repo
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data SearchReposResult = SearchReposResult {
-  searchReposTotalCount :: Int
-  ,searchReposRepos :: [Repo]
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Repo = Repo {
-   repoSshUrl :: Maybe String
-  ,repoDescription :: Maybe String
-  ,repoCreatedAt :: Maybe GithubDate
-  ,repoHtmlUrl :: String
-  ,repoSvnUrl :: Maybe String
-  ,repoForks :: Maybe Int
-  ,repoHomepage :: Maybe String
-  ,repoFork :: Maybe Bool
-  ,repoGitUrl :: Maybe String
-  ,repoPrivate :: Bool
-  ,repoCloneUrl :: Maybe String
-  ,repoSize :: Maybe Int
-  ,repoUpdatedAt :: Maybe GithubDate
-  ,repoWatchers :: Maybe Int
-  ,repoOwner :: GithubOwner
-  ,repoName :: String
-  ,repoLanguage :: Maybe String
-  ,repoMasterBranch :: Maybe String
-  ,repoPushedAt :: Maybe GithubDate   -- ^ this is Nothing for new repositories
-  ,repoId :: Int
-  ,repoUrl :: String
-  ,repoOpenIssues :: Maybe Int
-  ,repoHasWiki :: Maybe Bool
-  ,repoHasIssues :: Maybe Bool
-  ,repoHasDownloads :: Maybe Bool
-  ,repoParent :: Maybe RepoRef
-  ,repoSource :: Maybe RepoRef
-  ,repoHooksUrl :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data RepoRef = RepoRef GithubOwner String -- Repo owner and name
- deriving (Show, Data, Typeable, Eq, Ord)
-
-data SearchCodeResult = SearchCodeResult {
-  searchCodeTotalCount :: Int
-  ,searchCodeCodes :: [Code]
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Code = Code {
-   codeName :: String
-  ,codePath :: String
-  ,codeSha :: String
-  ,codeUrl :: String
-  ,codeGitUrl :: String
-  ,codeHtmlUrl :: String
-  ,codeRepo :: Repo
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Content = ContentFile ContentData | ContentDirectory [ContentData]
- deriving (Show, Data, Typeable, Eq, Ord)
-
-data ContentData = ContentData {
-   contentType :: String
-  ,contentEncoding :: String
-  ,contentSize :: Int
-  ,contentName :: String
-  ,contentPath :: String
-  ,contentData :: String
-  ,contentSha :: String
-  ,contentUrl :: String
-  ,contentGitUrl :: String
-  ,contentHtmlUrl :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Contributor
-  -- | An existing Github user, with their number of contributions, avatar
-  -- URL, login, URL, ID, and Gravatar ID.
-  = KnownContributor Int String String String Int String
-  -- | An unknown Github user with their number of contributions and recorded name.
-  | AnonymousContributor Int String
- deriving (Show, Data, Typeable, Eq, Ord)
-
--- | This is only used for the FromJSON instance.
-data Languages = Languages { getLanguages :: [Language] }
-  deriving (Show, Data, Typeable, Eq, Ord)
-
--- | A programming language with the name and number of characters written in
--- it.
-data Language = Language String Int
- deriving (Show, Data, Typeable, Eq, Ord)
-
-data Tag = Tag {
-   tagName :: String
-  ,tagZipballUrl :: String
-  ,tagTarballUrl :: String
-  ,tagCommit :: BranchCommit
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data Branch = Branch {
-   branchName :: String
-  ,branchCommit :: BranchCommit
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data BranchCommit = BranchCommit {
-   branchCommitSha :: String
-  ,branchCommitUrl :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data DetailedOwner = DetailedUser {
-   detailedOwnerCreatedAt :: GithubDate
-  ,detailedOwnerType :: String
-  ,detailedOwnerPublicGists :: Int
-  ,detailedOwnerAvatarUrl :: String
-  ,detailedOwnerFollowers :: Int
-  ,detailedOwnerFollowing :: Int
-  ,detailedOwnerHireable :: Maybe Bool
-  ,detailedOwnerGravatarId :: Maybe String
-  ,detailedOwnerBlog :: Maybe String
-  ,detailedOwnerBio :: Maybe String
-  ,detailedOwnerPublicRepos :: Int
-  ,detailedOwnerName :: Maybe String
-  ,detailedOwnerLocation :: Maybe String
-  ,detailedOwnerCompany :: Maybe String
-  ,detailedOwnerEmail :: Maybe String
-  ,detailedOwnerUrl :: String
-  ,detailedOwnerId :: Int
-  ,detailedOwnerHtmlUrl :: String
-  ,detailedOwnerLogin :: String
-  }
-  | DetailedOrganization {
-   detailedOwnerCreatedAt :: GithubDate
-  ,detailedOwnerType :: String
-  ,detailedOwnerPublicGists :: Int
-  ,detailedOwnerAvatarUrl :: String
-  ,detailedOwnerFollowers :: Int
-  ,detailedOwnerFollowing :: Int
-  ,detailedOwnerBlog :: Maybe String
-  ,detailedOwnerBio :: Maybe String
-  ,detailedOwnerPublicRepos :: Int
-  ,detailedOwnerName :: Maybe String
-  ,detailedOwnerLocation :: Maybe String
-  ,detailedOwnerCompany :: Maybe String
-  ,detailedOwnerUrl :: String
-  ,detailedOwnerId :: Int
-  ,detailedOwnerHtmlUrl :: String
-  ,detailedOwnerLogin :: String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data RepoWebhook = RepoWebhook {
-   repoWebhookUrl :: String
-  ,repoWebhookTestUrl :: String
-  ,repoWebhookId :: Integer
-  ,repoWebhookName :: String
-  ,repoWebhookActive :: Bool
-  ,repoWebhookEvents :: [RepoWebhookEvent]
-  ,repoWebhookConfig :: M.Map String String
-  ,repoWebhookLastResponse :: RepoWebhookResponse
-  ,repoWebhookUpdatedAt :: GithubDate
-  ,repoWebhookCreatedAt :: GithubDate
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data RepoWebhookEvent =
-   WebhookWildcardEvent
- | WebhookCommitCommentEvent
- | WebhookCreateEvent
- | WebhookDeleteEvent
- | WebhookDeploymentEvent
- | WebhookDeploymentStatusEvent
- | WebhookForkEvent
- | WebhookGollumEvent
- | WebhookIssueCommentEvent
- | WebhookIssuesEvent
- | WebhookMemberEvent
- | WebhookPageBuildEvent
- | WebhookPublicEvent
- | WebhookPullRequestReviewCommentEvent
- | WebhookPullRequestEvent
- | WebhookPushEvent
- | WebhookReleaseEvent
- | WebhookStatusEvent
- | WebhookTeamAddEvent
- | WebhookWatchEvent
-   deriving (Show, Data, Typeable, Eq, Ord)
-
-data RepoWebhookResponse = RepoWebhookResponse {
-   repoWebhookResponseCode :: Maybe Int
-  ,repoWebhookResponseStatus :: String
-  ,repoWebhookResponseMessage :: Maybe String
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data PullRequestEvent = PullRequestEvent {
-   pullRequestEventAction :: PullRequestEventType
-  ,pullRequestEventNumber :: Int
-  ,pullRequestEventPullRequest :: DetailedPullRequest
-  ,pullRequestRepository :: Repo
-  ,pullRequestSender :: GithubOwner
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data PullRequestEventType =
-    PullRequestOpened
-  | PullRequestClosed
-  | PullRequestSynchronized
-  | PullRequestReopened
-  | PullRequestAssigned
-  | PullRequestUnassigned
-  | PullRequestLabeled
-  | PullRequestUnlabeled
-  deriving (Show, Data, Typeable, Eq, Ord)
-
-data PingEvent = PingEvent {
-   pingEventZen :: String
-  ,pingEventHook :: RepoWebhook
-  ,pingEventHookId :: Int
-} deriving (Show, Data, Typeable, Eq, Ord)
-
-data EditPullRequestState =
-    EditPullRequestStateOpen
-  | EditPullRequestStateClosed
-  deriving Show
diff --git a/Github/Events.hs b/Github/Events.hs
deleted file mode 100644
--- a/Github/Events.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module Github.Events (
- parseEvent
-) where
-
-import qualified Data.ByteString.Lazy.Char8 as LBS
-
-import Data.Aeson (FromJSON)
-
-import Github.Data.Definitions (Error(..))
-import Github.Private (parseJson)
-
-parseEvent :: (FromJSON b, Show b) => LBS.ByteString -> Either Error b
-parseEvent = parseJson
diff --git a/Github/Gists.hs b/Github/Gists.hs
deleted file mode 100644
--- a/Github/Gists.hs
+++ /dev/null
@@ -1,35 +0,0 @@
--- | The gists API as described at <http://developer.github.com/v3/gists/>.
-module Github.Gists (
- gists
-,gists'
-,gist
-,gist'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The list of all gists created by the user 
--- 
--- > gists' (Just ("github-username", "github-password")) "mike-burns"
-gists' :: Maybe GithubAuth -> String -> IO (Either Error [Gist])
-gists' auth userName = githubGet' auth ["users", userName, "gists"]
-
--- | The list of all public gists created by the user.
---
--- > gists "mike-burns"
-gists :: String -> IO (Either Error [Gist])
-gists = gists' Nothing
-
--- | A specific gist, given its id, with authentication credentials
---
--- > gist' (Just ("github-username", "github-password")) "225074"
-gist' :: Maybe GithubAuth -> String -> IO (Either Error Gist)
-gist' auth reqGistId = githubGet' auth ["gists", reqGistId]
-
--- | A specific gist, given its id.
---
--- > gist "225074"
-gist :: String -> IO (Either Error Gist)
-gist = gist' Nothing
diff --git a/Github/Gists/Comments.hs b/Github/Gists/Comments.hs
deleted file mode 100644
--- a/Github/Gists/Comments.hs
+++ /dev/null
@@ -1,22 +0,0 @@
--- | The loving comments people have left on Gists, described on
--- <http://developer.github.com/v3/gists/comments/>.
-module Github.Gists.Comments (
- commentsOn
-,comment
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All the comments on a Gist, given the Gist ID.
---
--- > commentsOn "1174060"
-commentsOn :: String -> IO (Either Error [GistComment])
-commentsOn reqGistId = githubGet ["gists", reqGistId, "comments"]
-
--- | A specific comment, by the comment ID.
---
--- > comment "62449"
-comment :: String -> IO (Either Error GistComment)
-comment reqCommentId = githubGet ["gists", "comments", reqCommentId]
diff --git a/Github/GitData/Blobs.hs b/Github/GitData/Blobs.hs
deleted file mode 100644
--- a/Github/GitData/Blobs.hs
+++ /dev/null
@@ -1,16 +0,0 @@
--- | The API for dealing with git blobs from Github repos, as described in
--- <http://developer.github.com/v3/git/blobs/>.
-module Github.GitData.Blobs (
- blob
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | Get a blob by SHA1.
---
--- > blob "thoughtbot" "paperclip" "bc5c51d1ece1ee45f94b056a0f5a1674d7e8cba9"
-blob :: String -> String -> String -> IO (Either Error Blob)
-blob user reqRepoName sha =
-  githubGet ["repos", user, reqRepoName, "git", "blobs", sha]
diff --git a/Github/GitData/Commits.hs b/Github/GitData/Commits.hs
deleted file mode 100644
--- a/Github/GitData/Commits.hs
+++ /dev/null
@@ -1,16 +0,0 @@
--- | The API for underlying git commits of a Github repo, as described on
--- <http://developer.github.com/v3/git/commits/>.
-module Github.GitData.Commits (
- commit
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | A single commit, by SHA1.
---
--- > commit "thoughtbot" "paperclip" "bc5c51d1ece1ee45f94b056a0f5a1674d7e8cba9"
-commit :: String -> String -> String -> IO (Either Error GitCommit)
-commit user reqRepoName sha =
-  githubGet ["repos", user, reqRepoName, "git", "commits", sha]
diff --git a/Github/GitData/References.hs b/Github/GitData/References.hs
deleted file mode 100644
--- a/Github/GitData/References.hs
+++ /dev/null
@@ -1,38 +0,0 @@
--- | The underlying git references on a Github repo, exposed for the world to
--- see. The git internals documentation will also prove handy for understanding
--- these. API documentation at <http://developer.github.com/v3/git/refs/>.
-module Github.GitData.References (
- reference
-,references
-,createReference
-,namespacedReferences
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | A single reference by the ref name.
---
--- > reference "mike-burns" "github" "heads/master"
-reference :: String -> String -> String -> IO (Either Error GitReference)
-reference user reqRepoName ref =
-  githubGet ["repos", user, reqRepoName, "git", "refs", ref]
-
--- | The history of references for a repo.
---
--- > references "mike-burns" "github"
-references :: String -> String -> IO (Either Error [GitReference])
-references user reqRepoName =
-  githubGet ["repos", user, reqRepoName, "git", "refs"]
-
-createReference :: GithubAuth -> String -> String -> NewGitReference -> IO (Either Error GitReference)
-createReference auth owner reqRepoName newRef =
-  githubPost auth ["repos", owner, reqRepoName, "git", "refs"] newRef
-
--- | Limited references by a namespace.
---
--- > namespacedReferences "thoughtbot" "paperclip" "tags"
-namespacedReferences :: String -> String -> String -> IO (Either Error [GitReference])
-namespacedReferences user reqRepoName namespace =
-  githubGet ["repos", user, reqRepoName, "git", "refs", namespace]
diff --git a/Github/GitData/Trees.hs b/Github/GitData/Trees.hs
deleted file mode 100644
--- a/Github/GitData/Trees.hs
+++ /dev/null
@@ -1,37 +0,0 @@
--- | The underlying tree of SHA1s and files that make up a git repo. The API is
--- described on <http://developer.github.com/v3/git/trees/>.
-module Github.GitData.Trees (
- tree
-,nestedTree
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | A tree for a SHA1.
---
--- > tree (Just ("github-username", "github-password")) "thoughtbot" "paperclip" "fe114451f7d066d367a1646ca7ac10e689b46844"
-tree' :: Maybe GithubAuth -> String -> String -> String -> IO (Either Error Tree)
-tree' auth user reqRepoName sha =
-  githubGet' auth ["repos", user, reqRepoName, "git", "trees", sha]
-
--- | A tree for a SHA1.
---
--- > tree "thoughtbot" "paperclip" "fe114451f7d066d367a1646ca7ac10e689b46844"
-tree :: String -> String -> String -> IO (Either Error Tree)
-tree = tree' Nothing
-
--- | A recursively-nested tree for a SHA1.
---
--- > nestedTree' (Just ("github-username", "github-password")) "thoughtbot" "paperclip" "fe114451f7d066d367a1646ca7ac10e689b46844"
-nestedTree' :: Maybe GithubAuth -> String -> String -> String -> IO (Either Error Tree)
-nestedTree' auth user reqRepoName sha =
-  githubGetWithQueryString' auth ["repos", user, reqRepoName, "git", "trees", sha]
-                                 "recursive=1"
-
--- | A recursively-nested tree for a SHA1.
---
--- > nestedTree "thoughtbot" "paperclip" "fe114451f7d066d367a1646ca7ac10e689b46844"
-nestedTree :: String -> String -> String -> IO (Either Error Tree)
-nestedTree = nestedTree' Nothing
diff --git a/Github/Issues.hs b/Github/Issues.hs
deleted file mode 100644
--- a/Github/Issues.hs
+++ /dev/null
@@ -1,129 +0,0 @@
-{-# LANGUAGE CPP, OverloadedStrings #-}
--- | The issues API as described on <http://developer.github.com/v3/issues/>.
-module Github.Issues (
- issue
-,issue'
-,issuesForRepo
-,issuesForRepo'
-,IssueLimitation(..)
-,createIssue
-,newIssue
-,editIssue
-,editOfIssue
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-import Data.List (intercalate)
-#if MIN_VERSION_base(4, 8, 0)
-import Data.Time (defaultTimeLocale)
-#else
-import System.Locale (defaultTimeLocale)
-#endif
-
-import Data.Time.Format (formatTime)
-import Data.Time.Clock (UTCTime(..))
-
--- | A data structure for describing how to filter issues. This is used by
--- @issuesForRepo@.
-data IssueLimitation =
-      AnyMilestone -- ^ Issues appearing in any milestone. [default]
-    | NoMilestone -- ^ Issues without a milestone.
-    | MilestoneId Int -- ^ Only issues that are in the milestone with the given id.
-    | Open -- ^ Only open issues. [default]
-    | OnlyClosed -- ^ Only closed issues.
-    | Unassigned -- ^ Issues to which no one has been assigned ownership.
-    | AnyAssignment -- ^ All issues regardless of assignment. [default]
-    | AssignedTo String -- ^ Only issues assigned to the user with the given login.
-    | Mentions String -- ^ Issues which mention the given string, taken to be a user's login.
-    | Labels [String] -- ^ A list of labels to filter by.
-    | Ascending -- ^ Sort ascending.
-    | Descending -- ^ Sort descending. [default]
-    | Since UTCTime -- ^ Only issues created since the specified date and time.
-    | PerPage Int -- ^ Download this many issues per query
-
-
--- | Details on a specific issue, given the repo owner and name, and the issue
--- number.'
---
--- > issue' (Just ("github-username", "github-password")) "thoughtbot" "paperclip" "462"
-issue' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error Issue)
-issue' auth user reqRepoName reqIssueNumber =
-  githubGet' auth ["repos", user, reqRepoName, "issues", show reqIssueNumber]
-
--- | Details on a specific issue, given the repo owner and name, and the issue
--- number.
---
--- > issue "thoughtbot" "paperclip" "462"
-issue :: String -> String -> Int -> IO (Either Error Issue)
-issue = issue' Nothing
-
--- | All issues for a repo (given the repo owner and name), with optional
--- restrictions as described in the @IssueLimitation@ data type.
---
--- > issuesForRepo' (Just ("github-username", "github-password")) "thoughtbot" "paperclip" [NoMilestone, OnlyClosed, Mentions "jyurek", Ascending]
-issuesForRepo' :: Maybe GithubAuth -> String -> String -> [IssueLimitation] -> IO (Either Error [Issue])
-issuesForRepo' auth user reqRepoName issueLimitations =
-  githubGetWithQueryString' 
-    auth
-    ["repos", user, reqRepoName, "issues"]
-    (queryStringFromLimitations issueLimitations)
-  where
-    queryStringFromLimitations = intercalate "&" . map convert
-
-    convert AnyMilestone     = "milestone=*"
-    convert NoMilestone      = "milestone=none"
-    convert (MilestoneId n)  = "milestone=" ++ show n
-    convert Open             = "state=open"
-    convert OnlyClosed       = "state=closed"
-    convert Unassigned       = "assignee=none"
-    convert AnyAssignment    = "assignee=*"
-    convert (AssignedTo u)   = "assignee=" ++ u
-    convert (Mentions u)     = "mentioned=" ++ u
-    convert (Labels l)       = "labels=" ++ intercalate "," l
-    convert Ascending        = "direction=asc"
-    convert Descending       = "direction=desc"
-    convert (PerPage n)      = "per_page=" ++ show n
-    convert (Since t)        =
-      "since=" ++ formatTime defaultTimeLocale "%FT%TZ" t
-
--- | All issues for a repo (given the repo owner and name), with optional
--- restrictions as described in the @IssueLimitation@ data type.
---
--- > issuesForRepo "thoughtbot" "paperclip" [NoMilestone, OnlyClosed, Mentions "jyurek", Ascending]
-issuesForRepo :: String -> String -> [IssueLimitation] -> IO (Either Error [Issue])
-issuesForRepo = issuesForRepo' Nothing
-
-
--- Creating new issues.
-
-newIssue :: String -> NewIssue
-newIssue title = NewIssue title Nothing Nothing Nothing Nothing
-
-
--- |
--- Create a new issue.
---
--- > createIssue (GithubUser (user, password)) user repo
--- >  (newIssue "some_repo") {...}
-createIssue :: GithubAuth -> String -> String -> NewIssue
-            -> IO (Either Error Issue)
-createIssue auth user repo = githubPost auth ["repos", user, repo, "issues"]
-
-
--- Editing issues.
-
-editOfIssue :: EditIssue
-editOfIssue = EditIssue Nothing Nothing Nothing Nothing Nothing Nothing
-
-
--- |
--- Edit an issue.
---
--- > editIssue (GithubUser (user, password)) user repo issue
--- >  editOfIssue {...}
-editIssue :: GithubAuth -> String -> String -> Int -> EditIssue
-            -> IO (Either Error Issue)
-editIssue auth user repo iss =
-  githubPatch auth ["repos", user, repo, "issues", show iss]
diff --git a/Github/Issues/Comments.hs b/Github/Issues/Comments.hs
deleted file mode 100644
--- a/Github/Issues/Comments.hs
+++ /dev/null
@@ -1,59 +0,0 @@
--- | The Github issue comments API from
--- <http://developer.github.com/v3/issues/comments/>.
-module Github.Issues.Comments (
- comment
-,comments
-,comments'
-,createComment
-,editComment
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | A specific comment, by ID.
---
--- > comment "thoughtbot" "paperclip" 1468184
-comment :: String -> String -> Int -> IO (Either Error IssueComment)
-comment user reqRepoName reqCommentId =
-  githubGet ["repos", user, reqRepoName, "issues", "comments", show reqCommentId]
-
--- | All comments on an issue, by the issue's number.
---
--- > comments "thoughtbot" "paperclip" 635
-comments :: String -> String -> Int -> IO (Either Error [IssueComment])
-comments user reqRepoName reqIssueNumber =
-  githubGet ["repos", user, reqRepoName, "issues", show reqIssueNumber, "comments"]
-
--- | All comments on an issue, by the issue's number, using authentication.
---
--- > comments' (GithubUser (user, password)) "thoughtbot" "paperclip" 635
-comments' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error [IssueComment])
-comments' auth user reqRepoName reqIssueNumber =
-  githubGet' auth ["repos", user, reqRepoName, "issues", show reqIssueNumber, "comments"]
-
-
-
--- |
--- Create a new comment.
---
--- > createComment (GithubUser (user, password)) user repo issue
--- >  "some words"
-createComment :: GithubAuth -> String -> String -> Int -> String
-            -> IO (Either Error Comment)
-createComment auth user repo iss body =
-  githubPost auth
-  ["repos", user, repo, "issues", show iss, "comments"] (NewComment body)
-
-
--- |
--- Edit a comment.
---
--- > editComment (GithubUser (user, password)) user repo commentid
--- >  "new words"
-editComment :: GithubAuth -> String -> String -> Int -> String
-            -> IO (Either Error Comment)
-editComment auth user repo commid body =
-  githubPatch auth ["repos", user, repo, "issues", "comments", show commid]
-  (EditComment body)
diff --git a/Github/Issues/Events.hs b/Github/Issues/Events.hs
deleted file mode 100644
--- a/Github/Issues/Events.hs
+++ /dev/null
@@ -1,56 +0,0 @@
--- | The Github issue events API, which is described on
--- <http://developer.github.com/v3/issues/events/>
-module Github.Issues.Events (
- eventsForIssue
-,eventsForIssue'
-,eventsForRepo
-,eventsForRepo'
-,event
-,event'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All events that have happened on an issue.
---
--- > eventsForIssue "thoughtbot" "paperclip" 49
-eventsForIssue :: String -> String -> Int -> IO (Either Error [Event])
-eventsForIssue user reqRepoName reqIssueNumber =
-  githubGet ["repos", user, reqRepoName, "issues", show reqIssueNumber, "events"]
-
--- | All events that have happened on an issue, using authentication.
---
--- > eventsForIssue' (GithubUser (user, password)) "thoughtbot" "paperclip" 49
-eventsForIssue' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error [Event])
-eventsForIssue' auth user reqRepoName reqIssueNumber =
-  githubGet' auth ["repos", user, reqRepoName, "issues", show reqIssueNumber, "events"]
-
--- | All the events for all issues in a repo.
---
--- > eventsForRepo "thoughtbot" "paperclip"
-eventsForRepo :: String -> String -> IO (Either Error [Event])
-eventsForRepo user reqRepoName =
-  githubGet ["repos", user, reqRepoName, "issues", "events"]
-
--- | All the events for all issues in a repo, using authentication.
---
--- > eventsForRepo' (GithubUser (user, password)) "thoughtbot" "paperclip"
-eventsForRepo' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Event])
-eventsForRepo' auth user reqRepoName =
-  githubGet' auth ["repos", user, reqRepoName, "issues", "events"]
-
--- | Details on a specific event, by the event's ID.
---
--- > event "thoughtbot" "paperclip" 5335772
-event :: String -> String -> Int -> IO (Either Error Event)
-event user reqRepoName reqEventId =
-  githubGet ["repos", user, reqRepoName, "issues", "events", show reqEventId]
-
--- | Details on a specific event, by the event's ID, using authentication.
---
--- > event' (GithubUser (user, password)) "thoughtbot" "paperclip" 5335772
-event' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error Event)
-event' auth user reqRepoName reqEventId =
-  githubGet' auth ["repos", user, reqRepoName, "issues", "events", show reqEventId]
diff --git a/Github/Issues/Labels.hs b/Github/Issues/Labels.hs
deleted file mode 100644
--- a/Github/Issues/Labels.hs
+++ /dev/null
@@ -1,39 +0,0 @@
--- | The API for dealing with labels on Github issues, as described on
--- <http://developer.github.com/v3/issues/labels/>.
-module Github.Issues.Labels (
- label
-,labelsOnRepo
-,labelsOnIssue
-,labelsOnMilestone
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All the labels available to use on any issue in the repo.
---
--- > labelsOnRepo "thoughtbot" "paperclip"
-labelsOnRepo :: String -> String -> IO (Either Error [IssueLabel])
-labelsOnRepo user reqRepoName = githubGet ["repos", user, reqRepoName, "labels"]
-
--- | The labels on an issue in a repo.
---
--- > labelsOnIssue "thoughtbot" "paperclip" 585
-labelsOnIssue :: String -> String -> Int ->  IO (Either Error [IssueLabel])
-labelsOnIssue user reqRepoName reqIssueId =
-  githubGet ["repos", user, reqRepoName, "issues", show reqIssueId, "labels"]
-
--- | All the labels on a repo's milestone, given the milestone ID.
---
--- > labelsOnMilestone "thoughtbot" "paperclip" 2
-labelsOnMilestone :: String -> String -> Int ->  IO (Either Error [IssueLabel])
-labelsOnMilestone user reqRepoName milestoneId =
-  githubGet ["repos", user, reqRepoName, "milestones", show milestoneId, "labels"]
-
--- | A label, by name.
---
--- > Github.label "thoughtbot" "paperclip" "bug"
-label :: String -> String -> String -> IO (Either Error IssueLabel)
-label user reqRepoName reqLabelName =
-  githubGet ["repos", user, reqRepoName, "labels", reqLabelName]
diff --git a/Github/Issues/Milestones.hs b/Github/Issues/Milestones.hs
deleted file mode 100644
--- a/Github/Issues/Milestones.hs
+++ /dev/null
@@ -1,30 +0,0 @@
--- | The milestones API as described on
--- <http://developer.github.com/v3/issues/milestones/>.
-module Github.Issues.Milestones (
- milestones
-,milestones'
-,milestone
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All milestones in the repo.
---
--- > milestones "thoughtbot" "paperclip"
-milestones :: String -> String -> IO (Either Error [Milestone])
-milestones = milestones' Nothing
-
--- | All milestones in the repo, using authentication.
---
--- > milestones' (GithubUser (user, password)) "thoughtbot" "paperclip"
-milestones' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Milestone])
-milestones' auth user reqRepoName = githubGet' auth ["repos", user, reqRepoName, "milestones"]
-
--- | Details on a specific milestone, given it's milestone number.
---
--- > milestone "thoughtbot" "paperclip" 2
-milestone :: String -> String -> Int -> IO (Either Error Milestone)
-milestone user reqRepoName reqMilestoneNumber =
-  githubGet ["repos", user, reqRepoName, "milestones", show reqMilestoneNumber]
diff --git a/Github/Organizations.hs b/Github/Organizations.hs
deleted file mode 100644
--- a/Github/Organizations.hs
+++ /dev/null
@@ -1,35 +0,0 @@
--- | The orgs API as described on <http://developer.github.com/v3/orgs/>.
-module Github.Organizations (
- publicOrganizationsFor
-,publicOrganizationsFor'
-,publicOrganization
-,publicOrganization'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The public organizations for a user, given the user's login, with authorization
---
--- > publicOrganizationsFor' (Just ("github-username", "github-password")) "mike-burns"
-publicOrganizationsFor' :: Maybe GithubAuth -> String -> IO (Either Error [SimpleOrganization])
-publicOrganizationsFor' auth userName = githubGet' auth ["users", userName, "orgs"]
-
--- | The public organizations for a user, given the user's login.
---
--- > publicOrganizationsFor "mike-burns"
-publicOrganizationsFor :: String -> IO (Either Error [SimpleOrganization])
-publicOrganizationsFor = publicOrganizationsFor' Nothing
-
--- | Details on a public organization. Takes the organization's login.
---
--- > publicOrganization' (Just ("github-username", "github-password")) "thoughtbot"
-publicOrganization' :: Maybe GithubAuth -> String -> IO (Either Error Organization)
-publicOrganization' auth reqOrganizationName = githubGet' auth ["orgs", reqOrganizationName]
-
--- | Details on a public organization. Takes the organization's login.
---
--- > publicOrganization "thoughtbot"
-publicOrganization :: String -> IO (Either Error Organization)
-publicOrganization = publicOrganization' Nothing
diff --git a/Github/Organizations/Members.hs b/Github/Organizations/Members.hs
deleted file mode 100644
--- a/Github/Organizations/Members.hs
+++ /dev/null
@@ -1,15 +0,0 @@
--- | The organization members API as described on
--- <http://developer.github.com/v3/orgs/members/>.
-module Github.Organizations.Members (
- membersOf
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All the users who are members of the specified organization.
---
--- > membersOf "thoughtbot"
-membersOf :: String -> IO (Either Error [GithubOwner])
-membersOf organization = githubGet ["orgs", organization, "members"]
diff --git a/Github/Private.hs b/Github/Private.hs
deleted file mode 100644
--- a/Github/Private.hs
+++ /dev/null
@@ -1,192 +0,0 @@
-{-# LANGUAGE OverloadedStrings, StandaloneDeriving, DeriveDataTypeable #-}
-{-# LANGUAGE CPP, FlexibleContexts #-}
-module Github.Private where
-
-import Github.Data
-import Data.Aeson
-import Data.Attoparsec.ByteString.Lazy
-import Data.Data
-import Data.Monoid
-import Control.Applicative
-import Data.List
-import Data.CaseInsensitive (mk)
-import qualified Data.ByteString.Char8 as BS
-import qualified Data.ByteString.Lazy.Char8 as LBS
-import Network.HTTP.Types (Status(..), notFound404)
-import Network.HTTP.Conduit
--- import Data.Conduit (ResourceT)
-import qualified Control.Exception as E
-import Data.Maybe (fromMaybe)
-
--- | user/password for HTTP basic access authentication
-data GithubAuth = GithubBasicAuth BS.ByteString BS.ByteString
-                | GithubOAuth String
-                deriving (Show, Data, Typeable, Eq, Ord)
-
-githubGet :: (FromJSON b, Show b) => [String] -> IO (Either Error b)
-githubGet = githubGet' Nothing
-
-githubGet' :: (FromJSON b, Show b) => Maybe GithubAuth -> [String] -> IO (Either Error b)
-githubGet' auth paths =
-  githubAPI (BS.pack "GET")
-            (buildUrl paths)
-            auth
-            (Nothing :: Maybe Value)
-
-githubGetWithQueryString :: (FromJSON b, Show b) => [String] -> String -> IO (Either Error b)
-githubGetWithQueryString = githubGetWithQueryString' Nothing
-
-githubGetWithQueryString' :: (FromJSON b, Show b) => Maybe GithubAuth -> [String] -> String -> IO (Either Error b)
-githubGetWithQueryString' auth paths qs =
-  githubAPI (BS.pack "GET")
-            (buildUrl paths ++ "?" ++ qs)
-            auth
-            (Nothing :: Maybe Value)
-
-githubPost :: (ToJSON a, Show a, FromJSON b, Show b) => GithubAuth -> [String] -> a -> IO (Either Error b)
-githubPost auth paths body =
-  githubAPI (BS.pack "POST")
-            (buildUrl paths)
-            (Just auth)
-            (Just body)
-
-githubPatch :: (ToJSON a, Show a, FromJSON b, Show b) => GithubAuth -> [String] -> a -> IO (Either Error b)
-githubPatch auth paths body =
-  githubAPI (BS.pack "PATCH")
-            (buildUrl paths)
-            (Just auth)
-            (Just body)
-
-buildUrl :: [String] -> String
-buildUrl paths = "https://api.github.com/" ++ intercalate "/" paths
-
-githubAPI :: (ToJSON a, Show a, FromJSON b, Show b) => BS.ByteString -> String
-          -> Maybe GithubAuth -> Maybe a -> IO (Either Error b)
-githubAPI apimethod url auth body = do
-  result <- doHttps apimethod url auth (encodeBody body)
-  case result of
-      Left e     -> return (Left (HTTPConnectionError e))
-      Right resp -> either Left (\x -> jsonResultToE (LBS.pack (show x))
-                                                   (fromJSON x))
-                          <$> handleBody resp
-
-  where
-    encodeBody = Just . RequestBodyLBS . encode . toJSON
-
-    handleBody resp = either (return . Left) (handleJson resp)
-                             (parseJsonRaw (responseBody resp))
-
-    -- This is an "escaping" version of "for", which returns (Right esc) if
-    -- the value 'v' is Nothing; otherwise, it extracts the value from the
-    -- Maybe, applies f, and return an IO (Either Error b).
-    forE :: b -> Maybe a -> (a -> IO (Either Error b))
-         -> IO (Either Error b)
-    forE = flip . maybe . return . Right
-
-    handleJson resp gotjson@(Array ary) =
-        -- Determine whether the output was paginated, and if so, we must
-        -- recurse to obtain the subsequent pages, and append those result
-        -- bodies to the current one.  The aggregate will then be parsed.
-        forE gotjson (lookup "Link" (responseHeaders resp)) $ \l ->
-            forE gotjson (getNextUrl (BS.unpack l)) $ \nu ->
-                either (return . Left . HTTPConnectionError)
-                       (\nextResp -> do
-                             nextJson <- handleBody nextResp
-                             return $ (\(Array x) -> Array (ary <> x))
-                                          <$> nextJson)
-                       =<< doHttps apimethod nu auth Nothing
-    handleJson _ gotjson = return (Right gotjson)
-
-    getNextUrl l =
-        if "rel=\"next\"" `isInfixOf` l
-        then let s  = l
-                 s' = Data.List.tail $ Data.List.dropWhile (/= '<') s
-             in Just (Data.List.takeWhile (/= '>') s')
-        else Nothing
-
-doHttps :: BS.ByteString
-           -> [Char]
-           -> Maybe GithubAuth
-           -> Maybe RequestBody
-           -> IO (Either E.SomeException (Response LBS.ByteString))    
-doHttps reqMethod url auth body = do
-  let reqBody = fromMaybe (RequestBodyBS $ BS.pack "") body
-      reqHeaders = maybe [] getOAuth auth
-      Just uri = parseUrl url
-      request = uri { method = reqMethod
-                    , secure = True
-                    , port = 443
-                    , requestBody = reqBody
-                    , responseTimeout = Just 20000000
-                    , requestHeaders = reqHeaders <>
-                                       [("User-Agent", "github.hs/0.7.4")]
-                                       <> [("Accept", "application/vnd.github.preview")]
-                    , checkStatus = successOrMissing
-                    }
-      authRequest = getAuthRequest auth request
-
-  (getResponse authRequest >>= return . Right) `E.catches` [
-      -- Re-throw AsyncException, otherwise execution will not terminate on
-      -- SIGINT (ctrl-c).  All AsyncExceptions are re-thrown (not just
-      -- UserInterrupt) because all of them indicate severe conditions and
-      -- should not occur during normal operation.
-      E.Handler (\e -> E.throw (e :: E.AsyncException)),
-      E.Handler (\e -> (return . Left) (e :: E.SomeException))
-      ]
-  where
-    getAuthRequest (Just (GithubBasicAuth user pass)) = applyBasicAuth user pass
-    getAuthRequest _ = id
-    getOAuth (GithubOAuth token) = [(mk (BS.pack "Authorization"),
-                                     BS.pack ("token " ++ token))]
-    getOAuth _ = []
-    getResponse request = withManager $ \manager -> httpLbs request manager
-#if MIN_VERSION_http_conduit(1, 9, 0)
-    successOrMissing s@(Status sci _) hs cookiejar
-#else
-    successOrMissing s@(Status sci _) hs
-#endif
-      | (200 <= sci && sci < 300) || sci == 404 = Nothing
-#if MIN_VERSION_http_conduit(1, 9, 0)
-      | otherwise = Just $ E.toException $ StatusCodeException s hs cookiejar
-#else
-      | otherwise = Just $ E.toException $ StatusCodeException s hs
-#endif
-
-doHttpsStatus :: BS.ByteString -> String -> GithubAuth -> Maybe RequestBody -> IO (Either Error Status)
-doHttpsStatus reqMethod url auth payload = do
-  result <- doHttps reqMethod url (Just auth) payload
-  case result of
-    Left e -> return (Left (HTTPConnectionError e))
-    Right resp ->
-      let status = responseStatus resp
-          headers = responseHeaders resp
-      in if status == notFound404
-            -- doHttps silently absorbs 404 errors, but for this operation
-            -- we want the user to know if they've tried to delete a
-            -- non-existent repository
-         then return (Left (HTTPConnectionError
-                            (E.toException
-                             (StatusCodeException status headers
-#if MIN_VERSION_http_conduit(1, 9, 0)
-                                 (responseCookieJar resp)
-#endif
-                                 ))))
-             else return (Right status)
-
-parseJsonRaw :: LBS.ByteString -> Either Error Value
-parseJsonRaw jsonString =
-  let parsed = parse json jsonString in
-  case parsed of
-       Data.Attoparsec.ByteString.Lazy.Done _ jsonResult -> Right jsonResult
-       (Fail _ _ e) -> Left $ ParseError e
-
-jsonResultToE :: Show b => LBS.ByteString -> Data.Aeson.Result b
-              -> Either Error b
-jsonResultToE jsonString result = case result of
-    Success s -> Right s
-    Error e   -> Left $ JsonError $
-                 e ++ " on the JSON: " ++ LBS.unpack jsonString
-
-parseJson :: (FromJSON b, Show b) => LBS.ByteString -> Either Error b
-parseJson jsonString = either Left (jsonResultToE jsonString . fromJSON)
-                              (parseJsonRaw jsonString)
diff --git a/Github/PullRequests.hs b/Github/PullRequests.hs
deleted file mode 100644
--- a/Github/PullRequests.hs
+++ /dev/null
@@ -1,114 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
--- | The pull requests API as documented at
--- <http://developer.github.com/v3/pulls/>.
-module Github.PullRequests (
- pullRequestsFor'
-,pullRequest'
-,pullRequestCommits'
-,pullRequestFiles'
-,pullRequestsFor
-,pullRequest
-,pullRequestCommits
-,pullRequestFiles
-,isPullRequestMerged
-,mergePullRequest
-,createPullRequest
-,updatePullRequest
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-import Network.HTTP.Types
-import qualified Data.Map as M
-import Network.HTTP.Conduit (RequestBody(RequestBodyLBS))
-import Data.Aeson
-
--- | All pull requests for the repo, by owner and repo name.
--- | With authentification
---
--- > pullRequestsFor' (Just ("github-username", "github-password")) "rails" "rails"
-pullRequestsFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [PullRequest])
-pullRequestsFor' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "pulls"]
-
--- | All pull requests for the repo, by owner and repo name.
---
--- > pullRequestsFor "rails" "rails"
-pullRequestsFor :: String -> String -> IO (Either Error [PullRequest])
-pullRequestsFor = pullRequestsFor' Nothing
-
--- | A detailed pull request, which has much more information. This takes the
--- repo owner and name along with the number assigned to the pull request.
--- | With authentification
---
--- > pullRequest' (Just ("github-username", "github-password")) "thoughtbot" "paperclip" 562
-pullRequest' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error DetailedPullRequest)
-pullRequest' auth userName reqRepoName number =
-  githubGet' auth ["repos", userName, reqRepoName, "pulls", show number]
-
--- | A detailed pull request, which has much more information. This takes the
--- repo owner and name along with the number assigned to the pull request.
---
--- > pullRequest "thoughtbot" "paperclip" 562
-pullRequest :: String -> String -> Int -> IO (Either Error DetailedPullRequest)
-pullRequest = pullRequest' Nothing
-
--- | All the commits on a pull request, given the repo owner, repo name, and
--- the number of the pull request.
--- | With authentification
---
--- > pullRequestCommits' (Just ("github-username", "github-password")) "thoughtbot" "paperclip" 688
-pullRequestCommits' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error [Commit])
-pullRequestCommits' auth userName reqRepoName number =
-  githubGet' auth ["repos", userName, reqRepoName, "pulls", show number, "commits"]
-
--- | All the commits on a pull request, given the repo owner, repo name, and
--- the number of the pull request.
---
--- > pullRequestCommits "thoughtbot" "paperclip" 688
-pullRequestCommits :: String -> String -> Int -> IO (Either Error [Commit])
-pullRequestCommits = pullRequestCommits' Nothing
-
--- | The individual files that a pull request patches. Takes the repo owner and
--- name, plus the number assigned to the pull request.
--- | With authentification
---
--- > pullRequestFiles' (Just ("github-username", "github-password")) "thoughtbot" "paperclip" 688
-pullRequestFiles' :: Maybe GithubAuth -> String -> String -> Int -> IO (Either Error [File])
-pullRequestFiles' auth userName reqRepoName number =
-  githubGet' auth ["repos", userName, reqRepoName, "pulls", show number, "files"]
--- | The individual files that a pull request patches. Takes the repo owner and
--- name, plus the number assigned to the pull request.
---
--- > pullRequestFiles "thoughtbot" "paperclip" 688
-pullRequestFiles :: String -> String -> Int -> IO (Either Error [File])
-pullRequestFiles = pullRequestFiles' Nothing
-
--- | Check if pull request has been merged
-isPullRequestMerged :: GithubAuth -> String -> String -> Int -> IO(Either Error Status)
-isPullRequestMerged auth reqRepoOwner reqRepoName reqPullRequestNumber =
-  doHttpsStatus "GET" (buildUrl ["repos", reqRepoOwner, reqRepoName, "pulls", (show reqPullRequestNumber), "merge"]) auth Nothing
-
--- | Merge a pull request
-mergePullRequest :: GithubAuth -> String -> String -> Int -> Maybe String -> IO(Either Error Status)
-mergePullRequest auth reqRepoOwner reqRepoName reqPullRequestNumber commitMessage =
-  doHttpsStatus "PUT" (buildUrl ["repos", reqRepoOwner, reqRepoName, "pulls", (show reqPullRequestNumber), "merge"]) auth (Just . RequestBodyLBS . encode . toJSON $ (buildCommitMessageMap commitMessage))
-
--- | Update a pull request
-updatePullRequest :: GithubAuth -> String -> String -> Int -> EditPullRequest -> IO (Either Error DetailedPullRequest)
-updatePullRequest auth reqRepoOwner reqRepoName reqPullRequestNumber editPullRequest =
-  githubPatch auth ["repos", reqRepoOwner, reqRepoName, "pulls", show reqPullRequestNumber] editPullRequest
-
-
-buildCommitMessageMap :: Maybe String -> M.Map String String
-buildCommitMessageMap (Just commitMessage) = M.singleton "commit_message" commitMessage
-buildCommitMessageMap _ = M.empty
-
-createPullRequest :: GithubAuth
-                  -> String
-                  -> String
-                  -> CreatePullRequest
-                  -> IO (Either Error DetailedPullRequest)
-createPullRequest auth reqUserName reqRepoName createPR =
-    githubPost auth ["repos", reqUserName, reqRepoName, "pulls"] createPR
diff --git a/Github/Repos.hs b/Github/Repos.hs
deleted file mode 100644
--- a/Github/Repos.hs
+++ /dev/null
@@ -1,342 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE CPP #-}
--- | The Github Repos API, as documented at
--- <http://developer.github.com/v3/repos/>
-module Github.Repos (
-
--- * Querying repositories
- userRepos
-,userRepos'
-,userRepo
-,userRepo'
-,organizationRepos
-,organizationRepos'
-,organizationRepo
-,organizationRepo'
-,contributors
-,contributors'
-,contributorsWithAnonymous
-,contributorsWithAnonymous'
-,languagesFor
-,languagesFor'
-,tagsFor
-,tagsFor'
-,branchesFor
-,branchesFor'
-,contentsFor
-,contentsFor'
-,module Github.Data
-,RepoPublicity(..)
-
--- ** Create
-,createRepo
-,createOrganizationRepo
-,newRepo
-,NewRepo(..)
-
--- ** Edit
-,editRepo
-,def
-,Edit(..)
-
--- ** Delete
-,deleteRepo
-) where
-
-import Data.Default
-import Data.Aeson.Types
-import Github.Data
-import Github.Private
-import Network.HTTP.Conduit
-import Control.Applicative
-import qualified Control.Exception as E
-import Network.HTTP.Types
-
--- | Filter the list of the user's repos using any of these constructors.
-data RepoPublicity =
-    All     -- ^ All repos accessible to the user.
-  | Owner   -- ^ Only repos owned by the user.
-  | Public  -- ^ Only public repos.
-  | Private -- ^ Only private repos.
-  | Member  -- ^ Only repos to which the user is a member but not an owner.
- deriving (Show, Eq)
-
--- | The repos for a user, by their login. Can be restricted to just repos they
--- own, are a member of, or publicize. Private repos are currently not
--- supported.
---
--- > userRepos "mike-burns" All
-userRepos :: String -> RepoPublicity -> IO (Either Error [Repo])
-userRepos = userRepos' Nothing
-
--- | The repos for a user, by their login.
--- With authentication, but note that private repos are currently not supported.
---
--- > userRepos' (Just (GithubBasicAuth (user, password))) "mike-burns" All
-userRepos' :: Maybe GithubAuth -> String -> RepoPublicity -> IO (Either Error [Repo])
-userRepos' auth userName All =
-  githubGetWithQueryString' auth ["users", userName, "repos"] "type=all"
-userRepos' auth userName Owner =
-  githubGetWithQueryString' auth ["users", userName, "repos"] "type=owner"
-userRepos' auth userName Member =
-  githubGetWithQueryString' auth ["users", userName, "repos"] "type=member"
-userRepos' auth userName Public =
-  githubGetWithQueryString' auth ["users", userName, "repos"] "type=public"
-userRepos' _auth _userName Private =
-  return $ Left $ UserError "Cannot access private repos using userRepos"
-
--- | The repos for an organization, by the organization name.
---
--- > organizationRepos "thoughtbot"
-organizationRepos :: String -> IO (Either Error [Repo])
-organizationRepos = organizationRepos' Nothing
-
--- | The repos for an organization, by the organization name.
--- With authentication.
---
--- > organizationRepos (Just (GithubBasicAuth (user, password))) "thoughtbot"
-organizationRepos' :: Maybe GithubAuth -> String -> IO (Either Error [Repo])
-organizationRepos' auth orgName = githubGet' auth ["orgs", orgName, "repos"]
-
--- | A specific organization repo, by the organization name.
---
--- > organizationRepo "thoughtbot" "github"
-organizationRepo :: String -> String -> IO (Either Error Repo)
-organizationRepo = organizationRepo' Nothing
-
--- | A specific organization repo, by the organization name.
--- With authentication.
---
--- > organizationRepo (Just (GithubBasicAuth (user, password))) "thoughtbot" "github"
-organizationRepo' :: Maybe GithubAuth -> String -> String -> IO (Either Error Repo)
-organizationRepo' auth orgName reqRepoName = githubGet' auth ["orgs", orgName, reqRepoName]
-
--- | Details on a specific repo, given the owner and repo name.
---
--- > userRepo "mike-burns" "github"
-userRepo :: String -> String -> IO (Either Error Repo)
-userRepo = userRepo' Nothing
-
--- | Details on a specific repo, given the owner and repo name.
--- With authentication.
---
--- > userRepo' (Just (GithubBasicAuth (user, password))) "mike-burns" "github"
-userRepo' :: Maybe GithubAuth -> String -> String -> IO (Either Error Repo)
-userRepo' auth userName reqRepoName = githubGet' auth ["repos", userName, reqRepoName]
-
--- | The contributors to a repo, given the owner and repo name.
---
--- > contributors "thoughtbot" "paperclip"
-contributors :: String -> String -> IO (Either Error [Contributor])
-contributors = contributors' Nothing
-
--- | The contributors to a repo, given the owner and repo name.
--- With authentication.
---
--- > contributors' (Just (GithubBasicAuth (user, password))) "thoughtbot" "paperclip"
-contributors' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Contributor])
-contributors' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "contributors"]
-
--- | The contributors to a repo, including anonymous contributors (such as
--- deleted users or git commits with unknown email addresses), given the owner
--- and repo name.
---
--- > contributorsWithAnonymous "thoughtbot" "paperclip"
-contributorsWithAnonymous :: String -> String -> IO (Either Error [Contributor])
-contributorsWithAnonymous = contributorsWithAnonymous' Nothing
-
--- | The contributors to a repo, including anonymous contributors (such as
--- deleted users or git commits with unknown email addresses), given the owner
--- and repo name.
--- With authentication.
---
--- > contributorsWithAnonymous' (Just (GithubBasicAuth (user, password))) "thoughtbot" "paperclip"
-contributorsWithAnonymous' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Contributor])
-contributorsWithAnonymous' auth userName reqRepoName =
-  githubGetWithQueryString' auth
-    ["repos", userName, reqRepoName, "contributors"]
-    "anon=true"
-
-
--- | The programming languages used in a repo along with the number of
--- characters written in that language. Takes the repo owner and name.
---
--- > languagesFor "mike-burns" "ohlaunch"
-languagesFor :: String -> String -> IO (Either Error [Language])
-languagesFor = languagesFor' Nothing
-
--- | The programming languages used in a repo along with the number of
--- characters written in that language. Takes the repo owner and name.
--- With authentication.
---
--- > languagesFor' (Just (GithubBasicAuth (user, password))) "mike-burns" "ohlaunch"
-languagesFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Language])
-languagesFor' auth userName reqRepoName = do
-  result <- githubGet' auth ["repos", userName, reqRepoName, "languages"]
-  return $ either Left (Right . getLanguages) result
-
--- | The git tags on a repo, given the repo owner and name.
---
--- > tagsFor "thoughtbot" "paperclip"
-tagsFor :: String -> String -> IO (Either Error [Tag])
-tagsFor = tagsFor' Nothing
-
--- | The git tags on a repo, given the repo owner and name.
--- With authentication.
---
--- > tagsFor' (Just (GithubBasicAuth (user, password))) "thoughtbot" "paperclip"
-tagsFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Tag])
-tagsFor' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "tags"]
-
--- | The git branches on a repo, given the repo owner and name.
---
--- > branchesFor "thoughtbot" "paperclip"
-branchesFor :: String -> String -> IO (Either Error [Branch])
-branchesFor = branchesFor' Nothing
-
--- | The git branches on a repo, given the repo owner and name.
--- With authentication.
---
--- > branchesFor' (Just (GithubBasicAuth (user, password))) "thoughtbot" "paperclip"
-branchesFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Branch])
-branchesFor' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "branches"]
-
--- | The contents of a file or directory in a repo, given the repo owner, name, and path to the file
---
--- > contentsFor "thoughtbot" "paperclip" "README.md"
-contentsFor :: String -> String -> String -> Maybe String -> IO (Either Error Content)
-contentsFor = contentsFor' Nothing
-
--- | The contents of a file or directory in a repo, given the repo owner, name, and path to the file
--- With Authentication
---
--- > contentsFor' (Just (GithubBasicAuth (user, password))) "thoughtbot" "paperclip" "README.md"
-contentsFor' :: Maybe GithubAuth ->  String -> String -> String -> Maybe String -> IO (Either Error Content)
-contentsFor' auth userName reqRepoName reqContentPath ref =
-  githubGetWithQueryString' auth
-  ["repos", userName, reqRepoName, "contents", reqContentPath] $
-  maybe "" ("ref="++) ref
-
-
-data NewRepo = NewRepo {
-  newRepoName         :: String
-, newRepoDescription  :: (Maybe String)
-, newRepoHomepage     :: (Maybe String)
-, newRepoPrivate      :: (Maybe Bool)
-, newRepoHasIssues    :: (Maybe Bool)
-, newRepoHasWiki      :: (Maybe Bool)
-, newRepoAutoInit     :: (Maybe Bool)
-} deriving Show
-
-instance ToJSON  NewRepo where
-  toJSON (NewRepo { newRepoName         = name
-                  , newRepoDescription  = description
-                  , newRepoHomepage     = homepage
-                  , newRepoPrivate      = private
-                  , newRepoHasIssues    = hasIssues
-                  , newRepoHasWiki      = hasWiki
-                  , newRepoAutoInit     = autoInit
-                  }) = object
-                  [ "name"                .= name
-                  , "description"         .= description
-                  , "homepage"            .= homepage
-                  , "private"             .= private
-                  , "has_issues"          .= hasIssues
-                  , "has_wiki"            .= hasWiki
-                  , "auto_init"           .= autoInit
-                  ]
-
-newRepo :: String -> NewRepo
-newRepo name = NewRepo name Nothing Nothing Nothing Nothing Nothing Nothing
-
--- |
--- Create a new repository.
---
--- > createRepo (GithubBasicAuth (user, password)) (newRepo "some_repo") {newRepoHasIssues = Just False}
-createRepo :: GithubAuth -> NewRepo -> IO (Either Error Repo)
-createRepo auth = githubPost auth ["user", "repos"]
-
--- |
--- Create a new repository for an organization.
---
--- > createOrganizationRepo (GithubBasicAuth (user, password)) "thoughtbot" (newRepo "some_repo") {newRepoHasIssues = Just False}
-createOrganizationRepo :: GithubAuth -> String -> NewRepo -> IO (Either Error Repo)
-createOrganizationRepo auth org = githubPost auth ["orgs", org, "repos"]
-
-data Edit = Edit {
-  editName         :: Maybe String
-, editDescription  :: Maybe String
-, editHomepage     :: Maybe String
-, editPublic       :: Maybe Bool
-, editHasIssues    :: Maybe Bool
-, editHasWiki      :: Maybe Bool
-, editHasDownloads :: Maybe Bool
-} deriving Show
-
-instance Default Edit where
-  def = Edit def def def def def def def
-
-instance ToJSON  Edit where
-  toJSON (Edit { editName         = name
-               , editDescription  = description
-               , editHomepage     = homepage
-               , editPublic       = public
-               , editHasIssues    = hasIssues
-               , editHasWiki      = hasWiki
-               , editHasDownloads = hasDownloads
-               }) = object
-               [ "name"          .= name
-               , "description"   .= description
-               , "homepage"      .= homepage
-               , "public"        .= public
-               , "has_issues"    .= hasIssues
-               , "has_wiki"      .= hasWiki
-               , "has_downloads" .= hasDownloads
-               ]
-
--- |
--- Edit an existing repository.
---
--- > editRepo (GithubBasicAuth (user, password)) "some_user" "some_repo" def {editDescription = Just "some description"}
-editRepo :: GithubAuth
-     -> String      -- ^ owner
-     -> String      -- ^ repository name
-     -> Edit
-     -> IO (Either Error Repo)
-editRepo auth user repo body = githubPatch auth ["repos", user, repo] b
-  where
-    -- if no name is given, use curent name
-    b = body {editName = editName body <|> Just repo}
-
--- |
--- Delete an existing repository.
---
--- > deleteRepo (GithubBasicAuth (user, password)) "thoughtbot" "some_repo"
-deleteRepo :: GithubAuth
-           -> String      -- ^ owner
-           -> String      -- ^ repository name
-           -> IO (Either Error ())
-deleteRepo auth owner repo = do
-  result <- doHttps "DELETE" url (Just auth) Nothing
-  case result of
-      Left e -> return (Left (HTTPConnectionError e))
-      Right resp ->
-          let status = responseStatus resp
-              headers = responseHeaders resp
-          in if status == notFound404
-                -- doHttps silently absorbs 404 errors, but for this operation
-                -- we want the user to know if they've tried to delete a
-                -- non-existent repository
-             then return (Left (HTTPConnectionError
-                                (E.toException
-                                 (StatusCodeException status headers
-#if MIN_VERSION_http_conduit(1, 9, 0)
-                                 (responseCookieJar resp)
-#endif
-                                 ))))
-             else return (Right ())
-  where
-    url = "https://api.github.com/repos/" ++ owner ++ "/" ++ repo
diff --git a/Github/Repos/Collaborators.hs b/Github/Repos/Collaborators.hs
deleted file mode 100644
--- a/Github/Repos/Collaborators.hs
+++ /dev/null
@@ -1,36 +0,0 @@
--- | The repo collaborators API as described on
--- <http://developer.github.com/v3/repos/collaborators/>.
-module Github.Repos.Collaborators (
- collaboratorsOn
-,isCollaboratorOn
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
-import Data.ByteString.Char8 (pack)
-import qualified Network.HTTP.Conduit as C (responseStatus)
-import qualified Network.HTTP.Types as T (statusCode)
-
--- | All the users who have collaborated on a repo.
---
--- > collaboratorsOn "thoughtbot" "paperclip"
-collaboratorsOn :: String -> String -> IO (Either Error [GithubOwner])
-collaboratorsOn userName reqRepoName =
-  githubGet ["repos", userName, reqRepoName, "collaborators"]
-
--- | Whether the user is collaborating on a repo. Takes the user in question,
--- the user who owns the repo, and the repo name.
---
--- > isCollaboratorOn "mike-burns" "thoughtbot" "paperclip"
--- > isCollaboratorOn "johnson" "thoughtbot" "paperclip"
-isCollaboratorOn :: String -> String -> String -> IO (Either Error Bool)
-isCollaboratorOn userName repoOwnerName reqRepoName = do
-   result <- doHttps (pack "GET")
-                     (buildUrl ["repos", repoOwnerName, reqRepoName, "collaborators", userName])
-                     Nothing
-                     Nothing
-   return $ either (Left . HTTPConnectionError)
-                   (Right . (204 ==) . T.statusCode . C.responseStatus)
-                   result
diff --git a/Github/Repos/Commits.hs b/Github/Repos/Commits.hs
deleted file mode 100644
--- a/Github/Repos/Commits.hs
+++ /dev/null
@@ -1,53 +0,0 @@
--- | The repo commits API as described on
--- <http://developer.github.com/v3/repos/commits/>.
-module Github.Repos.Commits (
- commitsFor
-,commit
-,commentsFor
-,commitCommentsFor
-,commitCommentFor
-,diff
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The commit history for a repo.
---
--- > commitsFor "mike-burns" "github"
-commitsFor :: String -> String -> IO (Either Error [Commit])
-commitsFor user repo = githubGet ["repos", user, repo, "commits"]
-
--- | Details on a specific SHA1 for a repo.
---
--- > commit "mike-burns" "github" "9d1a9a361266c3c890b1108ad2fdf52f824b1b81"
-commit :: String -> String -> String -> IO (Either Error Commit)
-commit user repo sha1 = githubGet ["repos", user, repo, "commits", sha1]
-
--- | All the comments on a Github repo.
---
--- > commentsFor "thoughtbot" "paperclip"
-commentsFor :: String -> String -> IO (Either Error [Comment])
-commentsFor user repo = githubGet ["repos", user, repo, "comments"]
-
--- | Just the comments on a specific SHA for a given Github repo.
---
--- > commitCommentsFor "thoughtbot" "paperclip" "41f685f6e01396936bb8cd98e7cca517e2c7d96b"
-commitCommentsFor :: String -> String -> String -> IO (Either Error [Comment])
-commitCommentsFor user repo sha1 =
-  githubGet ["repos", user, repo, "commits", sha1, "comments"]
-
--- | A comment, by its ID, relative to the Github repo.
---
--- > commitCommentFor "thoughtbot" "paperclip" "669575"
-commitCommentFor :: String -> String -> String -> IO (Either Error Comment)
-commitCommentFor user repo reqCommentId =
-  githubGet ["repos", user, repo, "comments", reqCommentId]
-
--- | The diff between two treeishes on a repo.
---
--- > diff "thoughtbot" "paperclip" "41f685f6e01396936bb8cd98e7cca517e2c7d96b" "HEAD"
-diff :: String -> String -> String -> String -> IO (Either Error Diff)
-diff user repo base headref =
-  githubGet ["repos", user, repo, "compare", base ++ "..." ++ headref]
diff --git a/Github/Repos/Forks.hs b/Github/Repos/Forks.hs
deleted file mode 100644
--- a/Github/Repos/Forks.hs
+++ /dev/null
@@ -1,24 +0,0 @@
--- | Hot forking action, as described at
--- <http://developer.github.com/v3/repos/forks/>.
-module Github.Repos.Forks (
- forksFor
-,forksFor'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All the repos that are forked off the given repo.
---
--- > forksFor "thoughtbot" "paperclip"
-forksFor :: String -> String -> IO (Either Error [Repo])
-forksFor = forksFor' Nothing
-
--- | All the repos that are forked off the given repo.
--- | With authentication
---
--- > forksFor' (Just (GithubUser (user, password))) "thoughtbot" "paperclip"
-forksFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [Repo])
-forksFor' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "forks"]
diff --git a/Github/Repos/Starring.hs b/Github/Repos/Starring.hs
deleted file mode 100644
--- a/Github/Repos/Starring.hs
+++ /dev/null
@@ -1,28 +0,0 @@
--- | The repo starring API as described on
--- <http://developer.github.com/v3/repos/starring/>.
-module Github.Repos.Starring (
- stargazersFor
-,reposStarredBy
-,myStarred
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The list of users that have starred the specified Github repo.
---
--- > userInfoFor' Nothing "mike-burns"
-stargazersFor :: Maybe GithubAuth -> String -> String -> IO (Either Error [GithubOwner])
-stargazersFor auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "stargazers"]
-
--- | All the public repos starred by the specified user.
---
--- > reposStarredBy Nothing "croaky"
-reposStarredBy :: Maybe GithubAuth -> String -> IO (Either Error [Repo])
-reposStarredBy auth userName = githubGet' auth ["users", userName, "starred"]
-
--- | All the repos starred by the authenticated user.
-myStarred :: GithubAuth -> IO (Either Error [Repo])
-myStarred auth = githubGet' (Just auth) ["user", "starred"]
diff --git a/Github/Repos/Subscribing.hs b/Github/Repos/Subscribing.hs
deleted file mode 100644
--- a/Github/Repos/Subscribing.hs
+++ /dev/null
@@ -1,39 +0,0 @@
--- | The repo subscribing API as described on
--- <http://developer.github.com/v3/repos/watching/>.
-module Github.Repos.Subscribing (
- subscribersFor
-,subscribersFor'
-,reposSubscribedToBy
-,reposSubscribedToBy'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The list of users that are subscribed to the specified Github repo.
---
--- > subscribersFor "thoughtbot" "paperclip"
-subscribersFor :: String -> String -> IO (Either Error [GithubOwner])
-subscribersFor = subscribersFor' Nothing
-
--- | The list of users that are subscribed to the specified Github repo.
--- | With authentication
---
--- > subscribersFor' (Just (GithubUser (user, password))) "thoughtbot" "paperclip"
-subscribersFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [GithubOwner])
-subscribersFor' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "subscribers"]
-
--- | All the public repos subscribed to by the specified user.
---
--- > reposSubscribedToBy "croaky"
-reposSubscribedToBy :: String -> IO (Either Error [Repo])
-reposSubscribedToBy = reposSubscribedToBy' Nothing
-
--- | All the public repos subscribed to by the specified user.
--- | With authentication
---
--- > reposSubscribedToBy' (Just (GithubUser (user, password))) "croaky"
-reposSubscribedToBy' :: Maybe GithubAuth -> String -> IO (Either Error [Repo])
-reposSubscribedToBy' auth userName = githubGet' auth ["users", userName, "subscriptions"]
diff --git a/Github/Repos/Watching.hs b/Github/Repos/Watching.hs
deleted file mode 100644
--- a/Github/Repos/Watching.hs
+++ /dev/null
@@ -1,39 +0,0 @@
--- | The repo watching API as described on
--- <http://developer.github.com/v3/repos/watching/>.
-module Github.Repos.Watching (
- watchersFor
-,watchersFor'
-,reposWatchedBy
-,reposWatchedBy'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The list of users that are watching the specified Github repo.
---
--- > watchersFor "thoughtbot" "paperclip"
-watchersFor :: String -> String -> IO (Either Error [GithubOwner])
-watchersFor = watchersFor' Nothing
-
--- | The list of users that are watching the specified Github repo.
--- | With authentication
---
--- > watchersFor' (Just (GithubUser (user, password))) "thoughtbot" "paperclip"
-watchersFor' :: Maybe GithubAuth -> String -> String -> IO (Either Error [GithubOwner])
-watchersFor' auth userName reqRepoName =
-  githubGet' auth ["repos", userName, reqRepoName, "watchers"]
-
--- | All the public repos watched by the specified user.
---
--- > reposWatchedBy "croaky"
-reposWatchedBy :: String -> IO (Either Error [Repo])
-reposWatchedBy = reposWatchedBy' Nothing
-
--- | All the public repos watched by the specified user.
--- | With authentication
---
--- > reposWatchedBy' (Just (GithubUser (user, password))) "croaky"
-reposWatchedBy' :: Maybe GithubAuth -> String -> IO (Either Error [Repo])
-reposWatchedBy' auth userName = githubGet' auth ["users", userName, "watched"]
diff --git a/Github/Repos/Webhooks.hs b/Github/Repos/Webhooks.hs
deleted file mode 100644
--- a/Github/Repos/Webhooks.hs
+++ /dev/null
@@ -1,116 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE CPP #-}
--- | The webhooks API, as described at
--- <https://developer.github.com/v3/repos/hooks/>
--- <https://developer.github.com/webhooks>
-
-module Github.Repos.Webhooks (
-
--- * Querying repositories
-  webhooksFor'
- ,webhookFor'
-
--- ** Create
- ,createRepoWebhook'
-
--- ** Edit  
- ,editRepoWebhook'
-
--- ** Test  
- ,testPushRepoWebhook'
- ,pingRepoWebhook'
-
--- ** Delete  
- ,deleteRepoWebhook'
- ,NewRepoWebhook(..)
- ,EditRepoWebhook(..)
- ,RepoOwner
- ,RepoName
- ,RepoWebhookId
-) where
-
-import Github.Data
-import Github.Private
-import qualified Data.Map as M
-import Network.HTTP.Conduit
-import Network.HTTP.Types
-import Data.Aeson
-
-type RepoOwner = String
-type RepoName = String
-type RepoWebhookId = Int
-    
-data NewRepoWebhook = NewRepoWebhook {
-  newRepoWebhookName :: String
- ,newRepoWebhookConfig :: M.Map String String
- ,newRepoWebhookEvents :: Maybe [RepoWebhookEvent]
- ,newRepoWebhookActive :: Maybe Bool
-} deriving Show
-
-data EditRepoWebhook = EditRepoWebhook {
-  editRepoWebhookConfig :: Maybe (M.Map String String)
- ,editRepoWebhookEvents :: Maybe [RepoWebhookEvent]
- ,editRepoWebhookAddEvents :: Maybe [RepoWebhookEvent]
- ,editRepoWebhookRemoveEvents :: Maybe [RepoWebhookEvent]
- ,editRepoWebhookActive :: Maybe Bool
-} deriving Show
-                  
-instance ToJSON NewRepoWebhook where
-  toJSON (NewRepoWebhook { newRepoWebhookName = name
-                         , newRepoWebhookConfig = config
-                         , newRepoWebhookEvents = events
-                         , newRepoWebhookActive = active
-
-             }) = object
-             [ "name" .= name
-             , "config" .= config
-             , "events" .= events
-             , "active" .= active
-             ]
-
-instance ToJSON EditRepoWebhook where             
-  toJSON (EditRepoWebhook { editRepoWebhookConfig = config
-                          , editRepoWebhookEvents = events
-                          , editRepoWebhookAddEvents = addEvents
-                          , editRepoWebhookRemoveEvents = removeEvents
-                          , editRepoWebhookActive = active
-             }) = object
-             [ "config" .= config
-             , "events" .= events
-             , "add_events" .= addEvents
-             , "remove_events" .= removeEvents
-             , "active" .= active
-             ]
-             
-webhooksFor' :: GithubAuth -> RepoOwner -> RepoName -> IO (Either Error [RepoWebhook])
-webhooksFor' auth owner reqRepoName =
-  githubGet' (Just auth) ["repos", owner, reqRepoName, "hooks"]
-
-webhookFor' :: GithubAuth -> RepoOwner -> RepoName -> RepoWebhookId -> IO (Either Error RepoWebhook)
-webhookFor' auth owner reqRepoName webhookId =
-  githubGet' (Just auth) ["repos", owner, reqRepoName, "hooks", (show webhookId)]
-
-createRepoWebhook' :: GithubAuth -> RepoOwner -> RepoName -> NewRepoWebhook -> IO (Either Error RepoWebhook)
-createRepoWebhook' auth owner reqRepoName = githubPost auth ["repos", owner, reqRepoName, "hooks"]
-
-editRepoWebhook' :: GithubAuth -> RepoOwner -> RepoName -> RepoWebhookId -> EditRepoWebhook -> IO (Either Error RepoWebhook)
-editRepoWebhook' auth owner reqRepoName webhookId edit = githubPatch auth ["repos", owner, reqRepoName, "hooks", (show webhookId)] edit
-                                                            
-testPushRepoWebhook' :: GithubAuth -> RepoOwner -> RepoName -> RepoWebhookId -> IO (Either Error Status)
-testPushRepoWebhook' auth owner reqRepoName webhookId =
-  doHttpsStatus "POST" (createWebhookOpUrl owner reqRepoName webhookId (Just "tests")) auth (Just . RequestBodyLBS . encode $ (decode "{}" :: Maybe (M.Map String Int))) 
-
-pingRepoWebhook' :: GithubAuth -> RepoOwner -> RepoName -> RepoWebhookId -> IO (Either Error Status)
-pingRepoWebhook' auth owner reqRepoName webhookId =
-  doHttpsStatus "POST" (createWebhookOpUrl owner reqRepoName webhookId (Just "pings")) auth Nothing
-
-deleteRepoWebhook' :: GithubAuth -> RepoOwner -> RepoName -> RepoWebhookId -> IO (Either Error Status)
-deleteRepoWebhook' auth owner reqRepoName webhookId =
-  doHttpsStatus "DELETE" (createWebhookOpUrl owner reqRepoName webhookId Nothing) auth Nothing
-
-createBaseWebhookUrl :: RepoOwner -> RepoName -> RepoWebhookId -> String
-createBaseWebhookUrl owner reqRepoName webhookId = "https://api.github.com/repos/" ++ owner ++ "/" ++ reqRepoName ++ "/hooks/" ++ (show webhookId)
-
-createWebhookOpUrl :: RepoOwner -> RepoName -> RepoWebhookId -> Maybe String -> String
-createWebhookOpUrl owner reqRepoName webhookId Nothing = createBaseWebhookUrl owner reqRepoName webhookId
-createWebhookOpUrl owner reqRepoName webhookId (Just operation) = createBaseWebhookUrl owner reqRepoName webhookId ++ "/" ++ operation
diff --git a/Github/Repos/Webhooks/Validate.hs b/Github/Repos/Webhooks/Validate.hs
deleted file mode 100644
--- a/Github/Repos/Webhooks/Validate.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
--- | Verification of incomming webhook payloads, as described at
--- <https://developer.github.com/webhooks/securing/>
-
-module Github.Repos.Webhooks.Validate (
-  isValidPayload
-) where
-
-import Control.Applicative
-import Crypto.Hash
-import qualified Data.ByteString.Char8 as BS
-import Data.Byteable (constEqBytes, toBytes)
-import qualified Data.ByteString.Base16 as Hex
-import Data.Monoid
-
-
--- | Validates a given payload against a given HMAC hexdigest using a given
--- secret.
--- Returns 'True' iff the given hash is non-empty and it's a valid signature of
--- the payload.
-isValidPayload
-  :: String             -- ^ the secret
-  -> Maybe String       -- ^ the hash provided by the remote party
-                        -- in @X-Hub-Signature@ (if any),
-                        -- including the 'sha1=...' prefix
-  -> BS.ByteString      -- ^ the body
-  -> Bool
-isValidPayload secret shaOpt payload = maybe False (constEqBytes sign) shaOptBS
-  where
-    shaOptBS = BS.pack <$> shaOpt
-    hexDigest = Hex.encode . toBytes . hmacGetDigest
-
-    hm = hmac (BS.pack secret) payload :: HMAC SHA1
-    sign = "sha1=" <> hexDigest hm
diff --git a/Github/Search.hs b/Github/Search.hs
deleted file mode 100644
--- a/Github/Search.hs
+++ /dev/null
@@ -1,42 +0,0 @@
--- | The Github Search API, as described at
--- <http://developer.github.com/v3/search/>.
-module Github.Search(
- searchRepos'
-,searchRepos
-,searchCode'
-,searchCode
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | Perform a repository search.
--- | With authentication.
---
--- > searchRepos' (Just $ GithubBasicAuth "github-username" "github-password') "q=a in%3Aname language%3Ahaskell created%3A>2013-10-01&per_page=100"
-searchRepos' :: Maybe GithubAuth -> String -> IO (Either Error SearchReposResult)
-searchRepos' auth queryString = githubGetWithQueryString' auth ["search", "repositories"] queryString
-
--- | Perform a repository search.
--- | Without authentication.
---
--- > searchRepos "q=a in%3Aname language%3Ahaskell created%3A>2013-10-01&per_page=100"
-searchRepos :: String -> IO (Either Error SearchReposResult)
-searchRepos = searchRepos' Nothing 
-
--- | Perform a code search.
--- | With authentication.
---
--- > searchCode' (Just $ GithubBasicAuth "github-username" "github-password') "q=a in%3Aname language%3Ahaskell created%3A>2013-10-01&per_page=100"
-searchCode' :: Maybe GithubAuth -> String -> IO (Either Error SearchCodeResult)
-searchCode' auth queryString = githubGetWithQueryString' auth ["search", "code"] queryString
-
--- | Perform a code search.
--- | Without authentication.
---
--- > searchCode "q=addClass+in:file+language:js+repo:jquery/jquery"
-searchCode :: String -> IO (Either Error SearchCodeResult)
-searchCode = searchCode' Nothing 
-
-
diff --git a/Github/Users.hs b/Github/Users.hs
deleted file mode 100644
--- a/Github/Users.hs
+++ /dev/null
@@ -1,23 +0,0 @@
--- | The Github Users API, as described at
--- <http://developer.github.com/v3/users/>.
-module Github.Users (
- userInfoFor
-,userInfoFor'
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | The information for a single user, by login name.
--- | With authentification
---
--- > userInfoFor' (Just ("github-username", "github-password")) "mike-burns"
-userInfoFor' :: Maybe GithubAuth -> String -> IO (Either Error DetailedOwner)
-userInfoFor' auth userName = githubGet' auth ["users", userName]
-
--- | The information for a single user, by login name.
---
--- > userInfoFor "mike-burns"
-userInfoFor :: String -> IO (Either Error DetailedOwner)
-userInfoFor = userInfoFor' Nothing
diff --git a/Github/Users/Followers.hs b/Github/Users/Followers.hs
deleted file mode 100644
--- a/Github/Users/Followers.hs
+++ /dev/null
@@ -1,22 +0,0 @@
--- | The user followers API as described on
--- <http://developer.github.com/v3/users/followers/>.
-module Github.Users.Followers (
- usersFollowing
-,usersFollowedBy
-,module Github.Data
-) where
-
-import Github.Data
-import Github.Private
-
--- | All the users following the given user.
---
--- > usersFollowing "mike-burns"
-usersFollowing :: String -> IO (Either Error [GithubOwner])
-usersFollowing userName = githubGet ["users", userName, "followers"]
-
--- | All the users that the given user follows.
---
--- > usersFollowedBy "mike-burns"
-usersFollowedBy :: String -> IO (Either Error [GithubOwner])
-usersFollowedBy userName = githubGet ["users", userName, "following"]
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,10 +1,15 @@
-Github
+GitHub
 ------
 
-The Github API v3 for Haskell.
+[![Hackage version](https://img.shields.io/hackage/v/github.svg?label=Hackage&color=informational)](http://hackage.haskell.org/package/github)
+[![github on Stackage Nightly](https://stackage.org/package/github/badge/nightly)](https://stackage.org/nightly/package/github)
+[![Stackage LTS version](https://www.stackage.org/package/github/badge/lts?label=Stackage)](https://www.stackage.org/package/github)
+[![Haskell-CI](https://github.com/haskell-github/github/actions/workflows/haskell-ci.yml/badge.svg)](https://github.com/haskell-github/github/actions/workflows/haskell-ci.yml)
 
-Some functions are missing; these are functions where the Github API did
-not work as expected. The full Github API is in beta and constantly
+The GitHub API v3 for Haskell.
+
+Some functions are missing; these are functions where the GitHub API did
+not work as expected. The full GitHub API is in beta and constantly
 improving.
 
 Installation
@@ -12,55 +17,89 @@
 
 In your project's cabal file:
 
-    -- Packages needed in order to build this package.
-    Build-depends:       github
+```cabal
+Build-depends:       github
+```
 
 Or from the command line:
 
-    cabal install github
+```sh
+cabal v1-install github
+```
 
 Example Usage
 =============
 
 See the samples in the
-[samples/](https://github.com/fpco/github/tree/master/samples) directory.
+[samples/](https://github.com/haskell-github/github/tree/master/samples) directory.
 
+Note: some samples might be outdated.
+
 Documentation
 =============
 
-For details see the reference documentation on Hackage.
+For details see the reference [documentation on Hackage][hackage].
 
 Each module lines up with the hierarchy of
-[documentation from the Github API](http://developer.github.com/v3/).
+[documentation from the GitHub API](https://docs.github.com/en/rest).
 
-Each function has a sample written for it.
+Request functions (ending with `R`) construct a data type which can be executed
+in `IO` by `executeRequest` functions. They are all listed in the root `GitHub`
+module.
 
-All functions produce an `IO (Either Error a)`, where `a` is the actual thing
+IO functions produce an `IO (Either Error a)`, where `a` is the actual thing
 you want. You must call the function using IO goodness, then dispatch on the
 possible error message. Here's an example from the samples:
 
-    import qualified Github.Users.Followers as Github
-    import Data.List (intercalate)
+Many function have samples under
+[`samples/`](https://github.com/haskell-github/github/tree/master/samples) directory.
 
-    main = do
-      possibleUsers <- Github.usersFollowing "mike-burns"
-      putStrLn $ either (("Error: "++) . show)
-                        (intercalate "\n" . map formatUser)
+```hs
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+import Prelude.Compat
+
+import Data.Text         (Text, pack)
+import Data.Text.IO as T (putStrLn)
+import Data.Monoid       ((<>))
+
+import GitHub (github')
+import qualified GitHub
+
+main :: IO ()
+main = do
+    possibleUsers <- github' GitHub.usersFollowingR "phadej"
+    T.putStrLn $ either (("Error: " <>) . pack . show)
+                        (foldMap ((<> "\n") . formatUser))
                         possibleUsers
 
-    formatUser = Github.githubOwnerLogin
+formatUser :: GitHub.SimpleUser -> Text
+formatUser = GitHub.untagName . GitHub.simpleUserLogin
+```
 
 Contributions
 =============
 
 Please see
-[CONTRIBUTING.md](https://github.com/fpco/github/blob/master/CONTRIBUTING.md)
+[CONTRIBUTING.md](https://github.com/haskell-github/github/blob/master/CONTRIBUTING.md)
 for details on how you can help.
 
 Copyright
 =========
 
-Copyright 2011, 2012 Mike Burns.
-Copyright 2013-2014 John Wiegley.
+Copyright 2011-2012 Mike Burns.
+Copyright 2013-2015 John Wiegley.
+Copyright 2016-2019 Oleg Grenrus.
 
 Available under the BSD 3-clause license.
+
+[hackage]: https://hackage.haskell.org/package/github "Hackage"
+
+Alternative
+===========
+
+Library [`github-rest`](https://hackage.haskell.org/package/github-rest)
+also provides an interface to the GitHub API.
+It compares itself to `github` here:
+https://github.com/LeapYear/github-rest#comparison-to-other-libraries
diff --git a/fixtures/actions/artifact.json b/fixtures/actions/artifact.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/artifact.json
@@ -0,0 +1,19 @@
+{
+  "id": 416767789,
+  "node_id": "MDg6QXJ0aWZhY3Q0MTY3Njc3ODk=",
+  "name": "dist-without-markdown",
+  "size_in_bytes": 42718,
+  "url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/artifacts/416767789",
+  "archive_download_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/artifacts/416767789/zip",
+  "expired": false,
+  "created_at": "2022-10-29T22:18:21Z",
+  "updated_at": "2022-10-29T22:18:23Z",
+  "expires_at": "2023-01-27T22:18:16Z",
+  "workflow_run": {
+    "id": 3353148947,
+    "repository_id": 559365297,
+    "head_repository_id": 559365297,
+    "head_branch": "main",
+    "head_sha": "601593ecb1d8a57a04700fdb445a28d4186b8954"
+  }
+}
diff --git a/fixtures/actions/artifacts-list.json b/fixtures/actions/artifacts-list.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/artifacts-list.json
@@ -0,0 +1,43 @@
+{
+  "total_count": 23809,
+  "artifacts": [
+    {
+      "id": 416737084,
+      "node_id": "MDg6QXJ0aWZhY3Q0MTY3MzcwODQ=",
+      "name": "doc-html",
+      "size_in_bytes": 61667543,
+      "url": "https://api.github.com/repos/python/cpython/actions/artifacts/416737084",
+      "archive_download_url": "https://api.github.com/repos/python/cpython/actions/artifacts/416737084/zip",
+      "expired": false,
+      "created_at": "2022-10-29T20:56:24Z",
+      "updated_at": "2022-10-29T20:56:25Z",
+      "expires_at": "2023-01-27T20:50:21Z",
+      "workflow_run": {
+        "id": 3352897496,
+        "repository_id": 81598961,
+        "head_repository_id": 101955313,
+        "head_branch": "backport-bfecff5-3.11",
+        "head_sha": "692cd77975413d71ff0951072df686e6f38711c8"
+      }
+    },
+    {
+      "id": 416712612,
+      "node_id": "MDg6QXJ0aWZhY3Q0MTY3MTI2MTI=",
+      "name": "doc-html",
+      "size_in_bytes": 61217330,
+      "url": "https://api.github.com/repos/python/cpython/actions/artifacts/416712612",
+      "archive_download_url": "https://api.github.com/repos/python/cpython/actions/artifacts/416712612/zip",
+      "expired": false,
+      "created_at": "2022-10-29T19:53:19Z",
+      "updated_at": "2022-10-29T19:53:20Z",
+      "expires_at": "2023-01-27T19:49:12Z",
+      "workflow_run": {
+        "id": 3352724493,
+        "repository_id": 81598961,
+        "head_repository_id": 559335486,
+        "head_branch": "patch-1",
+        "head_sha": "62eb88a66d1d35f7701873d8b698a2f8d7e84fa5"
+      }
+    }
+  ]
+}
diff --git a/fixtures/actions/cache-list.json b/fixtures/actions/cache-list.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/cache-list.json
@@ -0,0 +1,14 @@
+{
+  "total_count": 1,
+  "actions_caches": [
+    {
+      "id": 1,
+      "ref": "refs/heads/main",
+      "key": "cache_key",
+      "version": "f5f850afdadd47730296d4ffa900de95f6bbafb75dc1e8475df1fa6ae79dcece",
+      "last_accessed_at": "2022-10-30T00:08:14.223333300Z",
+      "created_at": "2022-10-30T00:08:14.223333300Z",
+      "size_in_bytes": 26586
+    }
+  ]
+}
diff --git a/fixtures/actions/org-cache-usage.json b/fixtures/actions/org-cache-usage.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/org-cache-usage.json
@@ -0,0 +1,4 @@
+{
+  "total_active_caches_size_in_bytes": 26586,
+  "total_active_caches_count": 1
+}
diff --git a/fixtures/actions/org-public-key.json b/fixtures/actions/org-public-key.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/org-public-key.json
@@ -0,0 +1,4 @@
+{
+  "key_id": "568250167242549743",
+  "key": "KHVvOxB765kjkShEgUu27QCzl5XxKz/L20V+KRsWf0w="
+}
diff --git a/fixtures/actions/org-secrets-list.json b/fixtures/actions/org-secrets-list.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/org-secrets-list.json
@@ -0,0 +1,18 @@
+{
+  "total_count": 2,
+  "secrets": [
+    {
+      "name": "TEST_SECRET",
+      "created_at": "2022-10-31T00:08:12Z",
+      "updated_at": "2022-10-31T00:08:12Z",
+      "visibility": "all"
+    },
+    {
+      "name": "TEST_SELECTED",
+      "created_at": "2022-10-31T00:08:43Z",
+      "updated_at": "2022-10-31T00:08:43Z",
+      "visibility": "selected",
+      "selected_repositories_url": "https://api.github.com/orgs/kote-test-org-actions/actions/secrets/TEST_SELECTED/repositories"
+    }
+  ]
+}
diff --git a/fixtures/actions/repo-cache-usage.json b/fixtures/actions/repo-cache-usage.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/repo-cache-usage.json
@@ -0,0 +1,5 @@
+{
+  "full_name": "python/cpython",
+  "active_caches_size_in_bytes": 55000268087,
+  "active_caches_count": 171
+}
diff --git a/fixtures/actions/selected-repositories-for-secret.json b/fixtures/actions/selected-repositories-for-secret.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/selected-repositories-for-secret.json
@@ -0,0 +1,72 @@
+{
+  "total_count": 1,
+  "repositories": [
+    {
+      "id": 559365297,
+      "node_id": "R_kgDOIVc8sQ",
+      "name": "actions-api",
+      "full_name": "kote-test-org-actions/actions-api",
+      "private": true,
+      "owner": {
+        "login": "kote-test-org-actions",
+        "id": 116976977,
+        "node_id": "O_kgDOBvjtUQ",
+        "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/kote-test-org-actions",
+        "html_url": "https://github.com/kote-test-org-actions",
+        "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+        "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+        "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+        "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+        "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+        "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+        "type": "Organization",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/kote-test-org-actions/actions-api",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+      "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+      "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+      "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+      "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+      "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+      "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+      "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+      "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+      "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+      "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+      "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+      "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+      "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+      "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+    }
+  ]
+}
diff --git a/fixtures/actions/workflow-job.json b/fixtures/actions/workflow-job.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/workflow-job.json
@@ -0,0 +1,113 @@
+{
+  "id": 9183275828,
+  "run_id": 3353449941,
+  "run_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941",
+  "run_attempt": 1,
+  "node_id": "CR_kwDOIVc8sc8AAAACI12rNA",
+  "head_sha": "3156f684232a3adec5085c920d2006aca80f2798",
+  "url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/jobs/9183275828",
+  "html_url": "https://github.com/kote-test-org-actions/actions-api/actions/runs/3353449941/jobs/5556228789",
+  "status": "completed",
+  "conclusion": "success",
+  "started_at": "2022-10-30T00:09:29Z",
+  "completed_at": "2022-10-30T00:09:49Z",
+  "name": "check-bats-version",
+  "steps": [
+    {
+      "name": "Set up job",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 1,
+      "started_at": "2022-10-29T17:09:29.000-07:00",
+      "completed_at": "2022-10-29T17:09:32.000-07:00"
+    },
+    {
+      "name": "Run actions/checkout@v3",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 2,
+      "started_at": "2022-10-29T17:09:32.000-07:00",
+      "completed_at": "2022-10-29T17:09:33.000-07:00"
+    },
+    {
+      "name": "Run actions/setup-node@v3",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 3,
+      "started_at": "2022-10-29T17:09:34.000-07:00",
+      "completed_at": "2022-10-29T17:09:39.000-07:00"
+    },
+    {
+      "name": "Run npm install -g bats",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 4,
+      "started_at": "2022-10-29T17:09:40.000-07:00",
+      "completed_at": "2022-10-29T17:09:42.000-07:00"
+    },
+    {
+      "name": "Run bats -v",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 5,
+      "started_at": "2022-10-29T17:09:42.000-07:00",
+      "completed_at": "2022-10-29T17:09:42.000-07:00"
+    },
+    {
+      "name": "Archive Test",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 6,
+      "started_at": "2022-10-29T17:09:42.000-07:00",
+      "completed_at": "2022-10-29T17:09:46.000-07:00"
+    },
+    {
+      "name": "Cache",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 7,
+      "started_at": "2022-10-29T17:09:46.000-07:00",
+      "completed_at": "2022-10-29T17:09:47.000-07:00"
+    },
+    {
+      "name": "Post Cache",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 12,
+      "started_at": "2022-10-29T17:09:49.000-07:00",
+      "completed_at": "2022-10-29T17:09:47.000-07:00"
+    },
+    {
+      "name": "Post Run actions/setup-node@v3",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 13,
+      "started_at": "2022-10-29T17:09:49.000-07:00",
+      "completed_at": "2022-10-29T17:09:49.000-07:00"
+    },
+    {
+      "name": "Post Run actions/checkout@v3",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 14,
+      "started_at": "2022-10-29T17:09:49.000-07:00",
+      "completed_at": "2022-10-29T17:09:49.000-07:00"
+    },
+    {
+      "name": "Complete job",
+      "status": "completed",
+      "conclusion": "success",
+      "number": 15,
+      "started_at": "2022-10-29T17:09:47.000-07:00",
+      "completed_at": "2022-10-29T17:09:47.000-07:00"
+    }
+  ],
+  "check_run_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/check-runs/9183275828",
+  "labels": [
+    "ubuntu-latest"
+  ],
+  "runner_id": 1,
+  "runner_name": "Hosted Agent",
+  "runner_group_id": 2,
+  "runner_group_name": "GitHub Actions"
+}
diff --git a/fixtures/actions/workflow-list.json b/fixtures/actions/workflow-list.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/workflow-list.json
@@ -0,0 +1,17 @@
+{
+  "total_count": 1,
+  "workflows": [
+    {
+      "id": 39065091,
+      "node_id": "W_kwDOIVc8sc4CVBYD",
+      "name": "learn-github-actions",
+      "path": ".github/workflows/make_artifact.yaml",
+      "state": "active",
+      "created_at": "2022-10-29T15:17:59.000-07:00",
+      "updated_at": "2022-10-29T15:17:59.000-07:00",
+      "url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/workflows/39065091",
+      "html_url": "https://github.com/kote-test-org-actions/actions-api/blob/main/.github/workflows/make_artifact.yaml",
+      "badge_url": "https://github.com/kote-test-org-actions/actions-api/workflows/learn-github-actions/badge.svg"
+    }
+  ]
+}
diff --git a/fixtures/actions/workflow-runs-list.json b/fixtures/actions/workflow-runs-list.json
new file mode 100644
--- /dev/null
+++ b/fixtures/actions/workflow-runs-list.json
@@ -0,0 +1,665 @@
+{
+  "total_count": 3,
+  "workflow_runs": [
+    {
+      "id": 3353449941,
+      "name": "K0Te is learning GitHub Actions",
+      "node_id": "WFR_kwLOIVc8sc7H4ZXV",
+      "head_branch": "main",
+      "head_sha": "3156f684232a3adec5085c920d2006aca80f2798",
+      "path": ".github/workflows/make_artifact.yaml",
+      "display_title": "K0Te is learning GitHub Actions",
+      "run_number": 3,
+      "event": "push",
+      "status": "completed",
+      "conclusion": "success",
+      "workflow_id": 39065091,
+      "check_suite_id": 9030268154,
+      "check_suite_node_id": "CS_kwDOIVc8sc8AAAACGj70-g",
+      "url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941",
+      "html_url": "https://github.com/kote-test-org-actions/actions-api/actions/runs/3353449941",
+      "pull_requests": [],
+      "created_at": "2022-10-30T00:09:22Z",
+      "updated_at": "2022-10-30T00:09:50Z",
+      "actor": {
+        "login": "K0Te",
+        "id": 6162155,
+        "node_id": "MDQ6VXNlcjYxNjIxNTU=",
+        "avatar_url": "https://avatars.githubusercontent.com/u/6162155?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/K0Te",
+        "html_url": "https://github.com/K0Te",
+        "followers_url": "https://api.github.com/users/K0Te/followers",
+        "following_url": "https://api.github.com/users/K0Te/following{/other_user}",
+        "gists_url": "https://api.github.com/users/K0Te/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/K0Te/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/K0Te/subscriptions",
+        "organizations_url": "https://api.github.com/users/K0Te/orgs",
+        "repos_url": "https://api.github.com/users/K0Te/repos",
+        "events_url": "https://api.github.com/users/K0Te/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/K0Te/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "run_attempt": 1,
+      "referenced_workflows": [],
+      "run_started_at": "2022-10-30T00:09:22Z",
+      "triggering_actor": {
+        "login": "K0Te",
+        "id": 6162155,
+        "node_id": "MDQ6VXNlcjYxNjIxNTU=",
+        "avatar_url": "https://avatars.githubusercontent.com/u/6162155?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/K0Te",
+        "html_url": "https://github.com/K0Te",
+        "followers_url": "https://api.github.com/users/K0Te/followers",
+        "following_url": "https://api.github.com/users/K0Te/following{/other_user}",
+        "gists_url": "https://api.github.com/users/K0Te/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/K0Te/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/K0Te/subscriptions",
+        "organizations_url": "https://api.github.com/users/K0Te/orgs",
+        "repos_url": "https://api.github.com/users/K0Te/repos",
+        "events_url": "https://api.github.com/users/K0Te/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/K0Te/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "jobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941/jobs",
+      "logs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941/logs",
+      "check_suite_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/check-suites/9030268154",
+      "artifacts_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941/artifacts",
+      "cancel_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941/cancel",
+      "rerun_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353449941/rerun",
+      "previous_attempt_url": null,
+      "workflow_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/workflows/39065091",
+      "head_commit": {
+        "id": "3156f684232a3adec5085c920d2006aca80f2798",
+        "tree_id": "f51ba8632086ca7af92f5e58c1dc98df1c62d7ce",
+        "message": "up",
+        "timestamp": "2022-10-30T00:09:16Z",
+        "author": {
+          "name": "Oleg Nykolyn",
+          "email": "juravel2@gmail.com"
+        },
+        "committer": {
+          "name": "Oleg Nykolyn",
+          "email": "juravel2@gmail.com"
+        }
+      },
+      "repository": {
+        "id": 559365297,
+        "node_id": "R_kgDOIVc8sQ",
+        "name": "actions-api",
+        "full_name": "kote-test-org-actions/actions-api",
+        "private": true,
+        "owner": {
+          "login": "kote-test-org-actions",
+          "id": 116976977,
+          "node_id": "O_kgDOBvjtUQ",
+          "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/kote-test-org-actions",
+          "html_url": "https://github.com/kote-test-org-actions",
+          "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+          "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+          "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+          "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+          "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+          "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/kote-test-org-actions/actions-api",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+        "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+        "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+        "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+        "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+        "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+        "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+        "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+        "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+        "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+        "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+        "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+        "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+        "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+        "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+      },
+      "head_repository": {
+        "id": 559365297,
+        "node_id": "R_kgDOIVc8sQ",
+        "name": "actions-api",
+        "full_name": "kote-test-org-actions/actions-api",
+        "private": true,
+        "owner": {
+          "login": "kote-test-org-actions",
+          "id": 116976977,
+          "node_id": "O_kgDOBvjtUQ",
+          "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/kote-test-org-actions",
+          "html_url": "https://github.com/kote-test-org-actions",
+          "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+          "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+          "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+          "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+          "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+          "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/kote-test-org-actions/actions-api",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+        "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+        "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+        "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+        "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+        "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+        "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+        "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+        "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+        "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+        "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+        "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+        "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+        "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+        "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+      }
+    },
+    {
+      "id": 3353445625,
+      "name": "K0Te is learning GitHub Actions",
+      "node_id": "WFR_kwLOIVc8sc7H4YT5",
+      "head_branch": "main",
+      "head_sha": "2d2486b9aecb80bf916717f47f7c312431d3ceb6",
+      "path": ".github/workflows/make_artifact.yaml",
+      "display_title": "K0Te is learning GitHub Actions",
+      "run_number": 2,
+      "event": "push",
+      "status": "completed",
+      "conclusion": "success",
+      "workflow_id": 39065091,
+      "check_suite_id": 9030259685,
+      "check_suite_node_id": "CS_kwDOIVc8sc8AAAACGj7T5Q",
+      "url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353445625",
+      "html_url": "https://github.com/kote-test-org-actions/actions-api/actions/runs/3353445625",
+      "pull_requests": [],
+      "created_at": "2022-10-30T00:07:49Z",
+      "updated_at": "2022-10-30T00:08:19Z",
+      "actor": {
+        "login": "K0Te",
+        "id": 6162155,
+        "node_id": "MDQ6VXNlcjYxNjIxNTU=",
+        "avatar_url": "https://avatars.githubusercontent.com/u/6162155?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/K0Te",
+        "html_url": "https://github.com/K0Te",
+        "followers_url": "https://api.github.com/users/K0Te/followers",
+        "following_url": "https://api.github.com/users/K0Te/following{/other_user}",
+        "gists_url": "https://api.github.com/users/K0Te/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/K0Te/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/K0Te/subscriptions",
+        "organizations_url": "https://api.github.com/users/K0Te/orgs",
+        "repos_url": "https://api.github.com/users/K0Te/repos",
+        "events_url": "https://api.github.com/users/K0Te/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/K0Te/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "run_attempt": 1,
+      "referenced_workflows": [],
+      "run_started_at": "2022-10-30T00:07:49Z",
+      "triggering_actor": {
+        "login": "K0Te",
+        "id": 6162155,
+        "node_id": "MDQ6VXNlcjYxNjIxNTU=",
+        "avatar_url": "https://avatars.githubusercontent.com/u/6162155?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/K0Te",
+        "html_url": "https://github.com/K0Te",
+        "followers_url": "https://api.github.com/users/K0Te/followers",
+        "following_url": "https://api.github.com/users/K0Te/following{/other_user}",
+        "gists_url": "https://api.github.com/users/K0Te/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/K0Te/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/K0Te/subscriptions",
+        "organizations_url": "https://api.github.com/users/K0Te/orgs",
+        "repos_url": "https://api.github.com/users/K0Te/repos",
+        "events_url": "https://api.github.com/users/K0Te/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/K0Te/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "jobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353445625/jobs",
+      "logs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353445625/logs",
+      "check_suite_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/check-suites/9030259685",
+      "artifacts_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353445625/artifacts",
+      "cancel_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353445625/cancel",
+      "rerun_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353445625/rerun",
+      "previous_attempt_url": null,
+      "workflow_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/workflows/39065091",
+      "head_commit": {
+        "id": "2d2486b9aecb80bf916717f47f7c312431d3ceb6",
+        "tree_id": "21d858674ab650ea734b7efbf05442a21685d121",
+        "message": "up",
+        "timestamp": "2022-10-30T00:07:44Z",
+        "author": {
+          "name": "Oleg Nykolyn",
+          "email": "juravel2@gmail.com"
+        },
+        "committer": {
+          "name": "Oleg Nykolyn",
+          "email": "juravel2@gmail.com"
+        }
+      },
+      "repository": {
+        "id": 559365297,
+        "node_id": "R_kgDOIVc8sQ",
+        "name": "actions-api",
+        "full_name": "kote-test-org-actions/actions-api",
+        "private": true,
+        "owner": {
+          "login": "kote-test-org-actions",
+          "id": 116976977,
+          "node_id": "O_kgDOBvjtUQ",
+          "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/kote-test-org-actions",
+          "html_url": "https://github.com/kote-test-org-actions",
+          "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+          "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+          "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+          "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+          "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+          "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/kote-test-org-actions/actions-api",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+        "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+        "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+        "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+        "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+        "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+        "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+        "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+        "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+        "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+        "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+        "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+        "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+        "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+        "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+      },
+      "head_repository": {
+        "id": 559365297,
+        "node_id": "R_kgDOIVc8sQ",
+        "name": "actions-api",
+        "full_name": "kote-test-org-actions/actions-api",
+        "private": true,
+        "owner": {
+          "login": "kote-test-org-actions",
+          "id": 116976977,
+          "node_id": "O_kgDOBvjtUQ",
+          "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/kote-test-org-actions",
+          "html_url": "https://github.com/kote-test-org-actions",
+          "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+          "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+          "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+          "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+          "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+          "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/kote-test-org-actions/actions-api",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+        "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+        "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+        "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+        "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+        "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+        "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+        "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+        "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+        "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+        "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+        "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+        "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+        "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+        "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+      }
+    },
+    {
+      "id": 3353148947,
+      "name": "K0Te is learning GitHub Actions",
+      "node_id": "WFR_kwLOIVc8sc7H3P4T",
+      "head_branch": "main",
+      "head_sha": "601593ecb1d8a57a04700fdb445a28d4186b8954",
+      "path": ".github/workflows/make_artifact.yaml",
+      "display_title": "K0Te is learning GitHub Actions",
+      "run_number": 1,
+      "event": "push",
+      "status": "completed",
+      "conclusion": "success",
+      "workflow_id": 39065091,
+      "check_suite_id": 9029740591,
+      "check_suite_node_id": "CS_kwDOIVc8sc8AAAACGjboLw",
+      "url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353148947",
+      "html_url": "https://github.com/kote-test-org-actions/actions-api/actions/runs/3353148947",
+      "pull_requests": [],
+      "created_at": "2022-10-29T22:18:02Z",
+      "updated_at": "2022-10-29T22:18:22Z",
+      "actor": {
+        "login": "K0Te",
+        "id": 6162155,
+        "node_id": "MDQ6VXNlcjYxNjIxNTU=",
+        "avatar_url": "https://avatars.githubusercontent.com/u/6162155?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/K0Te",
+        "html_url": "https://github.com/K0Te",
+        "followers_url": "https://api.github.com/users/K0Te/followers",
+        "following_url": "https://api.github.com/users/K0Te/following{/other_user}",
+        "gists_url": "https://api.github.com/users/K0Te/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/K0Te/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/K0Te/subscriptions",
+        "organizations_url": "https://api.github.com/users/K0Te/orgs",
+        "repos_url": "https://api.github.com/users/K0Te/repos",
+        "events_url": "https://api.github.com/users/K0Te/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/K0Te/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "run_attempt": 1,
+      "referenced_workflows": [],
+      "run_started_at": "2022-10-29T22:18:02Z",
+      "triggering_actor": {
+        "login": "K0Te",
+        "id": 6162155,
+        "node_id": "MDQ6VXNlcjYxNjIxNTU=",
+        "avatar_url": "https://avatars.githubusercontent.com/u/6162155?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/K0Te",
+        "html_url": "https://github.com/K0Te",
+        "followers_url": "https://api.github.com/users/K0Te/followers",
+        "following_url": "https://api.github.com/users/K0Te/following{/other_user}",
+        "gists_url": "https://api.github.com/users/K0Te/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/K0Te/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/K0Te/subscriptions",
+        "organizations_url": "https://api.github.com/users/K0Te/orgs",
+        "repos_url": "https://api.github.com/users/K0Te/repos",
+        "events_url": "https://api.github.com/users/K0Te/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/K0Te/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "jobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353148947/jobs",
+      "logs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353148947/logs",
+      "check_suite_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/check-suites/9029740591",
+      "artifacts_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353148947/artifacts",
+      "cancel_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353148947/cancel",
+      "rerun_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/runs/3353148947/rerun",
+      "previous_attempt_url": null,
+      "workflow_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/actions/workflows/39065091",
+      "head_commit": {
+        "id": "601593ecb1d8a57a04700fdb445a28d4186b8954",
+        "tree_id": "7aa2d4e6f4e0ddb277fe2f35f7615651ee01c5a2",
+        "message": "test",
+        "timestamp": "2022-10-29T22:17:55Z",
+        "author": {
+          "name": "Oleg Nykolyn",
+          "email": "juravel2@gmail.com"
+        },
+        "committer": {
+          "name": "Oleg Nykolyn",
+          "email": "juravel2@gmail.com"
+        }
+      },
+      "repository": {
+        "id": 559365297,
+        "node_id": "R_kgDOIVc8sQ",
+        "name": "actions-api",
+        "full_name": "kote-test-org-actions/actions-api",
+        "private": true,
+        "owner": {
+          "login": "kote-test-org-actions",
+          "id": 116976977,
+          "node_id": "O_kgDOBvjtUQ",
+          "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/kote-test-org-actions",
+          "html_url": "https://github.com/kote-test-org-actions",
+          "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+          "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+          "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+          "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+          "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+          "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/kote-test-org-actions/actions-api",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+        "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+        "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+        "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+        "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+        "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+        "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+        "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+        "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+        "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+        "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+        "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+        "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+        "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+        "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+      },
+      "head_repository": {
+        "id": 559365297,
+        "node_id": "R_kgDOIVc8sQ",
+        "name": "actions-api",
+        "full_name": "kote-test-org-actions/actions-api",
+        "private": true,
+        "owner": {
+          "login": "kote-test-org-actions",
+          "id": 116976977,
+          "node_id": "O_kgDOBvjtUQ",
+          "avatar_url": "https://avatars.githubusercontent.com/u/116976977?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/kote-test-org-actions",
+          "html_url": "https://github.com/kote-test-org-actions",
+          "followers_url": "https://api.github.com/users/kote-test-org-actions/followers",
+          "following_url": "https://api.github.com/users/kote-test-org-actions/following{/other_user}",
+          "gists_url": "https://api.github.com/users/kote-test-org-actions/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/kote-test-org-actions/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/kote-test-org-actions/subscriptions",
+          "organizations_url": "https://api.github.com/users/kote-test-org-actions/orgs",
+          "repos_url": "https://api.github.com/users/kote-test-org-actions/repos",
+          "events_url": "https://api.github.com/users/kote-test-org-actions/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/kote-test-org-actions/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/kote-test-org-actions/actions-api",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/kote-test-org-actions/actions-api",
+        "forks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/forks",
+        "keys_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/teams",
+        "hooks_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/hooks",
+        "issue_events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/events",
+        "assignees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/tags",
+        "blobs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/languages",
+        "stargazers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/stargazers",
+        "contributors_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contributors",
+        "subscribers_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscribers",
+        "subscription_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/subscription",
+        "commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/merges",
+        "archive_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/downloads",
+        "issues_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/labels{/name}",
+        "releases_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/kote-test-org-actions/actions-api/deployments"
+      }
+    }
+  ]
+}
diff --git a/fixtures/issue-search.json b/fixtures/issue-search.json
new file mode 100644
--- /dev/null
+++ b/fixtures/issue-search.json
@@ -0,0 +1,98 @@
+{
+  "total_count": 2,
+  "incomplete_results": false,
+  "items": [
+    {
+      "url": "https://api.github.com/repos/phadej/github/issues/130",
+      "labels_url": "https://api.github.com/repos/phadej/github/issues/130/labels{/name}",
+      "comments_url": "https://api.github.com/repos/phadej/github/issues/130/comments",
+      "events_url": "https://api.github.com/repos/phadej/github/issues/130/events",
+      "html_url": "https://github.com/phadej/github/pull/130",
+      "id": 123898390,
+      "number": 130,
+      "title": "Make test runner more robust",
+      "user": {
+        "login": "phadej",
+        "id": 51087,
+        "avatar_url": "https://avatars.githubusercontent.com/u/51087?v=3",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/phadej",
+        "html_url": "https://github.com/phadej",
+        "followers_url": "https://api.github.com/users/phadej/followers",
+        "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+        "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+        "organizations_url": "https://api.github.com/users/phadej/orgs",
+        "repos_url": "https://api.github.com/users/phadej/repos",
+        "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/phadej/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "labels": [
+
+      ],
+      "state": "closed",
+      "locked": false,
+      "assignee": null,
+      "assignees": [],
+      "milestone": null,
+      "comments": 0,
+      "created_at": "2015-12-25T21:37:39Z",
+      "updated_at": "2015-12-26T08:57:52Z",
+      "closed_at": "2015-12-25T23:32:12Z",
+      "pull_request": {
+        "url": "https://api.github.com/repos/phadej/github/pulls/130",
+        "html_url": "https://github.com/phadej/github/pull/130",
+        "diff_url": "https://github.com/phadej/github/pull/130.diff",
+        "patch_url": "https://github.com/phadej/github/pull/130.patch"
+      },
+      "body": "As they use access token, it's highly unlikely it will be rate limited. ATM there's only one request per test job. i.e. travis could be re-enabled.\r\n\r\nExample run https://travis-ci.org/phadej/github/builds/98815089\r\nSome tests are pending as secret is made for this `jwiegley/github` repository.",
+      "score": 0.75566536
+    },
+    {
+      "url": "https://api.github.com/repos/phadej/github/issues/127",
+      "labels_url": "https://api.github.com/repos/phadej/github/issues/127/labels{/name}",
+      "comments_url": "https://api.github.com/repos/phadej/github/issues/127/comments",
+      "events_url": "https://api.github.com/repos/phadej/github/issues/127/events",
+      "html_url": "https://github.com/phadej/github/issues/127",
+      "id": 119694665,
+      "number": 127,
+      "title": "Decouple request creation from execution",
+      "user": {
+        "login": "phadej",
+        "id": 51087,
+        "avatar_url": "https://avatars.githubusercontent.com/u/51087?v=3",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/phadej",
+        "html_url": "https://github.com/phadej",
+        "followers_url": "https://api.github.com/users/phadej/followers",
+        "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+        "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+        "organizations_url": "https://api.github.com/users/phadej/orgs",
+        "repos_url": "https://api.github.com/users/phadej/repos",
+        "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/phadej/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "labels": [
+
+      ],
+      "state": "open",
+      "locked": false,
+      "assignee": null,
+      "assignees": [],
+      "milestone": null,
+      "comments": 2,
+      "created_at": "2015-12-01T11:09:03Z",
+      "updated_at": "2015-12-25T19:15:33Z",
+      "closed_at": null,
+      "body": "After working with this API, and making few others, I found that separating request creation and execution is better (i.e. more flexible) design.\r\n\r\nNow one cannot use different network client or add new endpoints.\r\n\r\nShorly\r\n\r\n```hs\r\n-- New stuff:\r\ndata GithubRequest a = GithubRequestGet Url\r\n                     | ...\r\n\r\n-- or alternatively\r\ndata GithubRequest a where\r\n  GithubRequestGet :: Url -> GithubRequest a\r\n  GithubRequestMultiGet :: Url -> GithubRequest [a]\r\n\r\nexecGithubRequest :: FromJSON a => GithubRequest a -> IO (Either Error a)\r\nexecGithubRequest' :: FromJSON a => Maybe GithubAuth -> GithubRequest a -> IO (Either Error a)\r\n\r\npublicOrganizationForRequest :: String -> GithubRequest [SimpleOrganisation]\r\npublicOrganizationForRequest org = GithubRequestGet ...\r\n\r\n-- Old IO methods become:\r\npublicOrganizationsFor :: String -> IO (Either Error [SimpleOrganization])\r\npublicOrganizationsFor = execGithubRequest . publicOrganizationForRequest\r\n\r\npublicOrganizationsFor' :: Maybe GithubAuth -> String -> IO (Either Error [SimpleOrganization])\r\npublicOrganizationsFor' auth = execGithubRequest' auth . publicOrganizationForRequest\r\n```\r\n\r\nHow does this sound? I can make a refactoring, it's quite straight-forward.",
+      "score": 0.7265285
+    }
+  ]
+}
diff --git a/fixtures/list-teams.json b/fixtures/list-teams.json
new file mode 100644
--- /dev/null
+++ b/fixtures/list-teams.json
@@ -0,0 +1,13 @@
+[
+  {
+    "id": 1,
+    "url": "https://api.github.com/teams/1",
+    "name": "Justice League",
+    "slug": "justice-league",
+    "description": "A great team.",
+    "privacy": "closed",
+    "permission": "admin",
+    "members_url": "https://api.github.com/teams/1/members{/member}",
+    "repositories_url": "https://api.github.com/teams/1/repos"
+  }
+]
diff --git a/fixtures/members-list.json b/fixtures/members-list.json
new file mode 100644
--- /dev/null
+++ b/fixtures/members-list.json
@@ -0,0 +1,21 @@
+[
+  {
+    "login": "octocat",
+    "id": 1,
+    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/octocat",
+    "html_url": "https://github.com/octocat",
+    "followers_url": "https://api.github.com/users/octocat/followers",
+    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+    "organizations_url": "https://api.github.com/users/octocat/orgs",
+    "repos_url": "https://api.github.com/users/octocat/repos",
+    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/octocat/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+]
diff --git a/fixtures/pull-request-approved-review.json b/fixtures/pull-request-approved-review.json
new file mode 100644
--- /dev/null
+++ b/fixtures/pull-request-approved-review.json
@@ -0,0 +1,38 @@
+{
+  "id": 80,
+  "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=",
+  "user": {
+    "login": "octocat",
+    "id": 1,
+    "node_id": "MDQ6VXNlcjE=",
+    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/octocat",
+    "html_url": "https://github.com/octocat",
+    "followers_url": "https://api.github.com/users/octocat/followers",
+    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+    "organizations_url": "https://api.github.com/users/octocat/orgs",
+    "repos_url": "https://api.github.com/users/octocat/repos",
+    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/octocat/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "body": "Here is the body for the review.",
+  "state": "APPROVED",
+  "html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
+  "pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
+  "_links": {
+    "html": {
+      "href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
+    },
+    "pull_request": {
+      "href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
+    }
+  },
+  "submitted_at": "2019-11-17T17:43:43Z",
+  "commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091"
+}
diff --git a/fixtures/pull-request-opened.json b/fixtures/pull-request-opened.json
new file mode 100644
--- /dev/null
+++ b/fixtures/pull-request-opened.json
@@ -0,0 +1,311 @@
+{
+  "url": "https://api.github.com/repos/phadej/github/pulls/9",
+  "id": 144079630,
+  "html_url": "https://github.com/phadej/github/pull/9",
+  "diff_url": "https://github.com/phadej/github/pull/9.diff",
+  "patch_url": "https://github.com/phadej/github/pull/9.patch",
+  "issue_url": "https://api.github.com/repos/phadej/github/issues/9",
+  "number": 9,
+  "state": "open",
+  "locked": false,
+  "title": "Fetch my pull requests",
+  "user": {
+    "login": "phadej",
+    "id": 123898390,
+    "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/phadej",
+    "html_url": "https://github.com/phadej",
+    "followers_url": "https://api.github.com/users/phadej/followers",
+    "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+    "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+    "organizations_url": "https://api.github.com/users/phadej/orgs",
+    "repos_url": "https://api.github.com/users/phadej/repos",
+    "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/phadej/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "body": "",
+  "created_at": "2017-10-01T17:22:12Z",
+  "updated_at": "2017-10-01T17:22:12Z",
+  "closed_at": null,
+  "merged_at": null,
+  "merge_commit_sha": null,
+  "assignee": null,
+  "assignees": [
+  ],
+  "milestone": null,
+  "commits_url": "https://api.github.com/repos/phadej/github/pulls/9/commits",
+  "review_comments_url": "https://api.github.com/repos/phadej/github/pulls/9/comments",
+  "review_comment_url": "https://api.github.com/repos/phadej/github/pulls/comments{/number}",
+  "comments_url": "https://api.github.com/repos/phadej/github/issues/9/comments",
+  "statuses_url": "https://api.github.com/repos/phadej/github/statuses/20218048bb9529de09f1fdaa9126f60ffeb07ce5",
+  "head": {
+    "label": "phadej:fetch-my-pull-requests",
+    "ref": "fetch-my-pull-requests",
+    "sha": "20218048bb9529de09f1fdaa9126f60ffeb07ce5",
+    "user": {
+      "login": "phadej",
+      "id": 123898390,
+      "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/phadej",
+      "html_url": "https://github.com/phadej",
+      "followers_url": "https://api.github.com/users/phadej/followers",
+      "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+      "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+      "organizations_url": "https://api.github.com/users/phadej/orgs",
+      "repos_url": "https://api.github.com/users/phadej/repos",
+      "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/phadej/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "repo": {
+      "id": 102602684,
+      "name": "github",
+      "full_name": "phadej/github",
+      "owner": {
+        "login": "phadej",
+        "id": 123898390,
+        "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/phadej",
+        "html_url": "https://github.com/phadej",
+        "followers_url": "https://api.github.com/users/phadej/followers",
+        "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+        "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+        "organizations_url": "https://api.github.com/users/phadej/orgs",
+        "repos_url": "https://api.github.com/users/phadej/repos",
+        "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/phadej/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "private": true,
+      "html_url": "https://github.com/phadej/github",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/phadej/github",
+      "forks_url": "https://api.github.com/repos/phadej/github/forks",
+      "keys_url": "https://api.github.com/repos/phadej/github/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/phadej/github/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/phadej/github/teams",
+      "hooks_url": "https://api.github.com/repos/phadej/github/hooks",
+      "issue_events_url": "https://api.github.com/repos/phadej/github/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/phadej/github/events",
+      "assignees_url": "https://api.github.com/repos/phadej/github/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/phadej/github/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/phadej/github/tags",
+      "blobs_url": "https://api.github.com/repos/phadej/github/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/phadej/github/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/phadej/github/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/phadej/github/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/phadej/github/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/phadej/github/languages",
+      "stargazers_url": "https://api.github.com/repos/phadej/github/stargazers",
+      "contributors_url": "https://api.github.com/repos/phadej/github/contributors",
+      "subscribers_url": "https://api.github.com/repos/phadej/github/subscribers",
+      "subscription_url": "https://api.github.com/repos/phadej/github/subscription",
+      "commits_url": "https://api.github.com/repos/phadej/github/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/phadej/github/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/phadej/github/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/phadej/github/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/phadej/github/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/phadej/github/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/phadej/github/merges",
+      "archive_url": "https://api.github.com/repos/phadej/github/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/phadej/github/downloads",
+      "issues_url": "https://api.github.com/repos/phadej/github/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/phadej/github/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/phadej/github/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/phadej/github/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/phadej/github/labels{/name}",
+      "releases_url": "https://api.github.com/repos/phadej/github/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/phadej/github/deployments",
+      "created_at": "2017-09-06T11:54:37Z",
+      "updated_at": "2017-09-06T11:55:42Z",
+      "pushed_at": "2017-10-01T16:58:54Z",
+      "git_url": "git://github.com/phadej/github.git",
+      "ssh_url": "git@github.com:phadej/github.git",
+      "clone_url": "https://github.com/phadej/github.git",
+      "svn_url": "https://github.com/phadej/github",
+      "homepage": null,
+      "size": 335,
+      "stargazers_count": 0,
+      "watchers_count": 0,
+      "language": "Haskell",
+      "has_issues": true,
+      "has_projects": true,
+      "has_downloads": true,
+      "has_wiki": true,
+      "has_pages": false,
+      "forks_count": 0,
+      "mirror_url": null,
+      "open_issues_count": 1,
+      "forks": 0,
+      "open_issues": 1,
+      "watchers": 0,
+      "default_branch": "master"
+    }
+  },
+  "base": {
+    "label": "phadej:master",
+    "ref": "master",
+    "sha": "cb686149c0d88af16de61488a1ba70a6c71a2b65",
+    "user": {
+      "login": "phadej",
+      "id": 123898390,
+      "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/phadej",
+      "html_url": "https://github.com/phadej",
+      "followers_url": "https://api.github.com/users/phadej/followers",
+      "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+      "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+      "organizations_url": "https://api.github.com/users/phadej/orgs",
+      "repos_url": "https://api.github.com/users/phadej/repos",
+      "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/phadej/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "repo": {
+      "id": 102602684,
+      "name": "github",
+      "full_name": "phadej/github",
+      "owner": {
+        "login": "phadej",
+        "id": 123898390,
+        "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/phadej",
+        "html_url": "https://github.com/phadej",
+        "followers_url": "https://api.github.com/users/phadej/followers",
+        "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+        "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+        "organizations_url": "https://api.github.com/users/phadej/orgs",
+        "repos_url": "https://api.github.com/users/phadej/repos",
+        "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/phadej/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "private": true,
+      "html_url": "https://github.com/phadej/github",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/phadej/github",
+      "forks_url": "https://api.github.com/repos/phadej/github/forks",
+      "keys_url": "https://api.github.com/repos/phadej/github/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/phadej/github/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/phadej/github/teams",
+      "hooks_url": "https://api.github.com/repos/phadej/github/hooks",
+      "issue_events_url": "https://api.github.com/repos/phadej/github/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/phadej/github/events",
+      "assignees_url": "https://api.github.com/repos/phadej/github/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/phadej/github/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/phadej/github/tags",
+      "blobs_url": "https://api.github.com/repos/phadej/github/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/phadej/github/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/phadej/github/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/phadej/github/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/phadej/github/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/phadej/github/languages",
+      "stargazers_url": "https://api.github.com/repos/phadej/github/stargazers",
+      "contributors_url": "https://api.github.com/repos/phadej/github/contributors",
+      "subscribers_url": "https://api.github.com/repos/phadej/github/subscribers",
+      "subscription_url": "https://api.github.com/repos/phadej/github/subscription",
+      "commits_url": "https://api.github.com/repos/phadej/github/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/phadej/github/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/phadej/github/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/phadej/github/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/phadej/github/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/phadej/github/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/phadej/github/merges",
+      "archive_url": "https://api.github.com/repos/phadej/github/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/phadej/github/downloads",
+      "issues_url": "https://api.github.com/repos/phadej/github/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/phadej/github/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/phadej/github/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/phadej/github/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/phadej/github/labels{/name}",
+      "releases_url": "https://api.github.com/repos/phadej/github/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/phadej/github/deployments",
+      "created_at": "2017-09-06T11:54:37Z",
+      "updated_at": "2017-09-06T11:55:42Z",
+      "pushed_at": "2017-10-01T16:58:54Z",
+      "git_url": "git://github.com/phadej/github.git",
+      "ssh_url": "git@github.com:phadej/github.git",
+      "clone_url": "https://github.com/phadej/github.git",
+      "svn_url": "https://github.com/phadej/github",
+      "homepage": null,
+      "size": 335,
+      "stargazers_count": 0,
+      "watchers_count": 0,
+      "language": "Haskell",
+      "has_issues": true,
+      "has_projects": true,
+      "has_downloads": true,
+      "has_wiki": true,
+      "has_pages": false,
+      "forks_count": 0,
+      "mirror_url": null,
+      "open_issues_count": 1,
+      "forks": 0,
+      "open_issues": 1,
+      "watchers": 0,
+      "default_branch": "master"
+    }
+  },
+  "_links": {
+    "self": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/9"
+    },
+    "html": {
+      "href": "https://github.com/phadej/github/pull/9"
+    },
+    "issue": {
+      "href": "https://api.github.com/repos/phadej/github/issues/9"
+    },
+    "comments": {
+      "href": "https://api.github.com/repos/phadej/github/issues/9/comments"
+    },
+    "review_comments": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/9/comments"
+    },
+    "review_comment": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/comments{/number}"
+    },
+    "commits": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/9/commits"
+    },
+    "statuses": {
+      "href": "https://api.github.com/repos/phadej/github/statuses/20218048bb9529de09f1fdaa9126f60ffeb07ce5"
+    }
+  },
+  "author_association": "OWNER",
+  "merged": false,
+  "mergeable": null,
+  "rebaseable": null,
+  "mergeable_state": "unknown",
+  "merged_by": null,
+  "comments": 0,
+  "review_comments": 0,
+  "maintainer_can_modify": false,
+  "commits": 6,
+  "additions": 363,
+  "deletions": 48,
+  "changed_files": 19
+}
diff --git a/fixtures/pull-request-pending-review.json b/fixtures/pull-request-pending-review.json
new file mode 100644
--- /dev/null
+++ b/fixtures/pull-request-pending-review.json
@@ -0,0 +1,37 @@
+{
+  "id": 80,
+  "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=",
+  "user": {
+    "login": "octocat",
+    "id": 1,
+    "node_id": "MDQ6VXNlcjE=",
+    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/octocat",
+    "html_url": "https://github.com/octocat",
+    "followers_url": "https://api.github.com/users/octocat/followers",
+    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+    "organizations_url": "https://api.github.com/users/octocat/orgs",
+    "repos_url": "https://api.github.com/users/octocat/repos",
+    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/octocat/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "body": "Here is the body for the review.",
+  "state": "PENDING",
+  "html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
+  "pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
+  "_links": {
+    "html": {
+      "href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
+    },
+    "pull_request": {
+      "href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
+    }
+  },
+  "commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091"
+}
diff --git a/fixtures/pull-request-review-requested.json b/fixtures/pull-request-review-requested.json
new file mode 100644
--- /dev/null
+++ b/fixtures/pull-request-review-requested.json
@@ -0,0 +1,351 @@
+{
+  "url": "https://api.github.com/repos/phadej/github/pulls/9",
+  "id": 144079630,
+  "html_url": "https://github.com/phadej/github/pull/9",
+  "diff_url": "https://github.com/phadej/github/pull/9.diff",
+  "patch_url": "https://github.com/phadej/github/pull/9.patch",
+  "issue_url": "https://api.github.com/repos/phadej/github/issues/9",
+  "number": 9,
+  "state": "open",
+  "locked": false,
+  "title": "Fetch my pull requests",
+  "user": {
+    "login": "phadej",
+    "id": 123898390,
+    "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/phadej",
+    "html_url": "https://github.com/phadej",
+    "followers_url": "https://api.github.com/users/phadej/followers",
+    "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+    "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+    "organizations_url": "https://api.github.com/users/phadej/orgs",
+    "repos_url": "https://api.github.com/users/phadej/repos",
+    "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/phadej/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "body": "",
+  "created_at": "2017-10-01T17:22:12Z",
+  "updated_at": "2017-10-01T17:22:12Z",
+  "closed_at": null,
+  "merged_at": null,
+  "merge_commit_sha": null,
+  "assignee": null,
+  "assignees": [
+    {
+      "login": "phadej",
+      "id": 123898390,
+      "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/phadej",
+      "html_url": "https://github.com/phadej",
+      "followers_url": "https://api.github.com/users/phadej/followers",
+      "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+      "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+      "organizations_url": "https://api.github.com/users/phadej/orgs",
+      "repos_url": "https://api.github.com/users/phadej/repos",
+      "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/phadej/received_events",
+      "type": "User",
+      "site_admin": false
+    }
+  ],
+  "requested_reviewers": [
+    {
+      "login": "phadej",
+      "id": 123898390,
+      "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/phadej",
+      "html_url": "https://github.com/phadej",
+      "followers_url": "https://api.github.com/users/phadej/followers",
+      "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+      "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+      "organizations_url": "https://api.github.com/users/phadej/orgs",
+      "repos_url": "https://api.github.com/users/phadej/repos",
+      "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/phadej/received_events",
+      "type": "User",
+      "site_admin": false
+    }
+  ],
+  "milestone": null,
+  "commits_url": "https://api.github.com/repos/phadej/github/pulls/9/commits",
+  "review_comments_url": "https://api.github.com/repos/phadej/github/pulls/9/comments",
+  "review_comment_url": "https://api.github.com/repos/phadej/github/pulls/comments{/number}",
+  "comments_url": "https://api.github.com/repos/phadej/github/issues/9/comments",
+  "statuses_url": "https://api.github.com/repos/phadej/github/statuses/20218048bb9529de09f1fdaa9126f60ffeb07ce5",
+  "head": {
+    "label": "phadej:fetch-my-pull-requests",
+    "ref": "fetch-my-pull-requests",
+    "sha": "20218048bb9529de09f1fdaa9126f60ffeb07ce5",
+    "user": {
+      "login": "phadej",
+      "id": 123898390,
+      "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/phadej",
+      "html_url": "https://github.com/phadej",
+      "followers_url": "https://api.github.com/users/phadej/followers",
+      "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+      "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+      "organizations_url": "https://api.github.com/users/phadej/orgs",
+      "repos_url": "https://api.github.com/users/phadej/repos",
+      "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/phadej/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "repo": {
+      "id": 102602684,
+      "name": "github",
+      "full_name": "phadej/github",
+      "owner": {
+        "login": "phadej",
+        "id": 123898390,
+        "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/phadej",
+        "html_url": "https://github.com/phadej",
+        "followers_url": "https://api.github.com/users/phadej/followers",
+        "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+        "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+        "organizations_url": "https://api.github.com/users/phadej/orgs",
+        "repos_url": "https://api.github.com/users/phadej/repos",
+        "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/phadej/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "private": true,
+      "html_url": "https://github.com/phadej/github",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/phadej/github",
+      "forks_url": "https://api.github.com/repos/phadej/github/forks",
+      "keys_url": "https://api.github.com/repos/phadej/github/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/phadej/github/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/phadej/github/teams",
+      "hooks_url": "https://api.github.com/repos/phadej/github/hooks",
+      "issue_events_url": "https://api.github.com/repos/phadej/github/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/phadej/github/events",
+      "assignees_url": "https://api.github.com/repos/phadej/github/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/phadej/github/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/phadej/github/tags",
+      "blobs_url": "https://api.github.com/repos/phadej/github/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/phadej/github/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/phadej/github/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/phadej/github/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/phadej/github/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/phadej/github/languages",
+      "stargazers_url": "https://api.github.com/repos/phadej/github/stargazers",
+      "contributors_url": "https://api.github.com/repos/phadej/github/contributors",
+      "subscribers_url": "https://api.github.com/repos/phadej/github/subscribers",
+      "subscription_url": "https://api.github.com/repos/phadej/github/subscription",
+      "commits_url": "https://api.github.com/repos/phadej/github/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/phadej/github/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/phadej/github/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/phadej/github/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/phadej/github/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/phadej/github/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/phadej/github/merges",
+      "archive_url": "https://api.github.com/repos/phadej/github/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/phadej/github/downloads",
+      "issues_url": "https://api.github.com/repos/phadej/github/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/phadej/github/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/phadej/github/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/phadej/github/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/phadej/github/labels{/name}",
+      "releases_url": "https://api.github.com/repos/phadej/github/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/phadej/github/deployments",
+      "created_at": "2017-09-06T11:54:37Z",
+      "updated_at": "2017-09-06T11:55:42Z",
+      "pushed_at": "2017-10-01T16:58:54Z",
+      "git_url": "git://github.com/phadej/github.git",
+      "ssh_url": "git@github.com:phadej/github.git",
+      "clone_url": "https://github.com/phadej/github.git",
+      "svn_url": "https://github.com/phadej/github",
+      "homepage": null,
+      "size": 335,
+      "stargazers_count": 0,
+      "watchers_count": 0,
+      "language": "Haskell",
+      "has_issues": true,
+      "has_projects": true,
+      "has_downloads": true,
+      "has_wiki": true,
+      "has_pages": false,
+      "forks_count": 0,
+      "mirror_url": null,
+      "open_issues_count": 1,
+      "forks": 0,
+      "open_issues": 1,
+      "watchers": 0,
+      "default_branch": "master"
+    }
+  },
+  "base": {
+    "label": "phadej:master",
+    "ref": "master",
+    "sha": "cb686149c0d88af16de61488a1ba70a6c71a2b65",
+    "user": {
+      "login": "phadej",
+      "id": 123898390,
+      "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/phadej",
+      "html_url": "https://github.com/phadej",
+      "followers_url": "https://api.github.com/users/phadej/followers",
+      "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+      "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+      "organizations_url": "https://api.github.com/users/phadej/orgs",
+      "repos_url": "https://api.github.com/users/phadej/repos",
+      "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/phadej/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "repo": {
+      "id": 102602684,
+      "name": "github",
+      "full_name": "phadej/github",
+      "owner": {
+        "login": "phadej",
+        "id": 123898390,
+        "avatar_url": "https://avatars3.githubusercontent.com/u/123898390?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/phadej",
+        "html_url": "https://github.com/phadej",
+        "followers_url": "https://api.github.com/users/phadej/followers",
+        "following_url": "https://api.github.com/users/phadej/following{/other_user}",
+        "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",
+        "organizations_url": "https://api.github.com/users/phadej/orgs",
+        "repos_url": "https://api.github.com/users/phadej/repos",
+        "events_url": "https://api.github.com/users/phadej/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/phadej/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "private": true,
+      "html_url": "https://github.com/phadej/github",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/phadej/github",
+      "forks_url": "https://api.github.com/repos/phadej/github/forks",
+      "keys_url": "https://api.github.com/repos/phadej/github/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/phadej/github/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/phadej/github/teams",
+      "hooks_url": "https://api.github.com/repos/phadej/github/hooks",
+      "issue_events_url": "https://api.github.com/repos/phadej/github/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/phadej/github/events",
+      "assignees_url": "https://api.github.com/repos/phadej/github/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/phadej/github/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/phadej/github/tags",
+      "blobs_url": "https://api.github.com/repos/phadej/github/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/phadej/github/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/phadej/github/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/phadej/github/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/phadej/github/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/phadej/github/languages",
+      "stargazers_url": "https://api.github.com/repos/phadej/github/stargazers",
+      "contributors_url": "https://api.github.com/repos/phadej/github/contributors",
+      "subscribers_url": "https://api.github.com/repos/phadej/github/subscribers",
+      "subscription_url": "https://api.github.com/repos/phadej/github/subscription",
+      "commits_url": "https://api.github.com/repos/phadej/github/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/phadej/github/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/phadej/github/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/phadej/github/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/phadej/github/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/phadej/github/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/phadej/github/merges",
+      "archive_url": "https://api.github.com/repos/phadej/github/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/phadej/github/downloads",
+      "issues_url": "https://api.github.com/repos/phadej/github/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/phadej/github/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/phadej/github/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/phadej/github/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/phadej/github/labels{/name}",
+      "releases_url": "https://api.github.com/repos/phadej/github/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/phadej/github/deployments",
+      "created_at": "2017-09-06T11:54:37Z",
+      "updated_at": "2017-09-06T11:55:42Z",
+      "pushed_at": "2017-10-01T16:58:54Z",
+      "git_url": "git://github.com/phadej/github.git",
+      "ssh_url": "git@github.com:phadej/github.git",
+      "clone_url": "https://github.com/phadej/github.git",
+      "svn_url": "https://github.com/phadej/github",
+      "homepage": null,
+      "size": 335,
+      "stargazers_count": 0,
+      "watchers_count": 0,
+      "language": "Haskell",
+      "has_issues": true,
+      "has_projects": true,
+      "has_downloads": true,
+      "has_wiki": true,
+      "has_pages": false,
+      "forks_count": 0,
+      "mirror_url": null,
+      "open_issues_count": 1,
+      "forks": 0,
+      "open_issues": 1,
+      "watchers": 0,
+      "default_branch": "master"
+    }
+  },
+  "_links": {
+    "self": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/9"
+    },
+    "html": {
+      "href": "https://github.com/phadej/github/pull/9"
+    },
+    "issue": {
+      "href": "https://api.github.com/repos/phadej/github/issues/9"
+    },
+    "comments": {
+      "href": "https://api.github.com/repos/phadej/github/issues/9/comments"
+    },
+    "review_comments": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/9/comments"
+    },
+    "review_comment": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/comments{/number}"
+    },
+    "commits": {
+      "href": "https://api.github.com/repos/phadej/github/pulls/9/commits"
+    },
+    "statuses": {
+      "href": "https://api.github.com/repos/phadej/github/statuses/20218048bb9529de09f1fdaa9126f60ffeb07ce5"
+    }
+  },
+  "author_association": "OWNER",
+  "merged": false,
+  "mergeable": null,
+  "rebaseable": null,
+  "mergeable_state": "unknown",
+  "merged_by": null,
+  "comments": 0,
+  "review_comments": 0,
+  "maintainer_can_modify": false,
+  "commits": 6,
+  "additions": 363,
+  "deletions": 48,
+  "changed_files": 19
+}
diff --git a/fixtures/pull-request-team-review-requested.json b/fixtures/pull-request-team-review-requested.json
new file mode 100644
--- /dev/null
+++ b/fixtures/pull-request-team-review-requested.json
@@ -0,0 +1,362 @@
+{
+  "url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/910",
+  "id": 529597962,
+  "node_id": "MDExOlB1bGxSZXF1ZXN0NTI5NTk3OTYy",
+  "html_url": "https://github.com/tahoe-lafs/tahoe-lafs/pull/910",
+  "diff_url": "https://github.com/tahoe-lafs/tahoe-lafs/pull/910.diff",
+  "patch_url": "https://github.com/tahoe-lafs/tahoe-lafs/pull/910.patch",
+  "issue_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/910",
+  "number": 910,
+  "state": "open",
+  "locked": false,
+  "title": "Fix NodeMaker's use of the WeakValueDictionary",
+  "user": {
+    "login": "exarkun",
+    "id": 254565,
+    "node_id": "MDQ6VXNlcjI1NDU2NQ==",
+    "avatar_url": "https://avatars1.githubusercontent.com/u/254565?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/exarkun",
+    "html_url": "https://github.com/exarkun",
+    "followers_url": "https://api.github.com/users/exarkun/followers",
+    "following_url": "https://api.github.com/users/exarkun/following{/other_user}",
+    "gists_url": "https://api.github.com/users/exarkun/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/exarkun/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/exarkun/subscriptions",
+    "organizations_url": "https://api.github.com/users/exarkun/orgs",
+    "repos_url": "https://api.github.com/users/exarkun/repos",
+    "events_url": "https://api.github.com/users/exarkun/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/exarkun/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "body": "https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3539",
+  "created_at": "2020-11-30T14:46:37Z",
+  "updated_at": "2020-12-02T17:23:41Z",
+  "closed_at": null,
+  "merged_at": null,
+  "merge_commit_sha": "3c97064ee5f71357c88f7940a91da8859641c2c6",
+  "assignee": null,
+  "assignees": [
+
+  ],
+  "requested_reviewers": [
+
+  ],
+  "requested_teams": [
+    {
+      "name": "Tahoe Committers",
+      "id": 121616,
+      "node_id": "MDQ6VGVhbTEyMTYxNg==",
+      "slug": "tahoe-committers",
+      "description": null,
+      "privacy": "closed",
+      "url": "https://api.github.com/organizations/1156454/team/121616",
+      "html_url": "https://github.com/orgs/tahoe-lafs/teams/tahoe-committers",
+      "members_url": "https://api.github.com/organizations/1156454/team/121616/members{/member}",
+      "repositories_url": "https://api.github.com/organizations/1156454/team/121616/repos",
+      "permission": "push",
+      "parent": null
+    }
+  ],
+  "labels": [
+
+  ],
+  "milestone": null,
+  "draft": false,
+  "commits_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/910/commits",
+  "review_comments_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/910/comments",
+  "review_comment_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/comments{/number}",
+  "comments_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/910/comments",
+  "statuses_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/statuses/ef2f7e61364c6a3187d2ab4859adfc4031213bdd",
+  "head": {
+    "label": "tahoe-lafs:3539.nodemaker-weakrefdict",
+    "ref": "3539.nodemaker-weakrefdict",
+    "sha": "ef2f7e61364c6a3187d2ab4859adfc4031213bdd",
+    "user": {
+      "login": "tahoe-lafs",
+      "id": 1156454,
+      "node_id": "MDEyOk9yZ2FuaXphdGlvbjExNTY0NTQ=",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/1156454?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/tahoe-lafs",
+      "html_url": "https://github.com/tahoe-lafs",
+      "followers_url": "https://api.github.com/users/tahoe-lafs/followers",
+      "following_url": "https://api.github.com/users/tahoe-lafs/following{/other_user}",
+      "gists_url": "https://api.github.com/users/tahoe-lafs/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/tahoe-lafs/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/tahoe-lafs/subscriptions",
+      "organizations_url": "https://api.github.com/users/tahoe-lafs/orgs",
+      "repos_url": "https://api.github.com/users/tahoe-lafs/repos",
+      "events_url": "https://api.github.com/users/tahoe-lafs/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/tahoe-lafs/received_events",
+      "type": "Organization",
+      "site_admin": false
+    },
+    "repo": {
+      "id": 3007569,
+      "node_id": "MDEwOlJlcG9zaXRvcnkzMDA3NTY5",
+      "name": "tahoe-lafs",
+      "full_name": "tahoe-lafs/tahoe-lafs",
+      "private": false,
+      "owner": {
+        "login": "tahoe-lafs",
+        "id": 1156454,
+        "node_id": "MDEyOk9yZ2FuaXphdGlvbjExNTY0NTQ=",
+        "avatar_url": "https://avatars1.githubusercontent.com/u/1156454?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/tahoe-lafs",
+        "html_url": "https://github.com/tahoe-lafs",
+        "followers_url": "https://api.github.com/users/tahoe-lafs/followers",
+        "following_url": "https://api.github.com/users/tahoe-lafs/following{/other_user}",
+        "gists_url": "https://api.github.com/users/tahoe-lafs/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/tahoe-lafs/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/tahoe-lafs/subscriptions",
+        "organizations_url": "https://api.github.com/users/tahoe-lafs/orgs",
+        "repos_url": "https://api.github.com/users/tahoe-lafs/repos",
+        "events_url": "https://api.github.com/users/tahoe-lafs/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/tahoe-lafs/received_events",
+        "type": "Organization",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/tahoe-lafs/tahoe-lafs",
+      "description": "The Tahoe-LAFS decentralized secure filesystem.",
+      "fork": false,
+      "url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs",
+      "forks_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/forks",
+      "keys_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/teams",
+      "hooks_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/hooks",
+      "issue_events_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/events",
+      "assignees_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/tags",
+      "blobs_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/languages",
+      "stargazers_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/stargazers",
+      "contributors_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/contributors",
+      "subscribers_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/subscribers",
+      "subscription_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/subscription",
+      "commits_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/merges",
+      "archive_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/downloads",
+      "issues_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/labels{/name}",
+      "releases_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/deployments",
+      "created_at": "2011-12-18T19:33:55Z",
+      "updated_at": "2020-12-02T20:24:23Z",
+      "pushed_at": "2020-12-02T20:27:05Z",
+      "git_url": "git://github.com/tahoe-lafs/tahoe-lafs.git",
+      "ssh_url": "git@github.com:tahoe-lafs/tahoe-lafs.git",
+      "clone_url": "https://github.com/tahoe-lafs/tahoe-lafs.git",
+      "svn_url": "https://github.com/tahoe-lafs/tahoe-lafs",
+      "homepage": "https://tahoe-lafs.org/",
+      "size": 73606,
+      "stargazers_count": 1018,
+      "watchers_count": 1018,
+      "language": "Python",
+      "has_issues": false,
+      "has_projects": false,
+      "has_downloads": true,
+      "has_wiki": false,
+      "has_pages": false,
+      "forks_count": 236,
+      "mirror_url": null,
+      "archived": false,
+      "disabled": false,
+      "open_issues_count": 21,
+      "license": {
+        "key": "other",
+        "name": "Other",
+        "spdx_id": "NOASSERTION",
+        "url": null,
+        "node_id": "MDc6TGljZW5zZTA="
+      },
+      "forks": 236,
+      "open_issues": 21,
+      "watchers": 1018,
+      "default_branch": "master"
+    }
+  },
+  "base": {
+    "label": "tahoe-lafs:master",
+    "ref": "master",
+    "sha": "fba386cb8ee2b48a34c0d954b5c6b5b080d3234e",
+    "user": {
+      "login": "tahoe-lafs",
+      "id": 1156454,
+      "node_id": "MDEyOk9yZ2FuaXphdGlvbjExNTY0NTQ=",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/1156454?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/tahoe-lafs",
+      "html_url": "https://github.com/tahoe-lafs",
+      "followers_url": "https://api.github.com/users/tahoe-lafs/followers",
+      "following_url": "https://api.github.com/users/tahoe-lafs/following{/other_user}",
+      "gists_url": "https://api.github.com/users/tahoe-lafs/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/tahoe-lafs/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/tahoe-lafs/subscriptions",
+      "organizations_url": "https://api.github.com/users/tahoe-lafs/orgs",
+      "repos_url": "https://api.github.com/users/tahoe-lafs/repos",
+      "events_url": "https://api.github.com/users/tahoe-lafs/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/tahoe-lafs/received_events",
+      "type": "Organization",
+      "site_admin": false
+    },
+    "repo": {
+      "id": 3007569,
+      "node_id": "MDEwOlJlcG9zaXRvcnkzMDA3NTY5",
+      "name": "tahoe-lafs",
+      "full_name": "tahoe-lafs/tahoe-lafs",
+      "private": false,
+      "owner": {
+        "login": "tahoe-lafs",
+        "id": 1156454,
+        "node_id": "MDEyOk9yZ2FuaXphdGlvbjExNTY0NTQ=",
+        "avatar_url": "https://avatars1.githubusercontent.com/u/1156454?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/tahoe-lafs",
+        "html_url": "https://github.com/tahoe-lafs",
+        "followers_url": "https://api.github.com/users/tahoe-lafs/followers",
+        "following_url": "https://api.github.com/users/tahoe-lafs/following{/other_user}",
+        "gists_url": "https://api.github.com/users/tahoe-lafs/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/tahoe-lafs/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/tahoe-lafs/subscriptions",
+        "organizations_url": "https://api.github.com/users/tahoe-lafs/orgs",
+        "repos_url": "https://api.github.com/users/tahoe-lafs/repos",
+        "events_url": "https://api.github.com/users/tahoe-lafs/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/tahoe-lafs/received_events",
+        "type": "Organization",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/tahoe-lafs/tahoe-lafs",
+      "description": "The Tahoe-LAFS decentralized secure filesystem.",
+      "fork": false,
+      "url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs",
+      "forks_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/forks",
+      "keys_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/teams",
+      "hooks_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/hooks",
+      "issue_events_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/events",
+      "assignees_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/tags",
+      "blobs_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/languages",
+      "stargazers_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/stargazers",
+      "contributors_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/contributors",
+      "subscribers_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/subscribers",
+      "subscription_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/subscription",
+      "commits_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/merges",
+      "archive_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/downloads",
+      "issues_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/labels{/name}",
+      "releases_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/deployments",
+      "created_at": "2011-12-18T19:33:55Z",
+      "updated_at": "2020-12-02T20:24:23Z",
+      "pushed_at": "2020-12-02T20:27:05Z",
+      "git_url": "git://github.com/tahoe-lafs/tahoe-lafs.git",
+      "ssh_url": "git@github.com:tahoe-lafs/tahoe-lafs.git",
+      "clone_url": "https://github.com/tahoe-lafs/tahoe-lafs.git",
+      "svn_url": "https://github.com/tahoe-lafs/tahoe-lafs",
+      "homepage": "https://tahoe-lafs.org/",
+      "size": 73606,
+      "stargazers_count": 1018,
+      "watchers_count": 1018,
+      "language": "Python",
+      "has_issues": false,
+      "has_projects": false,
+      "has_downloads": true,
+      "has_wiki": false,
+      "has_pages": false,
+      "forks_count": 236,
+      "mirror_url": null,
+      "archived": false,
+      "disabled": false,
+      "open_issues_count": 21,
+      "license": {
+        "key": "other",
+        "name": "Other",
+        "spdx_id": "NOASSERTION",
+        "url": null,
+        "node_id": "MDc6TGljZW5zZTA="
+      },
+      "forks": 236,
+      "open_issues": 21,
+      "watchers": 1018,
+      "default_branch": "master"
+    }
+  },
+  "_links": {
+    "self": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/910"
+    },
+    "html": {
+      "href": "https://github.com/tahoe-lafs/tahoe-lafs/pull/910"
+    },
+    "issue": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/910"
+    },
+    "comments": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/issues/910/comments"
+    },
+    "review_comments": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/910/comments"
+    },
+    "review_comment": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/comments{/number}"
+    },
+    "commits": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/pulls/910/commits"
+    },
+    "statuses": {
+      "href": "https://api.github.com/repos/tahoe-lafs/tahoe-lafs/statuses/ef2f7e61364c6a3187d2ab4859adfc4031213bdd"
+    }
+  },
+  "author_association": "MEMBER",
+  "active_lock_reason": null,
+  "merged": false,
+  "mergeable": true,
+  "rebaseable": true,
+  "mergeable_state": "clean",
+  "merged_by": null,
+  "comments": 1,
+  "review_comments": 0,
+  "maintainer_can_modify": false,
+  "commits": 5,
+  "additions": 223,
+  "deletions": 4,
+  "changed_files": 5
+}
diff --git a/fixtures/user-bot.json b/fixtures/user-bot.json
new file mode 100644
--- /dev/null
+++ b/fixtures/user-bot.json
@@ -0,0 +1,32 @@
+{
+  "login": "mike-burns",
+  "id": 4550,
+  "avatar_url": "https://avatars.githubusercontent.com/u/4550?v=3",
+  "gravatar_id": "",
+  "url": "https://api.github.com/users/mike-burns",
+  "html_url": "https://github.com/mike-burns",
+  "followers_url": "https://api.github.com/users/mike-burns/followers",
+  "following_url": "https://api.github.com/users/mike-burns/following{/other_user}",
+  "gists_url": "https://api.github.com/users/mike-burns/gists{/gist_id}",
+  "starred_url": "https://api.github.com/users/mike-burns/starred{/owner}{/repo}",
+  "subscriptions_url": "https://api.github.com/users/mike-burns/subscriptions",
+  "organizations_url": "https://api.github.com/users/mike-burns/orgs",
+  "repos_url": "https://api.github.com/users/mike-burns/repos",
+  "events_url": "https://api.github.com/users/mike-burns/events{/privacy}",
+  "received_events_url": "https://api.github.com/users/mike-burns/received_events",
+  "type": "Bot",
+  "site_admin": false,
+  "name": "Mike Burns",
+  "company": "thoughtbot",
+  "blog": "http://mike-burns.com/",
+  "location": "Stockholm, Sweden",
+  "email": "mburns@thoughtbot.com",
+  "hireable": true,
+  "bio": null,
+  "public_repos": 35,
+  "public_gists": 32,
+  "followers": 171,
+  "following": 0,
+  "created_at": "2008-04-03T17:54:24Z",
+  "updated_at": "2015-10-02T16:53:25Z"
+}
diff --git a/fixtures/user-organizations.json b/fixtures/user-organizations.json
new file mode 100644
--- /dev/null
+++ b/fixtures/user-organizations.json
@@ -0,0 +1,9 @@
+[
+  {
+    "login": "github",
+    "id": 1,
+    "url": "https://api.github.com/orgs/github",
+    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+    "description": "A great organization"
+  }
+]
diff --git a/fixtures/user.json b/fixtures/user.json
new file mode 100644
--- /dev/null
+++ b/fixtures/user.json
@@ -0,0 +1,32 @@
+{
+  "login": "mike-burns",
+  "id": 4550,
+  "avatar_url": "https://avatars.githubusercontent.com/u/4550?v=3",
+  "gravatar_id": "",
+  "url": "https://api.github.com/users/mike-burns",
+  "html_url": "https://github.com/mike-burns",
+  "followers_url": "https://api.github.com/users/mike-burns/followers",
+  "following_url": "https://api.github.com/users/mike-burns/following{/other_user}",
+  "gists_url": "https://api.github.com/users/mike-burns/gists{/gist_id}",
+  "starred_url": "https://api.github.com/users/mike-burns/starred{/owner}{/repo}",
+  "subscriptions_url": "https://api.github.com/users/mike-burns/subscriptions",
+  "organizations_url": "https://api.github.com/users/mike-burns/orgs",
+  "repos_url": "https://api.github.com/users/mike-burns/repos",
+  "events_url": "https://api.github.com/users/mike-burns/events{/privacy}",
+  "received_events_url": "https://api.github.com/users/mike-burns/received_events",
+  "type": "User",
+  "site_admin": false,
+  "name": "Mike Burns",
+  "company": "thoughtbot",
+  "blog": "http://mike-burns.com/",
+  "location": "Stockholm, Sweden",
+  "email": "mburns@thoughtbot.com",
+  "hireable": true,
+  "bio": null,
+  "public_repos": 35,
+  "public_gists": 32,
+  "followers": 171,
+  "following": 0,
+  "created_at": "2008-04-03T17:54:24Z",
+  "updated_at": "2015-10-02T16:53:25Z"
+}
diff --git a/github.cabal b/github.cabal
--- a/github.cabal
+++ b/github.cabal
@@ -1,219 +1,271 @@
--- github.cabal auto-generated by cabal init. For additional options,
--- see
--- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.
--- The name of the package.
-Name:                github
-
--- The package version. See the Haskell package versioning policy
--- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
--- standards guiding when and how versions should be incremented.
-Version:             0.13.2
-
--- A short (one-line) description of the package.
-Synopsis:            Access to the Github API, v3.
-
--- A longer description of the package.
-Description:         The Github API provides programmatic access to the full
-                     Github Web site, from Issues to Gists to repos down to the underlying git data
-                     like references and trees. This library wraps all of that, exposing a basic but
-                     Haskell-friendly set of functions and data structures.
-                     .
-                     For more of an overview please see the README: <https://github.com/jwiegley/github/blob/master/README.md>
-
--- The license under which the package is released.
-License:             BSD3
-
--- The file containing the license text.
-License-file:        LICENSE
-
--- The package author(s).
-Author:              Mike Burns, John Wiegley
-
--- An email address to which users can send suggestions, bug reports,
--- and patches.
-Maintainer:          johnw@newartisans.com
+cabal-version:      2.4
+name:               github
+version:            0.30.0.2
+synopsis:           Access to the GitHub API, v3.
+category:           Network
+description:
+  The GitHub API provides programmatic access to the full
+  GitHub Web site, from Issues to Gists to repos down to the underlying git data
+  like references and trees. This library wraps all of that, exposing a basic but
+  Haskell-friendly set of functions and data structures.
+  .
+  For supported endpoints see "GitHub" module.
+  .
+  > import qualified GitHub as GH
+  >
+  > main :: IO ()
+  > main = do
+  >     possibleUser <- GH.github' GH.userInfoForR "phadej"
+  >     print possibleUser
+  .
+  For more of an overview please see the README: <https://github.com/haskell-github/github/blob/master/README.md>
 
-Homepage:            https://github.com/jwiegley/github
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             Mike Burns, John Wiegley, Oleg Grenrus
+maintainer:         Andreas Abel
+homepage:           https://github.com/haskell-github/github
+build-type:         Simple
+copyright:
+  Copyright 2012-2013 Mike Burns, Copyright 2013-2015 John Wiegley, Copyright 2016-2021 Oleg Grenrus
 
--- A copyright notice.
-Copyright:           Copyright 2012-2013 Mike Burns, Copyright 2013-2015 John Wiegley
+tested-with:
+  GHC == 9.14.1
+  GHC == 9.12.2
+  GHC == 9.10.3
+  GHC == 9.8.4
+  GHC == 9.6.7
+  GHC == 9.4.8
+  GHC == 9.2.8
+  GHC == 9.0.2
+  GHC == 8.10.7
+  GHC == 8.8.4
+  GHC == 8.6.5
+  GHC == 8.4.4
+  GHC == 8.2.2
 
-Category:            Network APIs
+extra-doc-files:
+  README.md
+  CHANGELOG.md
 
-Build-type:          Simple
+extra-source-files:
+  fixtures/**/*.json
 
--- Extra files to be distributed with the package, such as examples or
--- a README.
-Extra-source-files:  README.md
-                    ,samples/Gists/Comments/ShowComment.hs
-                    ,samples/Gists/Comments/ShowComments.hs
-                    ,samples/Gists/ListGists.hs
-                    ,samples/Gists/ShowGist.hs
-                    ,samples/GitData/Commits/GitShow.hs
-                    ,samples/GitData/References/GitCreateReference.hs
-                    ,samples/GitData/References/GitLsRemote.hs
-                    ,samples/GitData/References/GitLsRemoteTags.hs
-                    ,samples/GitData/References/GitLsRemoteWithRef.hs
-                    ,samples/GitData/Trees/GitLsTree.hs
-                    ,samples/GitData/Trees/GitLsTreeRecursively.hs
-                    ,samples/Issues/Comments/ShowComment.hs
-                    ,samples/Issues/Comments/ShowComments.hs
-                    ,samples/Issues/Events/ShowEvent.hs
-                    ,samples/Issues/Events/ShowIssueEvents.hs
-                    ,samples/Issues/Events/ShowRepoEvents.hs
-                    ,samples/Issues/Labels/ShowIssueLabels.hs
-                    ,samples/Issues/Labels/ShowLabel.hs
-                    ,samples/Issues/Labels/ShowMilestoneLabels.hs
-                    ,samples/Issues/Labels/ShowRepoLabels.hs
-                    ,samples/Issues/Milestones/ShowMilestone.hs
-                    ,samples/Issues/Milestones/ShowMilestones.hs
-                    ,samples/Issues/ShowIssue.hs
-                    ,samples/Issues/ShowRepoIssues.hs
-                    ,samples/Organizations/Members/ShowMembers.hs
-                    ,samples/Organizations/ShowPublicOrganization.hs
-                    ,samples/Organizations/ShowPublicOrganizations.hs
-                    ,samples/Pulls/Diff.hs
-                    ,samples/Pulls/ListPulls.hs
-                    ,samples/Pulls/ReviewComments/ListComments.hs
-                    ,samples/Pulls/ReviewComments/ShowComment.hs
-                    ,samples/Pulls/ShowCommits.hs
-                    ,samples/Pulls/ShowPull.hs
-                    ,samples/Search/SearchRepos.hs
-                    ,samples/Repos/Collaborators/IsCollaborator.hs
-                    ,samples/Repos/Collaborators/ListCollaborators.hs
-                    ,samples/Repos/Commits/CommitComment.hs
-                    ,samples/Repos/Commits/CommitComments.hs
-                    ,samples/Repos/Commits/GitDiff.hs
-                    ,samples/Repos/Commits/GitLog.hs
-                    ,samples/Repos/Commits/GitShow.hs
-                    ,samples/Repos/Commits/RepoComments.hs
-                    ,samples/Repos/Forks/ListForks.hs
-                    ,samples/Repos/ListBranches.hs
-                    ,samples/Repos/ListContributors.hs
-                    ,samples/Repos/ListContributorsWithAnonymous.hs
-                    ,samples/Repos/ListLanguages.hs
-                    ,samples/Repos/ListOrgRepos.hs
-                    ,samples/Repos/ListTags.hs
-                    ,samples/Repos/ListUserRepos.hs
-                    ,samples/Repos/ShowRepo.hs
-                    ,samples/Repos/Watching/ListWatched.hs
-                    ,samples/Repos/Watching/ListWatchers.hs
-                    ,samples/Repos/Starring/ListStarred.hs
-                    ,samples/Repos/Webhooks/CreateWebhook.hs
-                    ,samples/Repos/Webhooks/DeleteWebhook.hs
-                    ,samples/Repos/Webhooks/EditWebhook.hs
-                    ,samples/Repos/Webhooks/ListWebhook.hs
-                    ,samples/Repos/Webhooks/ListWebhooks.hs
-                    ,samples/Repos/Webhooks/PingWebhook.hs
-                    ,samples/Repos/Webhooks/TestPushWebhook.hs
-                    ,samples/Users/Followers/ListFollowers.hs
-                    ,samples/Users/Followers/ListFollowing.hs
-                    ,samples/Users/ShowUser.hs
-                    ,LICENSE
+source-repository head
+  type:     git
+  location: https://github.com/haskell-github/github.git
 
+flag openssl
+  description: "Use http-client-openssl"
+  manual:      True
+  default:     False
 
--- Constraint on the version of Cabal needed to build this package.
-Cabal-version:       >=1.10
+library
+  default-language:   Haskell2010
+  ghc-options:
+    -Wall
+    -Wcompat
+    -Wno-star-is-type
+      -- The star-is-type warning cannot be sensiblity addressed while supporting GHC 7.
+  hs-source-dirs:     src
+  default-extensions:
+    DataKinds
+    DeriveDataTypeable
+    DeriveGeneric
+    LambdaCase
+    OverloadedStrings
+    ScopedTypeVariables
+    TypeOperators
 
-source-repository head
-  type: git
-  location: git://github.com/jwiegley/github.git
+  other-extensions:
+    CPP
+    FlexibleContexts
+    FlexibleInstances
+    GADTs
+    KindSignatures
+    RecordWildCards
+    StandaloneDeriving
 
-Library
-  -- Modules exported by the library.
-  Default-Language: Haskell2010
-  Exposed-modules: Github.Auth,
-                   Github.Data,
-                   Github.Data.Definitions,
-                   Github.Events,
-                   Github.Gists,
-                   Github.Gists.Comments,
-                   Github.GitData.Commits,
-                   Github.GitData.References,
-                   Github.GitData.Trees,
-                   Github.GitData.Blobs,
-                   Github.Issues,
-                   Github.Issues.Comments,
-                   Github.Issues.Events,
-                   Github.Issues.Labels,
-                   Github.Issues.Milestones,
-                   Github.Organizations,
-                   Github.Organizations.Members,
-                   Github.PullRequests,
-                   Github.Repos,
-                   Github.Repos.Collaborators,
-                   Github.Repos.Commits,
-                   Github.Repos.Forks,
-                   Github.Repos.Watching,
-                   Github.Repos.Starring,
-                   Github.Repos.Subscribing,
-                   Github.Repos.Webhooks
-                   Github.Repos.Webhooks.Validate,
-                   Github.Users,
-                   Github.Users.Followers
-                   Github.Search
+  exposed-modules:
+    GitHub
+    GitHub.Auth
+    GitHub.Data
+    GitHub.Data.Actions.Common
+    GitHub.Data.Actions.Artifacts
+    GitHub.Data.Actions.Cache
+    GitHub.Data.Actions.Secrets
+    GitHub.Data.Actions.Workflows
+    GitHub.Data.Actions.WorkflowJobs
+    GitHub.Data.Actions.WorkflowRuns
+    GitHub.Data.Activities
+    GitHub.Data.Comments
+    GitHub.Data.Content
+    GitHub.Data.Definitions
+    GitHub.Data.DeployKeys
+    GitHub.Data.Deployments
+    GitHub.Data.Email
+    GitHub.Data.Enterprise
+    GitHub.Data.Enterprise.Organizations
+    GitHub.Data.Events
+    GitHub.Data.Gists
+    GitHub.Data.GitData
+    GitHub.Data.Id
+    GitHub.Data.Invitation
+    GitHub.Data.Issues
+    GitHub.Data.Milestone
+    GitHub.Data.Name
+    GitHub.Data.Options
+    GitHub.Data.PublicSSHKeys
+    GitHub.Data.PullRequests
+    GitHub.Data.RateLimit
+    GitHub.Data.Reactions
+    GitHub.Data.Releases
+    GitHub.Data.Repos
+    GitHub.Data.Request
+    GitHub.Data.Reviews
+    GitHub.Data.Search
+    GitHub.Data.Statuses
+    GitHub.Data.Teams
+    GitHub.Data.URL
+    GitHub.Data.Webhooks
+    GitHub.Data.Webhooks.Validate
+    GitHub.Endpoints.Actions.Artifacts
+    GitHub.Endpoints.Actions.Cache
+    GitHub.Endpoints.Actions.Secrets
+    GitHub.Endpoints.Actions.Workflows
+    GitHub.Endpoints.Actions.WorkflowJobs
+    GitHub.Endpoints.Actions.WorkflowRuns
+    GitHub.Endpoints.Activity.Events
+    GitHub.Endpoints.Activity.Notifications
+    GitHub.Endpoints.Activity.Starring
+    GitHub.Endpoints.Activity.Watching
+    GitHub.Endpoints.Enterprise.Organizations
+    GitHub.Endpoints.Gists
+    GitHub.Endpoints.Gists.Comments
+    GitHub.Endpoints.GitData.Blobs
+    GitHub.Endpoints.GitData.Commits
+    GitHub.Endpoints.GitData.References
+    GitHub.Endpoints.GitData.Trees
+    GitHub.Endpoints.Issues
+    GitHub.Endpoints.Issues.Comments
+    GitHub.Endpoints.Issues.Events
+    GitHub.Endpoints.Issues.Labels
+    GitHub.Endpoints.Issues.Milestones
+    GitHub.Endpoints.Organizations
+    GitHub.Endpoints.Organizations.Members
+    GitHub.Endpoints.Organizations.OutsideCollaborators
+    GitHub.Endpoints.Organizations.Teams
+    GitHub.Endpoints.PullRequests
+    GitHub.Endpoints.PullRequests.Comments
+    GitHub.Endpoints.PullRequests.Reviews
+    GitHub.Endpoints.RateLimit
+    GitHub.Endpoints.Reactions
+    GitHub.Endpoints.Repos
+    GitHub.Endpoints.Repos.Collaborators
+    GitHub.Endpoints.Repos.Comments
+    GitHub.Endpoints.Repos.Commits
+    GitHub.Endpoints.Repos.Contents
+    GitHub.Endpoints.Repos.DeployKeys
+    GitHub.Endpoints.Repos.Deployments
+    GitHub.Endpoints.Repos.Forks
+    GitHub.Endpoints.Repos.Invitations
+    GitHub.Endpoints.Repos.Releases
+    GitHub.Endpoints.Repos.Statuses
+    GitHub.Endpoints.Repos.Webhooks
+    GitHub.Endpoints.Search
+    GitHub.Endpoints.Users
+    GitHub.Endpoints.Users.Emails
+    GitHub.Endpoints.Users.Followers
+    GitHub.Endpoints.Users.PublicSSHKeys
+    GitHub.Enterprise
+    GitHub.Internal.Prelude
+    GitHub.Request
 
-  -- Packages needed in order to build this package.
-  Build-depends: base >= 4.0 && < 5.0,
-                 time,
-                 aeson >= 0.6.1.0,
-                 attoparsec >= 0.10.3.0,
-                 bytestring,
-                 case-insensitive >= 0.4.0.4,
-                 containers,
-                 hashable,
-                 text,
-                 old-locale,
-                 HTTP,
-                 network,
-                 http-conduit >= 1.8,
-                 conduit,
-                 failure,
-                 http-types,
-                 data-default,
-                 vector,
-                 unordered-containers >= 0.2 && < 0.3,
-                 cryptohash >= 0.11,
-                 byteable >= 0.1.0,
-                 base16-bytestring >= 0.1.1.6
+  other-modules:   Paths_github
+  autogen-modules: Paths_github
 
-  -- Modules not exported by this package.
-  Other-modules:       Github.Private
+  -- Packages bundles with GHC, mtl and text are also here
+  -- Lower bounds at least those of https://www.stackage.org/lts-10.0 (GHC 8.2.2)
+  build-depends:
+      base          >=4.10     && <5
+    , binary        >=0.8.5.1  && <0.11
+    , bytestring    >=0.10.8.2 && <0.13
+    , containers    >=0.5.10.2 && <1
+    , deepseq       >=1.4.3.0  && <1.6
+    , exceptions    >=0.10.2   && <0.11
+    , mtl           >=2.2.1    && <2.4
+    , text          >=1.2.2.2  && <2.2
+    , time          >=1.8.0.2  && <2
+    , transformers  >=0.5.2.0  && <0.7
 
-  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
-  -- Build-tools:
+  -- other packages
+  build-depends:
+      aeson                 >=1.4.0.0    && <1.6 || >=2.0.1.0 && <2.3
+    , base-compat           >=0.11.1     && <1
+    , base16-bytestring     >=0.1.1.6    && <1.1
+    , binary-instances      >=1          && <1.1
+    , cryptohash-sha1       >=0.11.100.1 && <0.12
+    , hashable              >=1.2.7.0    && <2
+    , http-client           >=0.5.12     && <0.8
+    , http-link-header      >=1.0.3.1    && <1.3
+    , http-types            >=0.12.3     && <0.13
+    , iso8601-time          >=0.1.5      && <0.2
+    , network-uri           >=2.6.1.0    && <2.7
+    , tagged                >=0.8.5      && <0.9
+    , unordered-containers  >=0.2.10.0   && <0.3
+    , vector                >=0.12.0.1   && <0.14
 
-  GHC-Options: -Wall -fno-warn-orphans
+  if flag(openssl)
+    build-depends:
+        HsOpenSSL              >=0.11.4.16 && <0.12
+      , HsOpenSSL-x509-system  >=0.1.0.3   && <0.2
+      , http-client-openssl    >=0.2.2.0   && <0.4
 
+  else
+    build-depends:
+        http-client-tls  >=0.3.5.3 && <0.5
+      , tls              >=1.4.1
 
 test-suite github-test
-  default-language: Haskell2010
-  type: exitcode-stdio-1.0
-  hs-source-dirs: spec, .
-  main-is: Spec.hs
-  build-depends: base >= 4.0 && < 5.0,
-                 time,
-                 aeson >= 0.6.1.0,
-                 attoparsec >= 0.10.3.0,
-                 bytestring,
-                 case-insensitive >= 0.4.0.4,
-                 containers,
-                 hashable,
-                 text,
-                 old-locale,
-                 HTTP,
-                 network,
-                 http-conduit >= 1.8,
-                 conduit,
-                 failure,
-                 http-types,
-                 data-default,
-                 vector,
-                 unordered-containers >= 0.2 && < 0.3,
-                 cryptohash >= 0.11,
-                 byteable >= 0.1.0,
-                 base16-bytestring >= 0.1.1.6
-
-                 , hspec
+  default-language:   Haskell2010
+  type:               exitcode-stdio-1.0
+  hs-source-dirs:     spec
+  main-is:            Spec.hs
+  ghc-options:        -Wall -threaded
+  build-tool-depends: hspec-discover:hspec-discover >=2.7.1 && <2.12
+  other-extensions:   TemplateHaskell
+  other-modules:
+    GitHub.Actions.ArtifactsSpec
+    GitHub.Actions.CacheSpec
+    GitHub.Actions.SecretsSpec
+    GitHub.Actions.WorkflowJobSpec
+    GitHub.Actions.WorkflowRunsSpec
+    GitHub.Actions.WorkflowSpec
+    GitHub.ActivitySpec
+    GitHub.CommitsSpec
+    GitHub.EventsSpec
+    GitHub.IssuesSpec
+    GitHub.OrganizationsSpec
+    GitHub.PublicSSHKeysSpec
+    GitHub.PullRequestReviewsSpec
+    GitHub.PullRequestsSpec
+    GitHub.RateLimitSpec
+    GitHub.ReleasesSpec
+    GitHub.ReposSpec
+    GitHub.ReviewDecodeSpec
+    GitHub.SearchSpec
+    GitHub.UsersSpec
 
-  ghc-options: -Wall -fno-warn-orphans
+  build-depends:
+      aeson
+    , base
+    , base-compat
+    , bytestring
+    , file-embed
+    , github
+    , hspec                 >=2.6.1 && <2.12
+    , http-client
+    , tagged
+    , text
+    , unordered-containers
+    , vector
diff --git a/samples/Gists/Comments/ShowComment.hs b/samples/Gists/Comments/ShowComment.hs
deleted file mode 100644
--- a/samples/Gists/Comments/ShowComment.hs
+++ /dev/null
@@ -1,16 +0,0 @@
-module ShowComment where
-
-import qualified Github.Gists.Comments as Github
-
-main = do
-  possibleComment <- Github.comment "62449"
-  case possibleComment of
-    (Left error)  -> putStrLn $ "Error: " ++ (show error)
-    (Right comment) -> putStrLn $ formatComment comment
-
-formatComment comment =
-  (Github.githubOwnerLogin $ Github.gistCommentUser comment) ++ "\n" ++
-    (formatGithubDate $ Github.gistCommentUpdatedAt comment) ++ "\n\n" ++
-    (Github.gistCommentBody comment)
-
-formatGithubDate = show . Github.fromGithubDate
diff --git a/samples/Gists/Comments/ShowComments.hs b/samples/Gists/Comments/ShowComments.hs
deleted file mode 100644
--- a/samples/Gists/Comments/ShowComments.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module ShowComments where
-
-import qualified Github.Gists.Comments as Github
-import Data.List (intercalate)
-
-main = do
-  possibleComments <- Github.commentsOn "1174060"
-  case possibleComments of
-    (Left error)  -> putStrLn $ "Error: " ++ (show error)
-    (Right comments) -> putStrLn $ intercalate "\n\n" $ map formatComment comments
-
-formatComment comment =
-  (Github.githubOwnerLogin $ Github.gistCommentUser comment) ++ "\n" ++
-    (formatGithubDate $ Github.gistCommentUpdatedAt comment) ++ "\n\n" ++
-    (Github.gistCommentBody comment)
-
-formatGithubDate = show . Github.fromGithubDate
diff --git a/samples/Gists/ListGists.hs b/samples/Gists/ListGists.hs
deleted file mode 100644
--- a/samples/Gists/ListGists.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-module ListGists where
-
-import qualified Github.Gists as Github
-import Data.List (intercalate)
-
-main = do
-  possibleGists <- Github.gists "mike-burns"
-  case possibleGists of
-    (Left error)  -> putStrLn $ "Error: " ++ (show error)
-    (Right gists) -> putStrLn $ intercalate "\n\n" $ map formatGist gists
-
-formatGist gist =
-  (Github.gistId gist) ++ "\n" ++
-    (maybe "indescribable" id $ Github.gistDescription gist) ++ "\n" ++
-    (Github.gistHtmlUrl gist)
diff --git a/samples/Gists/ShowGist.hs b/samples/Gists/ShowGist.hs
deleted file mode 100644
--- a/samples/Gists/ShowGist.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-module ShowGist where
-
-import qualified Github.Gists as Github
-import Data.List (intercalate)
-
-main = do
-  possibleGist <- Github.gist "23084"
-  case possibleGist of
-    (Left error)  -> putStrLn $ "Error: " ++ (show error)
-    (Right gist) -> putStrLn $ formatGist gist
-
-formatGist gist =
-  (Github.gistId gist) ++ "\n" ++
-    (maybe "indescribable" id $ Github.gistDescription gist) ++ "\n" ++
-    (Github.gistHtmlUrl gist) ++ "\n\n" ++
-    (intercalate "\n\n" $ map formatGistFile $ Github.gistFiles gist)
-
-formatGistFile gistFile =
-  (Github.gistFileFilename gistFile) ++ ":\n" ++
-    maybe "[empty]" id (Github.gistFileContent gistFile)
diff --git a/samples/GitData/Commits/GitShow.hs b/samples/GitData/Commits/GitShow.hs
deleted file mode 100644
--- a/samples/GitData/Commits/GitShow.hs
+++ /dev/null
@@ -1,23 +0,0 @@
-module GitShow where
-
-import qualified Github.GitData.Commits as Github
-import Data.Maybe (fromJust)
-
-main = do
-  possibleCommit <- Github.commit "thoughtbot" "paperclip" "bc5c51d1ece1ee45f94b056a0f5a1674d7e8cba9"
-  case possibleCommit of
-    (Left error)    -> putStrLn $ "Error: " ++ (show error)
-    (Right commit) -> putStrLn $ formatCommit commit
-
-formatCommit :: Github.GitCommit -> String
-formatCommit commit =
-  "commit " ++ (fromJust $ Github.gitCommitSha commit) ++
-    "\nAuthor: " ++ (formatAuthor author) ++
-    "\nDate:   " ++ (show $ Github.fromGithubDate $ Github.gitUserDate author) ++
-    "\n\n\t" ++ (Github.gitCommitMessage commit) ++ "\n"
-  where author = Github.gitCommitAuthor commit
-
-formatAuthor :: Github.GitUser -> String
-formatAuthor author =
-  (Github.gitUserName author) ++ " <" ++ (Github.gitUserEmail author) ++ ">"
-
diff --git a/samples/GitData/References/GitCreateReference.hs b/samples/GitData/References/GitCreateReference.hs
deleted file mode 100644
--- a/samples/GitData/References/GitCreateReference.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-module GitCreateRef where
-
-import qualified Github.Auth as Auth
-import Github.GitData.References
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  newlyCreatedGitRef <- createReference auth "myrepo" "myowner" NewGitReference {
-       newGitReferenceRef = "refs/heads/fav_tag"
-      ,newGitReferenceSha = "aa218f56b14c9653891f9e74264a383fa43fefbd"
-    }
-  case newlyCreatedGitRef of
-   (Left err) -> putStrLn $ "Error: " ++ show err
-   (Right newRef) -> putStrLn . formatReference $ newRef
-                        
-formatReference :: GitReference -> String
-formatReference ref  =
-  (gitObjectSha $ gitReferenceObject ref) ++ "\t" ++ (gitReferenceRef ref)
diff --git a/samples/GitData/References/GitLsRemote.hs b/samples/GitData/References/GitLsRemote.hs
deleted file mode 100644
--- a/samples/GitData/References/GitLsRemote.hs
+++ /dev/null
@@ -1,16 +0,0 @@
-module GitLsRemote where
-
-import qualified Github.GitData.References as Github
-import Data.List (intercalate)
-
-main = do
-  possibleReferences <- Github.references "mike-burns" "github"
-  case possibleReferences of
-       (Left error)       -> putStrLn $ "Error: " ++ show error
-       (Right references) -> do
-         putStrLn "From git@github.com:mike-burns/github.git"
-         putStrLn $ intercalate "\n" $ map formatReference references
-
-formatReference reference =
-  (Github.gitObjectSha $ Github.gitReferenceObject reference) ++
-    "\t" ++ (Github.gitReferenceRef reference)
diff --git a/samples/GitData/References/GitLsRemoteTags.hs b/samples/GitData/References/GitLsRemoteTags.hs
deleted file mode 100644
--- a/samples/GitData/References/GitLsRemoteTags.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module GitLsRemoteTags where
-
-import qualified Github.GitData.References as Github
-import Data.List (intercalate)
-
-main = do
-  possibleReferences <- Github.namespacedReferences "thoughtbot" "paperclip" "tags"
-  case possibleReferences of
-       (Left error)       -> putStrLn $ "Error: " ++ show error
-       (Right references) -> do
-         putStrLn "From git@github.com:thoughtbot/paperclip.git"
-         putStrLn $ intercalate "\n" $ map formatReference references
-
-formatReference reference =
-  (Github.gitObjectSha $ Github.gitReferenceObject reference) ++
-    "\t" ++ (Github.gitReferenceRef reference)
-
diff --git a/samples/GitData/References/GitLsRemoteWithRef.hs b/samples/GitData/References/GitLsRemoteWithRef.hs
deleted file mode 100644
--- a/samples/GitData/References/GitLsRemoteWithRef.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module GitLsRemoteWithRef where
-
-import qualified Github.GitData.References as Github
-
-main = do
-  possibleReference <- Github.reference "mike-burns" "github" "heads/master"
-  putStrLn $ either (\e -> "Error: " ++ show e)
-                    formatReference
-                    possibleReference
-
-formatReference reference =
-  (Github.gitObjectSha $ Github.gitReferenceObject reference) ++
-    "\t" ++ (Github.gitReferenceRef reference)
diff --git a/samples/GitData/Trees/GitLsTree.hs b/samples/GitData/Trees/GitLsTree.hs
deleted file mode 100644
--- a/samples/GitData/Trees/GitLsTree.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-module GitLsTree where
-
-import qualified Github.GitData.Trees as Github
-import Data.List (intercalate)
-
-main = do
-  possibleTree <- Github.tree "thoughtbot" "paperclip" "fe114451f7d066d367a1646ca7ac10e689b46844"
-  case possibleTree of
-       (Left error)  -> putStrLn $ "Error: " ++ show error
-       (Right tree) -> putStrLn $ formatTree tree
-
-formatTree tree =
-  intercalate "\n" $ map formatGitTree $ Github.treeGitTrees tree
-
-formatGitTree gitTree =
-  (Github.gitTreeMode gitTree) ++ " " ++
-    (Github.gitTreeType gitTree) ++ " " ++
-    (Github.gitTreeSha gitTree) ++ "\t" ++
-    (Github.gitTreePath gitTree)
diff --git a/samples/GitData/Trees/GitLsTreeRecursively.hs b/samples/GitData/Trees/GitLsTreeRecursively.hs
deleted file mode 100644
--- a/samples/GitData/Trees/GitLsTreeRecursively.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-module GitLsTreeRecursively where
-
-import qualified Github.GitData.Trees as Github
-import Data.List (intercalate)
-
-main = do
-  possibleTree <- Github.nestedTree "thoughtbot" "paperclip" "fe114451f7d066d367a1646ca7ac10e689b46844"
-  case possibleTree of
-       (Left error)  -> putStrLn $ "Error: " ++ show error
-       (Right tree) -> putStrLn $ formatTree tree
-
-formatTree tree =
-  intercalate "\n" $ map formatGitTree $ Github.treeGitTrees tree
-
-formatGitTree gitTree =
-  (Github.gitTreeMode gitTree) ++ " " ++
-    (Github.gitTreeType gitTree) ++ " " ++
-    (Github.gitTreeSha gitTree) ++ "\t" ++
-    (Github.gitTreePath gitTree)
diff --git a/samples/Issues/Comments/ShowComment.hs b/samples/Issues/Comments/ShowComment.hs
deleted file mode 100644
--- a/samples/Issues/Comments/ShowComment.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-module ShowComment where
-
-import qualified Github.Issues.Comments as Github
-
-main = do
-  possibleComment <- Github.comment "thoughtbot" "paperclip" 1468184
-  putStrLn $ either (\e -> "Error: " ++ show e)
-                    formatComment
-                    possibleComment
-
-formatComment comment =
-  (Github.githubOwnerLogin $ Github.issueCommentUser comment) ++
-    " commented " ++
-    (show $ Github.fromGithubDate $ Github.issueCommentUpdatedAt comment) ++
-    "\n" ++ (Github.issueCommentBody comment)
diff --git a/samples/Issues/Comments/ShowComments.hs b/samples/Issues/Comments/ShowComments.hs
deleted file mode 100644
--- a/samples/Issues/Comments/ShowComments.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module ShowComments where
-
-import qualified Github.Issues.Comments as Github
-import Data.List (intercalate)
-
-main = do
-  possibleComments <- Github.comments "thoughtbot" "paperclip" 635
-  case possibleComments of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right issues) ->
-         putStrLn $ intercalate "\n\n" $ map formatComment issues
-
-formatComment comment =
-  (Github.githubOwnerLogin $ Github.issueCommentUser comment) ++
-    " commented " ++
-    (show $ Github.fromGithubDate $ Github.issueCommentUpdatedAt comment) ++
-    "\n" ++ (Github.issueCommentBody comment)
diff --git a/samples/Issues/Events/ShowEvent.hs b/samples/Issues/Events/ShowEvent.hs
deleted file mode 100644
--- a/samples/Issues/Events/ShowEvent.hs
+++ /dev/null
@@ -1,38 +0,0 @@
-module ShowEvents where
-
-import qualified Github.Issues.Events as Github
-import Data.List (intercalate)
-import Data.Maybe (fromJust)
-
-main = do
-  possibleEvent <- Github.event "thoughtbot" "paperclip" 5335772
-  case possibleEvent of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right event) -> do
-         putStrLn $ formatEvent event
-
-formatEvent event = formatEvent' event (Github.eventType event)
-  where
-  formatEvent' event Github.Closed =
-    "Closed on " ++ createdAt event ++ " by " ++ loginName event ++
-      withCommitId event (\commitId -> " in the commit " ++ commitId)
-  formatEvent' event Github.Reopened =
-    "Reopened on " ++ createdAt event ++ " by " ++ loginName event
-  formatEvent' event Github.Subscribed =
-    loginName event ++ " is subscribed to receive notifications"
-  formatEvent' event Github.Unsubscribed =
-    loginName event ++ " is unsubscribed from notifications"
-  formatEvent' event Github.Merged =
-    "Issue merged by " ++ loginName event ++ " on " ++ createdAt event ++
-      (withCommitId event $ \commitId -> " in the commit " ++ commitId)
-  formatEvent' event Github.Referenced =
-    withCommitId event $ \commitId ->
-      "Issue referenced from " ++ commitId ++ " by " ++ loginName event
-  formatEvent' event Github.Mentioned =
-    loginName event ++ " was mentioned in the issue's body"
-  formatEvent' event Github.Assigned =
-    "Issue assigned to " ++ loginName event ++ " on " ++ createdAt event
-
-loginName = Github.githubOwnerLogin . Github.eventActor
-createdAt = show . Github.fromGithubDate . Github.eventCreatedAt
-withCommitId event f = maybe "" f (Github.eventCommitId event)
diff --git a/samples/Issues/Events/ShowIssueEvents.hs b/samples/Issues/Events/ShowIssueEvents.hs
deleted file mode 100644
--- a/samples/Issues/Events/ShowIssueEvents.hs
+++ /dev/null
@@ -1,38 +0,0 @@
-module ShowIssueEvents where
-
-import qualified Github.Issues.Events as Github
-import Data.List (intercalate)
-
-main = do
-  possibleEvents <- Github.eventsForIssue "thoughtbot" "paperclip" 49
-  case possibleEvents of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right events) -> do
-         putStrLn "Issue #49:\n"
-         putStrLn $ intercalate "\n" $ map formatEvent events
-
-formatEvent event = formatEvent' event (Github.eventType event)
-  where
-  formatEvent' event Github.Closed =
-    "Closed on " ++ createdAt event ++ " by " ++ loginName event ++
-      withCommitId event (\commitId -> " in the commit " ++ commitId)
-  formatEvent' event Github.Reopened =
-    "Reopened on " ++ createdAt event ++ " by " ++ loginName event
-  formatEvent' event Github.Subscribed =
-    loginName event ++ " is subscribed to receive notifications"
-  formatEvent' event Github.Unsubscribed =
-    loginName event ++ " is unsubscribed from notifications"
-  formatEvent' event Github.Merged =
-    "Issue merged by " ++ loginName event ++ " on " ++ createdAt event ++
-      (withCommitId event $ \commitId -> " in the commit " ++ commitId)
-  formatEvent' event Github.Referenced =
-    withCommitId event $ \commitId ->
-      "Issue referenced from " ++ commitId ++ " by " ++ loginName event
-  formatEvent' event Github.Mentioned =
-    loginName event ++ " was mentioned in the issue's body"
-  formatEvent' event Github.Assigned =
-    "Issue assigned to " ++ loginName event ++ " on " ++ createdAt event
-
-loginName = Github.githubOwnerLogin . Github.eventActor
-createdAt = show . Github.fromGithubDate . Github.eventCreatedAt
-withCommitId event f = maybe "" f (Github.eventCommitId event)
diff --git a/samples/Issues/Events/ShowRepoEvents.hs b/samples/Issues/Events/ShowRepoEvents.hs
deleted file mode 100644
--- a/samples/Issues/Events/ShowRepoEvents.hs
+++ /dev/null
@@ -1,41 +0,0 @@
-module ShowRepoEvents where
-
-import qualified Github.Issues.Events as Github
-import Data.List (intercalate)
-import Data.Maybe (fromJust)
-
-main = do
-  possibleEvents <- Github.eventsForRepo "thoughtbot" "paperclip"
-  case possibleEvents of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right events) -> do
-         putStrLn $ intercalate "\n" $ map formatEvent events
-
-formatEvent event =
-  "Issue #" ++ issueNumber event ++ ": " ++
-    formatEvent' event (Github.eventType event)
-  where
-  formatEvent' event Github.Closed =
-    "closed on " ++ createdAt event ++ " by " ++ loginName event ++
-      withCommitId event (\commitId -> " in the commit " ++ commitId)
-  formatEvent' event Github.Reopened =
-    "reopened on " ++ createdAt event ++ " by " ++ loginName event
-  formatEvent' event Github.Subscribed =
-    loginName event ++ " is subscribed to receive notifications"
-  formatEvent' event Github.Unsubscribed =
-    loginName event ++ " is unsubscribed from notifications"
-  formatEvent' event Github.Merged =
-    "merged by " ++ loginName event ++ " on " ++ createdAt event ++
-      (withCommitId event $ \commitId -> " in the commit " ++ commitId)
-  formatEvent' event Github.Referenced =
-    withCommitId event $ \commitId ->
-      "referenced from " ++ commitId ++ " by " ++ loginName event
-  formatEvent' event Github.Mentioned =
-    loginName event ++ " was mentioned in the issue's body"
-  formatEvent' event Github.Assigned =
-    "assigned to " ++ loginName event ++ " on " ++ createdAt event
-
-loginName = Github.githubOwnerLogin . Github.eventActor
-createdAt = show . Github.fromGithubDate . Github.eventCreatedAt
-withCommitId event f = maybe "" f (Github.eventCommitId event)
-issueNumber = show . Github.issueNumber . fromJust . Github.eventIssue
diff --git a/samples/Issues/Labels/ShowIssueLabels.hs b/samples/Issues/Labels/ShowIssueLabels.hs
deleted file mode 100644
--- a/samples/Issues/Labels/ShowIssueLabels.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module ShowIssueLabels where
-
-import qualified Github.Issues.Labels as Github
-import Data.List (intercalate)
-
-main = do
-  possibleLabels <- Github.labelsOnIssue "thoughtbot" "paperclip" 585
-  case possibleLabels of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right labels) -> do
-         putStrLn $ intercalate "\n" $ map formatLabel labels
-
-formatLabel label =
-  (Github.labelName label) ++ ", colored " ++ (Github.labelColor label)
diff --git a/samples/Issues/Labels/ShowLabel.hs b/samples/Issues/Labels/ShowLabel.hs
deleted file mode 100644
--- a/samples/Issues/Labels/ShowLabel.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module ShowLabel where
-
-import qualified Github.Issues.Labels as Github
-
-main = do
-  possibleLabel <- Github.label "thoughtbot" "paperclip" "bug"
-  case possibleLabel of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right label) -> putStrLn $ formatLabel label
-
-formatLabel label =
-  (Github.labelName label) ++ ", colored " ++ (Github.labelColor label)
-
diff --git a/samples/Issues/Labels/ShowMilestoneLabels.hs b/samples/Issues/Labels/ShowMilestoneLabels.hs
deleted file mode 100644
--- a/samples/Issues/Labels/ShowMilestoneLabels.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-module ShowMilestoneLabels where
-
-import qualified Github.Issues.Labels as Github
-import Data.List (intercalate)
-
-main = do
-  possibleLabels <- Github.labelsOnMilestone "thoughtbot" "paperclip" 2
-  case possibleLabels of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right labels) -> do
-         putStrLn $ intercalate "\n" $ map formatLabel labels
-
-formatLabel label =
-  (Github.labelName label) ++ ", colored " ++ (Github.labelColor label)
-
diff --git a/samples/Issues/Labels/ShowRepoLabels.hs b/samples/Issues/Labels/ShowRepoLabels.hs
deleted file mode 100644
--- a/samples/Issues/Labels/ShowRepoLabels.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module ShowRepoLabels where
-
-import qualified Github.Issues.Labels as Github
-import Data.List (intercalate)
-
-main = do
-  possibleLabels <- Github.labelsOnRepo "thoughtbot" "paperclip"
-  case possibleLabels of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right labels) -> do
-         putStrLn $ intercalate "\n" $ map formatLabel labels
-
-formatLabel label =
-  (Github.labelName label) ++ ", colored " ++ (Github.labelColor label)
diff --git a/samples/Issues/Milestones/ShowMilestone.hs b/samples/Issues/Milestones/ShowMilestone.hs
deleted file mode 100644
--- a/samples/Issues/Milestones/ShowMilestone.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module ShowMilestone where
-
-import qualified Github.Issues.Milestones as Github
-import Data.List (intercalate)
-
-main = do
-  possibleMilestone <- Github.milestone "thoughtbot" "paperclip" 2
-  case possibleMilestone of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right milestone) ->
-         putStrLn $ formatMilestone milestone
-
-formatMilestone milestone =
-  (Github.milestoneTitle milestone) ++ ", as created by " ++
-    (loginName milestone) ++ " on " ++ (createdAt milestone) ++
-    formatDueOn (Github.milestoneDueOn milestone) ++ " and has the " ++
-    (Github.milestoneState milestone) ++ " status"
-
-formatDueOn Nothing = ""
-formatDueOn (Just milestoneDate) = ", is due on " ++ dueOn milestoneDate
-
-loginName = Github.githubOwnerLogin . Github.milestoneCreator
-createdAt = show . Github.fromGithubDate . Github.milestoneCreatedAt
-dueOn = show . Github.fromGithubDate
diff --git a/samples/Issues/Milestones/ShowMilestones.hs b/samples/Issues/Milestones/ShowMilestones.hs
deleted file mode 100644
--- a/samples/Issues/Milestones/ShowMilestones.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module ShowMilestones where
-
-import qualified Github.Issues.Milestones as Github
-import Data.List (intercalate)
-
-main = do
-  possibleMilestones <- Github.milestones "thoughtbot" "paperclip"
-  case possibleMilestones of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right milestones) ->
-         putStrLn $ intercalate "\n\n" $ map formatMilestone milestones
-
-formatMilestone milestone =
-  (Github.milestoneTitle milestone) ++ ", as created by " ++
-    (loginName milestone) ++ " on " ++ (createdAt milestone) ++
-    formatDueOn (Github.milestoneDueOn milestone) ++ " and has the " ++
-    (Github.milestoneState milestone) ++ " status"
-
-formatDueOn Nothing = ""
-formatDueOn (Just milestoneDate) = ", is due on " ++ dueOn milestoneDate
-
-loginName = Github.githubOwnerLogin . Github.milestoneCreator
-createdAt = show . Github.fromGithubDate . Github.milestoneCreatedAt
-dueOn = show . Github.fromGithubDate
diff --git a/samples/Issues/ShowIssue.hs b/samples/Issues/ShowIssue.hs
deleted file mode 100644
--- a/samples/Issues/ShowIssue.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module ShowIssue where
-
-import qualified Github.Issues as Github
-
-main = do
-  possibleIssue <- Github.issue "thoughtbot" "paperclip" 549
-  putStrLn $ either (\e -> "Error: " ++ show e)
-                    formatIssue
-                    possibleIssue
-
-formatIssue issue =
-  (Github.githubOwnerLogin $ Github.issueUser issue) ++
-    " opened this issue " ++
-    (show $ Github.fromGithubDate $ Github.issueCreatedAt issue) ++ "\n" ++
-    (Github.issueState issue) ++ " with " ++
-    (show $ Github.issueComments issue) ++ " comments" ++ "\n\n" ++
-    (Github.issueTitle issue)
diff --git a/samples/Issues/ShowRepoIssues.hs b/samples/Issues/ShowRepoIssues.hs
deleted file mode 100644
--- a/samples/Issues/ShowRepoIssues.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module ShowRepoIssue where
-
-import qualified Github.Issues as Github
-import Data.List (intercalate)
-
-main = do
-  let limitations = [Github.OnlyClosed, Github.Mentions "mike-burns", Github.AssignedTo "jyurek"]
-  possibleIssues <- Github.issuesForRepo "thoughtbot" "paperclip" limitations
-  case possibleIssues of
-       (Left error) -> putStrLn $ "Error: " ++ show error
-       (Right issues) ->
-         putStrLn $ intercalate "\n\n" $ map formatIssue issues
-
-formatIssue issue =
-  (Github.githubOwnerLogin $ Github.issueUser issue) ++
-    " opened this issue " ++
-    (show $ Github.fromGithubDate $ Github.issueCreatedAt issue) ++ "\n" ++
-    (Github.issueState issue) ++ " with " ++
-    (show $ Github.issueComments issue) ++ " comments" ++ "\n\n" ++
-    (Github.issueTitle issue)
-
diff --git a/samples/Organizations/Members/ShowMembers.hs b/samples/Organizations/Members/ShowMembers.hs
deleted file mode 100644
--- a/samples/Organizations/Members/ShowMembers.hs
+++ /dev/null
@@ -1,11 +0,0 @@
-module ShowMembers where
-
-import qualified Github.Organizations.Members as Github
-import Data.List (intercalate)
-
-main = do
-  possibleMembers <- Github.membersOf "thoughtbot"
-  case possibleMembers of
-       (Left error)  -> putStrLn $ "Error: " ++ (show error)
-       (Right members) ->
-         putStrLn $ intercalate "\n" $ map Github.githubOwnerLogin members
diff --git a/samples/Organizations/ShowPublicOrganization.hs b/samples/Organizations/ShowPublicOrganization.hs
deleted file mode 100644
--- a/samples/Organizations/ShowPublicOrganization.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-module ShowPublicOrganization where
-
-import qualified Github.Organizations as Github
-
-main = do
-  possibleOrganization <- Github.publicOrganization "thoughtbot"
-  case possibleOrganization of
-       (Left error)  -> putStrLn $ "Error: " ++ (show error)
-       (Right organization) ->
-         putStrLn $ formatOrganization organization
-
-formatOrganization organization =
-  (maybe "" (\s -> s ++ "\n") (Github.organizationName organization)) ++
-    (Github.organizationHtmlUrl organization) ++
-    (maybe "" (\s -> "\n" ++ s) (Github.organizationBlog organization))
diff --git a/samples/Organizations/ShowPublicOrganizations.hs b/samples/Organizations/ShowPublicOrganizations.hs
deleted file mode 100644
--- a/samples/Organizations/ShowPublicOrganizations.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module ShowPublicOrganizations where
-
-import qualified Github.Organizations as Github
-import Data.List (intercalate)
-
-main = do
-  possibleOrganizations <- Github.publicOrganizationsFor "mike-burns"
-  case possibleOrganizations of
-       (Left error)  -> putStrLn $ "Error: " ++ (show error)
-       (Right organizations) ->
-         putStrLn $ intercalate "\n" $ map formatOrganization organizations
-
-formatOrganization = Github.simpleOrganizationLogin
diff --git a/samples/Pulls/Diff.hs b/samples/Pulls/Diff.hs
deleted file mode 100644
--- a/samples/Pulls/Diff.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module Diff where
-
-import qualified Github.PullRequests as Github
-import Data.List
-
-main = do
-  possiblePullRequestFiles <- Github.pullRequestFiles "thoughtbot" "paperclip" 575
-  case possiblePullRequestFiles of
-       (Left error)    -> putStrLn $ "Error: " ++ (show error)
-       (Right files) -> putStrLn $ intercalate "\n\n" $ map formatFile files
-
-formatFile file =
-  Github.fileFilename file ++ "\n" ++ Github.filePatch file
diff --git a/samples/Pulls/ListPulls.hs b/samples/Pulls/ListPulls.hs
deleted file mode 100644
--- a/samples/Pulls/ListPulls.hs
+++ /dev/null
@@ -1,23 +0,0 @@
-module ListPulls where
-
-import qualified Github.PullRequests as Github
-import Data.List
-
-main = do
-  possiblePullRequests <- Github.pullRequestsFor "thoughtbot" "paperclip"
-  case possiblePullRequests of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right pullRequests) ->
-         putStrLn $ intercalate "\n\n" $ map formatPullRequest pullRequests
-
-formatPullRequest pullRequest =
-  (Github.pullRequestTitle pullRequest) ++ "\n" ++
-    (take 80 $ Github.pullRequestBody pullRequest) ++ "\n" ++
-    (Github.githubOwnerLogin $ Github.pullRequestUser pullRequest) ++
-    " submitted to thoughtbot/paperclip " ++
-    (formatGithubDate $ Github.pullRequestCreatedAt pullRequest) ++
-    " updated " ++
-    (formatGithubDate $ Github.pullRequestUpdatedAt pullRequest) ++ "\n" ++
-    (Github.pullRequestHtmlUrl pullRequest)
-
-formatGithubDate = show . Github.fromGithubDate
diff --git a/samples/Pulls/ReviewComments/ListComments.hs b/samples/Pulls/ReviewComments/ListComments.hs
deleted file mode 100644
--- a/samples/Pulls/ReviewComments/ListComments.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module ListComments where
-
-import qualified Github.PullRequests.ReviewComments as Github
-import Data.List
-
-main = do
-  possiblePullRequestComments <- Github.pullRequestReviewComments "thoughtbot" "factory_girl" 256
-  case possiblePullRequestComments of
-       (Left error)     -> putStrLn $ "Error: " ++ (show error)
-       (Right comments) -> putStrLn $ intercalate "\n\n" $ map formatComment comments
-
-formatComment :: Github.Comment -> String
-formatComment comment =
-  "Author: " ++ (formatAuthor $ Github.commentUser comment) ++
-    "\nUpdated: " ++ (show $ Github.commentUpdatedAt comment) ++
-    (maybe "" ("\nURL: "++) $ Github.commentHtmlUrl comment) ++
-    "\n\n" ++ (Github.commentBody comment)
-
-formatAuthor :: Github.GithubOwner -> String
-formatAuthor user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
diff --git a/samples/Pulls/ReviewComments/ShowComment.hs b/samples/Pulls/ReviewComments/ShowComment.hs
deleted file mode 100644
--- a/samples/Pulls/ReviewComments/ShowComment.hs
+++ /dev/null
@@ -1,22 +0,0 @@
-module ShowComments where
-
-import qualified Github.PullRequests.ReviewComments as Github
-import Data.List
-
-main = do
-  possiblePullRequestComment <- Github.pullRequestReviewComment "thoughtbot" "factory_girl" 301819
-  case possiblePullRequestComment of
-       (Left error)     -> putStrLn $ "Error: " ++ (show error)
-       (Right comment) -> putStrLn $ formatComment comment
-
-formatComment :: Github.Comment -> String
-formatComment comment =
-  "Author: " ++ (formatAuthor $ Github.commentUser comment) ++
-    "\nUpdated: " ++ (show $ Github.commentUpdatedAt comment) ++
-    (maybe "" ("\nURL: "++) $ Github.commentHtmlUrl comment) ++
-    "\n\n" ++ (Github.commentBody comment)
-
-formatAuthor :: Github.GithubOwner -> String
-formatAuthor user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
-
diff --git a/samples/Pulls/ShowCommits.hs b/samples/Pulls/ShowCommits.hs
deleted file mode 100644
--- a/samples/Pulls/ShowCommits.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module ShowCommits where
-
-import qualified Github.PullRequests as Github
-import Data.List
-import Data.Maybe
-
-main = do
-  possiblePullRequestCommits <- Github.pullRequestCommits "thoughtbot" "paperclip" 575
-  case possiblePullRequestCommits of
-       (Left error)    -> putStrLn $ "Error: " ++ (show error)
-       (Right commits) -> putStrLn $ intercalate "\n" $ map formatCommit commits
-
-formatCommit commit =
-  (formatAuthor $ Github.gitCommitAuthor gitCommit) ++ "\n" ++
-    (maybe "unknown SHA" id $ Github.gitCommitSha gitCommit) ++ "\n" ++
-    (Github.gitCommitMessage gitCommit)
-  where gitCommit = Github.commitGitCommit commit
-
-formatAuthor :: Github.GitUser -> String
-formatAuthor author =
-  (Github.gitUserName author) ++ " <" ++ (Github.gitUserEmail author) ++ ">"
diff --git a/samples/Pulls/ShowPull.hs b/samples/Pulls/ShowPull.hs
deleted file mode 100644
--- a/samples/Pulls/ShowPull.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module ShowPull where
-
-import qualified Github.PullRequests as Github
-import Data.List
-
-main = do
-  possiblePullRequest <- Github.pullRequest "thoughtbot" "paperclip" 575
-  case possiblePullRequest of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right pullRequest) -> putStrLn $ formatPullRequest pullRequest
-
-formatPullRequest p =
-  (Github.githubOwnerLogin $ Github.detailedPullRequestUser p) ++
-    " opened this pull request " ++
-    (formatGithubDate $ Github.detailedPullRequestCreatedAt p) ++ "\n" ++
-    (Github.detailedPullRequestTitle p) ++ "\n" ++
-    (Github.detailedPullRequestBody p) ++ "\n" ++
-    (Github.detailedPullRequestState p) ++ "\n" ++
-    "+" ++ (show $ Github.detailedPullRequestAdditions p) ++ " additions\n" ++
-    "-" ++ (show $ Github.detailedPullRequestDeletions p) ++ " deletions\n" ++
-    (show $ Github.detailedPullRequestComments p) ++ " comments\n" ++
-    (Github.detailedPullRequestHtmlUrl p)
-
-formatGithubDate = show . Github.fromGithubDate
diff --git a/samples/Repos/Collaborators/IsCollaborator.hs b/samples/Repos/Collaborators/IsCollaborator.hs
deleted file mode 100644
--- a/samples/Repos/Collaborators/IsCollaborator.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module IsCollaborator where
-
-import qualified Github.Repos.Collaborators as Github
-import Data.List
-
-main = do
-  let userName = "ubuwaits"
-  possiblyIsCollaborator <- Github.isCollaboratorOn userName "thoughtbot" "paperclip"
-  case possiblyIsCollaborator of
-    (Left error) -> putStrLn $ "Error: " ++ (show error)
-    (Right True) ->
-      putStrLn $ userName ++ " is a collaborator on thoughtbot's paperclip"
-    (Right False) ->
-      putStrLn $ userName ++ " does not collaborate on thoughtbot's paperclip"
diff --git a/samples/Repos/Collaborators/ListCollaborators.hs b/samples/Repos/Collaborators/ListCollaborators.hs
deleted file mode 100644
--- a/samples/Repos/Collaborators/ListCollaborators.hs
+++ /dev/null
@@ -1,15 +0,0 @@
-module ListCollaborators where
-
-import qualified Github.Repos.Collaborators as Github
-import Data.List
-
-main = do
-  possibleCollaborators <- Github.collaboratorsOn "thoughtbot" "paperclip"
-  case possibleCollaborators of
-    (Left error) -> putStrLn $ "Error: " ++ (show error)
-    (Right collaborators) ->
-      putStrLn $ intercalate "\n" $ map formatAuthor collaborators
-
-formatAuthor :: Github.GithubOwner -> String
-formatAuthor user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
diff --git a/samples/Repos/Commits/CommitComment.hs b/samples/Repos/Commits/CommitComment.hs
deleted file mode 100644
--- a/samples/Repos/Commits/CommitComment.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module CommitComment where
-
-import qualified Github.Repos.Commits as Github
-import Data.Maybe (maybe)
-
-main = do
-  possibleComment <- Github.commitCommentFor "thoughtbot" "paperclip" "669575"
-  case possibleComment of
-    (Left error)    -> putStrLn $ "Error: " ++ (show error)
-    (Right comment) -> putStrLn $ formatComment comment
-
-formatComment :: Github.Comment -> String
-formatComment comment =
-  "Author: " ++ (formatAuthor $ Github.commentUser comment) ++
-    "\nUpdated: " ++ (show $ Github.commentUpdatedAt comment) ++
-    (maybe "" ("\nURL: "++) $ Github.commentHtmlUrl comment) ++
-    "\n\n" ++ (Github.commentBody comment)
-
-formatAuthor :: Github.GithubOwner -> String
-formatAuthor user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
diff --git a/samples/Repos/Commits/CommitComments.hs b/samples/Repos/Commits/CommitComments.hs
deleted file mode 100644
--- a/samples/Repos/Commits/CommitComments.hs
+++ /dev/null
@@ -1,22 +0,0 @@
-module CommitComments where
-
-import qualified Github.Repos.Commits as Github
-import Data.List
-import Data.Maybe (maybe)
-
-main = do
-  possibleComments <- Github.commitCommentsFor "thoughtbot" "paperclip" "41f685f6e01396936bb8cd98e7cca517e2c7d96b"
-  case possibleComments of
-    (Left error)    -> putStrLn $ "Error: " ++ (show error)
-    (Right comments) -> putStrLn $ intercalate "\n\n" $  map formatComment comments
-
-formatComment :: Github.Comment -> String
-formatComment comment =
-  "Author: " ++ (formatAuthor $ Github.commentUser comment) ++
-    "\nUpdated: " ++ (show $ Github.commentUpdatedAt comment) ++
-    (maybe "" ("\nURL: "++) $ Github.commentHtmlUrl comment) ++
-    "\n\n" ++ (Github.commentBody comment)
-
-formatAuthor :: Github.GithubOwner -> String
-formatAuthor user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
diff --git a/samples/Repos/Commits/GitDiff.hs b/samples/Repos/Commits/GitDiff.hs
deleted file mode 100644
--- a/samples/Repos/Commits/GitDiff.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module GitDiff where
-
-import qualified Github.Repos.Commits as Github
-import Data.List
-
-main = do
-  possibleDiff <- Github.diff "thoughtbot" "paperclip" "41f685f6e01396936bb8cd98e7cca517e2c7d96b" "HEAD"
-  either (\error -> putStrLn $ "Error: " ++ (show error))
-         (putStrLn . showDiff)
-         possibleDiff
-
-showDiff diff =
- intercalate "\n\n" $ map Github.filePatch $ Github.diffFiles diff
diff --git a/samples/Repos/Commits/GitLog.hs b/samples/Repos/Commits/GitLog.hs
deleted file mode 100644
--- a/samples/Repos/Commits/GitLog.hs
+++ /dev/null
@@ -1,23 +0,0 @@
-module GitLog where
-
-import qualified Github.Repos.Commits as Github
-import Data.List
-
-main = do
-  possibleCommits <- Github.commitsFor "thoughtbot" "paperclip"
-  case possibleCommits of
-    (Left error)    -> putStrLn $ "Error: " ++ (show error)
-    (Right commits) -> putStrLn $ intercalate "\n\n" $ map formatCommit commits
-
-formatCommit :: Github.Commit -> String
-formatCommit commit =
-  "commit " ++ (Github.commitSha commit) ++
-    "\nAuthor: " ++ (formatAuthor author) ++
-    "\nDate:   " ++ (show $ Github.fromGithubDate $ Github.gitUserDate author) ++
-    "\n\n\t" ++ (Github.gitCommitMessage gitCommit)
-  where author = Github.gitCommitAuthor gitCommit
-        gitCommit = Github.commitGitCommit commit
-
-formatAuthor :: Github.GitUser -> String
-formatAuthor author =
-  (Github.gitUserName author) ++ " <" ++ (Github.gitUserEmail author) ++ ">"
diff --git a/samples/Repos/Commits/GitShow.hs b/samples/Repos/Commits/GitShow.hs
deleted file mode 100644
--- a/samples/Repos/Commits/GitShow.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-module GitShow where
-
-import qualified Github.Repos.Commits as Github
-import Data.List
-
-main = do
-  possibleCommit <- Github.commit "thoughtbot" "paperclip" "bc5c51d1ece1ee45f94b056a0f5a1674d7e8cba9"
-  case possibleCommit of
-    (Left error)    -> putStrLn $ "Error: " ++ (show error)
-    (Right commit) -> putStrLn $ formatCommit commit
-
-formatCommit :: Github.Commit -> String
-formatCommit commit =
-  "commit " ++ (Github.commitSha commit) ++
-    "\nAuthor: " ++ (formatAuthor author) ++
-    "\nDate:   " ++ (show $ Github.fromGithubDate $ Github.gitUserDate author) ++
-    "\n\n\t" ++ (Github.gitCommitMessage gitCommit) ++ "\n" ++
-    patches
-  where author = Github.gitCommitAuthor gitCommit
-        gitCommit = Github.commitGitCommit commit
-        patches = 
-          intercalate "\n" $ map Github.filePatch $ Github.commitFiles commit
-
-formatAuthor :: Github.GitUser -> String
-formatAuthor author =
-  (Github.gitUserName author) ++ " <" ++ (Github.gitUserEmail author) ++ ">"
-
diff --git a/samples/Repos/Commits/RepoComments.hs b/samples/Repos/Commits/RepoComments.hs
deleted file mode 100644
--- a/samples/Repos/Commits/RepoComments.hs
+++ /dev/null
@@ -1,22 +0,0 @@
-module RepoComments where
-
-import qualified Github.Repos.Commits as Github
-import Data.List
-import Data.Maybe (maybe)
-
-main = do
-  possibleComments <- Github.commentsFor "thoughtbot" "paperclip"
-  case possibleComments of
-    (Left error)    -> putStrLn $ "Error: " ++ (show error)
-    (Right comments) -> putStrLn $ intercalate "\n\n" $  map formatComment comments
-
-formatComment :: Github.Comment -> String
-formatComment comment =
-  "Author: " ++ (formatAuthor $ Github.commentUser comment) ++
-    "\nUpdated: " ++ (show $ Github.commentUpdatedAt comment) ++
-    (maybe "" ("\nURL: "++) $ Github.commentHtmlUrl comment) ++
-    "\n\n" ++ (Github.commentBody comment)
-
-formatAuthor :: Github.GithubOwner -> String
-formatAuthor user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
diff --git a/samples/Repos/Forks/ListForks.hs b/samples/Repos/Forks/ListForks.hs
deleted file mode 100644
--- a/samples/Repos/Forks/ListForks.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-module ListForks where
-
-import qualified Github.Repos.Forks as Github
-import Data.List
-
-main = do
-  possibleForks <- Github.forksFor "thoughtbot" "paperclip"
-  putStrLn $ either (("Error: "++) . show)
-                    (intercalate "\n\n" . map formatFork)
-                    possibleForks
-
-formatFork fork =
-  (Github.githubOwnerLogin $ Github.repoOwner fork) ++ "\t" ++
-  (formatPushedAt $ Github.repoPushedAt fork) ++ "\n" ++
-  (formatCloneUrl $ Github.repoCloneUrl fork)
-
-formatPushedAt Nothing         = ""
-formatPushedAt (Just pushedAt) = show $ Github.fromGithubDate pushedAt
-
-formatCloneUrl Nothing         = ""
-formatCloneUrl (Just cloneUrl) = cloneUrl
diff --git a/samples/Repos/ListBranches.hs b/samples/Repos/ListBranches.hs
deleted file mode 100644
--- a/samples/Repos/ListBranches.hs
+++ /dev/null
@@ -1,11 +0,0 @@
-module ListBranches where
-
-import qualified Github.Repos as Github
-import Data.List
-
-main = do
-  possibleBranches <- Github.branchesFor "thoughtbot" "paperclip"
-  case possibleBranches of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right branches) ->
-         putStrLn $ intercalate "\n" $ map Github.branchName branches
diff --git a/samples/Repos/ListContributors.hs b/samples/Repos/ListContributors.hs
deleted file mode 100644
--- a/samples/Repos/ListContributors.hs
+++ /dev/null
@@ -1,16 +0,0 @@
-module ListContributors where
-
-import qualified Github.Repos as Github
-import Data.List
-
-main = do
-  possibleContributors <- Github.contributors "thoughtbot" "paperclip"
-  case possibleContributors of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right contributors) ->
-         putStrLn $ intercalate "\n" $ map formatContributor contributors
-
-formatContributor (Github.KnownContributor contributions _ login _ _ _) =
-  (show $ contributions) ++ "\t" ++ login
-formatContributor (Github.AnonymousContributor contributions name) =
-  (show $ contributions) ++ "\t" ++ name
diff --git a/samples/Repos/ListContributorsWithAnonymous.hs b/samples/Repos/ListContributorsWithAnonymous.hs
deleted file mode 100644
--- a/samples/Repos/ListContributorsWithAnonymous.hs
+++ /dev/null
@@ -1,16 +0,0 @@
-module ListContributorsWithAnonymous where
-
-import qualified Github.Repos as Github
-import Data.List
-
-main = do
-  possibleContributors <- Github.contributorsWithAnonymous "thoughtbot" "paperclip"
-  case possibleContributors of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right contributors) ->
-         putStrLn $ intercalate "\n" $ map formatContributor contributors
-
-formatContributor (Github.KnownContributor contributions _ login _ _ _) =
-  (show $ contributions) ++ "\t" ++ login
-formatContributor (Github.AnonymousContributor contributions name) =
-  (show $ contributions) ++ "\t" ++ name
diff --git a/samples/Repos/ListLanguages.hs b/samples/Repos/ListLanguages.hs
deleted file mode 100644
--- a/samples/Repos/ListLanguages.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module ListLanguages where
-
-import qualified Github.Repos as Github
-import Data.List
-
-main = do
-  possibleLanguages <- Github.languagesFor "mike-burns" "ohlaunch"
-  case possibleLanguages of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right languages) ->
-         putStrLn $ intercalate "\n" $ map formatLanguage languages
-
-formatLanguage (Github.Language name characterCount) =
-  name ++ "\t" ++ show characterCount
diff --git a/samples/Repos/ListOrgRepos.hs b/samples/Repos/ListOrgRepos.hs
deleted file mode 100644
--- a/samples/Repos/ListOrgRepos.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-module ListOrgRepos where
-
-import qualified Github.Repos as Github
-import Data.List
-import Data.Maybe
-
-main = do
-  possibleRepos <- Github.organizationRepos "thoughtbot"
-  case possibleRepos of
-       (Left error)  -> putStrLn $ "Error: " ++ (show error)
-       (Right repos) -> putStrLn $ intercalate "\n\n" $ map formatRepo repos
-
-formatRepo repo =
-  (Github.repoName repo) ++ "\t" ++
-    (fromMaybe "" $ Github.repoDescription repo) ++ "\n" ++
-    (Github.repoHtmlUrl repo) ++ "\n" ++
-    (fromMaybe "" $ Github.repoCloneUrl repo) ++ "\t" ++
-    (formatDate $ Github.repoUpdatedAt repo) ++ "\n" ++
-    formatLanguage (Github.repoLanguage repo) ++
-    "watchers: " ++ (show $ Github.repoWatchers repo) ++ "\t" ++
-    "forks: " ++ (show $ Github.repoForks repo)
-
-formatDate (Just date) = show . Github.fromGithubDate $ date
-formatDate Nothing = "????"
-
-formatLanguage (Just language) = "language: " ++ language ++ "\t"
-formatLanguage Nothing = ""
diff --git a/samples/Repos/ListTags.hs b/samples/Repos/ListTags.hs
deleted file mode 100644
--- a/samples/Repos/ListTags.hs
+++ /dev/null
@@ -1,11 +0,0 @@
-module ListTags where
-
-import qualified Github.Repos as Github
-import Data.List
-
-main = do
-  possibleTags <- Github.tagsFor "thoughtbot" "paperclip"
-  case possibleTags of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right tags) ->
-         putStrLn $ intercalate "\n" $ map Github.tagName tags
diff --git a/samples/Repos/ListUserRepos.hs b/samples/Repos/ListUserRepos.hs
deleted file mode 100644
--- a/samples/Repos/ListUserRepos.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-module ListUserRepos where
-
-import qualified Github.Repos as Github
-import Data.List
-import Data.Maybe
-
-main = do
-  possibleRepos <- Github.userRepos "mike-burns" Github.Owner
-  case possibleRepos of
-       (Left error)  -> putStrLn $ "Error: " ++ (show error)
-       (Right repos) -> putStrLn $ intercalate "\n\n" $ map formatRepo repos
-
-formatRepo repo =
-  (Github.repoName repo) ++ "\t" ++
-    (fromMaybe "" $ Github.repoDescription repo) ++ "\n" ++
-    (Github.repoHtmlUrl repo) ++ "\n" ++
-    (fromMaybe "" $ Github.repoCloneUrl repo) ++ "\t" ++
-    (formatDate $ Github.repoUpdatedAt repo) ++ "\n" ++
-    formatLanguage (Github.repoLanguage repo) ++
-    "watchers: " ++ (show $ Github.repoWatchers repo) ++ "\t" ++
-    "forks: " ++ (show $ Github.repoForks repo)
-
-formatDate (Just date) = show . Github.fromGithubDate $ date
-formatDate Nothing = ""
-
-formatLanguage (Just language) = "language: " ++ language ++ "\t"
-formatLanguage Nothing = ""
diff --git a/samples/Repos/ShowRepo.hs b/samples/Repos/ShowRepo.hs
deleted file mode 100644
--- a/samples/Repos/ShowRepo.hs
+++ /dev/null
@@ -1,26 +0,0 @@
-module ShowRepo where
-
-import qualified Github.Repos as Github
-import Data.List
-import Data.Maybe
-
-main = do
-  possibleRepo <- Github.userRepo "mike-burns" "trylambda"
-  case possibleRepo of
-       (Left error) -> putStrLn $ "Error: " ++ (show error)
-       (Right repo) -> putStrLn $ formatRepo repo
-
-formatRepo repo =
-  (Github.repoName repo) ++ "\t" ++
-    (fromMaybe "" $ Github.repoDescription repo) ++ "\n" ++
-    (Github.repoHtmlUrl repo) ++ "\n" ++
-    (Github.repoCloneUrl repo) ++ "\t" ++
-    (formatDate $ Github.repoUpdatedAt repo) ++ "\n" ++
-    formatLanguage (Github.repoLanguage repo) ++
-    "watchers: " ++ (show $ Github.repoWatchers repo) ++ "\t" ++
-    "forks: " ++ (show $ Github.repoForks repo)
-
-formatDate = show . Github.fromGithubDate
-
-formatLanguage (Just language) = "language: " ++ language ++ "\t"
-formatLanguage Nothing = ""
diff --git a/samples/Repos/Starring/ListStarred.hs b/samples/Repos/Starring/ListStarred.hs
deleted file mode 100644
--- a/samples/Repos/Starring/ListStarred.hs
+++ /dev/null
@@ -1,25 +0,0 @@
-module ListStarred where
-
-import qualified Github.Repos.Starring as Github
-import Data.List (intercalate)
-import Data.Maybe (fromMaybe)
-
-main = do
-  possibleRepos <- Github.reposStarredBy Nothing "mike-burns"
-  putStrLn $ either (("Error: "++) . show)
-                    (intercalate "\n\n" . map formatRepo)
-                    possibleRepos
-
-formatRepo repo =
-  (Github.repoName repo) ++ "\t" ++
-    (fromMaybe "" $ Github.repoDescription repo) ++ "\n" ++
-    (Github.repoHtmlUrl repo) ++ "\n" ++
-    (fromMaybe "" $ Github.repoCloneUrl repo) ++ "\t" ++
-    (formatDate $ Github.repoUpdatedAt repo) ++ "\n" ++
-    formatLanguage (Github.repoLanguage repo)
-
-formatDate (Just date) = show . Github.fromGithubDate $ date
-formatDate Nothing = ""
-
-formatLanguage (Just language) = "language: " ++ language ++ "\t"
-formatLanguage Nothing = ""
diff --git a/samples/Repos/Watching/ListWatched.hs b/samples/Repos/Watching/ListWatched.hs
deleted file mode 100644
--- a/samples/Repos/Watching/ListWatched.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-module ListWatched where
-
-import qualified Github.Repos.Watching as Github
-import Data.List (intercalate)
-import Data.Maybe (fromMaybe)
-
-main = do
-  possibleRepos <- Github.reposWatchedBy "mike-burns"
-  putStrLn $ either (("Error: "++) . show)
-                    (intercalate "\n\n" . map formatRepo)
-                    possibleRepos
-
-formatRepo repo =
-  (Github.repoName repo) ++ "\t" ++
-    (fromMaybe "" $ Github.repoDescription repo) ++ "\n" ++
-    (Github.repoHtmlUrl repo) ++ "\n" ++
-    (fromMaybe "" $ Github.repoCloneUrl repo) ++ "\t" ++
-    (formatDate $ Github.repoUpdatedAt repo) ++ "\n" ++
-    formatLanguage (Github.repoLanguage repo) ++
-    "watchers: " ++ (show $ Github.repoWatchers repo) ++ "\t" ++
-    "forks: " ++ (show $ Github.repoForks repo)
-
-formatDate (Just date) = show . Github.fromGithubDate $ date
-formatDate Nothing = ""
-
-formatLanguage (Just language) = "language: " ++ language ++ "\t"
-formatLanguage Nothing = ""
diff --git a/samples/Repos/Watching/ListWatchers.hs b/samples/Repos/Watching/ListWatchers.hs
deleted file mode 100644
--- a/samples/Repos/Watching/ListWatchers.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module ListWatchers where
-
-import qualified Github.Repos.Watching as Github
-import Data.List (intercalate)
-
-main = do
-  possibleWatchers <- Github.watchersFor "doubledrones" "git-annex"
-  putStrLn $ either (("Error: "++) . show)
-                    (intercalate "\n" . map formatWatcher)
-                    possibleWatchers
-
-formatWatcher :: Github.GithubOwner -> String
-formatWatcher user =
-  (Github.githubOwnerLogin user) ++ " (" ++ (Github.githubOwnerUrl user) ++ ")"
diff --git a/samples/Repos/Webhooks/CreateWebhook.hs b/samples/Repos/Webhooks/CreateWebhook.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/CreateWebhook.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module CreateWebhook where
-
-import Github.Repos.Webhooks
-import qualified Github.Auth as Auth
-import Github.Data.Definitions
-import qualified Data.Map as M
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  let config = M.fromList [("url", "https://foo3.io"), ("content_type", "application/json"), ("insecure_ssl", "1")]
-  let webhookDef = NewRepoWebhook {
-        newRepoWebhookName = "web",
-        newRepoWebhookConfig = config,
-        newRepoWebhookEvents = Just [WebhookWildcardEvent],
-        newRepoWebhookActive = Just True
-      }
-  newWebhook <- createRepoWebhook' auth "repoOwner" "repoName" webhookDef
-  case newWebhook of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right webhook) -> putStrLn $ formatRepoWebhook webhook
-
-formatRepoWebhook :: RepoWebhook -> String
-formatRepoWebhook (RepoWebhook _ _ _ name _ _ _ _ _ _) = show name
diff --git a/samples/Repos/Webhooks/DeleteWebhook.hs b/samples/Repos/Webhooks/DeleteWebhook.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/DeleteWebhook.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-module DeleteWebhook where
-
-import Github.Repos.Webhooks
-import qualified Github.Auth as Auth
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  resp <- deleteRepoWebhook' auth "repoOwner" "repoName" 123
-  case resp of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right stat) -> putStrLn $ "Resp: " ++ (show stat)
diff --git a/samples/Repos/Webhooks/EditWebhook.hs b/samples/Repos/Webhooks/EditWebhook.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/EditWebhook.hs
+++ /dev/null
@@ -1,23 +0,0 @@
-module EditWebhook where
-
-import Github.Repos.Webhooks
-import qualified Github.Auth as Auth
-import Github.Data.Definitions
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  let editWebhookDef = EditRepoWebhook {
-        editRepoWebhookRemoveEvents = Just [WebhookWildcardEvent],
-        editRepoWebhookAddEvents = Just [WebhookCommitCommentEvent, WebhookGollumEvent],
-        editRepoWebhookConfig = Nothing,
-        editRepoWebhookEvents = Nothing,
-        editRepoWebhookActive = Just True
-      }
-  newWebhook <- editRepoWebhook' auth "repoOwner" "repoName" 123 editWebhookDef
-  case newWebhook of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right webhook) -> putStrLn $ formatRepoWebhook webhook
-
-formatRepoWebhook :: RepoWebhook -> String
-formatRepoWebhook (RepoWebhook _ _ _ name _ _ _ _ _ _) = show name
diff --git a/samples/Repos/Webhooks/ListWebhook.hs b/samples/Repos/Webhooks/ListWebhook.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/ListWebhook.hs
+++ /dev/null
@@ -1,16 +0,0 @@
-module ListWebhook where
-
-import qualified Github.Repos.Webhooks as W
-import qualified Github.Auth as Auth
-import qualified Github.Data.Definitions as Def
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  possibleWebhook <- W.webhookFor' auth "repoOwner" "repoName" 123
-  case possibleWebhook of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right webhook) -> putStrLn $ formatRepoWebhook webhook
-
-formatRepoWebhook :: Def.RepoWebhook -> String
-formatRepoWebhook (Def.RepoWebhook _ _ _ name _ _ _ _ _ _) = show name
diff --git a/samples/Repos/Webhooks/ListWebhooks.hs b/samples/Repos/Webhooks/ListWebhooks.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/ListWebhooks.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-module ListWebhooks where
-
-import qualified Github.Repos.Webhooks as W
-import qualified Github.Auth as Auth
-import qualified Github.Data.Definitions as Def
-import Data.List
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  possibleWebhooks <- W.webhooksFor' auth "repoOwner" "repoName"
-  case possibleWebhooks of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right webhooks) -> putStrLn $ intercalate "\n" $ map formatRepoWebhook webhooks
-
-formatRepoWebhook :: Def.RepoWebhook -> String
-formatRepoWebhook (Def.RepoWebhook _ _ _ name _ _ _ _ _ _) = show name
diff --git a/samples/Repos/Webhooks/PingWebhook.hs b/samples/Repos/Webhooks/PingWebhook.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/PingWebhook.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-module PingWebhook where
-
-import Github.Repos.Webhooks
-import qualified Github.Auth as Auth
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  resp <- pingRepoWebhook' auth "repoOwner" "repoName" 123
-  case resp of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right stat) -> putStrLn $ "Resp: " ++ (show stat)
diff --git a/samples/Repos/Webhooks/TestPushWebhook.hs b/samples/Repos/Webhooks/TestPushWebhook.hs
deleted file mode 100644
--- a/samples/Repos/Webhooks/TestPushWebhook.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-module TestPushWebhook where
-
-import Github.Repos.Webhooks
-import qualified Github.Auth as Auth
-
-main :: IO ()
-main = do
-  let auth = Auth.GithubOAuth "oauthtoken"
-  resp <- testPushRepoWebhook' auth "repoOwner" "repoName" 123
-  case resp of
-    (Left err) -> putStrLn $ "Error: " ++ (show err)
-    (Right stat) -> putStrLn $ "Resp: " ++ (show stat)
diff --git a/samples/Search/SearchRepos.hs b/samples/Search/SearchRepos.hs
deleted file mode 100644
--- a/samples/Search/SearchRepos.hs
+++ /dev/null
@@ -1,56 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SearchRepos where
-
-import qualified Github.Search as Github
-import qualified Github.Data as Github
-import Control.Monad (forM,forM_)
-import Data.Maybe (fromMaybe)
-import Data.List (intercalate)
-import System.Environment (getArgs)
-import Text.Printf (printf)
-import Data.Time.Clock (getCurrentTime, UTCTime(..))
-import Data.Time.LocalTime (utc,utcToLocalTime,localDay,localTimeOfDay,TimeOfDay(..))
-import Data.Time.Calendar (toGregorian)
-
-main = do
-  args <- getArgs
-  date <- case args of
-            (x:_)     -> return x
-            otherwise -> today
-  let query = "q=language%3Ahaskell created%3A>" ++ date ++ "&per_page=100"
-  let auth = Nothing
-  result <- Github.searchRepos' auth query
-  case result of
-    Left e  -> putStrLn $ "Error: " ++ show e
-    Right r -> do forM_ (Github.searchReposRepos r) (\r -> do
-                    putStrLn $ formatRepo r
-                    putStrLn ""
-                    )
-                  putStrLn $ "Count: " ++ show n ++ " Haskell repos created since " ++ date
-      where n = Github.searchReposTotalCount r
-
--- | return today (in UTC) formatted as YYYY-MM-DD
-today :: IO String
-today = do
-  now <- getCurrentTime
-  let day = localDay $ utcToLocalTime utc now
-      (y,m,d) = toGregorian day
-   in return $ printf "%d-%02d-%02d" y m d
-
-formatRepo :: Github.Repo -> String
-formatRepo r =
-  let fields = [ ("Name", Github.repoName)
-                 ,("URL",  Github.repoHtmlUrl)
-                 ,("Description", orEmpty . Github.repoDescription)
-                 ,("Created-At", formatMaybeDate . Github.repoCreatedAt)
-                 ,("Pushed-At", formatMaybeDate . Github.repoPushedAt)
-               ]
-  in intercalate "\n" $ map fmt fields
-    where fmt (s,f) = fill 12 (s ++ ":") ++ " " ++ f r
-          orEmpty = fromMaybe ""
-          fill n s = s ++ replicate n' ' '
-            where n' = max 0 (n - length s) 
-
-formatMaybeDate = maybe "???" formatDate
-
-formatDate = show . Github.fromGithubDate
diff --git a/samples/Users/Followers/ListFollowers.hs b/samples/Users/Followers/ListFollowers.hs
deleted file mode 100644
--- a/samples/Users/Followers/ListFollowers.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-module ListFollowers where
-
-import qualified Github.Users.Followers as Github
-import Data.List (intercalate)
-
-main = do
-  possibleUsers <- Github.usersFollowing "mike-burns"
-  putStrLn $ either (("Error: "++) . show)
-                    (intercalate "\n" . map formatUser)
-                    possibleUsers
-
-formatUser = Github.githubOwnerLogin
diff --git a/samples/Users/Followers/ListFollowing.hs b/samples/Users/Followers/ListFollowing.hs
deleted file mode 100644
--- a/samples/Users/Followers/ListFollowing.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module ListFollowing where
-
-import qualified Github.Users.Followers as Github
-import Data.List (intercalate)
-
-main = do
-  possibleUsers <- Github.usersFollowedBy "mike-burns"
-  putStrLn $ either (("Error: "++) . show)
-                    (intercalate "\n" . map formatUser)
-                    possibleUsers
-
-formatUser = Github.githubOwnerLogin
-
diff --git a/samples/Users/ShowUser.hs b/samples/Users/ShowUser.hs
deleted file mode 100644
--- a/samples/Users/ShowUser.hs
+++ /dev/null
@@ -1,52 +0,0 @@
-module ShowUser where
-
-import qualified Github.Users as Github
-import Data.Maybe (fromMaybe)
-
-main = do
-  possibleUser <- Github.userInfoFor "mike-burns"
-  putStrLn $ either (("Error: "++) . show) formatUser possibleUser
-
-formatUser user@(Github.DetailedOrganization {}) =
-  "Organization: " ++ (formatName userName login) ++ "\t" ++
-    (fromMaybe "" company) ++ "\t" ++
-    (fromMaybe "" location) ++ "\n" ++
-    (fromMaybe "" blog) ++ "\t" ++ "\n" ++
-    htmlUrl ++ "\t" ++ (formatDate createdAt) ++ "\n\n" ++
-    (fromMaybe "" bio)
-  where
-    userName = Github.detailedOwnerName user
-    login = Github.detailedOwnerLogin user
-    company = Github.detailedOwnerCompany user
-    location = Github.detailedOwnerLocation user
-    blog = Github.detailedOwnerBlog user
-    htmlUrl = Github.detailedOwnerHtmlUrl user
-    createdAt = Github.detailedOwnerCreatedAt user
-    bio = Github.detailedOwnerBio user
-
-formatUser user@(Github.DetailedUser {}) =
-  (formatName userName login) ++ "\t" ++ (fromMaybe "" company) ++ "\t" ++
-    (fromMaybe "" location) ++ "\n" ++
-    (fromMaybe "" blog) ++ "\t" ++ "<" ++ (fromMaybe "" email) ++ ">" ++ "\n" ++
-    htmlUrl ++ "\t" ++ (formatDate createdAt) ++ "\n" ++
-    "hireable: " ++ (formatHireable (fromMaybe False isHireable)) ++ "\n\n" ++
-    (fromMaybe "" bio)
-  where
-    userName = Github.detailedOwnerName user
-    login = Github.detailedOwnerLogin user
-    company = Github.detailedOwnerCompany user
-    location = Github.detailedOwnerLocation user
-    blog = Github.detailedOwnerBlog user
-    email = Github.detailedOwnerEmail user 
-    htmlUrl = Github.detailedOwnerHtmlUrl user
-    createdAt = Github.detailedOwnerCreatedAt user
-    isHireable = Github.detailedOwnerHireable user
-    bio = Github.detailedOwnerBio user
-
-formatName Nothing login = login
-formatName (Just name) login = name ++ "(" ++ login ++ ")"
-
-formatHireable True = "yes"
-formatHireable False = "no"
-
-formatDate = show . Github.fromGithubDate
diff --git a/spec/GitHub/Actions/ArtifactsSpec.hs b/spec/GitHub/Actions/ArtifactsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/Actions/ArtifactsSpec.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.Actions.ArtifactsSpec where
+
+import qualified GitHub as GH
+
+import Prelude ()
+import Prelude.Compat
+
+import           Data.Aeson         (eitherDecodeStrict)
+import           Data.ByteString    (ByteString)
+import           Data.Either.Compat (isRight)
+import           Data.FileEmbed     (embedFile)
+import           Data.Foldable      (for_)
+import           Data.String        (fromString)
+import qualified Data.Vector        as V
+import           System.Environment (lookupEnv)
+import           Test.Hspec
+                 (Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (GH.Auth -> IO ()) -> IO ()
+withAuth action = do
+    mtoken <- lookupEnv "GITHUB_TOKEN"
+    case mtoken of
+        Nothing    -> pendingWith "no GITHUB_TOKEN"
+        Just token -> action (GH.OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+    describe "artifactsForR" $ do
+        it "works" $ withAuth $ \auth -> for_ repos $ \(owner, repo) -> do
+            cs <- GH.executeRequest auth $
+                GH.artifactsForR owner repo mempty GH.FetchAll
+            cs `shouldSatisfy` isRight
+
+    describe "decoding artifacts payloads" $ do
+        it "decodes artifacts list payload" $ do
+            GH.withTotalCountTotalCount artifactList `shouldBe` 23809
+            V.length (GH.withTotalCountItems artifactList) `shouldBe` 2
+        it "decodes signle artifact payload" $ do
+            GH.artifactName artifact `shouldBe` "dist-without-markdown"
+            GH.artifactWorkflowRunHeadSha (GH.artifactWorkflowRun artifact) `shouldBe` "601593ecb1d8a57a04700fdb445a28d4186b8954"
+
+  where
+    repos =
+      [ ("thoughtbot", "paperclip")
+      , ("phadej", "github")
+      ]
+
+    artifactList :: GH.WithTotalCount GH.Artifact
+    artifactList =
+        fromRightS (eitherDecodeStrict artifactsListPayload)
+
+    artifact :: GH.Artifact
+    artifact =
+        fromRightS (eitherDecodeStrict artifactPayload)
+
+    artifactsListPayload :: ByteString
+    artifactsListPayload = $(embedFile "fixtures/actions/artifacts-list.json")
+
+    artifactPayload :: ByteString
+    artifactPayload = $(embedFile "fixtures/actions/artifact.json")
diff --git a/spec/GitHub/Actions/CacheSpec.hs b/spec/GitHub/Actions/CacheSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/Actions/CacheSpec.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.Actions.CacheSpec where
+
+import qualified GitHub as GH
+
+import Prelude ()
+import Prelude.Compat
+
+import           Data.Aeson      (eitherDecodeStrict)
+import           Data.ByteString (ByteString)
+import           Data.FileEmbed  (embedFile)
+import qualified Data.Vector     as V
+import           Test.Hspec      (Spec, describe, it, shouldBe)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+spec :: Spec
+spec = do
+    describe "decoding cache payloads" $ do
+        it "decodes cache list payload" $ do
+            V.length (GH.withTotalCountItems cacheList) `shouldBe` 1
+        it "decodes cache usage for repo" $ do
+            GH.repositoryCacheUsageFullName repoCacheUsage `shouldBe` "python/cpython"
+            GH.repositoryCacheUsageActiveCachesSizeInBytes repoCacheUsage `shouldBe` 55000268087
+            GH.repositoryCacheUsageActiveCachesCount repoCacheUsage `shouldBe` 171
+        it "decodes cache usage for org" $ do
+            GH.organizationCacheUsageTotalActiveCachesSizeInBytes orgCacheUsage `shouldBe` 26586
+            GH.organizationCacheUsageTotalActiveCachesCount orgCacheUsage `shouldBe` 1
+
+  where
+    cacheList :: GH.WithTotalCount GH.Cache
+    cacheList =
+        fromRightS (eitherDecodeStrict cacheListPayload)
+
+    repoCacheUsage :: GH.RepositoryCacheUsage
+    repoCacheUsage =
+        fromRightS (eitherDecodeStrict repoCacheUsagePayload)
+
+    orgCacheUsage :: GH.OrganizationCacheUsage
+    orgCacheUsage =
+        fromRightS (eitherDecodeStrict orgCacheUsagePayload)
+
+    cacheListPayload :: ByteString
+    cacheListPayload = $(embedFile "fixtures/actions/cache-list.json")
+
+    repoCacheUsagePayload :: ByteString
+    repoCacheUsagePayload = $(embedFile "fixtures/actions/repo-cache-usage.json")
+
+    orgCacheUsagePayload :: ByteString
+    orgCacheUsagePayload = $(embedFile "fixtures/actions/org-cache-usage.json")
diff --git a/spec/GitHub/Actions/SecretsSpec.hs b/spec/GitHub/Actions/SecretsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/Actions/SecretsSpec.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.Actions.SecretsSpec where
+
+import qualified GitHub as GH
+
+import Prelude ()
+import Prelude.Compat
+
+import           Data.Aeson      (eitherDecodeStrict)
+import           Data.ByteString (ByteString)
+import           Data.FileEmbed  (embedFile)
+import qualified Data.Vector     as V
+import           Test.Hspec      (Spec, describe, it, shouldBe)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+spec :: Spec
+spec = do
+    describe "decoding secrets payloads" $ do
+        it "decodes selected repo list payload" $ do
+            V.length (GH.withTotalCountItems repoList) `shouldBe` 1
+        it "decodes secret list payload" $ do
+            V.length (GH.withTotalCountItems orgSecretList) `shouldBe` 2
+        it "decodes public key payload" $ do
+            GH.publicKeyId orgPublicKey `shouldBe` "568250167242549743"
+
+  where
+    repoList :: GH.WithTotalCount GH.SelectedRepo
+    repoList =
+        fromRightS (eitherDecodeStrict repoListPayload)
+
+    orgSecretList:: GH.WithTotalCount GH.OrganizationSecret
+    orgSecretList=
+        fromRightS (eitherDecodeStrict orgSecretListPayload)
+
+    orgPublicKey:: GH.PublicKey
+    orgPublicKey=
+        fromRightS (eitherDecodeStrict orgPublicKeyPayload)
+
+    repoListPayload :: ByteString
+    repoListPayload = $(embedFile "fixtures/actions/selected-repositories-for-secret.json")
+
+    orgSecretListPayload :: ByteString
+    orgSecretListPayload = $(embedFile "fixtures/actions/org-secrets-list.json")
+
+    orgPublicKeyPayload :: ByteString
+    orgPublicKeyPayload = $(embedFile "fixtures/actions/org-public-key.json")
diff --git a/spec/GitHub/Actions/WorkflowJobSpec.hs b/spec/GitHub/Actions/WorkflowJobSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/Actions/WorkflowJobSpec.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.Actions.WorkflowJobSpec where
+
+import qualified GitHub         as GH
+import           GitHub.Data.Id
+
+import Prelude ()
+import Prelude.Compat
+
+import Data.Aeson      (eitherDecodeStrict)
+import Data.ByteString (ByteString)
+import Data.FileEmbed  (embedFile)
+import Test.Hspec      (Spec, describe, it, shouldBe)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+spec :: Spec
+spec = do
+    describe "decoding workflow jobs payloads" $ do
+        it "decodes workflow job" $ do
+            GH.jobId workflowJob `shouldBe` Id 9183275828
+
+  where
+    workflowJob:: GH.Job
+    workflowJob=
+        fromRightS (eitherDecodeStrict workflowJobPayload)
+
+    workflowJobPayload :: ByteString
+    workflowJobPayload = $(embedFile "fixtures/actions/workflow-job.json")
diff --git a/spec/GitHub/Actions/WorkflowRunsSpec.hs b/spec/GitHub/Actions/WorkflowRunsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/Actions/WorkflowRunsSpec.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.Actions.WorkflowRunsSpec where
+
+import qualified GitHub as GH
+
+import Prelude ()
+import Prelude.Compat
+
+import           Data.Aeson      (eitherDecodeStrict)
+import           Data.ByteString (ByteString)
+import           Data.FileEmbed  (embedFile)
+import qualified Data.Vector     as V
+import           Test.Hspec      (Spec, describe, it, shouldBe)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+spec :: Spec
+spec = do
+    describe "decoding workflow runs payloads" $ do
+        it "decodes workflow runs list" $ do
+            V.length (GH.withTotalCountItems workflowRunsList) `shouldBe` 3
+
+  where
+    workflowRunsList:: GH.WithTotalCount GH.WorkflowRun
+    workflowRunsList =
+        fromRightS (eitherDecodeStrict workflowRunsPayload)
+
+    workflowRunsPayload :: ByteString
+    workflowRunsPayload = $(embedFile "fixtures/actions/workflow-runs-list.json")
diff --git a/spec/GitHub/Actions/WorkflowSpec.hs b/spec/GitHub/Actions/WorkflowSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/Actions/WorkflowSpec.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.Actions.WorkflowSpec where
+
+import qualified GitHub as GH
+
+import Prelude ()
+import Prelude.Compat
+
+import           Data.Aeson      (eitherDecodeStrict)
+import           Data.ByteString (ByteString)
+import           Data.FileEmbed  (embedFile)
+import qualified Data.Vector     as V
+import           Test.Hspec      (Spec, describe, it, shouldBe)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+spec :: Spec
+spec = do
+    describe "decoding workflow payloads" $ do
+        it "decodes workflow list" $ do
+            V.length (GH.withTotalCountItems workflowList) `shouldBe` 1
+
+  where
+    workflowList:: GH.WithTotalCount GH.Workflow
+    workflowList =
+        fromRightS (eitherDecodeStrict workflowPayload)
+
+    workflowPayload :: ByteString
+    workflowPayload = $(embedFile "fixtures/actions/workflow-list.json")
diff --git a/spec/GitHub/ActivitySpec.hs b/spec/GitHub/ActivitySpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/ActivitySpec.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.ActivitySpec where
+
+import qualified GitHub
+
+import GitHub.Auth                        (Auth (..))
+import GitHub.Endpoints.Activity.Starring (myStarredAcceptStarR)
+import GitHub.Endpoints.Activity.Watching (watchersForR)
+import GitHub.Request                     (executeRequest)
+
+import Data.Either.Compat (isRight)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, pendingWith, shouldSatisfy)
+
+import qualified Data.Vector as V
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "watchersForR" $ do
+    it "works" $ withAuth $ \auth -> do
+      cs <- executeRequest auth $ watchersForR "haskell-github" "github" GitHub.FetchAll
+      cs `shouldSatisfy` isRight
+      V.length (fromRightS cs) `shouldSatisfy` (> 10)
+  describe "myStarredR" $ do
+      it "works" $ withAuth $ \auth -> do
+          cs <- executeRequest auth $ myStarredAcceptStarR (GitHub.FetchAtLeast 31)
+          cs `shouldSatisfy` isRight
+          fromRightS cs `shouldSatisfy` (\xs -> V.length xs > 30)
diff --git a/spec/GitHub/CommitsSpec.hs b/spec/GitHub/CommitsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/CommitsSpec.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.CommitsSpec where
+
+import GitHub.Auth                    (Auth (..))
+import GitHub.Endpoints.Repos.Commits (commitSha, commitsForR, diffR, mkCommitName, FetchCount (..))
+import GitHub.Request                 (github)
+
+import Control.Monad      (forM_)
+import Data.Either.Compat (isRight)
+import Data.List          (nub, sort)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, pendingWith, shouldBe,
+                           shouldSatisfy)
+
+import qualified Data.Vector as V
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "commitsFor" $ do
+    it "works" $ withAuth $ \auth -> do
+      cs <- github auth commitsForR "haskell-github" "github" FetchAll
+      cs `shouldSatisfy` isRight
+      V.length (fromRightS cs) `shouldSatisfy` (> 300)
+
+    -- Page size is 30, so we get 60 commits
+    it "limits the response" $ withAuth $ \auth -> do
+      cs <- github auth commitsForR "haskell-github" "github" (FetchAtLeast 40)
+      cs `shouldSatisfy` isRight
+      let cs' = fromRightS cs
+      V.length cs' `shouldSatisfy` (< 70)
+      let hashes = sort $ map commitSha $ V.toList cs'
+      hashes `shouldBe` nub hashes
+
+  describe "diff" $ do
+    it "works" $ withAuth $ \auth -> do
+      cs <- github auth commitsForR "haskell-github" "github" (FetchAtLeast 30)
+      cs `shouldSatisfy` isRight
+      let commits = take 10 . V.toList . fromRightS $ cs
+      let pairs = zip commits $ drop 1 commits
+      forM_ pairs $ \(a, b) -> do
+        d <- github auth diffR "haskell-github" "github" (commitSha a) (commitSha b)
+        d `shouldSatisfy` isRight
+
+    it "issue #155" $ withAuth $ \auth -> do
+      d <- github auth diffR "nomeata" "codespeed" (mkCommitName "ghc") (mkCommitName "tobami:master")
+      d `shouldSatisfy` isRight
+
+    -- diff that includes a commit where a submodule is removed
+    it "issue #339" $ withAuth $ \auth -> do
+      d <- github auth diffR "scott-fleischman" "repo-remove-submodule" "d03c152482169d809be9b1eab71dcf64d7405f76" "42cfd732b20cd093534f246e630b309186eb485d"
+      d `shouldSatisfy` isRight
diff --git a/spec/GitHub/EventsSpec.hs b/spec/GitHub/EventsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/EventsSpec.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.EventsSpec where
+
+import Data.Either        (isRight)
+import Data.String        (fromString)
+import Prelude ()
+import Prelude.Compat
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, shouldSatisfy,
+                           pendingWith)
+
+import qualified GitHub
+import GitHub.Data (Auth(..))
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Left xs) = error $ "Should be Right" ++ show xs
+fromRightS (Right xs) = xs
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "repositoryEventsR" $ do
+    it "returns non empty list of events" $ shouldSucceed $
+      GitHub.repositoryEventsR "haskell-github" "github" 1
+  describe "userEventsR" $ do
+    it "returns non empty list of events" $ shouldSucceed $ GitHub.userEventsR "phadej" 1
+  where shouldSucceed f = withAuth $ \auth -> do
+          cs <- GitHub.executeRequest auth $ f
+          cs `shouldSatisfy` isRight
+          length (fromRightS cs) `shouldSatisfy` (> 1)
diff --git a/spec/GitHub/IssuesSpec.hs b/spec/GitHub/IssuesSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/IssuesSpec.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.IssuesSpec where
+
+import qualified GitHub
+
+import Prelude ()
+import Prelude.Compat
+
+import Data.Either.Compat  (isRight)
+import Data.Foldable       (for_)
+import Data.String         (fromString)
+import Network.HTTP.Client (newManager, responseBody)
+import System.Environment  (lookupEnv)
+import Test.Hspec          (Spec, describe, expectationFailure, it, pendingWith, shouldSatisfy)
+
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (GitHub.Auth -> IO ()) -> IO ()
+withAuth action = do
+    mtoken <- lookupEnv "GITHUB_TOKEN"
+    case mtoken of
+        Nothing    -> pendingWith "no GITHUB_TOKEN"
+        Just token -> action (GitHub.OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+    describe "issuesForRepoR" $ do
+        it "works" $ withAuth $ \auth -> for_ repos $ \(owner, repo) -> do
+            cs <- GitHub.executeRequest auth $
+                GitHub.issuesForRepoR owner repo mempty GitHub.FetchAll
+            case cs of
+              Left e ->
+                expectationFailure . show $ e
+              Right cs' -> do
+                for_ cs' $ \i -> do
+                  cms <- GitHub.executeRequest auth $
+                    GitHub.commentsR owner repo (GitHub.issueNumber i) 1
+                  cms `shouldSatisfy` isRight
+
+    describe "issuesForRepoR paged" $ do
+        it "works" $ withAuth $ \auth -> for_ repos $ \(owner, repo) -> do
+            mgr <- newManager GitHub.tlsManagerSettings
+            ret <- GitHub.executeRequestWithMgrAndRes mgr auth $
+                GitHub.issuesForRepoR owner repo mempty (GitHub.FetchPage (GitHub.PageParams (Just 2) (Just 1)))
+
+            case ret of
+              Left e ->
+                expectationFailure . show $ e
+              Right res -> do
+                let issues = responseBody res
+                length issues `shouldSatisfy` (<= 2)
+
+                for_ issues $ \i -> do
+                  cms <- GitHub.executeRequest auth $
+                    GitHub.commentsR owner repo (GitHub.issueNumber i) 1
+                  cms `shouldSatisfy` isRight
+
+    describe "issueR" $ do
+        it "fetches issue #428" $ withAuth $ \auth -> do
+            resIss <- GitHub.executeRequest auth $
+                GitHub.issueR "haskell-github" "github" (GitHub.IssueNumber 428)
+            resIss `shouldSatisfy` isRight
+  where
+    repos =
+      [ ("thoughtbot", "paperclip")
+      , ("haskell-github", "github")
+      ]
diff --git a/spec/GitHub/OrganizationsSpec.hs b/spec/GitHub/OrganizationsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/OrganizationsSpec.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.OrganizationsSpec where
+
+import GitHub                                 (FetchCount (..), github)
+import GitHub.Auth                            (Auth (..))
+import GitHub.Data
+       (SimpleOrganization (..), SimpleOwner (..), SimpleTeam (..))
+import GitHub.Endpoints.Organizations         (publicOrganizationsForR)
+import GitHub.Endpoints.Organizations.Members (membersOfR)
+
+import Data.Aeson         (eitherDecodeStrict)
+import Data.Either.Compat (isRight)
+import Data.FileEmbed     (embedFile)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec
+       (Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "publicOrganizationsFor'" $ do
+    it "decodes simple organization json" $ do
+      let orgs = eitherDecodeStrict $(embedFile "fixtures/user-organizations.json")
+      simpleOrganizationLogin (head $ fromRightS orgs) `shouldBe` "github"
+
+    it "returns information about the user's organizations" $ withAuth $ \auth -> do
+      orgs <- github auth publicOrganizationsForR "mike-burns" FetchAll
+      orgs  `shouldSatisfy` isRight
+
+  describe "teamsOf" $ do
+    it "parse" $ do
+      let ts = eitherDecodeStrict $(embedFile "fixtures/list-teams.json")
+      simpleTeamName (head $ fromRightS ts) `shouldBe` "Justice League"
+
+  describe "membersOf" $ do
+    it "parse" $ do
+      let ms = eitherDecodeStrict $(embedFile "fixtures/members-list.json")
+      simpleOwnerLogin (head $ fromRightS ms) `shouldBe` "octocat"
+
+    it "works" $ withAuth $ \auth -> do
+      ms <- github auth membersOfR "haskell" FetchAll
+      ms `shouldSatisfy` isRight
diff --git a/spec/GitHub/PublicSSHKeysSpec.hs b/spec/GitHub/PublicSSHKeysSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/PublicSSHKeysSpec.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.PublicSSHKeysSpec where
+
+import GitHub
+       (Auth (..), FetchCount (..), PublicSSHKey (..),github)
+import GitHub.Endpoints.Users.PublicSSHKeys
+       (publicSSHKeyR, publicSSHKeysR, publicSSHKeysForR)
+
+import Data.Either.Compat (isRight)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, pendingWith, shouldSatisfy)
+
+import qualified Data.Vector as V
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "publicSSHKeysFor'" $ do
+    it "works" $ withAuth $ \auth -> do
+      keys <- github auth publicSSHKeysForR "phadej" FetchAll
+      V.length (fromRightS keys) `shouldSatisfy` (> 1)
+
+  describe "publicSSHKeys' and publicSSHKey'" $ do
+    it "works" $ withAuth $ \auth -> do
+      keys <- github auth publicSSHKeysR
+      V.length (fromRightS keys) `shouldSatisfy` (> 1)
+
+      key <- github auth publicSSHKeyR (publicSSHKeyId $ V.head (fromRightS keys))
+      key `shouldSatisfy` isRight
diff --git a/spec/GitHub/PullRequestReviewsSpec.hs b/spec/GitHub/PullRequestReviewsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/PullRequestReviewsSpec.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.PullRequestReviewsSpec where
+
+import qualified GitHub
+import           GitHub.Data (IssueNumber (IssueNumber))
+
+import Prelude ()
+import Prelude.Compat
+
+import Data.Either.Compat (isRight)
+import Data.Foldable      (for_)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, pendingWith, shouldSatisfy)
+
+withAuth :: (GitHub.Auth -> IO ()) -> IO ()
+withAuth action = do
+    mtoken <- lookupEnv "GITHUB_TOKEN"
+    case mtoken of
+        Nothing    -> pendingWith "no GITHUB_TOKEN"
+        Just token -> action (GitHub.OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+    describe "pullRequestReviewsR" $ do
+        it "works" $ withAuth $ \auth -> for_ prs $ \(owner, repo, prid) -> do
+            cs <- GitHub.executeRequest auth $
+                GitHub.pullRequestReviewsR owner repo prid GitHub.FetchAll
+            cs `shouldSatisfy` isRight
+  where
+    prs =
+      [("haskell-github", "github", IssueNumber 268)]
diff --git a/spec/GitHub/PullRequestsSpec.hs b/spec/GitHub/PullRequestsSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/PullRequestsSpec.hs
@@ -0,0 +1,164 @@
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE TemplateHaskell       #-}
+module GitHub.PullRequestsSpec where
+
+import qualified GitHub as GH
+
+import Prelude ()
+import Prelude.Compat
+
+import           Data.Aeson
+                 (FromJSON (..), eitherDecodeStrict, withObject, (.:))
+import           Data.ByteString            (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as LBS8
+import           Data.Either.Compat         (isRight)
+import           Data.FileEmbed             (embedFile)
+import           Data.Foldable              (for_)
+import           Data.String                (fromString)
+import           Data.Tagged                (Tagged (..))
+import           Data.Text                  (Text)
+import qualified Data.Vector                as V
+import           System.Environment         (lookupEnv)
+import           Test.Hspec
+                 (Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (GH.Auth -> IO ()) -> IO ()
+withAuth action = do
+    mtoken <- lookupEnv "GITHUB_TOKEN"
+    case mtoken of
+        Nothing    -> pendingWith "no GITHUB_TOKEN"
+        Just token -> action (GH.OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+    describe "pullRequestsForR" $ do
+        it "works" $ withAuth $ \auth -> for_ repos $ \(owner, repo) -> do
+            cs <- GH.executeRequest auth $
+                GH.pullRequestsForR owner repo opts GH.FetchAll
+            cs `shouldSatisfy` isRight
+
+    describe "pullRequestPatchR" $
+        it "works" $ withAuth $ \auth -> do
+            Right patch <- GH.executeRequest auth $
+                GH.pullRequestPatchR "haskell-github" "github" (GH.IssueNumber 349)
+            head (LBS8.lines patch) `shouldBe` "From c0e4ad33811be82e1f72ee76116345c681703103 Mon Sep 17 00:00:00 2001"
+
+    describe "decoding pull request payloads" $ do
+        it "decodes a pull request 'opened' payload" $ do
+            V.length (GH.simplePullRequestRequestedReviewers simplePullRequestOpened)
+                `shouldBe` 0
+
+            V.length (GH.pullRequestRequestedReviewers pullRequestOpened)
+                `shouldBe` 0
+
+        it "decodes a pull request 'review_requested' payload" $ do
+            V.length (GH.simplePullRequestRequestedReviewers simplePullRequestReviewRequested)
+                `shouldBe` 1
+
+            V.length (GH.pullRequestRequestedReviewers pullRequestReviewRequested)
+                `shouldBe` 1
+
+        it "decodes a pull request 'team_requested' payload" $ do
+          V.length (GH.simplePullRequestRequestedTeamReviewers simplePullRequestTeamReviewRequested)
+                `shouldBe` 1
+
+          V.length (GH.pullRequestRequestedTeamReviewers pullRequestTeamReviewRequested)
+                `shouldBe` 1
+
+    describe "checking if a pull request is merged" $ do
+        it "works" $ withAuth $ \auth -> do
+            b <- GH.executeRequest auth $ GH.isPullRequestMergedR "haskell-github" "github" (GH.IssueNumber 14)
+            b `shouldSatisfy` isRight
+            fromRightS b `shouldBe` True
+
+    describe "Draft Pull Request" $ do
+        it "works" $ withAuth $ \auth -> do
+            cs <- GH.executeRequest auth $
+                draftPullRequestsForR "haskell-github" "github" opts GH.FetchAll
+
+            cs `shouldSatisfy` isRight
+
+  where
+    repos =
+      [ ("thoughtbot", "paperclip")
+      , ("haskell-github", "github")
+      ]
+    opts = GH.stateClosed
+
+    simplePullRequestOpened :: GH.SimplePullRequest
+    simplePullRequestOpened =
+        fromRightS (eitherDecodeStrict prOpenedPayload)
+
+    pullRequestOpened :: GH.PullRequest
+    pullRequestOpened =
+        fromRightS (eitherDecodeStrict prOpenedPayload)
+
+    simplePullRequestReviewRequested :: GH.SimplePullRequest
+    simplePullRequestReviewRequested =
+        fromRightS (eitherDecodeStrict prReviewRequestedPayload)
+
+    simplePullRequestTeamReviewRequested :: GH.SimplePullRequest
+    simplePullRequestTeamReviewRequested =
+        fromRightS (eitherDecodeStrict prTeamReviewRequestedPayload)
+
+    pullRequestReviewRequested :: GH.PullRequest
+    pullRequestReviewRequested =
+        fromRightS (eitherDecodeStrict prReviewRequestedPayload)
+
+    pullRequestTeamReviewRequested :: GH.PullRequest
+    pullRequestTeamReviewRequested =
+        fromRightS (eitherDecodeStrict prTeamReviewRequestedPayload)
+
+    prOpenedPayload :: ByteString
+    prOpenedPayload = $(embedFile "fixtures/pull-request-opened.json")
+
+    prReviewRequestedPayload :: ByteString
+    prReviewRequestedPayload = $(embedFile "fixtures/pull-request-review-requested.json")
+
+    prTeamReviewRequestedPayload :: ByteString
+    prTeamReviewRequestedPayload = $(embedFile "fixtures/pull-request-team-review-requested.json")
+
+-------------------------------------------------------------------------------
+-- Draft Pull Requests
+-------------------------------------------------------------------------------
+
+draftPullRequestsForR
+    :: GH.Name GH.Owner
+    -> GH.Name GH.Repo
+    -> GH.PullRequestMod
+    -> GH.FetchCount
+    -> GH.GenRequest ('GH.MtPreview ShadowCat) k (V.Vector DraftPR)
+draftPullRequestsForR user repo opts = GH.PagedQuery
+    ["repos", GH.toPathPart user, GH.toPathPart repo, "pulls"]
+    (GH.prModToQueryString opts)
+
+data DraftPR = DraftPR
+    { dprId     :: !(GH.Id GH.PullRequest)
+    , dprNumber :: !GH.IssueNumber
+    , dprTitle  :: !Text
+    , dprDraft  :: !Bool
+    }
+  deriving (Show)
+
+instance FromJSON DraftPR where
+    parseJSON = withObject "DraftPR" $ \obj -> DraftPR
+        <$> obj .: "id"
+        <*> obj .: "number"
+        <*> obj .: "title"
+        <*> obj .: "draft"
+
+-- | @application/vnd.github.shadow-cat-preview+json@ <https://developer.github.com/v3/previews/#draft-pull-requests>
+data ShadowCat
+
+instance GH.PreviewAccept ShadowCat where
+    previewContentType = Tagged "application/vnd.github.shadow-cat-preview+json"
+
+instance FromJSON a => GH.PreviewParseResponse ShadowCat a where
+    previewParseResponse _ res = Tagged (GH.parseResponseJSON res)
diff --git a/spec/GitHub/RateLimitSpec.hs b/spec/GitHub/RateLimitSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/RateLimitSpec.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.RateLimitSpec where
+
+import qualified GitHub
+
+import Prelude ()
+import Prelude.Compat
+
+import Data.Either.Compat (isRight)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, pendingWith, shouldSatisfy)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (GitHub.Auth -> IO ()) -> IO ()
+withAuth action = do
+    mtoken <- lookupEnv "GITHUB_TOKEN"
+    case mtoken of
+        Nothing    -> pendingWith "no GITHUB_TOKEN"
+        Just token -> action (GitHub.OAuth $ fromString token)
+
+spec :: Spec
+spec = describe "rateLimitR" $
+    it "works" $ withAuth $ \auth -> do
+        cs <- GitHub.executeRequest auth GitHub.rateLimitR
+        cs `shouldSatisfy` isRight
diff --git a/spec/GitHub/ReleasesSpec.hs b/spec/GitHub/ReleasesSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/ReleasesSpec.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE OverloadedStrings #-}
+module GitHub.ReleasesSpec where
+
+import qualified GitHub
+
+import GitHub.Auth                     (Auth (..))
+import GitHub.Endpoints.Repos.Releases
+       (Release (..), latestReleaseR, releaseByTagNameR, releaseR, releasesR)
+import GitHub.Request                  (executeRequest)
+
+import Data.Either.Compat (isRight)
+import Data.Proxy         (Proxy (..))
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec
+       (Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
+
+import qualified Data.Vector as V
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  let v154Id   = GitHub.mkId (Proxy :: Proxy Release) 5254449
+      v154Text = "v1.5.4"
+  describe "releasesR" $ do
+    it "works" $ withAuth $ \auth -> do
+      rs <- executeRequest auth $ releasesR "calleerlandsson" "pick" GitHub.FetchAll
+      rs `shouldSatisfy` isRight
+      V.length (fromRightS rs) `shouldSatisfy` (> 14)
+  describe "releaseR" $ do
+    it "works" $ withAuth $ \auth -> do
+      rs <- executeRequest auth $ releaseR "calleerlandsson" "pick" v154Id
+      rs `shouldSatisfy` isRight
+      releaseTagName (fromRightS rs)`shouldBe` v154Text
+  describe "latestReleaseR" $ do
+    it "works" $ withAuth $ \auth -> do
+      rs <- executeRequest auth $ latestReleaseR "calleerlandsson" "pick"
+      rs `shouldSatisfy` isRight
+  describe "releaseByTagNameR" $ do
+    it "works" $ withAuth $ \auth -> do
+      rs <- executeRequest auth $ releaseByTagNameR "calleerlandsson" "pick" v154Text
+      rs `shouldSatisfy` isRight
+      releaseId (fromRightS rs)`shouldBe` v154Id
diff --git a/spec/GitHub/ReposSpec.hs b/spec/GitHub/ReposSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/ReposSpec.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+#if __GLASGOW_HASKELL__ >= 900
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+#endif
+
+module GitHub.ReposSpec where
+
+import GitHub
+       (Auth (..), FetchCount (..), Repo (..), RepoPublicity (..), github,
+       repositoryR)
+import GitHub.Endpoints.Repos (currentUserReposR, languagesForR, userReposR)
+
+import Data.Either.Compat (isRight)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec
+       (Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
+
+import qualified Data.HashMap.Strict as HM
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "repositoryR" $ do
+    it "works" $ withAuth $ \auth -> do
+      er <- github auth repositoryR "haskell-github" "github"
+      er `shouldSatisfy` isRight
+      let Right r = er
+      -- https://github.com/haskell-github/github/pull/219
+      repoDefaultBranch r `shouldBe` Just "master"
+
+  describe "currentUserRepos" $ do
+    it "works" $ withAuth $ \auth -> do
+      cs <- github auth currentUserReposR RepoPublicityAll FetchAll
+      cs `shouldSatisfy` isRight
+
+  describe "userRepos" $ do
+    it "works" $ withAuth $ \auth -> do
+      cs <- github auth userReposR "phadej" RepoPublicityAll FetchAll
+      cs `shouldSatisfy` isRight
+
+  describe "languagesFor'" $ do
+    it "works" $ withAuth $ \auth -> do
+      ls <- github auth languagesForR "haskell-github" "github"
+      ls `shouldSatisfy` isRight
+      fromRightS ls `shouldSatisfy` HM.member "Haskell"
diff --git a/spec/GitHub/ReviewDecodeSpec.hs b/spec/GitHub/ReviewDecodeSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/ReviewDecodeSpec.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.ReviewDecodeSpec where
+
+import Data.Aeson         (eitherDecodeStrict)
+import Data.Either.Compat (isRight)
+import Data.FileEmbed     (embedFile)
+import Test.Hspec
+       (Spec, describe, it, shouldSatisfy)
+
+import GitHub.Data (Review)
+
+spec :: Spec
+spec = do
+  describe "PENDING state" $ do
+    -- https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request
+    -- > Pull request reviews created in the PENDING state do not include the submitted_at property in the response.
+    it "decodes review when submitted_at is missing" $ do
+      let reviewInfo = eitherDecodeStrict $(embedFile "fixtures/pull-request-pending-review.json") :: Either String Review
+      reviewInfo `shouldSatisfy` isRight
+
+  describe "Other states" $ do
+    it "decodes review" $ do
+      let reviewInfo = eitherDecodeStrict $(embedFile "fixtures/pull-request-approved-review.json") :: Either String Review
+      reviewInfo `shouldSatisfy` isRight
diff --git a/spec/GitHub/SearchSpec.hs b/spec/GitHub/SearchSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/SearchSpec.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.SearchSpec where
+
+import Prelude ()
+import Prelude.Compat
+
+import Data.Aeson         (eitherDecodeStrict)
+import Data.FileEmbed     (embedFile)
+import Data.Proxy         (Proxy (..))
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec         (Spec, describe, it, pendingWith, shouldBe)
+
+import qualified Data.Vector as V
+
+import GitHub (github)
+import GitHub.Data
+       (Auth (..), Issue (..), IssueNumber (..), IssueState (..),
+       SimpleUser (..), User, mkId)
+import GitHub.Endpoints.Search (SearchResult' (..), SearchResult, searchIssuesR, searchUsersR)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "searchIssues" $ do
+    it "decodes issue search response JSON" $ do
+      let searchIssuesResult = fromRightS $ eitherDecodeStrict $(embedFile "fixtures/issue-search.json") :: SearchResult Issue
+      searchResultTotalCount searchIssuesResult `shouldBe` 2
+
+      let issues = searchResultResults searchIssuesResult
+      V.length issues `shouldBe` 2
+
+      let issue1 = issues V.! 0
+      issueId issue1 `shouldBe` mkId (Proxy :: Proxy Issue) 123898390
+      issueNumber issue1 `shouldBe` IssueNumber 130
+      issueTitle issue1 `shouldBe` "Make test runner more robust"
+      issueState issue1 `shouldBe` StateClosed
+
+      let issue2 = issues V.! 1
+      issueId issue2 `shouldBe` mkId (Proxy :: Proxy Issue) 119694665
+      issueNumber issue2 `shouldBe` IssueNumber 127
+      issueTitle issue2 `shouldBe` "Decouple request creation from execution"
+      issueState issue2 `shouldBe` StateOpen
+
+    it "performs an issue search via the API" $ withAuth $ \auth -> do
+      let query = "Decouple in:title repo:haskell-github/github created:<=2015-12-01"
+      issues <- fmap (searchResultResults . fromRightS) <$> github auth $ searchIssuesR query 5
+      length issues `shouldBe` 1
+      issueId (V.head issues) `shouldBe` mkId (Proxy :: Proxy Issue) 119694665
+
+  describe "searchUsers" $
+    it "performs a user search via the API" $ withAuth $ \auth -> do
+      let query = "oleg.grenrus@iki.fi created:<2020-01-01"
+      users <- fmap (searchResultResults . fromRightS) <$> github auth $ searchUsersR query 5
+      length users `shouldBe` 1
+      simpleUserId (V.head users) `shouldBe` mkId (Proxy :: Proxy User) 51087
diff --git a/spec/GitHub/UsersSpec.hs b/spec/GitHub/UsersSpec.hs
new file mode 100644
--- /dev/null
+++ b/spec/GitHub/UsersSpec.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+module GitHub.UsersSpec where
+
+import Data.Aeson         (eitherDecodeStrict)
+import Data.Either.Compat (isLeft, isRight)
+import Data.FileEmbed     (embedFile)
+import Data.String        (fromString)
+import System.Environment (lookupEnv)
+import Test.Hspec
+       (Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
+
+import qualified GitHub
+
+import GitHub.Data
+       (Auth (..), Organization (..), User (..), fromOwner)
+import GitHub.Endpoints.Users
+       (ownerInfoForR, userInfoCurrentR, userInfoForR)
+import GitHub.Endpoints.Users.Followers (usersFollowedByR, usersFollowingR)
+import GitHub.Request                   (github)
+
+fromRightS :: Show a => Either a b -> b
+fromRightS (Right b) = b
+fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
+
+fromLeftS :: Show b => Either a b -> a
+fromLeftS (Left b) = b
+fromLeftS (Right a) = error $ "Expected a Left and got a RIght" ++ show a
+
+withAuth :: (Auth -> IO ()) -> IO ()
+withAuth action = do
+  mtoken <- lookupEnv "GITHUB_TOKEN"
+  case mtoken of
+    Nothing    -> pendingWith "no GITHUB_TOKEN"
+    Just token -> action (OAuth $ fromString token)
+
+spec :: Spec
+spec = do
+  describe "userInfoFor" $ do
+    it "decodes user json" $ do
+      let userInfo = eitherDecodeStrict $(embedFile "fixtures/user.json")
+      userLogin (fromRightS userInfo) `shouldBe` "mike-burns"
+
+    it "decodes user-bot json" $ do
+      let userInfo = eitherDecodeStrict $(embedFile "fixtures/user-bot.json")
+      userLogin (fromRightS userInfo) `shouldBe` "mike-burns"
+
+    it "returns information about the user" $ withAuth $ \auth -> do
+      userInfo <- github auth userInfoForR "mike-burns"
+      userLogin (fromRightS userInfo) `shouldBe` "mike-burns"
+
+    it "catches http exceptions" $ withAuth $ \auth -> do
+      userInfo <- github auth userInfoForR "i-hope-this-user-will-never-exist"
+      userInfo `shouldSatisfy` isLeft
+
+    it "should fail for organization" $ withAuth $ \auth -> do
+      userInfo <- github auth userInfoForR "haskell"
+      userInfo `shouldSatisfy` isLeft
+
+  describe "ownerInfoFor" $ do
+    it "works for users and organizations" $ withAuth $ \auth -> do
+      a <- github auth ownerInfoForR "haskell"
+      b <- github auth ownerInfoForR "phadej"
+      a `shouldSatisfy` isRight
+      b `shouldSatisfy` isRight
+      (organizationLogin . fromRightS . fromOwner . fromRightS $ a) `shouldBe` "haskell"
+      (userLogin . fromLeftS . fromOwner . fromRightS $ b) `shouldBe` "phadej"
+
+  describe "userInfoCurrentR" $ do
+    it "returns information about the authenticated user" $ withAuth $ \auth -> do
+      userInfo <- github auth userInfoCurrentR
+      userInfo `shouldSatisfy` isRight
+
+  describe "usersFollowing" $ do
+    it "works" $ withAuth $ \auth -> do
+      us <- github auth usersFollowingR "phadej" (GitHub.FetchAtLeast 10)
+      us `shouldSatisfy` isRight
+
+  describe "usersFollowedBy" $ do
+    it "works" $ withAuth $ \auth -> do
+      us <- github auth usersFollowedByR "phadej" (GitHub.FetchAtLeast 10)
+      us `shouldSatisfy` isRight
diff --git a/src/GitHub.hs b/src/GitHub.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub.hs
@@ -0,0 +1,546 @@
+-- |
+-- This module re-exports all request constructors and data definitions from
+-- this package.
+--
+-- See "GitHub.Request" module for executing 'Request', in short
+-- use @'github' request@, for example
+--
+-- @
+-- 'github' 'userInfoForR'
+--   :: 'AuthMethod' am => am -> 'Name' 'User' -> IO (Either 'Error' 'User')
+-- @
+--
+-- The missing endpoints lists show which endpoints we know are missing, there
+-- might be more.
+
+module GitHub (
+    -- * Activity
+    -- | See <https://developer.github.com/v3/activity/>
+
+    -- ** Events
+    -- | See <https://developer.github.com/v3/activity/events/>
+    repositoryEventsR,
+    userEventsR,
+
+    -- ** Notifications
+    -- | See <https://developer.github.com/v3/activity/notifications/>
+    getNotificationsR,
+    markNotificationAsReadR,
+    markAllNotificationsAsReadR,
+
+    -- ** Starring
+    -- | See <https://developer.github.com/v3/activity/starring/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Check if you are starring a repository
+    stargazersForR,
+    reposStarredByR,
+    myStarredR,
+    myStarredAcceptStarR,
+    starRepoR,
+    unstarRepoR,
+
+    -- ** Watching
+    -- | See <https://developer.github.com/v3/activity/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Query a Repository Subscription
+    -- * Set a Repository Subscription
+    watchersForR,
+    reposWatchedByR,
+    unwatchRepoR,
+
+    -- * Gists
+    -- | See <https://developer.github.com/v3/gists/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Query a specific revision of a gist
+    -- * Edit a gist
+    -- * List gist commits
+    -- * Check if a gist is starred
+    -- * Fork a gist
+    -- * List gist forks
+    gistsR,
+    gistR,
+    createGistR,
+    starGistR,
+    unstarGistR,
+    deleteGistR,
+
+    -- ** Comments
+    -- | See <https://developer.github.com/v3/gists/comments/>
+    --
+    -- Missing endpoints:
+    -- * Create a comment
+    -- * Edit a comment
+    -- * Delete a comment
+    commentsOnR,
+    gistCommentR,
+
+    -- * Git Data
+    -- | See <https://developer.github.com/v3/git/>
+
+    -- ** Blobs
+    -- | See <https://developer.github.com/v3/git/blobs/>
+    blobR,
+
+    -- ** Commits
+    -- | See <https://developer.github.com/v3/git/commits/>
+    gitCommitR,
+
+    -- ** References
+    -- | See <https://developer.github.com/v3/git/refs/>
+    referenceR,
+    referencesR,
+    createReferenceR,
+    deleteReferenceR,
+    namespacedReferencesR,
+
+    -- ** Trees
+    -- | See <https://developer.github.com/v3/git/trees/>
+    treeR,
+    nestedTreeR,
+
+    -- * Issues
+    -- | See <https://developer.github.com/v3/issues/>
+    --
+    currentUserIssuesR,
+    organizationIssuesR,
+    issueR,
+    issuesForRepoR,
+    createIssueR,
+    editIssueR,
+
+    -- ** Comments
+    -- | See <https://developer.github.com/v3/issues/comments/>
+    --
+    commentR,
+    commentsR,
+    createCommentR,
+    deleteCommentR,
+    editCommentR,
+
+    -- ** Events
+    -- | See <https://developer.github.com/v3/issues/events/>
+    --
+    eventsForIssueR,
+    eventsForRepoR,
+    eventR,
+
+    -- ** Labels
+    -- | See <https://developer.github.com/v3/issues/labels/>
+    --
+    labelsOnRepoR,
+    labelR,
+    createLabelR,
+    updateLabelR,
+    deleteLabelR,
+    labelsOnIssueR,
+    addLabelsToIssueR,
+    removeLabelFromIssueR,
+    replaceAllLabelsForIssueR,
+    removeAllLabelsFromIssueR,
+    labelsOnMilestoneR,
+
+    -- ** Milestone
+    -- | See <https://developer.github.com/v3/issues/milestones/>
+    --
+    milestonesR,
+    milestoneR,
+    createMilestoneR,
+    updateMilestoneR,
+    deleteMilestoneR,
+
+    -- * Organizations
+    -- | See <https://developer.github.com/v3/orgs/>
+    --
+    -- Missing endpoints:
+    --
+    -- * List your organizations
+    -- * List all organizations
+    -- * Edit an organization
+    publicOrganizationsForR,
+    publicOrganizationR,
+    organizationsR,
+    -- ** Members
+    -- | See <https://developer.github.com/v3/orgs/members/>
+    --
+    -- Missing endpoints: All except /Members List/ and /Check Membership/
+    membersOfR,
+    membersOfWithR,
+    isMemberOfR,
+    orgInvitationsR,
+    orgMembershipR,
+    -- ** Outside Collaborators
+    -- | See <https://developer.github.com/v3/orgs/outside_collaborators/>
+    --
+    -- Missing endpoints: All except /Outside Collaborator List/
+    outsideCollaboratorsR,
+
+    -- ** Teams
+    -- | See <https://developer.github.com/v3/orgs/teams/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Query team member (deprecated)
+    -- * Add team member (deprecated)
+    -- * Remove team member (deprecated)
+    -- * Check if a team manages a repository
+    -- * Add team repository
+    -- * Remove team repository
+    teamsOfR,
+    teamInfoForR,
+    createTeamForR,
+    editTeamR,
+    deleteTeamR,
+    listTeamMembersR,
+    listTeamReposR,
+    teamMembershipInfoForR,
+    addTeamMembershipForR,
+    deleteTeamMembershipForR,
+    listTeamsCurrentR,
+
+    -- * Pull Requests
+    -- | See <https://developer.github.com/v3/pulls/>
+    pullRequestsForR,
+    pullRequestR,
+    pullRequestPatchR,
+    pullRequestDiffR,
+    createPullRequestR,
+    updatePullRequestR,
+    pullRequestCommitsR,
+    pullRequestFilesR,
+    isPullRequestMergedR,
+    mergePullRequestR,
+
+    -- ** Review comments
+    -- | See <https://developer.github.com/v3/pulls/comments/>
+    --
+    -- Missing endpoints:
+    --
+    -- * List comments in a repository
+    -- * Edit a comment
+    -- * Delete a comment
+    pullRequestCommentsR,
+    pullRequestCommentR,
+    createPullCommentR,
+    createPullCommentReplyR,
+
+    -- ** Pull request reviews
+    -- | See <https://developer.github.com/v3/pulls/reviews/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Delete a pending review
+    -- * Create a pull request review
+    -- * Submit a pull request review
+    -- * Dismiss a pull request review
+    pullRequestReviewsR,
+    pullRequestReviewR,
+    pullRequestReviewCommentsR,
+
+    -- * Repositories
+    -- | See <https://developer.github.com/v3/repos/>
+    --
+    -- Missing endpoints:
+    --
+    -- * List all public repositories
+    -- * List Teams
+    -- * Query Branch
+    -- * Enabling and disabling branch protection
+    currentUserReposR,
+    userReposR,
+    organizationReposR,
+    repositoryR,
+    contributorsR,
+    languagesForR,
+    tagsForR,
+    branchesForR,
+
+    -- ** Collaborators
+    -- | See <https://developer.github.com/v3/repos/collaborators/>
+    collaboratorsOnR,
+    collaboratorPermissionOnR,
+    isCollaboratorOnR,
+    addCollaboratorR,
+
+    -- ** Comments
+    -- | See <https://developer.github.com/v3/repos/comments/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Create a commit comment
+    -- * Update a commit comment
+    -- * Delete a commit comment
+    commentsForR,
+    commitCommentsForR,
+    commitCommentForR,
+
+    -- ** Commits
+    -- | See <https://developer.github.com/v3/repos/commits/>
+    commitsForR,
+    commitsWithOptionsForR,
+    commitR,
+    diffR,
+
+    -- ** Reactions
+    -- | See <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28>
+    issueReactionsR,
+    createIssueReactionR,
+    deleteIssueReactionR,
+    commentReactionsR,
+    createCommentReactionR,
+    deleteCommentReactionR,
+
+    -- ** Contents
+    -- | See <https://developer.github.com/v3/repos/contents/>
+    contentsForR,
+    readmeForR,
+    archiveForR,
+    createFileR,
+    updateFileR,
+    deleteFileR,
+
+    -- ** Deploy Keys
+    -- | See <https://developer.github.com/v3/repos/keys/>
+    deployKeysForR,
+    deployKeyForR,
+    createRepoDeployKeyR,
+    deleteRepoDeployKeyR,
+
+    -- ** Deployments
+    -- | See <https://developer.github.com/v3/repos/deployments/#deployments>
+    --
+    -- Missing endpoints:
+    -- * Get a single deployment
+    -- * Update a deployment
+    -- * Get a single deployment status
+    deploymentsWithOptionsForR,
+    createDeploymentR,
+    deploymentStatusesForR,
+    createDeploymentStatusR,
+
+    -- ** Forks
+    -- | See <https://developer.github.com/v3/repos/forks/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Create a fork
+    forksForR,
+
+    -- ** Statuses
+    -- | See <https://developer.github.com/v3/repos/statuses/>
+    createStatusR,
+    statusesForR,
+    statusForR,
+
+    -- ** Webhooks
+    -- | See <https://developer.github.com/v3/repos/hooks/>
+    webhooksForR,
+    webhookForR,
+    createRepoWebhookR,
+    editRepoWebhookR,
+    testPushRepoWebhookR,
+    pingRepoWebhookR,
+    deleteRepoWebhookR,
+
+    -- * Releases
+    releasesR,
+    releaseR,
+    latestReleaseR,
+    releaseByTagNameR,
+
+    -- ** Invitations
+    -- | See <https://developer.github.com/v3/repos/invitations/>
+    -- Missing endpoints:
+
+    -- * Delete a repository invitation
+    -- * Update a repository invitation
+    -- * Decline a repository invitation
+
+    listInvitationsOnR,
+    acceptInvitationFromR,
+    listInvitationsForR,
+
+
+    -- * Search
+    -- | See <https://developer.github.com/v3/search/>
+    searchReposR,
+    searchCodeR,
+    searchIssuesR,
+    searchUsersR,
+
+    -- * Users
+    -- | See <https://developer.github.com/v3/users/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Update the authenticated user
+    -- * Query all users
+    userInfoForR,
+    ownerInfoForR,
+    userInfoCurrentR,
+
+    -- ** Emails
+    -- | See <https://developer.github.com/v3/users/emails/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Add email address(es)
+    -- * Delete email address(es)
+    -- * Toggle primary email visibility
+    currentUserEmailsR,
+    currentUserPublicEmailsR,
+
+    -- ** Followers
+    -- | See <https://developer.github.com/v3/users/followers/>
+    --
+    -- Missing endpoints:
+    --
+    -- * Check if you are following a user
+    -- * Check if one user follows another
+    -- * Follow a user
+    -- * Unfollow a user
+    usersFollowingR,
+    usersFollowedByR,
+
+    -- ** Git SSH Keys
+    -- | See <https://developer.github.com/v3/users/keys/>
+    publicSSHKeysR,
+    publicSSHKeysForR,
+    publicSSHKeyR,
+    createUserPublicSSHKeyR,
+    deleteUserPublicSSHKeyR,
+
+    -- ** Rate Limit
+    -- | See <https://developer.github.com/v3/rate_limit/>
+    rateLimitR,
+
+    -- ** Actions - artifacts
+    -- | See <https://docs.github.com/en/rest/actions/artifacts>
+    artifactsForR,
+    artifactR,
+    deleteArtifactR,
+    downloadArtifactR,
+    artifactsForWorkflowRunR,
+
+    -- ** Actions - cache
+    -- | See <https://docs.github.com/en/rest/actions/cache>
+    cacheUsageOrganizationR,
+    cacheUsageByRepositoryR,
+    cacheUsageR,
+    cachesForRepoR,
+    deleteCacheR,
+
+    -- ** Actions - secrets
+    -- | See <https://docs.github.com/en/rest/actions/secrets>
+    organizationSecretsR,
+    organizationPublicKeyR,
+    organizationSecretR,
+    setOrganizationSecretR,
+    deleteOrganizationSecretR,
+    organizationSelectedRepositoriesForSecretR,
+    setOrganizationSelectedRepositoriesForSecretR,
+    addOrganizationSelectedRepositoriesForSecretR,
+    removeOrganizationSelectedRepositoriesForSecretR,
+    repoSecretsR,
+    repoPublicKeyR,
+    repoSecretR,
+    setRepoSecretR,
+    deleteRepoSecretR,
+    environmentSecretsR,
+    environmentPublicKeyR,
+    environmentSecretR,
+    setEnvironmentSecretR,
+    deleteEnvironmentSecretR,
+
+    -- ** Actions - workflow jobs
+    -- | See <https://docs.github.com/en/rest/actions/workflow-jobs>
+    jobR,
+    downloadJobLogsR,
+    jobsForWorkflowRunAttemptR,
+    jobsForWorkflowRunR,
+
+    -- ** Actions - workflow runs
+    -- | See <https://docs.github.com/en/rest/actions/workflow-runs>
+    reRunJobR,
+    workflowRunsR,
+    workflowRunR,
+    deleteWorkflowRunR,
+    workflowRunReviewHistoryR,
+    approveWorkflowRunR,
+    workflowRunAttemptR,
+    downloadWorkflowRunAttemptLogsR,
+    cancelWorkflowRunR,
+    downloadWorkflowRunLogsR,
+    deleteWorkflowRunLogsR,
+    reRunWorkflowR,
+    reRunFailedJobsR,
+    workflowRunsForWorkflowR,
+
+    -- ** Actions - workflows
+    -- | See <https://docs.github.com/en/rest/actions/workflows>
+    repositoryWorkflowsR,
+    workflowR,
+    disableWorkflowR,
+    triggerWorkflowR,
+    enableWorkflowR,
+
+    -- * Data definitions
+    module GitHub.Data,
+    -- * Request handling
+    module GitHub.Request,
+    ) where
+
+import GitHub.Data
+import GitHub.Endpoints.Actions.Artifacts
+import GitHub.Endpoints.Actions.Cache
+import GitHub.Endpoints.Actions.Secrets
+import GitHub.Endpoints.Actions.WorkflowJobs
+import GitHub.Endpoints.Actions.WorkflowRuns
+import GitHub.Endpoints.Actions.Workflows
+import GitHub.Endpoints.Activity.Events
+import GitHub.Endpoints.Activity.Notifications
+import GitHub.Endpoints.Activity.Starring
+import GitHub.Endpoints.Activity.Watching
+import GitHub.Endpoints.Gists
+import GitHub.Endpoints.Gists.Comments
+import GitHub.Endpoints.GitData.Blobs
+import GitHub.Endpoints.GitData.Commits
+import GitHub.Endpoints.GitData.References
+import GitHub.Endpoints.GitData.Trees
+import GitHub.Endpoints.Issues
+import GitHub.Endpoints.Issues.Comments
+import GitHub.Endpoints.Issues.Events
+import GitHub.Endpoints.Issues.Labels
+import GitHub.Endpoints.Issues.Milestones
+import GitHub.Endpoints.Organizations
+import GitHub.Endpoints.Organizations.Members
+import GitHub.Endpoints.Organizations.OutsideCollaborators
+import GitHub.Endpoints.Organizations.Teams
+import GitHub.Endpoints.PullRequests
+import GitHub.Endpoints.PullRequests.Comments
+import GitHub.Endpoints.PullRequests.Reviews
+import GitHub.Endpoints.Reactions
+import GitHub.Endpoints.RateLimit
+import GitHub.Endpoints.Repos
+import GitHub.Endpoints.Repos.Collaborators
+import GitHub.Endpoints.Repos.Comments
+import GitHub.Endpoints.Repos.Commits
+import GitHub.Endpoints.Repos.Contents
+import GitHub.Endpoints.Repos.DeployKeys
+import GitHub.Endpoints.Repos.Deployments
+import GitHub.Endpoints.Repos.Forks
+import GitHub.Endpoints.Repos.Invitations
+import GitHub.Endpoints.Repos.Releases
+import GitHub.Endpoints.Repos.Statuses
+import GitHub.Endpoints.Repos.Webhooks
+import GitHub.Endpoints.Search
+import GitHub.Endpoints.Users
+import GitHub.Endpoints.Users.Emails
+import GitHub.Endpoints.Users.Followers
+import GitHub.Endpoints.Users.PublicSSHKeys
+import GitHub.Request
diff --git a/src/GitHub/Auth.hs b/src/GitHub/Auth.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Auth.hs
@@ -0,0 +1,58 @@
+module GitHub.Auth (
+    Auth (..),
+    Token,
+    JWTToken,
+    AuthMethod,
+    endpoint,
+    setAuthRequest
+    ) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.ByteString     as BS
+import qualified Data.Text.Encoding  as TE
+import qualified Network.HTTP.Client as HTTP
+
+type Token = BS.ByteString
+type JWTToken = Text
+
+-- | The Github auth data type
+data Auth
+    = BasicAuth BS.ByteString BS.ByteString  -- ^ Username and password
+    | OAuth Token                            -- ^ OAuth token
+    | JWT JWTToken                           -- ^ JWT Token
+    | EnterpriseOAuth Text Token             -- ^ Custom endpoint and OAuth token
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Auth
+instance Binary Auth
+instance Hashable Auth
+
+-- | A type class for different authentication methods
+--
+-- Note the '()' intance, which doee nothing, i.e. is unauthenticated.
+class AuthMethod a where
+    -- | Custom API endpoint without trailing slash
+    endpoint       :: a -> Maybe Text
+    -- | A function which sets authorisation on an HTTP request
+    setAuthRequest :: a -> HTTP.Request -> HTTP.Request
+
+instance AuthMethod () where
+    endpoint _ = Nothing
+    setAuthRequest _ = id
+
+instance AuthMethod Auth where
+    endpoint (BasicAuth _ _)       = Nothing
+    endpoint (OAuth _)             = Nothing
+    endpoint (JWT _)             = Nothing
+    endpoint (EnterpriseOAuth e _) = Just e
+
+    setAuthRequest (BasicAuth u p)       = HTTP.applyBasicAuth u p
+    setAuthRequest (OAuth t)             = setAuthHeader $ "token " <> t
+    setAuthRequest (JWT t)               = setAuthHeader $ "Bearer " <> TE.encodeUtf8 t
+    setAuthRequest (EnterpriseOAuth _ t) = setAuthHeader $ "token " <> t
+
+setAuthHeader :: BS.ByteString -> HTTP.Request -> HTTP.Request
+setAuthHeader auth req =
+    req { HTTP.requestHeaders = ("Authorization", auth) : HTTP.requestHeaders req }
diff --git a/src/GitHub/Data.hs b/src/GitHub/Data.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data.hs
@@ -0,0 +1,155 @@
+-- |
+-- This module re-exports the @GitHub.Data.@ and "GitHub.Auth" submodules.
+
+module GitHub.Data (
+    -- * Tagged types
+    -- ** Name
+    Name,
+    mkName,
+    untagName,
+    mkOwnerName,
+    mkUserName,
+    mkTeamName,
+    mkOrganizationName,
+    mkRepoName,
+    mkCommitName,
+    fromUserName,
+    fromOrganizationName,
+    -- ** Id
+    Id,
+    mkId,
+    untagId,
+    mkOwnerId,
+    mkUserId,
+    mkTeamId,
+    mkOrganizationId,
+    mkRepoId,
+    fromUserId,
+    fromOrganizationId,
+    -- * IssueNumber
+    IssueNumber (..),
+    -- * Module re-exports
+    module GitHub.Auth,
+    module GitHub.Data.Actions.Common,
+    module GitHub.Data.Actions.Artifacts,
+    module GitHub.Data.Actions.Cache,
+    module GitHub.Data.Actions.Secrets,
+    module GitHub.Data.Actions.Workflows,
+    module GitHub.Data.Actions.WorkflowJobs,
+    module GitHub.Data.Actions.WorkflowRuns,
+    module GitHub.Data.Activities,
+    module GitHub.Data.Comments,
+    module GitHub.Data.Content,
+    module GitHub.Data.Definitions,
+    module GitHub.Data.DeployKeys,
+    module GitHub.Data.Deployments,
+    module GitHub.Data.Email,
+    module GitHub.Data.Events,
+    module GitHub.Data.Gists,
+    module GitHub.Data.GitData,
+    module GitHub.Data.Invitation,
+    module GitHub.Data.Issues,
+    module GitHub.Data.Milestone,
+    module GitHub.Data.Options,
+    module GitHub.Data.PublicSSHKeys,
+    module GitHub.Data.PullRequests,
+    module GitHub.Data.RateLimit,
+    module GitHub.Data.Releases,
+    module GitHub.Data.Reactions,
+    module GitHub.Data.Repos,
+    module GitHub.Data.Request,
+    module GitHub.Data.Reviews,
+    module GitHub.Data.Search,
+    module GitHub.Data.Statuses,
+    module GitHub.Data.Teams,
+    module GitHub.Data.URL,
+    module GitHub.Data.Webhooks,
+    module GitHub.Data.Webhooks.Validate,
+    ) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import GitHub.Auth
+import GitHub.Data.Actions.Common
+import GitHub.Data.Actions.Artifacts
+import GitHub.Data.Actions.Secrets
+import GitHub.Data.Actions.Cache
+import GitHub.Data.Actions.Workflows
+import GitHub.Data.Actions.WorkflowJobs
+import GitHub.Data.Actions.WorkflowRuns
+import GitHub.Data.Activities
+import GitHub.Data.Comments
+import GitHub.Data.Content
+import GitHub.Data.Definitions
+import GitHub.Data.DeployKeys
+import GitHub.Data.Deployments
+import GitHub.Data.Email
+import GitHub.Data.Events
+import GitHub.Data.Gists
+import GitHub.Data.GitData
+import GitHub.Data.Id
+import GitHub.Data.Invitation
+import GitHub.Data.Issues
+import GitHub.Data.Milestone
+import GitHub.Data.Name
+import GitHub.Data.Options
+import GitHub.Data.PublicSSHKeys
+import GitHub.Data.PullRequests
+import GitHub.Data.RateLimit
+import GitHub.Data.Releases
+import GitHub.Data.Reactions
+import GitHub.Data.Repos
+import GitHub.Data.Request
+import GitHub.Data.Reviews
+import GitHub.Data.Search
+import GitHub.Data.Statuses
+import GitHub.Data.Teams
+import GitHub.Data.URL
+import GitHub.Data.Webhooks
+import GitHub.Data.Webhooks.Validate
+
+mkOwnerId :: Int -> Id Owner
+mkOwnerId = Id
+
+mkOwnerName :: Text -> Name Owner
+mkOwnerName = N
+
+mkUserId :: Int -> Id User
+mkUserId = Id
+
+mkUserName :: Text -> Name User
+mkUserName = N
+
+mkTeamId :: Int -> Id Team
+mkTeamId = Id
+
+mkTeamName :: Text -> Name Team
+mkTeamName = N
+
+mkOrganizationId :: Int -> Id Organization
+mkOrganizationId = Id
+
+mkOrganizationName :: Text -> Name Organization
+mkOrganizationName = N
+
+mkRepoId :: Int -> Id Repo
+mkRepoId = Id
+
+mkRepoName :: Text -> Name Repo
+mkRepoName = N
+
+mkCommitName :: Text -> Name Commit
+mkCommitName = N
+
+fromOrganizationName :: Name Organization -> Name Owner
+fromOrganizationName = N . untagName
+
+fromUserName :: Name User -> Name Owner
+fromUserName = N . untagName
+
+fromOrganizationId :: Id Organization -> Id Owner
+fromOrganizationId = Id . untagId
+
+fromUserId :: Id User -> Id Owner
+fromUserId = Id . untagId
diff --git a/src/GitHub/Data/Actions/Artifacts.hs b/src/GitHub/Data/Actions/Artifacts.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/Artifacts.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+
+module GitHub.Data.Actions.Artifacts (
+    Artifact(..),
+    ArtifactWorkflowRun(..),
+    ) where
+
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import GitHub.Data.Actions.Common       (WithTotalCount (WithTotalCount))
+import GitHub.Data.Actions.WorkflowRuns (WorkflowRun)
+import GitHub.Data.Repos                (Repo)
+
+-------------------------------------------------------------------------------
+-- Artifact
+-------------------------------------------------------------------------------
+
+data ArtifactWorkflowRun  = ArtifactWorkflowRun
+    { artifactWorkflowRunWorkflowRunId    :: !(Id WorkflowRun)
+    , artifactWorkflowRunRepositoryId     :: !(Id Repo)
+    , artifactWorkflowRunHeadRepositoryId :: !(Id Repo)
+    , artifactWorkflowRunHeadBranch       :: !Text
+    , artifactWorkflowRunHeadSha          :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data Artifact = Artifact
+    { artifactArchiveDownloadUrl :: !URL
+    , artifactCreatedAt          :: !UTCTime
+    , artifactExpired            :: !Bool
+    , artifactExpiresAt          :: !UTCTime
+    , artifactId                 :: !(Id Artifact)
+    , artifactName               :: !Text
+    , artifactNodeId             :: !Text
+    , artifactSizeInBytes        :: !Int
+    , artifactUpdatedAt          :: !UTCTime
+    , artifactUrl                :: !URL
+    , artifactWorkflowRun        :: !ArtifactWorkflowRun
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON ArtifactWorkflowRun where
+    parseJSON = withObject "ArtifactWorkflowRun" $ \o -> ArtifactWorkflowRun
+        <$> o .: "id"
+        <*> o .: "repository_id"
+        <*> o .: "head_repository_id"
+        <*> o .: "head_branch"
+        <*> o .: "head_sha"
+
+instance FromJSON Artifact where
+    parseJSON = withObject "Artifact" $ \o -> Artifact
+        <$> o .: "archive_download_url"
+        <*> o .: "created_at"
+        <*> o .: "expired"
+        <*> o .: "expires_at"
+        <*> o .: "id"
+        <*> o .: "name"
+        <*> o .: "node_id"
+        <*> o .: "size_in_bytes"
+        <*> o .: "updated_at"
+        <*> o .: "url"
+        <*> o .: "workflow_run"
+
+instance FromJSON (WithTotalCount Artifact) where
+    parseJSON = withObject "ArtifactList" $ \o -> WithTotalCount
+        <$> o .: "artifacts"
+        <*> o .: "total_count"
diff --git a/src/GitHub/Data/Actions/Cache.hs b/src/GitHub/Data/Actions/Cache.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/Cache.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+
+module GitHub.Data.Actions.Cache (
+    Cache(..),
+    RepositoryCacheUsage(..),
+    OrganizationCacheUsage(..)
+    ) where
+
+import GitHub.Data.Id          (Id)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import GitHub.Data.Actions.Common (WithTotalCount (WithTotalCount))
+
+-------------------------------------------------------------------------------
+-- Cache
+-------------------------------------------------------------------------------
+
+data Cache = Cache
+    { cacheId             :: !(Id Cache)
+    , cacheRef            :: !Text
+    , cacheKey            :: !Text
+    , cacheVersion        :: !Text
+    , cacheLastAccessedAt :: !UTCTime
+    , cacheCreatedAt      :: !UTCTime
+    , cacheSizeInBytes    :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data RepositoryCacheUsage = RepositoryCacheUsage
+    { repositoryCacheUsageFullName                :: !Text
+    , repositoryCacheUsageActiveCachesSizeInBytes :: !Int
+    , repositoryCacheUsageActiveCachesCount       :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data OrganizationCacheUsage = OrganizationCacheUsage
+    { organizationCacheUsageTotalActiveCachesSizeInBytes :: !Int
+    , organizationCacheUsageTotalActiveCachesCount       :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON Cache where
+    parseJSON = withObject "Cache" $ \o -> Cache
+        <$> o .: "id"
+        <*> o .: "ref"
+        <*> o .: "key"
+        <*> o .: "version"
+        <*> o .: "last_accessed_at"
+        <*> o .: "created_at"
+        <*> o .: "size_in_bytes"
+
+instance FromJSON (WithTotalCount Cache) where
+    parseJSON = withObject "CacheList" $ \o -> WithTotalCount
+        <$> o .: "actions_caches"
+        <*> o .: "total_count"
+
+instance FromJSON OrganizationCacheUsage where
+    parseJSON = withObject "OrganizationCacheUsage" $ \o -> OrganizationCacheUsage
+        <$> o .: "total_active_caches_size_in_bytes"
+        <*> o .: "total_active_caches_count"
+
+instance FromJSON RepositoryCacheUsage where
+    parseJSON = withObject "RepositoryCacheUsage" $ \o -> RepositoryCacheUsage
+        <$> o .: "full_name"
+        <*> o .: "active_caches_size_in_bytes"
+        <*> o .: "active_caches_count"
+
+instance FromJSON (WithTotalCount RepositoryCacheUsage) where
+    parseJSON = withObject "CacheUsageList" $ \o -> WithTotalCount
+        <$> o .: "repository_cache_usages"
+        <*> o .: "total_count"
diff --git a/src/GitHub/Data/Actions/Common.hs b/src/GitHub/Data/Actions/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/Common.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+
+module GitHub.Data.Actions.Common (
+    WithTotalCount(..),
+    ) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-------------------------------------------------------------------------------
+-- Common
+-------------------------------------------------------------------------------
+
+-- | A page of a paginated response.
+data WithTotalCount a = WithTotalCount
+    { withTotalCountItems      :: !(Vector a)
+        -- ^ A snippet of the answer.
+    , withTotalCountTotalCount :: !Int
+        -- ^ The total size of the answer.
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-- | Joining two pages of a paginated response.
+--   The 'withTotalCountTotalCount' is assumed to be the same in both pages,
+--   but this is not checked.
+instance Semigroup (WithTotalCount a) where
+    WithTotalCount items1 count1 <> WithTotalCount items2 _ =
+        WithTotalCount (items1 <> items2) count1
+
+instance Foldable WithTotalCount where
+    foldMap f (WithTotalCount items _) = foldMap f items
diff --git a/src/GitHub/Data/Actions/Secrets.hs b/src/GitHub/Data/Actions/Secrets.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/Secrets.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+{-# LANGUAGE RecordWildCards   #-}
+
+module GitHub.Data.Actions.Secrets (
+    OrganizationSecret(..),
+    PublicKey(..),
+    SetSecret(..),
+    SetRepoSecret(..),
+    SelectedRepo(..),
+    SetSelectedRepositories(..),
+    RepoSecret(..),
+    Environment(..),
+    ) where
+
+import GitHub.Data.Id          (Id)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Data.Maybe                 (maybeToList)
+import GitHub.Data.Actions.Common (WithTotalCount (WithTotalCount))
+import GitHub.Data.Name           (Name)
+import GitHub.Data.Repos          (Repo)
+
+-------------------------------------------------------------------------------
+-- Secret
+-------------------------------------------------------------------------------
+
+data OrganizationSecret = OrganizationSecret
+    { organizationSecretName :: !(Name OrganizationSecret)
+    , organizationSecretCreatedAt :: !UTCTime
+    , organizationSecretUpdatedAt :: !UTCTime
+    , organizationSecretVisibility :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data PublicKey = PublicKey
+    { publicKeyId :: !Text
+    , publicKeyKey :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data SetSecret = SetSecret
+    { setSecretPublicKeyId :: !Text
+    , setSecretEncryptedValue :: !Text
+    , setSecretVisibility :: !Text
+    , setSecretSelectedRepositoryIds :: !(Maybe [Id Repo])
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data SetRepoSecret = SetRepoSecret
+    { setRepoSecretPublicKeyId :: !Text
+    , setRepoSecretEncryptedValue :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data SelectedRepo = SelectedRepo
+    { selectedRepoRepoId :: !(Id Repo)
+    , selectedRepoRepoName :: !(Name Repo)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data SetSelectedRepositories = SetSelectedRepositories
+    { setSelectedRepositoriesRepositoryIds :: ![Id Repo]
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data RepoSecret = RepoSecret
+    { repoSecretName :: !(Name RepoSecret)
+    , repoSecretCreatedAt :: !UTCTime
+    , repoSecretUpdatedAt :: !UTCTime
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-- TODO move somewhere else?
+data Environment = Environment
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON OrganizationSecret where
+    parseJSON = withObject "Secret" $ \o -> OrganizationSecret
+        <$> o .: "name"
+        <*> o .: "created_at"
+        <*> o .: "updated_at"
+        <*> o .: "visibility"
+
+instance FromJSON (WithTotalCount OrganizationSecret) where
+    parseJSON = withObject "SecretList" $ \o -> WithTotalCount
+        <$> o .: "secrets"
+        <*> o .: "total_count"
+
+instance FromJSON PublicKey where
+    parseJSON = withObject "PublicKey" $ \o -> PublicKey
+        <$> o .: "key_id"
+        <*> o .: "key"
+
+instance FromJSON SelectedRepo where
+    parseJSON = withObject "SelectedRepo" $ \o -> SelectedRepo
+        <$> o .: "id"
+        <*> o .: "name"
+
+instance ToJSON SetSelectedRepositories where
+    toJSON SetSelectedRepositories{..} =
+        object
+            [ "selected_repository_ids" .=  setSelectedRepositoriesRepositoryIds
+            ]
+
+instance ToJSON SetSecret where
+    toJSON SetSecret{..} =
+        object $
+            [ "encrypted_value" .= setSecretEncryptedValue
+            ,  "key_id" .= setSecretPublicKeyId
+            ,  "visibility" .= setSecretVisibility
+            ] <> maybeToList (fmap ("selected_repository_ids" .=) setSecretSelectedRepositoryIds)
+
+instance ToJSON SetRepoSecret where
+    toJSON SetRepoSecret{..} =
+        object
+            [ "encrypted_value" .= setRepoSecretEncryptedValue
+            ,  "key_id" .= setRepoSecretPublicKeyId
+            ]
+
+instance FromJSON (WithTotalCount SelectedRepo) where
+    parseJSON = withObject "SelectedRepoList" $ \o -> WithTotalCount
+        <$> o .: "repositories"
+        <*> o .: "total_count"
+
+instance FromJSON RepoSecret where
+    parseJSON = withObject "RepoSecret" $ \o -> RepoSecret
+        <$> o .: "name"
+        <*> o .: "created_at"
+        <*> o .: "updated_at"
+
+instance FromJSON (WithTotalCount RepoSecret) where
+    parseJSON = withObject "RepoSecretList" $ \o -> WithTotalCount
+        <$> o .: "secrets"
+        <*> o .: "total_count"
diff --git a/src/GitHub/Data/Actions/WorkflowJobs.hs b/src/GitHub/Data/Actions/WorkflowJobs.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/WorkflowJobs.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+
+module GitHub.Data.Actions.WorkflowJobs (
+    JobStep(..),
+    Job(..),
+    ) where
+
+import Prelude ()
+import GitHub.Internal.Prelude
+       (Applicative ((<*>)), Data, Eq, FromJSON (parseJSON), Generic, Integer,
+       Ord, Show, Text, UTCTime, Vector, withObject, ($), (.:),
+       (<$>))
+
+import GitHub.Data.Id                   (Id)
+import GitHub.Data.Name                 (Name)
+import GitHub.Data.URL                  (URL)
+
+import GitHub.Data.Actions.Common       (WithTotalCount (WithTotalCount))
+import GitHub.Data.Actions.WorkflowRuns (WorkflowRun)
+
+-------------------------------------------------------------------------------
+-- Workflow jobs
+-------------------------------------------------------------------------------
+
+data JobStep = JobStep
+    { jobStepName        :: !(Name JobStep)
+    , jobStepStatus      :: !Text
+    , jobStepConclusion  :: !Text
+    , jobStepNumber      :: !Integer
+    , jobStepStartedAt   :: !UTCTime
+    , jobStepCompletedAt :: !UTCTime
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data Job = Job
+    { jobId              :: !(Id Job)
+    , jobRunId           :: !(Id WorkflowRun)
+    , jobRunUrl          :: !URL
+    , jobRunAttempt      :: !Integer
+    , jobNodeId          :: !Text
+    , jobHeadSha         :: !Text
+    , jobUrl             :: !URL
+    , jobHtmlUrl         :: !URL
+    , jobStatus          :: !Text
+    , jobConclusion      :: !Text
+    , jobStartedAt       :: !UTCTime
+    , jobCompletedAt     :: !UTCTime
+    , jobName            :: !(Name Job)
+    , jobSteps           :: !(Vector JobStep)
+    , jobRunCheckUrl     :: !URL
+    , jobLabels          :: !(Vector Text)
+    , jobRunnerId        :: !Integer
+    , jobRunnerName      :: !Text
+    , jobRunnerGroupId   :: !Integer
+    , jobRunnerGroupName :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON JobStep where
+    parseJSON = withObject "JobStep" $ \o -> JobStep
+        <$> o .: "name"
+        <*> o .: "status"
+        <*> o .: "conclusion"
+        <*> o .: "number"
+        <*> o .: "started_at"
+        <*> o .: "completed_at"
+
+instance FromJSON Job where
+    parseJSON = withObject "Job" $ \o -> Job
+        <$> o .: "id"
+        <*> o .: "run_id"
+        <*> o .: "run_url"
+        <*> o .: "run_attempt"
+        <*> o .: "node_id"
+        <*> o .: "head_sha"
+        <*> o .: "url"
+        <*> o .: "html_url"
+        <*> o .: "status"
+        <*> o .: "conclusion"
+        <*> o .: "started_at"
+        <*> o .: "completed_at"
+        <*> o .: "name"
+        <*> o .: "steps"
+        <*> o .: "check_run_url"
+        <*> o .: "labels"
+        <*> o .: "runner_id"
+        <*> o .: "runner_name"
+        <*> o .: "runner_group_id"
+        <*> o .: "runner_group_name"
+
+instance FromJSON (WithTotalCount Job) where
+    parseJSON = withObject "JobList" $ \o -> WithTotalCount
+        <$> o .: "jobs"
+        <*> o .: "total_count"
diff --git a/src/GitHub/Data/Actions/WorkflowRuns.hs b/src/GitHub/Data/Actions/WorkflowRuns.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/WorkflowRuns.hs
@@ -0,0 +1,91 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+
+module GitHub.Data.Actions.WorkflowRuns (
+    WorkflowRun(..),
+    RunAttempt(..),
+    ReviewHistory(..),
+    ) where
+
+import GitHub.Data.Actions.Common (WithTotalCount (WithTotalCount))
+import GitHub.Data.Definitions
+import GitHub.Data.URL            (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import GitHub.Data.Id   (Id)
+import GitHub.Data.Name (Name)
+
+-------------------------------------------------------------------------------
+-- Workflow runs
+-------------------------------------------------------------------------------
+
+data WorkflowRun  = WorkflowRun
+    { workflowRunWorkflowRunId :: !(Id WorkflowRun)
+    , workflowRunName :: !(Name WorkflowRun)
+    , workflowRunHeadBranch :: !Text
+    , workflowRunHeadSha :: !Text
+    , workflowRunPath :: !Text
+    , workflowRunDisplayTitle :: !Text
+    , workflowRunRunNumber :: !Integer
+    , workflowRunEvent :: !Text
+    , workflowRunStatus :: !Text
+    , workflowRunConclusion :: !(Maybe Text)
+    , workflowRunWorkflowId :: !Integer
+    , workflowRunUrl :: !URL
+    , workflowRunHtmlUrl :: !URL
+    , workflowRunCreatedAt :: !UTCTime
+    , workflowRunUpdatedAt :: !UTCTime
+    , workflowRunActor :: !SimpleUser
+    , workflowRunAttempt :: !Integer
+    , workflowRunStartedAt :: !UTCTime
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data RunAttempt = RunAttempt
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data ReviewHistory  = ReviewHistory
+    { reviewHistoryState :: !Text
+    , reviewHistoryComment :: !Text
+    , reviewHistoryUser :: !SimpleUser
+
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON WorkflowRun where
+    parseJSON = withObject "WorkflowRun" $ \o -> WorkflowRun
+        <$> o .: "id"
+        <*> o .: "name"
+        <*> o .: "head_branch"
+        <*> o .: "head_sha"
+        <*> o .: "path"
+        <*> o .: "display_title"
+        <*> o .: "run_number"
+        <*> o .: "event"
+        <*> o .: "status"
+        <*> o .: "conclusion"
+        <*> o .: "workflow_id"
+        <*> o .: "url"
+        <*> o .: "html_url"
+        <*> o .: "created_at"
+        <*> o .: "updated_at"
+        <*> o .: "actor"
+        <*> o .: "run_attempt"
+        <*> o .: "run_started_at"
+
+instance FromJSON (WithTotalCount WorkflowRun) where
+    parseJSON = withObject "WorkflowRunList" $ \o -> WithTotalCount
+        <$> o .: "workflow_runs"
+        <*> o .: "total_count"
+
+instance FromJSON ReviewHistory where
+    parseJSON = withObject "ReviewHistory" $ \o -> ReviewHistory
+        <$> o .: "state"
+        <*> o .: "comment"
+        <*> o .: "user"
diff --git a/src/GitHub/Data/Actions/Workflows.hs b/src/GitHub/Data/Actions/Workflows.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Actions/Workflows.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures    #-}
+
+module GitHub.Data.Actions.Workflows (
+    Workflow(..),
+    CreateWorkflowDispatchEvent(..),
+    ) where
+
+import Prelude ()
+import GitHub.Internal.Prelude
+
+import GitHub.Data.Actions.Common (WithTotalCount (WithTotalCount))
+import GitHub.Data.Id             (Id)
+import GitHub.Data.URL            (URL)
+
+data Workflow = Workflow
+    { workflowWorkflowId :: !(Id Workflow)
+    , workflowName       :: !Text
+    , workflowPath       :: !Text
+    , workflowState      :: !Text
+    , workflowCreatedAt  :: !UTCTime
+    , workflowUpdatedAt  :: !UTCTime
+    , workflowUrl        :: !URL
+    , workflowHtmlUrl    :: !URL
+    , workflowBadgeUrl   :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+data CreateWorkflowDispatchEvent a = CreateWorkflowDispatchEvent
+    { createWorkflowDispatchEventRef    :: !Text
+    , createWorkflowDispatchEventInputs :: !a
+    }
+  deriving (Show, Generic)
+
+instance (NFData a) => NFData (CreateWorkflowDispatchEvent a)
+instance (Binary a) => Binary (CreateWorkflowDispatchEvent a)
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON Workflow where
+    parseJSON = withObject "Workflow" $ \o -> Workflow
+        <$> o .: "id"
+        <*> o .: "name"
+        <*> o .: "path"
+        <*> o .: "state"
+        <*> o .: "created_at"
+        <*> o .: "updated_at"
+        <*> o .: "url"
+        <*> o .: "html_url"
+        <*> o .: "badge_url"
+
+instance FromJSON (WithTotalCount Workflow) where
+    parseJSON = withObject "WorkflowList" $ \o -> WithTotalCount
+        <$> o .: "workflows"
+        <*> o .: "total_count"
+
+instance ToJSON a => ToJSON (CreateWorkflowDispatchEvent a) where
+    toJSON (CreateWorkflowDispatchEvent ref inputs) =
+        object [ "ref" .= ref, "inputs" .= inputs ]
diff --git a/src/GitHub/Data/Activities.hs b/src/GitHub/Data/Activities.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Activities.hs
@@ -0,0 +1,114 @@
+module GitHub.Data.Activities where
+
+import GitHub.Data.Id          (Id, mkId)
+import GitHub.Data.Repos       (Repo, RepoRef)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+
+import Prelude ()
+
+import qualified Data.Text as T
+
+data RepoStarred = RepoStarred
+    { repoStarredStarredAt :: !UTCTime
+    , repoStarredRepo      :: !Repo
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoStarred
+instance Binary RepoStarred
+
+-- JSON Instances
+instance FromJSON RepoStarred where
+    parseJSON = withObject "RepoStarred" $ \o -> RepoStarred
+        <$> o .: "starred_at"
+        <*> o .: "repo"
+
+data Subject = Subject
+    { subjectTitle :: !Text
+    , subjectURL :: !(Maybe URL)
+    , subjectLatestCommentURL :: !(Maybe URL)
+    -- https://developer.github.com/v3/activity/notifications/ doesn't indicate
+    -- what the possible values for this field are.
+    -- TODO: Make an ADT for this.
+    , subjectType :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Subject
+instance Binary Subject
+
+instance FromJSON Subject where
+    parseJSON = withObject "Subject" $ \o -> Subject
+        <$> o .: "title"
+        <*> o .: "url"
+        <*> o .:? "latest_comment_url"
+        <*> o .: "type"
+
+data NotificationReason
+    = ApprovalRequestedReason
+    | AssignReason
+    | AuthorReason
+    | CommentReason
+    | CiActivityReason
+    | InvitationReason
+    | ManualReason
+    | MemberFeatureRequestedReason
+    | MentionReason
+    | ReviewRequestedReason
+    | SecurityAlertReason
+    | SecurityAdvisoryCreditReason
+    | StateChangeReason
+    | SubscribedReason
+    | TeamMentionReason
+  deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData NotificationReason
+instance Binary NotificationReason
+
+instance FromJSON NotificationReason where
+    parseJSON = withText "NotificationReason" $ \t -> case T.toLower t of
+      "approval_requested"       -> pure ApprovalRequestedReason
+      "assign"                   -> pure AssignReason
+      "author"                   -> pure AuthorReason
+      "comment"                  -> pure CommentReason
+      "ci_activity"              -> pure CiActivityReason
+      "invitation"               -> pure InvitationReason
+      "manual"                   -> pure ManualReason
+      "member_feature_requested" -> pure MemberFeatureRequestedReason
+      "mention"                  -> pure MentionReason
+      "review_requested"         -> pure ReviewRequestedReason
+      "security_alert"           -> pure SecurityAlertReason
+      "security_advisory_credit" -> pure SecurityAdvisoryCreditReason
+      "state_change"             -> pure StateChangeReason
+      "subscribed"               -> pure SubscribedReason
+      "team_mention"             -> pure TeamMentionReason
+      _                          -> fail $ "Unknown NotificationReason " ++ show t
+
+data Notification = Notification
+    -- XXX: The notification id field type IS in fact string. Not sure why gh
+    -- chose to do this when all the other ids are Numbers...
+    { notificationId :: !(Id Notification)
+    , notificationRepo :: !RepoRef
+    , notificationSubject :: !Subject
+    , notificationReason :: !NotificationReason
+    , notificationUnread :: !Bool
+    , notificationUpdatedAt :: !(Maybe UTCTime)
+    , notificationLastReadAt :: !(Maybe UTCTime)
+    , notificationUrl :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Notification
+instance Binary Notification
+
+instance FromJSON Notification where
+    parseJSON = withObject "Notification" $ \o -> Notification
+        <$> (mkId undefined . read <$> o .: "id")
+        <*> o .: "repository"
+        <*> o .: "subject"
+        <*> o .: "reason"
+        <*> o .: "unread"
+        <*> o .: "updated_at"
+        <*> o .: "last_read_at"
+        <*> o .: "url"
diff --git a/src/GitHub/Data/Comments.hs b/src/GitHub/Data/Comments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Comments.hs
@@ -0,0 +1,92 @@
+module GitHub.Data.Comments where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data Comment = Comment
+    { commentPosition  :: !(Maybe Int)
+    , commentLine      :: !(Maybe Int)
+    , commentBody      :: !Text
+    , commentCommitId  :: !(Maybe Text)
+    , commentUpdatedAt :: !UTCTime
+    , commentHtmlUrl   :: !(Maybe URL)
+    , commentUrl       :: !URL
+    , commentCreatedAt :: !(Maybe UTCTime)
+    , commentPath      :: !(Maybe Text)
+    , commentUser      :: !SimpleUser
+    , commentId        :: !(Id Comment)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Comment
+instance Binary Comment
+
+instance FromJSON Comment where
+    parseJSON = withObject "Comment" $ \o -> Comment
+        <$> o .:? "position"
+        <*> o .:? "line"
+        <*> o .: "body"
+        <*> o .:? "commit_id"
+        <*> o .: "updated_at"
+        <*> o .:? "html_url"
+        <*> o .: "url"
+        <*> o .: "created_at"
+        <*> o .:? "path"
+        <*> o .: "user"
+        <*> o .: "id"
+
+data NewComment = NewComment
+    { newCommentBody :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewComment
+instance Binary NewComment
+
+instance ToJSON NewComment where
+    toJSON (NewComment b) = object [ "body" .= b ]
+
+data EditComment = EditComment
+    { editCommentBody :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData EditComment
+instance Binary EditComment
+
+instance ToJSON EditComment where
+    toJSON (EditComment b) = object [ "body" .= b ]
+
+data NewPullComment = NewPullComment
+    { newPullCommentCommit   :: !Text
+    , newPullCommentPath     :: !Text
+    , newPullCommentPosition :: !Int
+    , newPullCommentBody     :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewPullComment
+instance Binary NewPullComment
+
+instance ToJSON NewPullComment where
+    toJSON (NewPullComment c path pos b) =
+        object [ "body" .= b
+               , "commit_id" .= c
+               , "path" .= path
+               , "position" .= pos
+               ]
+
+data PullCommentReply = PullCommentReply
+    { pullCommentReplyBody     :: Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PullCommentReply
+
+instance ToJSON PullCommentReply where
+    toJSON (PullCommentReply b) =
+        object [ "body" .= b
+               ]
diff --git a/src/GitHub/Data/Content.hs b/src/GitHub/Data/Content.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Content.hs
@@ -0,0 +1,213 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE CPP #-}
+
+module GitHub.Data.Content where
+
+import GitHub.Data.GitData
+import GitHub.Data.URL
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Data.Aeson.Types (Pair)
+import qualified Data.Text as T
+
+#if MIN_VERSION_aeson(2,0,0)
+import Data.Aeson (Key)
+#endif
+
+data Content
+  = ContentFile !ContentFileData
+  | ContentDirectory !(Vector ContentItem)
+ deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Content
+instance Binary Content
+
+data ContentFileData = ContentFileData {
+   contentFileInfo     :: !ContentInfo
+  ,contentFileEncoding :: !Text
+  ,contentFileSize     :: !Int
+  ,contentFileContent  :: !Text
+} deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ContentFileData
+instance Binary ContentFileData
+
+-- | An item in a directory listing.
+data ContentItem = ContentItem {
+   contentItemType :: !ContentItemType
+  ,contentItemInfo :: !ContentInfo
+} deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ContentItem
+instance Binary ContentItem
+
+data ContentItemType = ItemFile | ItemDir
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ContentItemType
+instance Binary ContentItemType
+
+-- | Information common to both kinds of Content: files and directories.
+data ContentInfo = ContentInfo {
+   contentName    :: !Text
+  ,contentPath    :: !Text
+  ,contentSha     :: !Text
+  ,contentUrl     :: !URL
+  ,contentGitUrl  :: !URL
+  ,contentHtmlUrl :: !URL
+} deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ContentInfo
+instance Binary ContentInfo
+
+data ContentResultInfo = ContentResultInfo
+    { contentResultInfo :: !ContentInfo
+    , contentResultSize :: !Int
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ContentResultInfo
+instance Binary ContentResultInfo
+
+data ContentResult = ContentResult
+    { contentResultContent  :: !ContentResultInfo
+    , contentResultCommit   :: !GitCommit
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ContentResult
+instance Binary ContentResult
+
+data Author = Author
+    { authorName  :: !Text
+    , authorEmail :: !Text
+    }
+    deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData Author
+instance Binary Author
+
+data CreateFile = CreateFile
+    { createFilePath      :: !Text
+    , createFileMessage   :: !Text
+    , createFileContent   :: !Text
+    , createFileBranch    :: !(Maybe Text)
+    , createFileAuthor    :: !(Maybe Author)
+    , createFileCommitter :: !(Maybe Author)
+    }
+    deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData CreateFile
+instance Binary CreateFile
+
+data UpdateFile = UpdateFile
+    { updateFilePath      :: !Text
+    , updateFileMessage   :: !Text
+    , updateFileContent   :: !Text
+    , updateFileSHA       :: !Text
+    , updateFileBranch    :: !(Maybe Text)
+    , updateFileAuthor    :: !(Maybe Author)
+    , updateFileCommitter :: !(Maybe Author)
+    }
+    deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData UpdateFile
+instance Binary UpdateFile
+
+data DeleteFile = DeleteFile
+    { deleteFilePath      :: !Text
+    , deleteFileMessage   :: !Text
+    , deleteFileSHA       :: !Text
+    , deleteFileBranch    :: !(Maybe Text)
+    , deleteFileAuthor    :: !(Maybe Author)
+    , deleteFileCommitter :: !(Maybe Author)
+    }
+    deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData DeleteFile
+instance Binary DeleteFile
+
+instance FromJSON Content where
+  parseJSON o@(Object _) = ContentFile <$> parseJSON o
+  parseJSON (Array os) = ContentDirectory <$> traverse parseJSON os
+  parseJSON _ = fail "Could not build a Content"
+
+instance FromJSON ContentFileData where
+  parseJSON = withObject "ContentFileData" $ \o ->
+    ContentFileData <$> parseJSON (Object o)
+                    <*> o .: "encoding"
+                    <*> o .: "size"
+                    <*> o .: "content"
+
+instance FromJSON ContentItem where
+  parseJSON = withObject "ContentItem" $ \o ->
+    ContentItem <$> o .: "type"
+                <*> parseJSON (Object o)
+
+instance FromJSON ContentItemType where
+  parseJSON = withText "ContentItemType" $ \t -> case T.toLower t of
+    "file" -> pure ItemFile
+    "dir"  -> pure ItemDir
+    _      -> fail $ "Unknown ContentItemType: " <> T.unpack t
+
+instance FromJSON ContentInfo where
+  parseJSON = withObject "ContentInfo" $ \o ->
+    ContentInfo <$> o .: "name"
+                <*> o .: "path"
+                <*> o .: "sha"
+                <*> o .: "url"
+                <*> o .: "git_url"
+                <*> o .: "html_url"
+
+instance FromJSON ContentResultInfo where
+  parseJSON = withObject "ContentResultInfo" $ \o ->
+    ContentResultInfo <$> parseJSON (Object o)
+                  <*> o .: "size"
+
+instance FromJSON ContentResult where
+  parseJSON = withObject "ContentResult" $ \o ->
+    ContentResult <$> o .: "content"
+                  <*> o .: "commit"
+
+instance ToJSON Author where
+  toJSON Author {..} = object
+    [ "name"  .= authorName
+    , "email" .= authorEmail
+    ]
+
+instance ToJSON CreateFile where
+  toJSON CreateFile {..} = object $
+    [ "path"       .= createFilePath
+    , "message"    .= createFileMessage
+    , "content"    .= createFileContent
+    ]
+    ++ "branch"    .=? createFileBranch
+    ++ "author"    .=? createFileAuthor
+    ++ "committer" .=? createFileCommitter
+
+instance ToJSON UpdateFile where
+  toJSON UpdateFile {..} = object $
+    [ "path"       .= updateFilePath
+    , "message"    .= updateFileMessage
+    , "content"    .= updateFileContent
+    , "sha"        .= updateFileSHA
+    ]
+    ++ "branch"    .=? updateFileBranch
+    ++ "author"    .=? updateFileAuthor
+    ++ "committer" .=? updateFileCommitter
+
+instance ToJSON DeleteFile where
+  toJSON DeleteFile {..} = object $
+    [ "path"       .= deleteFilePath
+    , "message"    .= deleteFileMessage
+    , "sha"        .= deleteFileSHA
+    ]
+    ++ "branch"    .=? deleteFileBranch
+    ++ "author"    .=? deleteFileAuthor
+    ++ "committer" .=? deleteFileCommitter
+
+#if MIN_VERSION_aeson(2,0,0)
+(.=?) :: ToJSON v => Key -> Maybe v -> [Pair]
+#else
+(.=?) :: ToJSON v => Text -> Maybe v -> [Pair]
+#endif
+name .=? value = maybe [] (pure . (name .=)) value
diff --git a/src/GitHub/Data/Definitions.hs b/src/GitHub/Data/Definitions.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Definitions.hs
@@ -0,0 +1,388 @@
+module GitHub.Data.Definitions where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Control.Monad       (mfilter)
+import Data.Aeson.Types    (Parser)
+import Network.HTTP.Client (HttpException)
+
+import qualified Control.Exception as E
+import qualified Data.ByteString   as BS
+import qualified Data.Text         as T
+
+import GitHub.Data.Id   (Id)
+import GitHub.Data.Name (Name)
+import GitHub.Data.URL  (URL (..))
+
+-- | Errors have been tagged according to their source, so you can more easily
+-- dispatch and handle them.
+data Error
+    = HTTPError !HttpException -- ^ A HTTP error occurred. The actual caught error is included.
+    | ParseError !Text -- ^ An error in the parser itself.
+    | JsonError !Text -- ^ The JSON is malformed or unexpected.
+    | UserError !Text -- ^ Incorrect input.
+    deriving (Show)
+
+instance E.Exception Error
+
+-- | Type of the repository owners.
+data OwnerType = OwnerUser | OwnerOrganization | OwnerBot
+    deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic, Data)
+
+instance NFData OwnerType
+instance Binary OwnerType
+
+data SimpleUser = SimpleUser
+    { simpleUserId        :: !(Id User)
+    , simpleUserLogin     :: !(Name User)
+    , simpleUserAvatarUrl :: !URL
+    , simpleUserUrl       :: !URL
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData SimpleUser
+instance Binary SimpleUser
+
+data SimpleOrganization = SimpleOrganization
+    { simpleOrganizationId        :: !(Id Organization)
+    , simpleOrganizationLogin     :: !(Name Organization)
+    , simpleOrganizationUrl       :: !URL
+    , simpleOrganizationAvatarUrl :: !URL
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData SimpleOrganization
+instance Binary SimpleOrganization
+
+-- | Sometimes we don't know the type of the owner, e.g. in 'Repo'
+data SimpleOwner = SimpleOwner
+    { simpleOwnerId        :: !(Id Owner)
+    , simpleOwnerLogin     :: !(Name Owner)
+    , simpleOwnerUrl       :: !URL
+    , simpleOwnerAvatarUrl :: !URL
+    , simpleOwnerType      :: !OwnerType
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData SimpleOwner
+instance Binary SimpleOwner
+
+data User = User
+    { userId          :: !(Id User)
+    , userLogin       :: !(Name User)
+    , userName        :: !(Maybe Text)
+    , userType        :: !OwnerType  -- ^ Should always be 'OwnerUser' or 'OwnerBot'
+    , userCreatedAt   :: !UTCTime
+    , userPublicGists :: !Int
+    , userAvatarUrl   :: !URL
+    , userFollowers   :: !Int
+    , userFollowing   :: !Int
+    , userHireable    :: !(Maybe Bool)
+    , userBlog        :: !(Maybe Text)
+    , userBio         :: !(Maybe Text)
+    , userPublicRepos :: !Int
+    , userLocation    :: !(Maybe Text)
+    , userCompany     :: !(Maybe Text)
+    , userEmail       :: !(Maybe Text)
+    , userUrl         :: !URL
+    , userHtmlUrl     :: !URL
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData User
+instance Binary User
+
+data Organization = Organization
+    { organizationId          :: !(Id Organization)
+    , organizationLogin       :: !(Name Organization)
+    , organizationName        :: !(Maybe Text)
+    , organizationType        :: !OwnerType  -- ^ Should always be 'OwnerOrganization'
+    , organizationBlog        :: !(Maybe Text)
+    , organizationLocation    :: !(Maybe Text)
+    , organizationFollowers   :: !Int
+    , organizationCompany     :: !(Maybe Text)
+    , organizationAvatarUrl   :: !URL
+    , organizationPublicGists :: !Int
+    , organizationHtmlUrl     :: !URL
+    , organizationEmail       :: !(Maybe Text)
+    , organizationFollowing   :: !Int
+    , organizationPublicRepos :: !Int
+    , organizationUrl         :: !URL
+    , organizationCreatedAt   :: !UTCTime
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Organization
+instance Binary Organization
+
+-- | In practice you can't have concrete values of 'Owner'.
+newtype Owner = Owner (Either User Organization)
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Owner
+instance Binary Owner
+
+fromOwner :: Owner -> Either User Organization
+fromOwner (Owner owner) = owner
+
+-- JSON instances
+
+instance FromJSON OwnerType where
+    parseJSON = withText "OwnerType" $ \t -> case T.toLower t of
+        "user"         -> pure $ OwnerUser
+        "organization" -> pure $ OwnerOrganization
+        "bot"          -> pure $ OwnerBot
+        _              -> fail $ "Unknown OwnerType: " <> T.unpack t
+
+instance FromJSON SimpleUser where
+    parseJSON = withObject "SimpleUser" $ \obj -> do
+        SimpleUser
+            <$> obj .: "id"
+            <*> obj .: "login"
+            <*> obj .: "avatar_url"
+            <*> obj .: "url"
+
+instance FromJSON SimpleOrganization where
+    parseJSON = withObject "SimpleOrganization" $ \obj ->
+        SimpleOrganization
+            <$> obj .: "id"
+            <*> obj .: "login"
+            <*> obj .: "url"
+            <*> obj .: "avatar_url"
+
+instance FromJSON SimpleOwner where
+    parseJSON = withObject "SimpleOwner" $ \obj -> do
+        SimpleOwner
+            <$> obj .: "id"
+            <*> obj .: "login"
+            <*> obj .: "url"
+            <*> obj .: "avatar_url"
+            <*> obj .: "type"
+
+parseUser :: Object -> Parser User
+parseUser obj = User
+    <$> obj .: "id"
+    <*> obj .: "login"
+    <*> obj .:? "name"
+    <*> obj .: "type"
+    <*> obj .: "created_at"
+    <*> obj .: "public_gists"
+    <*> obj .: "avatar_url"
+    <*> obj .: "followers"
+    <*> obj .: "following"
+    <*> obj .:? "hireable"
+    <*> obj .:? "blog"
+    <*> obj .:? "bio"
+    <*> obj .: "public_repos"
+    <*> obj .:? "location"
+    <*> obj .:? "company"
+    <*> obj .:? "email"
+    <*> obj .: "url"
+    <*> obj .: "html_url"
+
+parseOrganization :: Object -> Parser Organization
+parseOrganization obj = Organization
+    <$> obj .: "id"
+    <*> obj .: "login"
+    <*> obj .:? "name"
+    <*> obj .: "type"
+    <*> obj .:? "blog"
+    <*> obj .:? "location"
+    <*> obj .: "followers"
+    <*> obj .:? "company"
+    <*> obj .: "avatar_url"
+    <*> obj .: "public_gists"
+    <*> obj .: "html_url"
+    <*> obj .:? "email"
+    <*> obj .: "following"
+    <*> obj .: "public_repos"
+    <*> obj .: "url"
+    <*> obj .: "created_at"
+
+instance FromJSON User where
+    parseJSON = mfilter ((/= OwnerOrganization) . userType) . withObject "User" parseUser
+
+instance FromJSON Organization where
+    parseJSON = withObject "Organization" parseOrganization
+
+instance FromJSON Owner where
+    parseJSON = withObject "Owner" $ \obj -> do
+        t <- obj .: "type"
+        case t of
+            OwnerUser         -> Owner . Left <$> parseUser obj
+            OwnerBot          -> Owner . Left <$> parseUser obj
+            OwnerOrganization -> Owner . Right <$> parseOrganization obj
+
+-- | Filter members returned in the list.
+data OrgMemberFilter
+    = OrgMemberFilter2faDisabled  -- ^ Members without two-factor authentication enabled. Available for organization owners.
+    | OrgMemberFilterAll          -- ^ All members the authenticated user can see.
+    deriving (Show, Eq, Ord, Enum, Bounded, Data, Generic)
+
+-- | Filter members returned by their role.
+data OrgMemberRole
+    = OrgMemberRoleAll     -- ^ All members of the organization, regardless of role.
+    | OrgMemberRoleAdmin   -- ^ Organization owners.
+    | OrgMemberRoleMember  -- ^ Non-owner organization members.
+    deriving (Show, Eq, Ord, Enum, Bounded, Data, Generic)
+
+-- | Request query string
+type QueryString = [(BS.ByteString, Maybe BS.ByteString)]
+
+-- | Count of elements
+type Count = Int
+
+
+
+data MembershipRole
+    = MembershipRoleMember
+    | MembershipRoleAdmin
+    | MembershipRoleBillingManager
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData MembershipRole
+instance Binary MembershipRole
+
+instance FromJSON MembershipRole where
+    parseJSON = withText "MembershipRole" $ \t -> case T.toLower t of
+        "member"   -> pure MembershipRoleMember
+        "admin"           -> pure MembershipRoleAdmin
+        "billing_manager" -> pure MembershipRoleBillingManager
+        _                 -> fail $ "Unknown MembershipRole: " <> T.unpack t
+
+data MembershipState
+    = MembershipPending
+    | MembershipActive
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData MembershipState
+instance Binary MembershipState
+
+instance FromJSON MembershipState where
+    parseJSON = withText "MembershipState" $ \t -> case T.toLower t of
+        "active"  -> pure MembershipActive
+        "pending" -> pure MembershipPending
+        _         -> fail $ "Unknown MembershipState: " <> T.unpack t
+
+
+data Membership = Membership
+    { membershipUrl             :: !URL
+    , membershipState           :: !MembershipState
+    , membershipRole            :: !MembershipRole
+    , membershipOrganizationUrl :: !URL
+    , membershipOrganization    :: !SimpleOrganization
+    , membershipUser            :: !SimpleUser
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Membership
+instance Binary Membership
+
+instance FromJSON Membership where
+    parseJSON = withObject "Membership" $ \o -> Membership
+        <$> o .: "url"
+        <*> o .: "state"
+        <*> o .: "role"
+        <*> o .: "organization_url"
+        <*> o .: "organization"
+        <*> o .: "user"
+
+
+-------------------------------------------------------------------------------
+-- IssueNumber
+-------------------------------------------------------------------------------
+
+newtype IssueNumber = IssueNumber Int
+    deriving (Eq, Ord, Show, Generic, Data)
+
+unIssueNumber :: IssueNumber -> Int
+unIssueNumber (IssueNumber i) = i
+
+instance Hashable IssueNumber
+instance Binary IssueNumber
+
+instance NFData IssueNumber where
+    rnf (IssueNumber s) = rnf s
+
+instance FromJSON IssueNumber where
+    parseJSON = fmap IssueNumber . parseJSON
+
+instance ToJSON IssueNumber where
+    toJSON = toJSON . unIssueNumber
+
+-------------------------------------------------------------------------------
+-- IssueLabel
+-------------------------------------------------------------------------------
+
+data IssueLabel = IssueLabel
+    { labelColor :: !Text
+    , labelUrl   :: !URL
+    , labelName  :: !(Name IssueLabel)
+    , labelDesc  :: !(Maybe Text)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData IssueLabel
+instance Binary IssueLabel
+
+instance FromJSON IssueLabel where
+    parseJSON = withObject "IssueLabel" $ \o -> IssueLabel
+        <$> o .: "color"
+        <*> o .:? "url" .!= URL "" -- in events there aren't URL
+        <*> o .: "name"
+        <*> o .:? "description"
+
+
+-------------------------------------------------------------------------------
+-- NewIssueLabel
+-------------------------------------------------------------------------------
+
+data NewIssueLabel = NewIssueLabel
+    { newLabelColor :: !Text
+    , newLabelName  :: !(Name NewIssueLabel)
+    , newLabelDesc  :: !(Maybe Text)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewIssueLabel
+instance Binary NewIssueLabel
+
+
+instance ToJSON NewIssueLabel where
+    toJSON (NewIssueLabel color lblName lblDesc) = object $ filter notNull
+        [ "name" .= lblName
+        , "color" .= color
+        , "description" .= lblDesc
+        ]
+        where
+            notNull (_, Null) = False
+            notNull (_, _)    = True
+
+
+
+-------------------------------------------------------------------------------
+-- UpdateIssueLabel
+-------------------------------------------------------------------------------
+
+data UpdateIssueLabel = UpdateIssueLabel
+    { updateLabelColor :: !Text
+    , updateLabelName  :: !(Name UpdateIssueLabel)
+    , updateLabelDesc  :: !(Maybe Text)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData UpdateIssueLabel
+instance Binary UpdateIssueLabel
+
+
+instance ToJSON UpdateIssueLabel where
+    toJSON (UpdateIssueLabel color lblName lblDesc) = object $ filter notNull
+        [ "new_name" .= lblName
+        , "color" .= color
+        , "description" .= lblDesc
+        ]
+        where
+            notNull (_, Null) = False
+            notNull (_, _)    = True
diff --git a/src/GitHub/Data/DeployKeys.hs b/src/GitHub/Data/DeployKeys.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/DeployKeys.hs
@@ -0,0 +1,52 @@
+-----------------------------------------------------------------------------
+-- |
+-- License     :  BSD-3-Clause
+-- Maintainer  :  Todd Mohney <toddmohney@gmail.com>
+--
+module GitHub.Data.DeployKeys where
+
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data RepoDeployKey = RepoDeployKey
+    { repoDeployKeyId        :: !(Id RepoDeployKey)
+    , repoDeployKeyKey       :: !Text
+    , repoDeployKeyUrl       :: !URL
+    , repoDeployKeyTitle     :: !Text
+    , repoDeployKeyVerified  :: !Bool
+    , repoDeployKeyCreatedAt :: !UTCTime
+    , repoDeployKeyReadOnly  :: !Bool
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON RepoDeployKey where
+    parseJSON = withObject "RepoDeployKey" $ \o -> RepoDeployKey
+        <$> o .: "id"
+        <*> o .: "key"
+        <*> o .: "url"
+        <*> o .: "title"
+        <*> o .: "verified"
+        <*> o .: "created_at"
+        <*> o .: "read_only"
+
+data NewRepoDeployKey = NewRepoDeployKey
+    { newRepoDeployKeyKey      :: !Text
+    , newRepoDeployKeyTitle    :: !Text
+    , newRepoDeployKeyReadOnly :: !Bool
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance ToJSON NewRepoDeployKey where
+    toJSON (NewRepoDeployKey key title readOnly) = object
+        [ "key" .= key
+        , "title" .= title
+        , "read_only" .= readOnly
+        ]
+
+instance FromJSON NewRepoDeployKey where
+    parseJSON = withObject "RepoDeployKey" $ \o -> NewRepoDeployKey
+        <$> o .: "key"
+        <*> o .: "title"
+        <*> o .: "read_only"
diff --git a/src/GitHub/Data/Deployments.hs b/src/GitHub/Data/Deployments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Deployments.hs
@@ -0,0 +1,204 @@
+{-# LANGUAGE LambdaCase #-}
+
+module GitHub.Data.Deployments
+    ( DeploymentQueryOption (..)
+    , renderDeploymentQueryOption
+
+    , Deployment (..)
+    , CreateDeployment (..)
+
+    , DeploymentStatus (..)
+    , DeploymentStatusState (..)
+    , CreateDeploymentStatus (..)
+    ) where
+
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Control.Arrow (second)
+
+import Data.ByteString (ByteString)
+
+import GitHub.Data.Definitions (SimpleUser)
+import GitHub.Data.Id          (Id)
+import GitHub.Data.Name        (Name)
+import GitHub.Data.URL         (URL)
+
+import qualified Data.Aeson         as JSON
+import qualified Data.Text          as T
+import qualified Data.Text.Encoding as T
+
+data DeploymentQueryOption
+    = DeploymentQuerySha         !Text
+    | DeploymentQueryRef         !Text
+    | DeploymentQueryTask        !Text
+    | DeploymentQueryEnvironment !Text
+      deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData DeploymentQueryOption
+instance Binary DeploymentQueryOption
+
+renderDeploymentQueryOption :: DeploymentQueryOption -> (ByteString, ByteString)
+renderDeploymentQueryOption =
+    second T.encodeUtf8 . \case
+        DeploymentQuerySha         sha  -> ("sha",         sha)
+        DeploymentQueryRef         ref  -> ("ref",         ref)
+        DeploymentQueryTask        task -> ("task",        task)
+        DeploymentQueryEnvironment env  -> ("environment", env)
+
+data Deployment a = Deployment
+    { deploymentUrl           :: !URL
+    , deploymentId            :: !(Id   (Deployment a))
+    , deploymentSha           :: !(Name (Deployment a))
+    , deploymentRef           :: !Text
+    , deploymentTask          :: !Text
+    , deploymentPayload       :: !(Maybe a)
+    , deploymentEnvironment   :: !Text
+    , deploymentDescription   :: !Text
+    , deploymentCreator       :: !SimpleUser
+    , deploymentCreatedAt     :: !UTCTime
+    , deploymentUpdatedAt     :: !UTCTime
+    , deploymentStatusesUrl   :: !URL
+    , deploymentRepositoryUrl :: !URL
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData a => NFData (Deployment a)
+instance Binary a => Binary (Deployment a)
+
+instance FromJSON a => FromJSON (Deployment a) where
+    parseJSON = withObject "GitHub Deployment" $ \o ->
+        Deployment
+            <$> o .: "url"
+            <*> o .: "id"
+            <*> o .: "sha"
+            <*> o .: "ref"
+            <*> o .: "task"
+            <*> o .:? "payload"
+            <*> o .: "environment"
+            <*> o .: "description"
+            <*> o .: "creator"
+            <*> o .: "created_at"
+            <*> o .: "updated_at"
+            <*> o .: "statuses_url"
+            <*> o .: "repository_url"
+
+data CreateDeployment a = CreateDeployment
+    { createDeploymentRef              :: !Text
+    -- ^ Required. The ref to deploy. This can be a branch, tag, or SHA.
+    , createDeploymentTask             :: !(Maybe Text)
+    -- ^ Specifies a task to execute (e.g., deploy or deploy:migrations).
+    -- Default: deploy
+    , createDeploymentAutoMerge        :: !(Maybe Bool)
+    -- ^ Attempts to automatically merge the default branch into the requested
+    -- ref, if it is behind the default branch. Default: true
+    , createDeploymentRequiredContexts :: !(Maybe (Vector Text))
+    -- ^ The status contexts to verify against commit status checks. If this
+    -- parameter is omitted, then all unique contexts will be verified before a
+    -- deployment is created. To bypass checking entirely pass an empty array.
+    -- Defaults to all unique contexts.
+    , createDeploymentPayload          :: !(Maybe a)
+    -- ^ JSON payload with extra information about the deployment. Default: ""
+    , createDeploymentEnvironment      :: !(Maybe Text)
+    -- ^ Name for the target deployment environment (e.g., production, staging,
+    -- qa). Default: production
+    , createDeploymentDescription      :: !(Maybe Text)
+    -- ^ Short description of the deployment. Default: ""
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData a => NFData (CreateDeployment a)
+instance Binary a => Binary (CreateDeployment a)
+
+instance ToJSON a => ToJSON (CreateDeployment a) where
+    toJSON x =
+        JSON.object $ catMaybes
+            [ Just ("ref"          .=      createDeploymentRef x)
+            , ("task"              .=) <$> createDeploymentTask x
+            , ("auto_merge"        .=) <$> createDeploymentAutoMerge x
+            , ("required_contexts" .=) <$> createDeploymentRequiredContexts x
+            , ("payload"           .=) <$> createDeploymentPayload x
+            , ("environment"       .=) <$> createDeploymentEnvironment x
+            , ("description"       .=) <$> createDeploymentDescription x
+            ]
+
+data DeploymentStatus = DeploymentStatus
+    { deploymentStatusUrl           :: !URL
+    , deploymentStatusId            :: !(Id DeploymentStatus)
+    , deploymentStatusState         :: !DeploymentStatusState
+    , deploymentStatusCreator       :: !SimpleUser
+    , deploymentStatusDescription   :: !Text
+    , deploymentStatusTargetUrl     :: !URL
+    , deploymentStatusCreatedAt     :: !UTCTime
+    , deploymentStatusUpdatedAt     :: !UTCTime
+    , deploymentStatusDeploymentUrl :: !URL
+    , deploymentStatusRepositoryUrl :: !URL
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData DeploymentStatus
+instance Binary DeploymentStatus
+
+instance FromJSON DeploymentStatus where
+    parseJSON = withObject "GitHub DeploymentStatus" $ \o ->
+        DeploymentStatus
+            <$> o .: "url"
+            <*> o .: "id"
+            <*> o .: "state"
+            <*> o .: "creator"
+            <*> o .: "description"
+            <*> o .: "target_url"
+            <*> o .: "created_at"
+            <*> o .: "updated_at"
+            <*> o .: "deployment_url"
+            <*> o .: "repository_url"
+
+data DeploymentStatusState
+    = DeploymentStatusError
+    | DeploymentStatusFailure
+    | DeploymentStatusPending
+    | DeploymentStatusSuccess
+    | DeploymentStatusInactive
+      deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData DeploymentStatusState
+instance Binary DeploymentStatusState
+
+instance ToJSON DeploymentStatusState where
+    toJSON = \case
+        DeploymentStatusError    -> "error"
+        DeploymentStatusFailure  -> "failure"
+        DeploymentStatusPending  -> "pending"
+        DeploymentStatusSuccess  -> "success"
+        DeploymentStatusInactive -> "inactive"
+
+instance FromJSON DeploymentStatusState where
+    parseJSON = withText "DeploymentStatusState" $ \t -> case T.toLower t of
+        "error"    -> pure DeploymentStatusError
+        "failure"  -> pure DeploymentStatusFailure
+        "pending"  -> pure DeploymentStatusPending
+        "success"  -> pure DeploymentStatusSuccess
+        "inactive" -> pure DeploymentStatusInactive
+        _          -> fail $ "Unknown DeploymentStatusState: " <> T.unpack t
+
+data CreateDeploymentStatus = CreateDeploymentStatus
+    { createDeploymentStatusState       :: !DeploymentStatusState
+    -- ^ Required. The state of the status. Can be one of error, failure,
+    -- pending, or success.
+    , createDeploymentStatusTargetUrl   :: !(Maybe Text) -- TODO: should this be URL?
+    -- ^ The target URL to associate with this status. This URL should contain
+    -- output to keep the user updated while the task is running or serve as
+    -- historical information for what happened in the deployment. Default: ""
+    , createDeploymentStatusDescription :: !(Maybe Text)
+    -- ^ A short description of the status. Maximum length of 140 characters.
+    -- Default: ""
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData CreateDeploymentStatus
+instance Binary CreateDeploymentStatus
+
+instance ToJSON CreateDeploymentStatus where
+    toJSON x =
+        JSON.object $ catMaybes
+            [ Just ("state"  .= createDeploymentStatusState x)
+            , ("target_url"  .=) <$> createDeploymentStatusTargetUrl x
+            , ("description" .=) <$> createDeploymentStatusDescription x
+            ]
diff --git a/src/GitHub/Data/Email.hs b/src/GitHub/Data/Email.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Email.hs
@@ -0,0 +1,37 @@
+module GitHub.Data.Email where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text as T
+
+data EmailVisibility
+    = EmailVisibilityPrivate
+    | EmailVisibilityPublic
+    deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData EmailVisibility
+instance Binary EmailVisibility
+
+instance FromJSON EmailVisibility where
+    parseJSON = withText "EmailVisibility" $ \t -> case T.toLower t of
+        "private" -> pure EmailVisibilityPrivate
+        "public"  -> pure EmailVisibilityPublic
+        _         -> fail $ "Unknown EmailVisibility: " <> T.unpack t
+
+data Email = Email
+    { emailAddress    :: !Text
+    , emailVerified   :: !Bool
+    , emailPrimary    :: !Bool
+    , emailVisibility :: !(Maybe EmailVisibility)
+    } deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Email
+instance Binary Email
+
+instance FromJSON Email where
+    parseJSON = withObject "Email" $ \o -> Email
+        <$> o .:  "email"
+        <*> o .:  "verified"
+        <*> o .:  "primary"
+        <*> o .:? "visibility"
diff --git a/src/GitHub/Data/Enterprise.hs b/src/GitHub/Data/Enterprise.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Enterprise.hs
@@ -0,0 +1,9 @@
+-- |
+-- This module re-exports the @GitHub.Data.Enterprise.@ submodules.
+
+module GitHub.Data.Enterprise (
+    -- * Module re-exports
+    module GitHub.Data.Enterprise.Organizations,
+    ) where
+
+import GitHub.Data.Enterprise.Organizations
diff --git a/src/GitHub/Data/Enterprise/Organizations.hs b/src/GitHub/Data/Enterprise/Organizations.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Enterprise/Organizations.hs
@@ -0,0 +1,59 @@
+module GitHub.Data.Enterprise.Organizations where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Name (Name)
+import GitHub.Data.URL (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data CreateOrganization = CreateOrganization
+    { createOrganizationLogin       :: !(Name Organization)
+    , createOrganizationAdmin       :: !(Name User)
+    , createOrganizationProfileName :: !(Maybe Text)
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData CreateOrganization
+instance Binary CreateOrganization
+
+data RenameOrganization = RenameOrganization
+    { renameOrganizationLogin :: !(Name Organization)
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RenameOrganization
+instance Binary RenameOrganization
+
+data RenameOrganizationResponse = RenameOrganizationResponse
+    { renameOrganizationResponseMessage :: !Text
+    , renameOrganizationResponseUrl     :: !URL
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RenameOrganizationResponse
+instance Binary RenameOrganizationResponse
+
+-- JSON Instances
+
+instance ToJSON CreateOrganization where
+    toJSON (CreateOrganization login admin profileName) =
+        object $ filter notNull
+            [ "login"        .= login
+            , "admin"        .= admin
+            , "profile_name" .= profileName
+            ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _) = True
+
+instance ToJSON RenameOrganization where
+    toJSON (RenameOrganization login) =
+        object
+            [ "login" .= login
+            ]
+
+instance FromJSON RenameOrganizationResponse where
+    parseJSON = withObject "RenameOrganizationResponse" $ \o ->
+        RenameOrganizationResponse
+            <$> o .: "message"
+            <*> o .: "url"
diff --git a/src/GitHub/Data/Events.hs b/src/GitHub/Data/Events.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Events.hs
@@ -0,0 +1,29 @@
+module GitHub.Data.Events where
+
+import GitHub.Data.Definitions
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Events.
+--
+-- /TODO:/
+--
+-- * missing repo, org, payload, id
+--
+data Event = Event
+    -- { eventId        :: !(Id Event) -- id can be encoded as string.
+    { eventActor     :: !SimpleUser
+    , eventCreatedAt :: !UTCTime
+    , eventPublic    :: !Bool
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Event
+instance Binary Event
+
+instance FromJSON Event where
+    parseJSON = withObject "Event" $ \obj -> Event
+        -- <$> obj .: "id"
+        <$> obj .: "actor"
+        <*> obj .: "created_at"
+        <*> obj .: "public"
diff --git a/src/GitHub/Data/Gists.hs b/src/GitHub/Data/Gists.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Gists.hs
@@ -0,0 +1,118 @@
+module GitHub.Data.Gists where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.Name        (Name)
+import GitHub.Data.Repos       (Language)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data Gist = Gist
+    { gistUser        :: !SimpleUser
+    , gistGitPushUrl  :: !URL
+    , gistUrl         :: !URL
+    , gistDescription :: !(Maybe Text)
+    , gistCreatedAt   :: !UTCTime
+    , gistPublic      :: !Bool
+    , gistComments    :: !Int
+    , gistUpdatedAt   :: !UTCTime
+    , gistHtmlUrl     :: !URL
+    , gistId          :: !(Name Gist)
+    , gistFiles       :: !(HashMap Text GistFile)
+    , gistGitPullUrl  :: !URL
+    } deriving (Show, Data, Eq, Generic)
+
+instance NFData Gist
+instance Binary Gist
+
+instance FromJSON Gist where
+    parseJSON = withObject "Gist" $ \o -> Gist
+        <$> o .: "owner"
+        <*> o .: "git_push_url"
+        <*> o .: "url"
+        <*> o .:? "description"
+        <*> o .: "created_at"
+        <*> o .: "public"
+        <*> o .: "comments"
+        <*> o .: "updated_at"
+        <*> o .: "html_url"
+        <*> o .: "id"
+        <*> o .: "files"
+        <*> o .: "git_push_url"
+
+data GistFile = GistFile
+    { gistFileType     :: !Text
+    , gistFileRawUrl   :: !URL
+    , gistFileSize     :: !Int
+    , gistFileLanguage :: !(Maybe Language)
+    , gistFileFilename :: !Text
+    , gistFileContent  :: !(Maybe Text)
+    }
+  deriving (Show, Data, Eq, Generic)
+
+instance NFData GistFile
+instance Binary GistFile
+
+instance FromJSON GistFile where
+    parseJSON = withObject "GistFile" $ \o -> GistFile
+        <$> o .: "type"
+        <*> o .: "raw_url"
+        <*> o .: "size"
+        <*> o .:? "language"
+        <*> o .: "filename"
+        <*> o .:? "content"
+
+data GistComment = GistComment
+    { gistCommentUser      :: !SimpleUser
+    , gistCommentUrl       :: !URL
+    , gistCommentCreatedAt :: !UTCTime
+    , gistCommentBody      :: !Text
+    , gistCommentUpdatedAt :: !UTCTime
+    , gistCommentId        :: !(Id GistComment)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData GistComment
+instance Binary GistComment
+
+instance FromJSON GistComment where
+    parseJSON = withObject "GistComment" $ \o -> GistComment
+        <$> o .: "user"
+        <*> o .: "url"
+        <*> o .: "created_at"
+        <*> o .: "body"
+        <*> o .: "updated_at"
+        <*> o .: "id"
+
+data NewGist = NewGist
+    { newGistDescription :: !(Maybe Text)
+    , newGistFiles       :: !(HashMap Text NewGistFile)
+    , newGistPublic      :: !(Maybe Bool)
+    } deriving (Show, Data, Eq, Generic)
+
+instance NFData NewGist
+instance Binary NewGist
+
+instance ToJSON NewGist where
+    toJSON NewGist { newGistDescription = description
+                   , newGistFiles       = files
+                   , newGistPublic      = public
+                   } = object $ filter notNull
+                   [ "description"      .= description
+                   , "files"            .= files
+                   , "public"           .= public
+                   ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _)    = True
+
+data NewGistFile = NewGistFile
+    { newGistFileContent :: !Text
+    } deriving (Show, Data, Eq, Generic)
+
+instance NFData NewGistFile
+instance Binary NewGistFile
+
+instance ToJSON NewGistFile where
+    toJSON (NewGistFile c) = object ["content" .= c]
diff --git a/src/GitHub/Data/GitData.hs b/src/GitHub/Data/GitData.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/GitData.hs
@@ -0,0 +1,312 @@
+module GitHub.Data.GitData where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Name        (Name)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Vector as V
+
+-- | The options for querying commits.
+data CommitQueryOption
+    = CommitQuerySha !Text
+    | CommitQueryPath !Text
+    | CommitQueryAuthor !Text
+    | CommitQuerySince !UTCTime
+    | CommitQueryUntil !UTCTime
+  deriving (Show, Eq, Ord, Generic, Data)
+
+data Stats = Stats
+    { statsAdditions :: !Int
+    , statsTotal     :: !Int
+    , statsDeletions :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Stats
+instance Binary Stats
+
+data Commit = Commit
+    { commitSha       :: !(Name Commit)
+    , commitParents   :: !(Vector Tree)
+    , commitUrl       :: !URL
+    , commitGitCommit :: !GitCommit
+    , commitCommitter :: !(Maybe SimpleUser)
+    , commitAuthor    :: !(Maybe SimpleUser)
+    , commitFiles     :: !(Vector File)
+    , commitStats     :: !(Maybe Stats)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Commit
+instance Binary Commit
+
+data Tree = Tree
+    { treeSha      :: !(Name Tree)
+    , treeUrl      :: !URL
+    , treeGitTrees :: !(Vector GitTree)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Tree
+instance Binary Tree
+
+data GitTree = GitTree
+    { gitTreeType :: !Text
+    , gitTreeSha  :: !(Name GitTree)
+    -- Can be empty for submodule
+    , gitTreeUrl  :: !(Maybe URL)
+    , gitTreeSize :: !(Maybe Int)
+    , gitTreePath :: !Text
+    , gitTreeMode :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData GitTree
+instance Binary GitTree
+
+data GitCommit = GitCommit
+    { gitCommitMessage   :: !Text
+    , gitCommitUrl       :: !URL
+    , gitCommitCommitter :: !GitUser
+    , gitCommitAuthor    :: !GitUser
+    , gitCommitTree      :: !Tree
+    , gitCommitSha       :: !(Maybe (Name GitCommit))
+    , gitCommitParents   :: !(Vector Tree)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData GitCommit
+instance Binary GitCommit
+
+data Blob = Blob
+    { blobUrl      :: !URL
+    , blobEncoding :: !Text
+    , blobContent  :: !Text
+    , blobSha      :: !(Name Blob)
+    , blobSize     :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Blob
+instance Binary Blob
+
+data Tag = Tag
+    { tagName       :: !Text
+    , tagZipballUrl :: !URL
+    , tagTarballUrl :: !URL
+    , tagCommit     :: !BranchCommit
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Tag
+instance Binary Tag
+
+data Branch = Branch
+    { branchName   :: !Text
+    , branchCommit :: !BranchCommit
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Branch
+
+data BranchCommit = BranchCommit
+    { branchCommitSha :: !Text
+    , branchCommitUrl :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData BranchCommit
+instance Binary BranchCommit
+
+data Diff = Diff
+    { diffStatus       :: !Text
+    , diffBehindBy     :: !Int
+    , diffPatchUrl     :: !URL
+    , diffUrl          :: !URL
+    , diffBaseCommit   :: !Commit
+    , diffCommits      :: !(Vector Commit)
+    , diffTotalCommits :: !Int
+    , diffHtmlUrl      :: !URL
+    , diffFiles        :: !(Vector File)
+    , diffAheadBy      :: !Int
+    , diffDiffUrl      :: !URL
+    , diffPermalinkUrl :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Diff
+instance Binary Diff
+
+data NewGitReference = NewGitReference
+    { newGitReferenceRef :: !Text
+    , newGitReferenceSha :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewGitReference
+instance Binary NewGitReference
+
+data GitReference = GitReference
+    { gitReferenceObject :: !GitObject
+    , gitReferenceUrl    :: !URL
+    , gitReferenceRef    :: !(Name GitReference)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData GitReference
+instance Binary GitReference
+
+data GitObject = GitObject
+    { gitObjectType :: !Text
+    , gitObjectSha  :: !Text
+    , gitObjectUrl  :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData GitObject
+instance Binary GitObject
+
+data GitUser = GitUser
+    { gitUserName  :: !Text
+    , gitUserEmail :: !Text
+    , gitUserDate  :: !UTCTime
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData GitUser
+instance Binary GitUser
+
+data File = File
+    { fileBlobUrl   :: !(Maybe URL)
+    , fileStatus    :: !Text
+    , fileRawUrl    :: !(Maybe URL)
+    , fileAdditions :: !Int
+    , fileSha       :: !(Maybe Text)
+    , fileChanges   :: !Int
+    , filePatch     :: !(Maybe Text)
+    , fileFilename  :: !Text
+    , fileDeletions :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData File
+instance Binary File
+
+-- JSON instances
+
+instance FromJSON Stats where
+    parseJSON = withObject "Stats" $ \o -> Stats
+        <$> o .: "additions"
+        <*> o .: "total"
+        <*> o .: "deletions"
+
+instance FromJSON Commit where
+    parseJSON = withObject "Commit" $ \o -> Commit
+        <$> o .: "sha"
+        <*> o .: "parents"
+        <*> o .: "url"
+        <*> o .: "commit"
+        <*> (o .:? "committer" <|> pure Nothing)
+        <*> (o .:? "author" <|> pure Nothing)
+        <*> o .:? "files" .!= V.empty
+        <*> o .:? "stats"
+
+instance FromJSON Tree where
+    parseJSON = withObject "Tree" $ \o -> Tree
+        <$> o .: "sha"
+        <*> o .: "url"
+        <*> o .:? "tree" .!= V.empty
+
+instance FromJSON GitTree where
+    parseJSON = withObject "GitTree" $ \o -> GitTree
+        <$> o .: "type"
+        <*> o .: "sha"
+        <*> o .:? "url"
+        <*> o .:? "size"
+        <*> o .: "path"
+        <*> o .: "mode"
+
+instance FromJSON GitCommit where
+    parseJSON = withObject "GitCommit" $ \o -> GitCommit
+        <$> o .: "message"
+        <*> o .: "url"
+        <*> o .: "committer"
+        <*> o .: "author"
+        <*> o .: "tree"
+        <*> o .:? "sha"
+        <*> o .:? "parents" .!= V.empty
+
+instance FromJSON GitUser where
+    parseJSON = withObject "GitUser" $ \o -> GitUser
+        <$> o .: "name"
+        <*> o .: "email"
+        <*> o .: "date"
+
+instance FromJSON File where
+    parseJSON = withObject "File" $ \o -> File
+        <$> o .:? "blob_url"
+        <*> o .: "status"
+        <*> o .:? "raw_url"
+        <*> o .: "additions"
+        <*> o .:? "sha"
+        <*> o .: "changes"
+        <*> o .:? "patch"
+        <*> o .: "filename"
+        <*> o .: "deletions"
+
+instance ToJSON NewGitReference where
+    toJSON (NewGitReference r s) = object [ "ref" .= r, "sha" .= s  ]
+
+instance FromJSON GitReference where
+    parseJSON = withObject "GitReference" $ \o -> GitReference
+        <$> o .: "object"
+        <*> o .: "url"
+        <*> o .: "ref"
+
+instance FromJSON GitObject where
+    parseJSON = withObject "GitObject" $ \o -> GitObject
+        <$> o .: "type"
+        <*> o .: "sha"
+        <*> o .: "url"
+
+instance FromJSON Diff where
+    parseJSON = withObject "Diff" $ \o -> Diff
+        <$> o .: "status"
+        <*> o .: "behind_by"
+        <*> o .: "patch_url"
+        <*> o .: "url"
+        <*> o .: "base_commit"
+        <*> o .:? "commits" .!= V.empty
+        <*> o .: "total_commits"
+        <*> o .: "html_url"
+        <*> o .:? "files" .!= V.empty
+        <*> o .: "ahead_by"
+        <*> o .: "diff_url"
+        <*> o .: "permalink_url"
+
+instance FromJSON Blob where
+    parseJSON = withObject "Blob" $ \o -> Blob
+        <$> o .: "url"
+        <*> o .: "encoding"
+        <*> o .: "content"
+        <*> o .: "sha"
+        <*> o .: "size"
+
+instance FromJSON Tag where
+    parseJSON = withObject "Tag" $ \o -> Tag
+        <$> o .: "name"
+        <*> o .: "zipball_url"
+        <*> o .: "tarball_url"
+        <*> o .: "commit"
+
+instance FromJSON Branch where
+    parseJSON = withObject "Branch" $ \o -> Branch
+        <$> o .: "name"
+        <*> o .: "commit"
+
+instance FromJSON BranchCommit where
+    parseJSON = withObject "BranchCommit" $ \o -> BranchCommit
+        <$> o .: "sha"
+        <*> o .: "url"
diff --git a/src/GitHub/Data/Id.hs b/src/GitHub/Data/Id.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Id.hs
@@ -0,0 +1,31 @@
+module GitHub.Data.Id (
+    Id(..),
+    mkId,
+    untagId,
+    ) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Numeric identifier.
+newtype Id entity = Id Int
+    deriving (Eq, Ord, Show, Generic, Data)
+
+-- | Smart constructor for 'Id'.
+mkId :: proxy entity -> Int -> Id entity
+mkId _ = Id
+
+untagId :: Id entity -> Int
+untagId (Id name) = name
+
+instance Hashable (Id entity)
+instance Binary (Id entity)
+
+instance NFData (Id entity) where
+    rnf (Id s) = rnf s
+
+instance FromJSON (Id entity) where
+    parseJSON = fmap Id . parseJSON
+
+instance ToJSON (Id entity) where
+    toJSON = toJSON . untagId
diff --git a/src/GitHub/Data/Invitation.hs b/src/GitHub/Data/Invitation.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Invitation.hs
@@ -0,0 +1,82 @@
+module GitHub.Data.Invitation where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.Name        (Name)
+import GitHub.Data.Repos       (Repo)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text as T
+
+data Invitation = Invitation
+    { invitationId        :: !(Id Invitation)
+    -- TODO: technically either one should be, maybe both. use `these` ?
+    , invitationLogin     :: !(Maybe (Name User))
+    , invitationEmail     :: !(Maybe Text)
+    , invitationRole      :: !InvitationRole
+    , invitationCreatedAt :: !UTCTime
+    , inviter             :: !SimpleUser
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Invitation
+instance Binary Invitation
+
+instance FromJSON Invitation where
+    parseJSON = withObject "Invitation" $ \o -> Invitation
+        <$> o .: "id"
+        <*> o .:? "login"
+        <*> o .:? "email"
+        <*> o .: "role"
+        <*> o .: "created_at"
+        <*> o .: "inviter"
+
+
+data InvitationRole
+    = InvitationRoleDirectMember
+    | InvitationRoleAdmin
+    | InvitationRoleBillingManager
+    | InvitationRoleHiringManager
+    | InvitationRoleReinstate
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData InvitationRole
+instance Binary InvitationRole
+
+instance FromJSON InvitationRole where
+    parseJSON = withText "InvitationRole" $ \t -> case T.toLower t of
+        "direct_member"   -> pure InvitationRoleDirectMember
+        "admin"           -> pure InvitationRoleAdmin
+        "billing_manager" -> pure InvitationRoleBillingManager
+        "hiring_manager"  -> pure InvitationRoleHiringManager
+        "reinstate"       -> pure InvitationRoleReinstate
+        _                 -> fail $ "Unknown InvitationRole: " <> T.unpack t
+
+data RepoInvitation = RepoInvitation
+    { repoInvitationId         :: !(Id RepoInvitation)
+    , repoInvitationInvitee    :: !SimpleUser
+    , repoInvitationInviter    :: !SimpleUser
+    , repoInvitationRepo       :: !Repo
+    , repoInvitationUrl        :: !URL
+    , repoInvitationCreatedAt  :: !UTCTime
+    , repoInvitationPermission :: !Text
+    , repoInvitationHtmlUrl    :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoInvitation
+instance Binary RepoInvitation
+
+instance FromJSON RepoInvitation where
+    parseJSON = withObject "RepoInvitation" $ \o -> RepoInvitation
+        <$> o .: "id"
+        <*> o .: "invitee"
+        <*> o .: "inviter"
+        <*> o .: "repository"
+        <*> o .: "url"
+        <*> o .: "created_at"
+        <*> o .: "permissions"
+        <*> o .: "html_url"
diff --git a/src/GitHub/Data/Issues.hs b/src/GitHub/Data/Issues.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Issues.hs
@@ -0,0 +1,227 @@
+module GitHub.Data.Issues where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id           (Id)
+import GitHub.Data.Milestone    (Milestone)
+import GitHub.Data.Name         (Name)
+import GitHub.Data.Options      (IssueState, IssueStateReason)
+import GitHub.Data.PullRequests
+import GitHub.Data.URL          (URL)
+import GitHub.Internal.Prelude
+import Prelude                  ()
+
+import qualified Data.Text as T
+
+data Issue = Issue
+    { issueClosedAt    :: !(Maybe UTCTime)
+    , issueUpdatedAt   :: !UTCTime
+    , issueEventsUrl   :: !URL
+    , issueHtmlUrl     :: !(Maybe URL)
+    , issueClosedBy    :: !(Maybe SimpleUser)
+    , issueLabels      :: !(Vector IssueLabel)
+    , issueNumber      :: !IssueNumber
+    , issueAssignees   :: !(Vector SimpleUser)
+    , issueUser        :: !SimpleUser
+    , issueTitle       :: !Text
+    , issuePullRequest :: !(Maybe PullRequestReference)
+    , issueUrl         :: !URL
+    , issueCreatedAt   :: !UTCTime
+    , issueBody        :: !(Maybe Text)
+    , issueState       :: !IssueState
+    , issueId          :: !(Id Issue)
+    , issueComments    :: !Int
+    , issueMilestone   :: !(Maybe Milestone)
+    , issueStateReason :: !(Maybe IssueStateReason)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Issue
+instance Binary Issue
+
+data NewIssue = NewIssue
+    { newIssueTitle     :: !Text
+    , newIssueBody      :: !(Maybe Text)
+    , newIssueAssignees :: !(Vector (Name User))
+    , newIssueMilestone :: !(Maybe (Id Milestone))
+    , newIssueLabels    :: !(Maybe (Vector (Name IssueLabel)))
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewIssue
+instance Binary NewIssue
+
+data EditIssue = EditIssue
+    { editIssueTitle     :: !(Maybe Text)
+    , editIssueBody      :: !(Maybe Text)
+    , editIssueAssignees :: !(Maybe (Vector (Name User)))
+    , editIssueState     :: !(Maybe IssueState)
+    , editIssueMilestone :: !(Maybe (Id Milestone))
+    , editIssueLabels    :: !(Maybe (Vector (Name IssueLabel)))
+    }
+  deriving  (Show, Data, Eq, Ord, Generic)
+
+instance NFData EditIssue
+instance Binary EditIssue
+
+data IssueComment = IssueComment
+    { issueCommentUpdatedAt :: !UTCTime
+    , issueCommentUser      :: !SimpleUser
+    , issueCommentUrl       :: !URL
+    , issueCommentHtmlUrl   :: !URL
+    , issueCommentCreatedAt :: !UTCTime
+    , issueCommentBody      :: !Text
+    , issueCommentId        :: !Int
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData IssueComment
+instance Binary IssueComment
+
+-- | See <https://developer.github.com/v3/issues/events/#events-1>
+data EventType
+    = Mentioned             -- ^ The actor was @mentioned in an issue body.
+    | Subscribed            -- ^ The actor subscribed to receive notifications for an issue.
+    | Unsubscribed          -- ^ The issue was unsubscribed from by the actor.
+    | Referenced            -- ^ The issue was referenced from a commit message. The commit_id attribute is the commit SHA1 of where that happened.
+    | Merged                -- ^ The issue was merged by the actor. The commit_id attribute is the SHA1 of the HEAD commit that was merged.
+    | Assigned              -- ^ The issue was assigned to the actor.
+    | Closed                -- ^ The issue was closed by the actor. When the commit_id is present, it identifies the commit that closed the issue using “closes / fixes #NN” syntax.
+    | Reopened              -- ^ The issue was reopened by the actor.
+    | ActorUnassigned       -- ^ The issue was unassigned to the actor
+    | Labeled               -- ^ A label was added to the issue.
+    | Unlabeled             -- ^ A label was removed from the issue.
+    | Milestoned            -- ^ The issue was added to a milestone.
+    | Demilestoned          -- ^ The issue was removed from a milestone.
+    | Renamed               -- ^ The issue title was changed.
+    | Locked                -- ^ The issue was locked by the actor.
+    | Unlocked              -- ^ The issue was unlocked by the actor.
+    | HeadRefDeleted        -- ^ The pull request’s branch was deleted.
+    | HeadRefRestored       -- ^ The pull request’s branch was restored.
+    | ReviewRequested       -- ^ The actor requested review from the subject on this pull request.
+    | ReviewDismissed       -- ^ The actor dismissed a review from the pull request.
+    | ReviewRequestRemoved  -- ^ The actor removed the review request for the subject on this pull request.
+    | MarkedAsDuplicate     -- ^ A user with write permissions marked an issue as a duplicate of another issue or a pull request as a duplicate of another pull request.
+    | UnmarkedAsDuplicate   -- ^ An issue that a user had previously marked as a duplicate of another issue is no longer considered a duplicate, or a pull request that a user had previously marked as a duplicate of another pull request is no longer considered a duplicate.
+    | AddedToProject        -- ^ The issue was added to a project board.
+    | MovedColumnsInProject -- ^ The issue was moved between columns in a project board.
+    | RemovedFromProject    -- ^ The issue was removed from a project board.
+    | ConvertedNoteToIssue  -- ^ The issue was created by converting a note in a project board to an issue.
+  deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData EventType
+instance Binary EventType
+
+-- | Issue event
+data IssueEvent = IssueEvent
+    { issueEventActor     :: !SimpleUser
+    , issueEventType      :: !EventType
+    , issueEventCommitId  :: !(Maybe Text)
+    , issueEventUrl       :: !URL
+    , issueEventCreatedAt :: !UTCTime
+    , issueEventId        :: !Int
+    , issueEventIssue     :: !(Maybe Issue)
+    , issueEventLabel     :: !(Maybe IssueLabel)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData IssueEvent
+instance Binary IssueEvent
+
+instance FromJSON IssueEvent where
+    parseJSON = withObject "Event" $ \o -> IssueEvent
+        <$> o .: "actor"
+        <*> o .: "event"
+        <*> o .:? "commit_id"
+        <*> o .: "url"
+        <*> o .: "created_at"
+        <*> o .: "id"
+        <*> o .:? "issue"
+        <*> o .:? "label"
+
+instance FromJSON EventType where
+    parseJSON = withText "EventType" $ \t -> case T.toLower t of
+        "closed"                   -> pure Closed
+        "reopened"                 -> pure Reopened
+        "subscribed"               -> pure Subscribed
+        "merged"                   -> pure Merged
+        "referenced"               -> pure Referenced
+        "mentioned"                -> pure Mentioned
+        "assigned"                 -> pure Assigned
+        "unassigned"               -> pure ActorUnassigned
+        "labeled"                  -> pure Labeled
+        "unlabeled"                -> pure Unlabeled
+        "milestoned"               -> pure Milestoned
+        "demilestoned"             -> pure Demilestoned
+        "renamed"                  -> pure Renamed
+        "locked"                   -> pure Locked
+        "unlocked"                 -> pure Unlocked
+        "head_ref_deleted"         -> pure HeadRefDeleted
+        "head_ref_restored"        -> pure HeadRefRestored
+        "review_requested"         -> pure ReviewRequested
+        "review_dismissed"         -> pure ReviewDismissed
+        "review_request_removed"   -> pure ReviewRequestRemoved
+        "marked_as_duplicate"      -> pure MarkedAsDuplicate
+        "unmarked_as_duplicate"    -> pure UnmarkedAsDuplicate
+        "added_to_project"         -> pure AddedToProject
+        "moved_columns_in_project" -> pure MovedColumnsInProject
+        "removed_from_project"     -> pure RemovedFromProject
+        "converted_note_to_issue"  -> pure ConvertedNoteToIssue
+        "unsubscribed"             -> pure Unsubscribed -- not in api docs list
+        _                          -> fail $ "Unknown EventType: " <> T.unpack t
+
+instance FromJSON IssueComment where
+    parseJSON = withObject "IssueComment" $ \o -> IssueComment
+        <$> o .: "updated_at"
+        <*> o .: "user"
+        <*> o .: "url"
+        <*> o .: "html_url"
+        <*> o .: "created_at"
+        <*> o .: "body"
+        <*> o .: "id"
+
+instance FromJSON Issue where
+    parseJSON = withObject "Issue" $ \o -> Issue
+        <$> o .:? "closed_at"
+        <*> o .: "updated_at"
+        <*> o .: "events_url"
+        <*> o .: "html_url"
+        <*> o .:? "closed_by"
+        <*> o .: "labels"
+        <*> o .: "number"
+        <*> o .: "assignees"
+        <*> o .: "user"
+        <*> o .: "title"
+        <*> o .:? "pull_request"
+        <*> o .: "url"
+        <*> o .: "created_at"
+        <*> o .: "body"
+        <*> o .: "state"
+        <*> o .: "id"
+        <*> o .: "comments"
+        <*> o .:? "milestone"
+        <*> o .:? "state_reason"
+
+instance ToJSON NewIssue where
+    toJSON (NewIssue t b a m ls) = object $ filter notNull
+        [ "title"     .= t
+        , "body"      .= b
+        , "assignees" .= a
+        , "milestone" .= m
+        , "labels"    .= ls
+        ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _)    = True
+
+instance ToJSON EditIssue where
+    toJSON (EditIssue t b a s m ls) = object $ filter notNull
+        [ "title"     .= t
+        , "body"      .= b
+        , "assignees" .= a
+        , "state"     .= s
+        , "milestone" .= m
+        , "labels"    .= ls
+        ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _)    = True
diff --git a/src/GitHub/Data/Milestone.hs b/src/GitHub/Data/Milestone.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Milestone.hs
@@ -0,0 +1,83 @@
+module GitHub.Data.Milestone where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data Milestone = Milestone
+    { milestoneCreator      :: !SimpleUser
+    , milestoneDueOn        :: !(Maybe UTCTime)
+    , milestoneOpenIssues   :: !Int
+    , milestoneNumber       :: !(Id Milestone)
+    , milestoneClosedIssues :: !Int
+    , milestoneDescription  :: !(Maybe Text)
+    , milestoneTitle        :: !Text
+    , milestoneUrl          :: !URL
+    , milestoneCreatedAt    :: !UTCTime
+    , milestoneState        :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Milestone
+instance Binary Milestone
+
+instance FromJSON Milestone where
+    parseJSON = withObject "Milestone" $ \o -> Milestone
+        <$> o .: "creator"
+        <*> o .: "due_on"
+        <*> o .: "open_issues"
+        <*> o .: "number"
+        <*> o .: "closed_issues"
+        <*> o .: "description"
+        <*> o .: "title"
+        <*> o .: "url"
+        <*> o .: "created_at"
+        <*> o .: "state"
+
+data NewMilestone = NewMilestone
+    { newMilestoneTitle       :: !Text
+    , newMilestoneState       :: !Text
+    , newMilestoneDescription :: !(Maybe Text)
+    , newMilestoneDueOn       :: !(Maybe UTCTime)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewMilestone
+instance Binary NewMilestone
+
+
+instance ToJSON NewMilestone where
+    toJSON (NewMilestone title state desc due) = object $ filter notNull
+        [ "title"       .= title
+        , "state"       .= state
+        , "description" .= desc
+        , "due_on"      .= due
+        ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _)    = True
+
+data UpdateMilestone = UpdateMilestone
+  { updateMilestoneTitle       :: !(Maybe Text)
+  , updateMilestoneState       :: !(Maybe Text)
+  , updateMilestoneDescription :: !(Maybe Text)
+  , updateMilestoneDueOn       :: !(Maybe UTCTime)
+  }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData UpdateMilestone
+instance Binary UpdateMilestone
+
+
+instance ToJSON UpdateMilestone where
+  toJSON (UpdateMilestone title state desc due) = object $ filter notNull
+      [ "title"       .= title
+      , "state"       .= state
+      , "description" .= desc
+      , "due_on"      .= due
+      ]
+    where
+      notNull (_, Null) = False
+      notNull (_, _)    = True
diff --git a/src/GitHub/Data/Name.hs b/src/GitHub/Data/Name.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Name.hs
@@ -0,0 +1,44 @@
+module GitHub.Data.Name (
+    Name(..),
+    mkName,
+    untagName,
+    ) where
+
+import Prelude ()
+import GitHub.Internal.Prelude
+
+import Data.Aeson.Types
+       (FromJSONKey (..), ToJSONKey (..), fromJSONKeyCoerce, toJSONKeyText)
+
+newtype Name entity = N Text
+    deriving (Eq, Ord, Show, Generic, Data)
+
+-- | Smart constructor for 'Name'
+mkName :: proxy entity -> Text -> Name entity
+mkName _ = N
+
+untagName :: Name entity -> Text
+untagName (N name) = name
+
+instance Hashable (Name entity)
+instance Binary (Name entity)
+
+instance NFData (Name entity) where
+    rnf (N s) = rnf s
+
+instance FromJSON (Name entity) where
+    parseJSON = fmap N . parseJSON
+
+instance ToJSON (Name entity) where
+    toJSON = toJSON . untagName
+
+instance IsString (Name entity) where
+    fromString = N . fromString
+
+-- | @since 0.15.0.0
+instance ToJSONKey (Name entity) where
+    toJSONKey = toJSONKeyText untagName
+
+-- | @since 0.15.0.0
+instance FromJSONKey (Name entity) where
+    fromJSONKey = fromJSONKeyCoerce
diff --git a/src/GitHub/Data/Options.hs b/src/GitHub/Data/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Options.hs
@@ -0,0 +1,939 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE LambdaCase #-}
+
+-- |
+-- Module with modifiers for pull requests' and issues' listings.
+
+module GitHub.Data.Options (
+    -- * Common modifiers
+    stateOpen,
+    stateClosed,
+    stateAll,
+    sortAscending,
+    sortDescending,
+    sortByCreated,
+    sortByUpdated,
+    -- * Pull Requests
+    PullRequestMod,
+    prModToQueryString,
+    optionsBase,
+    optionsNoBase,
+    optionsHead,
+    optionsNoHead,
+    sortByPopularity,
+    sortByLongRunning,
+    -- * Issues
+    IssueMod,
+    issueModToQueryString,
+    sortByComments,
+    optionsLabels,
+    optionsSince,
+    optionsSinceAll,
+    optionsAssignedIssues,
+    optionsCreatedIssues,
+    optionsMentionedIssues,
+    optionsSubscribedIssues,
+    optionsAllIssues,
+    -- * Repo issues
+    IssueRepoMod,
+    issueRepoModToQueryString,
+    optionsCreator,
+    optionsMentioned,
+    optionsIrrelevantMilestone,
+    optionsAnyMilestone,
+    optionsNoMilestone,
+    optionsMilestone,
+    optionsIrrelevantAssignee,
+    optionsAnyAssignee,
+    optionsNoAssignee,
+    optionsAssignee,
+    -- * Actions artifacts
+    ArtifactMod,
+    artifactModToQueryString,
+    optionsArtifactName,
+    -- * Actions cache
+    CacheMod,
+    cacheModToQueryString,
+    optionsRef,
+    optionsNoRef,
+    optionsKey,
+    optionsNoKey,
+    optionsDirectionAsc,
+    optionsDirectionDesc,
+    sortByCreatedAt,
+    sortByLastAccessedAt,
+    sortBySizeInBytes,
+    -- * Actions workflow runs
+    WorkflowRunMod,
+    workflowRunModToQueryString,
+    optionsWorkflowRunActor,
+    optionsWorkflowRunBranch,
+    optionsWorkflowRunEvent,
+    optionsWorkflowRunStatus,
+    optionsWorkflowRunCreated,
+    optionsWorkflowRunHeadSha,
+    -- * Data
+    IssueState (..),
+    IssueStateReason (..),
+    MergeableState (..),
+    -- * Internal
+    HasState,
+    HasDirection,
+    HasCreatedUpdated,
+    HasComments,
+    HasLabels,
+    HasSince,
+    ) where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id, untagId)
+import GitHub.Data.Milestone   (Milestone)
+import GitHub.Data.Name        (Name, untagName)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text          as T
+import qualified Data.Text.Encoding as TE
+
+-------------------------------------------------------------------------------
+-- Data
+-------------------------------------------------------------------------------
+
+-- | 'GitHub.Data.Issues.Issue' or 'GitHub.Data.PullRequests.PullRequest' state
+data IssueState
+    = StateOpen
+    | StateClosed
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance ToJSON IssueState where
+    toJSON StateOpen    = String "open"
+    toJSON StateClosed  = String "closed"
+
+instance FromJSON IssueState where
+    parseJSON = withText "IssueState" $ \t -> case T.toLower t of
+        "open"   -> pure StateOpen
+        "closed" -> pure StateClosed
+        _        -> fail $ "Unknown IssueState: " <> T.unpack t
+
+instance NFData IssueState
+instance Binary IssueState
+
+-- | 'GitHub.Data.Issues.Issue' state reason
+data IssueStateReason
+    = StateReasonCompleted
+    | StateReasonDuplicate
+    | StateReasonNotPlanned
+    | StateReasonReopened
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance ToJSON IssueStateReason where
+    toJSON = String . \case
+      StateReasonCompleted  -> "completed"
+      StateReasonDuplicate  -> "duplicate"
+      StateReasonNotPlanned -> "not_planned"
+      StateReasonReopened   -> "reopened"
+
+instance FromJSON IssueStateReason where
+    parseJSON = withText "IssueStateReason" $ \t -> case T.toLower t of
+        "completed"   -> pure StateReasonCompleted
+        "duplicate"   -> pure StateReasonDuplicate
+        "not_planned" -> pure StateReasonNotPlanned
+        "reopened"    -> pure StateReasonReopened
+        _ -> fail $ "Unknown IssueStateReason: " <> T.unpack t
+
+instance NFData IssueStateReason
+instance Binary IssueStateReason
+
+-- | 'GitHub.Data.PullRequests.PullRequest' mergeable_state
+data MergeableState
+    = StateUnknown
+    | StateClean
+    | StateDirty
+    | StateUnstable
+    | StateBlocked
+    | StateBehind
+    | StateDraft
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance ToJSON MergeableState where
+    toJSON StateUnknown  = String "unknown"
+    toJSON StateClean    = String "clean"
+    toJSON StateDirty    = String "dirty"
+    toJSON StateUnstable = String "unstable"
+    toJSON StateBlocked  = String "blocked"
+    toJSON StateBehind   = String "behind"
+    toJSON StateDraft    = String "draft"
+
+instance FromJSON MergeableState where
+    parseJSON = withText "MergeableState" $ \t -> case T.toLower t of
+        "unknown"  -> pure StateUnknown
+        "clean"    -> pure StateClean
+        "dirty"    -> pure StateDirty
+        "unstable" -> pure StateUnstable
+        "blocked"  -> pure StateBlocked
+        "behind"   -> pure StateBehind
+        "draft"    -> pure StateDraft
+        _          -> fail $ "Unknown MergeableState: " <> T.unpack t
+
+instance NFData MergeableState
+instance Binary MergeableState
+
+data SortDirection
+    = SortAscending
+    | SortDescending
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData SortDirection
+instance Binary SortDirection
+
+-- PR
+
+data SortPR
+    = SortPRCreated
+    | SortPRUpdated
+    | SortPRPopularity
+    | SortPRLongRunning
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData SortPR
+instance Binary SortPR
+
+-- Issue
+data IssueFilter
+    = IssueFilterAssigned
+    | IssueFilterCreated
+    | IssueFilterMentioned
+    | IssueFilterSubscribed
+    | IssueFilterAll
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData IssueFilter
+instance Binary IssueFilter
+
+data SortIssue
+    = SortIssueCreated
+    | SortIssueUpdated
+    | SortIssueComments
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData SortIssue
+instance Binary SortIssue
+
+data FilterBy a
+    = FilterAny
+    | FilterNone
+    | FilterBy a
+    | FilterNotSpecified
+      -- ^ e.g. for milestones "any" means "any milestone".
+      -- I.e. won't show issues without mileston specified
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+-- Actions cache
+
+data SortCache
+    = SortCacheCreatedAt
+    | SortCacheLastAccessedAt
+    | SortCacheSizeInBytes
+  deriving
+    (Eq, Ord, Show, Enum, Bounded, Generic, Data)
+
+instance NFData SortCache
+instance Binary SortCache
+
+-------------------------------------------------------------------------------
+-- Classes
+-------------------------------------------------------------------------------
+
+class HasState mod where
+    state :: Maybe IssueState -> mod
+
+stateOpen :: HasState mod => mod
+stateOpen = state (Just StateOpen)
+
+stateClosed :: HasState mod => mod
+stateClosed = state (Just StateClosed)
+
+stateAll :: HasState mod => mod
+stateAll = state Nothing
+
+instance HasState PullRequestMod where
+    state s = PRMod $ \opts ->
+        opts { pullRequestOptionsState = s }
+
+instance HasState IssueMod where
+    state s = IssueMod $ \opts ->
+        opts { issueOptionsState = s }
+
+instance HasState IssueRepoMod where
+    state s = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsState = s }
+
+
+class HasDirection mod where
+    sortDir :: SortDirection -> mod
+
+sortAscending :: HasDirection mod => mod
+sortAscending = sortDir SortAscending
+
+sortDescending :: HasDirection mod => mod
+sortDescending = sortDir SortDescending
+
+instance HasDirection PullRequestMod where
+    sortDir x = PRMod $ \opts ->
+        opts { pullRequestOptionsDirection = x }
+
+instance HasDirection IssueMod where
+    sortDir x = IssueMod $ \opts ->
+        opts { issueOptionsDirection = x }
+
+instance HasDirection IssueRepoMod where
+    sortDir x = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsDirection = x }
+
+
+class HasCreatedUpdated mod where
+    sortByCreated :: mod
+    sortByUpdated :: mod
+
+instance HasCreatedUpdated PullRequestMod where
+    sortByCreated = PRMod $ \opts ->
+        opts { pullRequestOptionsSort = SortPRCreated }
+    sortByUpdated = PRMod $ \opts ->
+        opts { pullRequestOptionsSort = SortPRUpdated }
+
+instance HasCreatedUpdated IssueMod where
+    sortByCreated = IssueMod $ \opts ->
+        opts { issueOptionsSort = SortIssueCreated }
+    sortByUpdated = IssueMod $ \opts ->
+        opts { issueOptionsSort = SortIssueUpdated }
+
+instance HasCreatedUpdated IssueRepoMod where
+    sortByCreated = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsSort = SortIssueCreated }
+    sortByUpdated = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsSort = SortIssueUpdated }
+
+-------------------------------------------------------------------------------
+-- Pull Request
+-------------------------------------------------------------------------------
+
+-- | See <https://developer.github.com/v3/pulls/#parameters>.
+data PullRequestOptions = PullRequestOptions
+    { pullRequestOptionsState     :: !(Maybe IssueState)
+    , pullRequestOptionsHead      :: !(Maybe Text)
+    , pullRequestOptionsBase      :: !(Maybe Text)
+    , pullRequestOptionsSort      :: !SortPR
+    , pullRequestOptionsDirection :: !SortDirection
+    }
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+defaultPullRequestOptions :: PullRequestOptions
+defaultPullRequestOptions = PullRequestOptions
+    { pullRequestOptionsState     = Just StateOpen
+    , pullRequestOptionsHead      = Nothing
+    , pullRequestOptionsBase      = Nothing
+    , pullRequestOptionsSort      = SortPRCreated
+    , pullRequestOptionsDirection = SortDescending
+    }
+
+-- | See <https://developer.github.com/v3/pulls/#parameters>.
+newtype PullRequestMod = PRMod (PullRequestOptions -> PullRequestOptions)
+
+instance Semigroup PullRequestMod where
+    PRMod f <> PRMod g = PRMod (g . f)
+
+instance Monoid PullRequestMod where
+    mempty  = PRMod id
+    mappend = (<>)
+
+toPullRequestOptions :: PullRequestMod -> PullRequestOptions
+toPullRequestOptions (PRMod f) = f defaultPullRequestOptions
+
+prModToQueryString :: PullRequestMod -> QueryString
+prModToQueryString = pullRequestOptionsToQueryString . toPullRequestOptions
+
+pullRequestOptionsToQueryString :: PullRequestOptions -> QueryString
+pullRequestOptionsToQueryString (PullRequestOptions st head_ base sort dir) =
+    [ mk "state"     state'
+    , mk "sort"      sort'
+    , mk "direction" direction'
+    ] ++ catMaybes
+    [ mk "head" <$> head'
+    , mk "base" <$> base'
+    ]
+  where
+    mk k v = (k, Just v)
+    state' = case st of
+        Nothing          -> "all"
+        Just StateOpen   -> "open"
+        Just StateClosed -> "closed"
+    sort' = case sort of
+        SortPRCreated     -> "created"
+        SortPRUpdated     -> "updated"
+        SortPRPopularity  -> "popularity"
+        SortPRLongRunning -> "long-running"
+    direction' = case dir of
+       SortDescending -> "desc"
+       SortAscending  -> "asc"
+    head' = fmap TE.encodeUtf8 head_
+    base' = fmap TE.encodeUtf8 base
+
+-------------------------------------------------------------------------------
+-- Pull request modifiers
+-------------------------------------------------------------------------------
+
+optionsBase :: Text -> PullRequestMod
+optionsBase x = PRMod $ \opts ->
+    opts { pullRequestOptionsBase = Just x }
+
+optionsNoBase :: PullRequestMod
+optionsNoBase = PRMod $ \opts ->
+    opts { pullRequestOptionsBase = Nothing }
+
+optionsHead :: Text -> PullRequestMod
+optionsHead x = PRMod $ \opts ->
+    opts { pullRequestOptionsHead = Just x }
+
+optionsNoHead :: PullRequestMod
+optionsNoHead = PRMod $ \opts ->
+    opts { pullRequestOptionsHead = Nothing }
+
+sortByPopularity :: PullRequestMod
+sortByPopularity = PRMod $ \opts ->
+    opts { pullRequestOptionsSort = SortPRPopularity }
+
+sortByLongRunning :: PullRequestMod
+sortByLongRunning = PRMod $ \opts ->
+    opts { pullRequestOptionsSort = SortPRLongRunning }
+
+-------------------------------------------------------------------------------
+-- Issues
+-------------------------------------------------------------------------------
+
+-- | See <https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user--parameters>.
+data IssueOptions = IssueOptions
+    { issueOptionsFilter    :: !IssueFilter
+    , issueOptionsState     :: !(Maybe IssueState)
+    , issueOptionsLabels    :: ![Name IssueLabel] -- TODO: change to newtype
+    , issueOptionsSort      :: !SortIssue
+    , issueOptionsDirection :: !SortDirection
+    , issueOptionsSince     :: !(Maybe UTCTime)
+    }
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+defaultIssueOptions :: IssueOptions
+defaultIssueOptions = IssueOptions
+    { issueOptionsFilter    = IssueFilterAssigned
+    , issueOptionsState     = Just StateOpen
+    , issueOptionsLabels    = []
+    , issueOptionsSort      = SortIssueCreated
+    , issueOptionsDirection = SortDescending
+    , issueOptionsSince     = Nothing
+    }
+
+-- | See <https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user--parameters>.
+newtype IssueMod = IssueMod (IssueOptions -> IssueOptions)
+
+instance Semigroup IssueMod where
+    IssueMod f <> IssueMod g = IssueMod (g . f)
+
+instance Monoid IssueMod where
+    mempty  = IssueMod id
+    mappend = (<>)
+
+toIssueOptions :: IssueMod -> IssueOptions
+toIssueOptions (IssueMod f) = f defaultIssueOptions
+
+issueModToQueryString :: IssueMod -> QueryString
+issueModToQueryString = issueOptionsToQueryString . toIssueOptions
+
+issueOptionsToQueryString :: IssueOptions -> QueryString
+issueOptionsToQueryString (IssueOptions filt st labels sort dir since) =
+    [ mk "state"     state'
+    , mk "sort"      sort'
+    , mk "direction" direction'
+    , mk "filter" filt'
+    ] ++ catMaybes
+    [ mk "labels" <$> labels'
+    , mk "since" <$> since'
+    ]
+  where
+    mk k v = (k, Just v)
+    filt' = case filt of
+        IssueFilterAssigned   -> "assigned"
+        IssueFilterCreated    -> "created"
+        IssueFilterMentioned  -> "mentioned"
+        IssueFilterSubscribed -> "subscribed"
+        IssueFilterAll        -> "all"
+    state' = case st of
+        Nothing          -> "all"
+        Just StateOpen   -> "open"
+        Just StateClosed -> "closed"
+    sort' = case sort of
+        SortIssueCreated  -> "created"
+        SortIssueUpdated  -> "updated"
+        SortIssueComments -> "comments"
+    direction' = case dir of
+       SortDescending -> "desc"
+       SortAscending  -> "asc"
+
+    since' = fmap (TE.encodeUtf8 . T.pack . show) since
+    labels' = TE.encodeUtf8 . T.intercalate "," . fmap untagName <$> nullToNothing labels
+
+nullToNothing :: Foldable f => f a -> Maybe (f a)
+nullToNothing xs
+    | null xs   = Nothing
+    | otherwise = Just xs
+
+-------------------------------------------------------------------------------
+-- Issues modifiers
+-------------------------------------------------------------------------------
+
+class HasComments mod where
+    sortByComments :: mod
+
+instance HasComments IssueMod where
+    sortByComments = IssueMod $ \opts ->
+        opts { issueOptionsSort = SortIssueComments }
+
+instance HasComments IssueRepoMod where
+    sortByComments = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsSort = SortIssueComments }
+
+
+class HasLabels mod where
+    optionsLabels :: Foldable f => f (Name IssueLabel) -> mod
+
+instance HasLabels IssueMod where
+    optionsLabels lbls = IssueMod $ \opts ->
+        opts { issueOptionsLabels = toList lbls }
+
+instance HasLabels IssueRepoMod where
+    optionsLabels lbls = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsLabels = toList lbls }
+
+
+class HasSince mod where
+    optionsSince :: UTCTime -> mod
+    optionsSinceAll :: mod
+
+instance HasSince IssueMod where
+    optionsSince since = IssueMod $ \opts ->
+        opts { issueOptionsSince = Just since }
+    optionsSinceAll = IssueMod $ \opts ->
+        opts { issueOptionsSince = Nothing }
+
+instance HasSince IssueRepoMod where
+    optionsSince since = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsSince = Just since }
+    optionsSinceAll = IssueRepoMod $ \opts ->
+        opts { issueRepoOptionsSince = Nothing }
+
+-------------------------------------------------------------------------------
+-- Only issues modifiers
+-------------------------------------------------------------------------------
+
+optionsAssignedIssues, optionsCreatedIssues, optionsMentionedIssues,
+  optionsSubscribedIssues, optionsAllIssues  :: IssueMod
+optionsAssignedIssues   = issueFilter IssueFilterAssigned
+optionsCreatedIssues    = issueFilter IssueFilterCreated
+optionsMentionedIssues  = issueFilter IssueFilterMentioned
+optionsSubscribedIssues = issueFilter IssueFilterSubscribed
+optionsAllIssues        = issueFilter IssueFilterAll
+
+issueFilter :: IssueFilter -> IssueMod
+issueFilter f = IssueMod $ \opts ->
+    opts { issueOptionsFilter = f }
+
+-------------------------------------------------------------------------------
+-- Issues repo
+-------------------------------------------------------------------------------
+
+-- | Parameters of "list repository issues" (@get /repos/{owner}/{repo}/issues@).
+--
+-- See <https://docs.github.com/en/rest/reference/issues#list-repository-issues>.
+--
+data IssueRepoOptions = IssueRepoOptions
+    { issueRepoOptionsMilestone :: !(FilterBy (Id Milestone))   -- ^ 'optionsMilestone' etc.
+    , issueRepoOptionsState     :: !(Maybe IssueState)          -- ^ 'HasState'
+    , issueRepoOptionsAssignee  :: !(FilterBy (Name User))      -- ^ 'optionsAssignee' etc.
+    , issueRepoOptionsCreator   :: !(Maybe (Name User))         -- ^ 'optionsCreator'
+    , issueRepoOptionsMentioned :: !(Maybe (Name User))         -- ^ 'optionsMentioned'
+    , issueRepoOptionsLabels    :: ![Name IssueLabel]           -- ^ 'HasLabels'
+    , issueRepoOptionsSort      :: !SortIssue                   -- ^ 'HasCreatedUpdated' and 'HasComments'
+    , issueRepoOptionsDirection :: !SortDirection               -- ^ 'HasDirection'
+    , issueRepoOptionsSince     :: !(Maybe UTCTime)             -- ^ 'HasSince'
+    }
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+defaultIssueRepoOptions :: IssueRepoOptions
+defaultIssueRepoOptions = IssueRepoOptions
+    { issueRepoOptionsMilestone = FilterNotSpecified
+    , issueRepoOptionsState     = (Just StateOpen)
+    , issueRepoOptionsAssignee  = FilterNotSpecified
+    , issueRepoOptionsCreator   = Nothing
+    , issueRepoOptionsMentioned = Nothing
+    , issueRepoOptionsLabels    = []
+    , issueRepoOptionsSort      = SortIssueCreated
+    , issueRepoOptionsDirection = SortDescending
+    , issueRepoOptionsSince     = Nothing
+    }
+
+-- | See <https://developer.github.com/v3/issues/#parameters-1>.
+newtype IssueRepoMod = IssueRepoMod (IssueRepoOptions -> IssueRepoOptions)
+
+instance Semigroup IssueRepoMod where
+    IssueRepoMod f <> IssueRepoMod g = IssueRepoMod (g . f)
+
+instance Monoid IssueRepoMod where
+    mempty  = IssueRepoMod id
+    mappend = (<>)
+
+toIssueRepoOptions :: IssueRepoMod -> IssueRepoOptions
+toIssueRepoOptions (IssueRepoMod f) = f defaultIssueRepoOptions
+
+issueRepoModToQueryString :: IssueRepoMod -> QueryString
+issueRepoModToQueryString = issueRepoOptionsToQueryString . toIssueRepoOptions
+
+issueRepoOptionsToQueryString :: IssueRepoOptions -> QueryString
+issueRepoOptionsToQueryString IssueRepoOptions {..} =
+    [ mk "state"     state'
+    , mk "sort"      sort'
+    , mk "direction" direction'
+    ] ++ catMaybes
+    [ mk "milestone" <$> milestone'
+    , mk "assignee"  <$> assignee'
+    , mk "labels"    <$> labels'
+    , mk "since"     <$> since'
+    , mk "creator"   <$> creator'
+    , mk "mentioned" <$> mentioned'
+    ]
+  where
+    mk k v = (k, Just v)
+    filt f x = case x of
+        FilterAny          -> Just "*"
+        FilterNone         -> Just "none"
+        FilterBy x'        -> Just $ TE.encodeUtf8 $ f x'
+        FilterNotSpecified -> Nothing
+
+    milestone' = filt (T.pack . show . untagId) issueRepoOptionsMilestone
+    assignee'  = filt untagName issueRepoOptionsAssignee
+
+    state' = case issueRepoOptionsState of
+        Nothing          -> "all"
+        Just StateOpen   -> "open"
+        Just StateClosed -> "closed"
+    sort' = case issueRepoOptionsSort of
+        SortIssueCreated  -> "created"
+        SortIssueUpdated  -> "updated"
+        SortIssueComments -> "comments"
+    direction' = case issueRepoOptionsDirection of
+       SortDescending -> "desc"
+       SortAscending  -> "asc"
+
+    since'     = TE.encodeUtf8 . T.pack . show <$> issueRepoOptionsSince
+    labels'    = TE.encodeUtf8 . T.intercalate "," . fmap untagName <$> nullToNothing issueRepoOptionsLabels
+    creator'   = TE.encodeUtf8 . untagName <$> issueRepoOptionsCreator
+    mentioned' = TE.encodeUtf8 . untagName <$> issueRepoOptionsMentioned
+
+-------------------------------------------------------------------------------
+-- Issues repo modifiers
+-------------------------------------------------------------------------------
+
+-- | Issues created by a certain user.
+optionsCreator :: Name User -> IssueRepoMod
+optionsCreator u = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsCreator = Just u }
+
+-- | Issue mentioning the given user.
+optionsMentioned :: Name User -> IssueRepoMod
+optionsMentioned u = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsMentioned = Just u }
+
+-- | Don't care about milestones (default).
+--
+-- 'optionsAnyMilestone' means there should be some milestone, but it can be any.
+--
+-- See <https://developer.github.com/v3/issues/#list-issues-for-a-repository>
+optionsIrrelevantMilestone :: IssueRepoMod
+optionsIrrelevantMilestone = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsMilestone = FilterNotSpecified }
+
+-- | Issues that have a milestone.
+optionsAnyMilestone :: IssueRepoMod
+optionsAnyMilestone = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsMilestone = FilterAny }
+
+-- | Issues that have no milestone.
+optionsNoMilestone :: IssueRepoMod
+optionsNoMilestone = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsMilestone = FilterNone }
+
+-- | Issues with the given milestone.
+optionsMilestone :: Id Milestone -> IssueRepoMod
+optionsMilestone m = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsMilestone = FilterBy m }
+
+-- | Issues with or without assignee (default).
+optionsIrrelevantAssignee :: IssueRepoMod
+optionsIrrelevantAssignee = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsAssignee = FilterNotSpecified }
+
+-- | Issues assigned to someone.
+optionsAnyAssignee :: IssueRepoMod
+optionsAnyAssignee = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsAssignee = FilterAny }
+
+-- | Issues assigned to nobody.
+optionsNoAssignee :: IssueRepoMod
+optionsNoAssignee = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsAssignee = FilterNone }
+
+-- | Issues assigned to a specific user.
+optionsAssignee :: Name User -> IssueRepoMod
+optionsAssignee u = IssueRepoMod $ \opts ->
+    opts { issueRepoOptionsAssignee = FilterBy u }
+
+-------------------------------------------------------------------------------
+-- Actions artifacts
+-------------------------------------------------------------------------------
+
+-- | See <https://docs.github.com/en/rest/actions/artifacts#list-artifacts-for-a-repository>.
+data ArtifactOptions = ArtifactOptions
+    { artifactOptionsName :: !(Maybe Text)
+    }
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+defaultArtifactOptions :: ArtifactOptions
+defaultArtifactOptions = ArtifactOptions
+    { artifactOptionsName = Nothing
+    }
+
+-- | See <https://docs.github.com/en/rest/actions/artifacts#list-artifacts-for-a-repository>.
+newtype ArtifactMod = ArtifactMod (ArtifactOptions -> ArtifactOptions)
+
+instance Semigroup ArtifactMod where
+    ArtifactMod f <> ArtifactMod g = ArtifactMod (g . f)
+
+instance Monoid ArtifactMod where
+    mempty  = ArtifactMod id
+    mappend = (<>)
+
+-- | Filters artifacts by exact match on their name field.
+optionsArtifactName :: Text -> ArtifactMod
+optionsArtifactName n = ArtifactMod $ \opts ->
+    opts { artifactOptionsName = Just n }
+
+toArtifactOptions :: ArtifactMod -> ArtifactOptions
+toArtifactOptions (ArtifactMod f) = f defaultArtifactOptions
+
+artifactModToQueryString :: ArtifactMod -> QueryString
+artifactModToQueryString = artifactOptionsToQueryString . toArtifactOptions
+
+artifactOptionsToQueryString :: ArtifactOptions -> QueryString
+artifactOptionsToQueryString (ArtifactOptions name) =
+    catMaybes
+    [ mk "name" <$> name'
+    ]
+  where
+    mk k v = (k, Just v)
+    name' = fmap TE.encodeUtf8 name
+
+-------------------------------------------------------------------------------
+-- Actions cache
+-------------------------------------------------------------------------------
+
+-- | See <https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository>.
+data CacheOptions = CacheOptions
+    { cacheOptionsRef       :: !(Maybe Text)
+    , cacheOptionsKey       :: !(Maybe Text)
+    , cacheOptionsSort      :: !(Maybe SortCache)
+    , cacheOptionsDirection :: !(Maybe SortDirection)
+    }
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+defaultCacheOptions :: CacheOptions
+defaultCacheOptions = CacheOptions
+    { cacheOptionsRef       = Nothing
+    , cacheOptionsKey       = Nothing
+    , cacheOptionsSort      = Nothing
+    , cacheOptionsDirection = Nothing
+    }
+
+-- | See <https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository>.
+newtype CacheMod = CacheMod (CacheOptions -> CacheOptions)
+
+instance Semigroup CacheMod where
+    CacheMod f <> CacheMod g = CacheMod (g . f)
+
+instance Monoid CacheMod where
+    mempty  = CacheMod id
+    mappend = (<>)
+
+toCacheOptions :: CacheMod -> CacheOptions
+toCacheOptions (CacheMod f) = f defaultCacheOptions
+
+cacheModToQueryString :: CacheMod -> QueryString
+cacheModToQueryString = cacheOptionsToQueryString . toCacheOptions
+
+cacheOptionsToQueryString :: CacheOptions -> QueryString
+cacheOptionsToQueryString (CacheOptions ref key sort dir) =
+    catMaybes
+    [ mk "ref"        <$> ref'
+    , mk "key"        <$> key'
+    , mk "sort"       <$> sort'
+    , mk "directions" <$> direction'
+    ]
+  where
+    mk k v = (k, Just v)
+    sort' = sort <&> \case
+        SortCacheCreatedAt      -> "created_at"
+        SortCacheLastAccessedAt -> "last_accessed_at"
+        SortCacheSizeInBytes    -> "size_in_bytes"
+    direction' = dir <&> \case
+       SortDescending -> "desc"
+       SortAscending  -> "asc"
+    ref' = fmap TE.encodeUtf8 ref
+    key' = fmap TE.encodeUtf8 key
+
+-------------------------------------------------------------------------------
+-- Cache modifiers
+-------------------------------------------------------------------------------
+
+optionsRef :: Text -> CacheMod
+optionsRef x = CacheMod $ \opts ->
+    opts { cacheOptionsRef = Just x }
+
+optionsNoRef :: CacheMod
+optionsNoRef = CacheMod $ \opts ->
+    opts { cacheOptionsRef = Nothing }
+
+optionsKey :: Text -> CacheMod
+optionsKey x = CacheMod $ \opts ->
+    opts { cacheOptionsKey = Just x }
+
+optionsNoKey :: CacheMod
+optionsNoKey = CacheMod $ \opts ->
+    opts { cacheOptionsKey = Nothing }
+
+optionsDirectionAsc :: CacheMod
+optionsDirectionAsc = CacheMod $ \opts ->
+    opts { cacheOptionsDirection = Just SortAscending }
+
+optionsDirectionDesc :: CacheMod
+optionsDirectionDesc = CacheMod $ \opts ->
+    opts { cacheOptionsDirection = Just SortDescending }
+
+sortByCreatedAt :: CacheMod
+sortByCreatedAt = CacheMod $ \opts ->
+    opts { cacheOptionsSort = Just SortCacheCreatedAt }
+
+sortByLastAccessedAt :: CacheMod
+sortByLastAccessedAt = CacheMod $ \opts ->
+    opts { cacheOptionsSort = Just SortCacheLastAccessedAt }
+
+sortBySizeInBytes :: CacheMod
+sortBySizeInBytes = CacheMod $ \opts ->
+    opts { cacheOptionsSort = Just SortCacheSizeInBytes }
+
+-------------------------------------------------------------------------------
+-- Actions workflow runs
+-------------------------------------------------------------------------------
+
+-- | See <https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository>.
+data WorkflowRunOptions = WorkflowRunOptions
+    { workflowRunOptionsActor   :: !(Maybe Text)
+    , workflowRunOptionsBranch  :: !(Maybe Text)
+    , workflowRunOptionsEvent   :: !(Maybe Text)
+    , workflowRunOptionsStatus  :: !(Maybe Text)
+    , workflowRunOptionsCreated :: !(Maybe Text)
+    , workflowRunOptionsHeadSha :: !(Maybe Text)
+    }
+  deriving
+    (Eq, Ord, Show, Generic, Data)
+
+defaultWorkflowRunOptions :: WorkflowRunOptions
+defaultWorkflowRunOptions = WorkflowRunOptions
+    { workflowRunOptionsActor   = Nothing
+    , workflowRunOptionsBranch  = Nothing
+    , workflowRunOptionsEvent   = Nothing
+    , workflowRunOptionsStatus  = Nothing
+    , workflowRunOptionsCreated = Nothing
+    , workflowRunOptionsHeadSha = Nothing
+    }
+
+-- | See <https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository>.
+newtype WorkflowRunMod = WorkflowRunMod (WorkflowRunOptions -> WorkflowRunOptions)
+
+instance Semigroup WorkflowRunMod where
+    WorkflowRunMod f <> WorkflowRunMod g = WorkflowRunMod (g . f)
+
+instance Monoid WorkflowRunMod where
+    mempty  = WorkflowRunMod id
+    mappend = (<>)
+
+toWorkflowRunOptions :: WorkflowRunMod -> WorkflowRunOptions
+toWorkflowRunOptions (WorkflowRunMod f) = f defaultWorkflowRunOptions
+
+workflowRunModToQueryString :: WorkflowRunMod -> QueryString
+workflowRunModToQueryString = workflowRunOptionsToQueryString . toWorkflowRunOptions
+
+workflowRunOptionsToQueryString :: WorkflowRunOptions -> QueryString
+workflowRunOptionsToQueryString (WorkflowRunOptions actor branch event status created headSha) =
+    catMaybes
+    [ mk "actor"    <$> actor'
+    , mk "branch"   <$> branch'
+    , mk "event"    <$> event'
+    , mk "status"   <$> status'
+    , mk "created"  <$> created'
+    , mk "head_sha" <$> headSha'
+    ]
+  where
+    mk k v = (k, Just v)
+    actor'   = fmap TE.encodeUtf8 actor
+    branch'  = fmap TE.encodeUtf8 branch
+    event'   = fmap TE.encodeUtf8 event
+    status'  = fmap TE.encodeUtf8 status
+    created' = fmap TE.encodeUtf8 created
+    headSha' = fmap TE.encodeUtf8 headSha
+
+-------------------------------------------------------------------------------
+-- Workflow run modifiers
+-------------------------------------------------------------------------------
+
+optionsWorkflowRunActor :: Text -> WorkflowRunMod
+optionsWorkflowRunActor x = WorkflowRunMod $ \opts ->
+    opts { workflowRunOptionsActor = Just x }
+
+optionsWorkflowRunBranch :: Text -> WorkflowRunMod
+optionsWorkflowRunBranch x = WorkflowRunMod $ \opts ->
+    opts { workflowRunOptionsBranch = Just x }
+
+optionsWorkflowRunEvent :: Text -> WorkflowRunMod
+optionsWorkflowRunEvent x = WorkflowRunMod $ \opts ->
+    opts { workflowRunOptionsEvent = Just x }
+
+optionsWorkflowRunStatus :: Text -> WorkflowRunMod
+optionsWorkflowRunStatus x = WorkflowRunMod $ \opts ->
+    opts { workflowRunOptionsStatus = Just x }
+
+optionsWorkflowRunCreated :: Text -> WorkflowRunMod
+optionsWorkflowRunCreated x = WorkflowRunMod $ \opts ->
+    opts { workflowRunOptionsCreated = Just x }
+
+optionsWorkflowRunHeadSha :: Text -> WorkflowRunMod
+optionsWorkflowRunHeadSha x = WorkflowRunMod $ \opts ->
+    opts { workflowRunOptionsHeadSha = Just x }
diff --git a/src/GitHub/Data/PublicSSHKeys.hs b/src/GitHub/Data/PublicSSHKeys.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/PublicSSHKeys.hs
@@ -0,0 +1,60 @@
+-----------------------------------------------------------------------------
+-- |
+-- License     :  BSD-3-Clause
+-- Maintainer  :  Todd Mohney <toddmohney@gmail.com>
+--
+module GitHub.Data.PublicSSHKeys where
+
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data PublicSSHKeyBasic = PublicSSHKeyBasic
+    { basicPublicSSHKeyId        :: !(Id PublicSSHKey)
+    , basicPublicSSHKeyKey       :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON PublicSSHKeyBasic where
+    parseJSON = withObject "PublicSSHKeyBasic" $ \o -> PublicSSHKeyBasic
+        <$> o .: "id"
+        <*> o .: "key"
+
+data PublicSSHKey = PublicSSHKey
+    { publicSSHKeyId        :: !(Id PublicSSHKey)
+    , publicSSHKeyKey       :: !Text
+    , publicSSHKeyUrl       :: !URL
+    , publicSSHKeyTitle     :: !Text
+    , publicSSHKeyVerified  :: !Bool
+    , publicSSHKeyCreatedAt :: !(Maybe UTCTime)
+    , publicSSHKeyReadOnly  :: !Bool
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON PublicSSHKey where
+    parseJSON = withObject "PublicSSHKey" $ \o -> PublicSSHKey
+        <$> o .: "id"
+        <*> o .: "key"
+        <*> o .: "url"
+        <*> o .: "title"
+        <*> o .: "verified"
+        <*> o .:? "created_at"
+        <*> o .: "read_only"
+
+data NewPublicSSHKey = NewPublicSSHKey
+    { newPublicSSHKeyKey      :: !Text
+    , newPublicSSHKeyTitle    :: !Text
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance ToJSON NewPublicSSHKey where
+    toJSON (NewPublicSSHKey key title) = object
+        [ "key" .= key
+        , "title" .= title
+        ]
+
+instance FromJSON NewPublicSSHKey where
+    parseJSON = withObject "PublicSSHKey" $ \o -> NewPublicSSHKey
+        <$> o .: "key"
+        <*> o .: "title"
diff --git a/src/GitHub/Data/PullRequests.hs b/src/GitHub/Data/PullRequests.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/PullRequests.hs
@@ -0,0 +1,319 @@
+module GitHub.Data.PullRequests (
+    SimplePullRequest(..),
+    PullRequest(..),
+    EditPullRequest(..),
+    CreatePullRequest(..),
+    PullRequestLinks(..),
+    PullRequestCommit(..),
+    PullRequestEvent(..),
+    PullRequestEventType(..),
+    PullRequestReference(..),
+    MergeResult(..),
+    ) where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.Options     (IssueState (..), MergeableState (..))
+import GitHub.Data.Repos       (Repo)
+import GitHub.Data.URL         (URL)
+import GitHub.Data.Teams       (SimpleTeam)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text as T
+
+data SimplePullRequest = SimplePullRequest
+    { simplePullRequestClosedAt           :: !(Maybe UTCTime)
+    , simplePullRequestCreatedAt          :: !UTCTime
+    , simplePullRequestUser               :: !SimpleUser
+    , simplePullRequestPatchUrl           :: !URL
+    , simplePullRequestState              :: !IssueState
+    , simplePullRequestNumber             :: !IssueNumber
+    , simplePullRequestHtmlUrl            :: !URL
+    , simplePullRequestUpdatedAt          :: !UTCTime
+    , simplePullRequestBody               :: !(Maybe Text)
+    , simplePullRequestAssignees          :: (Vector SimpleUser)
+    , simplePullRequestRequestedReviewers :: (Vector SimpleUser)
+    , simplePullRequestRequestedTeamReviewers:: (Vector SimpleTeam)
+    , simplePullRequestIssueUrl           :: !URL
+    , simplePullRequestDiffUrl            :: !URL
+    , simplePullRequestUrl                :: !URL
+    , simplePullRequestLinks              :: !PullRequestLinks
+    , simplePullRequestMergedAt           :: !(Maybe UTCTime)
+    , simplePullRequestTitle              :: !Text
+    , simplePullRequestId                 :: !(Id PullRequest)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData SimplePullRequest
+instance Binary SimplePullRequest
+
+data PullRequest = PullRequest
+    { pullRequestClosedAt             :: !(Maybe UTCTime)
+    , pullRequestCreatedAt            :: !UTCTime
+    , pullRequestUser                 :: !SimpleUser
+    , pullRequestPatchUrl             :: !URL
+    , pullRequestState                :: !IssueState
+    , pullRequestNumber               :: !IssueNumber
+    , pullRequestHtmlUrl              :: !URL
+    , pullRequestUpdatedAt            :: !UTCTime
+    , pullRequestBody                 :: !(Maybe Text)
+    , pullRequestAssignees            :: (Vector SimpleUser)
+    , pullRequestRequestedReviewers   :: (Vector SimpleUser)
+    , pullRequestRequestedTeamReviewers :: (Vector SimpleTeam)
+    , pullRequestIssueUrl             :: !URL
+    , pullRequestDiffUrl              :: !URL
+    , pullRequestUrl                  :: !URL
+    , pullRequestLinks                :: !PullRequestLinks
+    , pullRequestMergedAt             :: !(Maybe UTCTime)
+    , pullRequestTitle                :: !Text
+    , pullRequestId                   :: !(Id PullRequest)
+    , pullRequestMergedBy             :: !(Maybe SimpleUser)
+    , pullRequestChangedFiles         :: !Int
+    , pullRequestHead                 :: !PullRequestCommit
+    , pullRequestComments             :: !Count
+    , pullRequestDeletions            :: !Count
+    , pullRequestAdditions            :: !Count
+    , pullRequestReviewComments       :: !Count
+    , pullRequestBase                 :: !PullRequestCommit
+    , pullRequestCommits              :: !Count
+    , pullRequestMerged               :: !Bool
+    , pullRequestMergeable            :: !(Maybe Bool)
+    , pullRequestMergeableState       :: !MergeableState
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PullRequest
+instance Binary PullRequest
+
+data EditPullRequest = EditPullRequest
+    { editPullRequestTitle :: !(Maybe Text)
+    , editPullRequestBody  :: !(Maybe Text)
+    , editPullRequestState :: !(Maybe IssueState)
+    , editPullRequestBase  :: !(Maybe Text)
+    , editPullRequestMaintainerCanModify
+                           :: !(Maybe Bool)
+    }
+  deriving (Show, Generic)
+
+instance NFData EditPullRequest
+instance Binary EditPullRequest
+
+data CreatePullRequest
+    = CreatePullRequest
+      { createPullRequestTitle :: !Text
+      , createPullRequestBody  :: !Text
+      , createPullRequestHead  :: !Text
+      , createPullRequestBase  :: !Text
+      }
+    | CreatePullRequestIssue
+      { createPullRequestIssueNum :: !Int
+      , createPullRequestHead     :: !Text
+      , createPullRequestBase     :: !Text
+      }
+  deriving (Show, Generic)
+
+instance NFData CreatePullRequest
+instance Binary CreatePullRequest
+
+data PullRequestLinks = PullRequestLinks
+    { pullRequestLinksReviewComments :: !URL
+    , pullRequestLinksComments       :: !URL
+    , pullRequestLinksHtml           :: !URL
+    , pullRequestLinksSelf           :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PullRequestLinks
+instance Binary PullRequestLinks
+
+data PullRequestCommit = PullRequestCommit
+    { pullRequestCommitLabel :: !Text
+    , pullRequestCommitRef   :: !Text
+    , pullRequestCommitSha   :: !Text
+    , pullRequestCommitUser  :: !SimpleUser
+    , pullRequestCommitRepo  :: !(Maybe Repo)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PullRequestCommit
+instance Binary PullRequestCommit
+
+data PullRequestEvent = PullRequestEvent
+    { pullRequestEventAction      :: !PullRequestEventType
+    , pullRequestEventNumber      :: !Int
+    , pullRequestEventPullRequest :: !PullRequest
+    , pullRequestRepository       :: !Repo
+    , pullRequestSender           :: !SimpleUser
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PullRequestEvent
+instance Binary PullRequestEvent
+
+data PullRequestEventType
+    = PullRequestOpened
+    | PullRequestClosed
+    | PullRequestSynchronized
+    | PullRequestReopened
+    | PullRequestAssigned
+    | PullRequestUnassigned
+    | PullRequestLabeled
+    | PullRequestUnlabeled
+    | PullRequestReviewRequested
+    | PullRequestReviewRequestRemoved
+    | PullRequestEdited
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PullRequestEventType
+instance Binary PullRequestEventType
+
+data PullRequestReference = PullRequestReference
+    { pullRequestReferenceHtmlUrl  :: !(Maybe URL)
+    , pullRequestReferencePatchUrl :: !(Maybe URL)
+    , pullRequestReferenceDiffUrl  :: !(Maybe URL)
+    }
+    deriving (Eq, Ord, Show, Generic, Data)
+
+instance NFData PullRequestReference
+instance Binary PullRequestReference
+
+
+-------------------------------------------------------------------------------
+-- JSON instances
+-------------------------------------------------------------------------------
+
+instance FromJSON SimplePullRequest where
+    parseJSON = withObject "SimplePullRequest" $ \o -> SimplePullRequest
+        <$> o .:? "closed_at"
+        <*> o .: "created_at"
+        <*> o .: "user"
+        <*> o .: "patch_url"
+        <*> o .: "state"
+        <*> o .: "number"
+        <*> o .: "html_url"
+        <*> o .: "updated_at"
+        <*> o .:? "body"
+        <*> o .: "assignees"
+        <*> o .:? "requested_reviewers" .!= mempty
+        <*> o .:? "requested_teams" .!= mempty
+        <*> o .: "issue_url"
+        <*> o .: "diff_url"
+        <*> o .: "url"
+        <*> o .: "_links"
+        <*> o .:? "merged_at"
+        <*> o .: "title"
+        <*> o .: "id"
+
+instance ToJSON EditPullRequest where
+    toJSON (EditPullRequest t b s base mcm) =
+        object $ filter notNull
+            [ "title" .= t
+            , "body"  .= b
+            , "state" .= s
+            , "base"  .= base
+            , "maintainer_can_modify"
+                      .= mcm
+            ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _) = True
+
+instance ToJSON CreatePullRequest where
+    toJSON (CreatePullRequest t b headPR basePR) =
+        object [ "title" .= t, "body" .= b, "head" .= headPR, "base" .= basePR ]
+    toJSON (CreatePullRequestIssue issueNum headPR basePR) =
+        object [ "issue" .= issueNum, "head" .= headPR, "base" .= basePR]
+
+instance FromJSON PullRequest where
+    parseJSON = withObject "PullRequest" $ \o -> PullRequest
+        <$> o .:? "closed_at"
+        <*> o .: "created_at"
+        <*> o .: "user"
+        <*> o .: "patch_url"
+        <*> o .: "state"
+        <*> o .: "number"
+        <*> o .: "html_url"
+        <*> o .: "updated_at"
+        <*> o .:? "body"
+        <*> o .: "assignees"
+        <*> o .:? "requested_reviewers" .!= mempty
+        <*> o .:? "requested_teams" .!= mempty
+        <*> o .: "issue_url"
+        <*> o .: "diff_url"
+        <*> o .: "url"
+        <*> o .: "_links"
+        <*> o .:? "merged_at"
+        <*> o .: "title"
+        <*> o .: "id"
+        <*> o .:? "merged_by"
+        <*> o .: "changed_files"
+        <*> o .: "head"
+        <*> o .: "comments"
+        <*> o .: "deletions"
+        <*> o .: "additions"
+        <*> o .: "review_comments"
+        <*> o .: "base"
+        <*> o .: "commits"
+        <*> o .: "merged"
+        <*> o .:? "mergeable"
+        <*> o .: "mergeable_state"
+
+instance FromJSON PullRequestLinks where
+    parseJSON = withObject "PullRequestLinks" $ \o -> PullRequestLinks
+        <$> fmap getHref (o .: "review_comments")
+        <*> fmap getHref (o .: "comments")
+        <*> fmap getHref (o .: "html")
+        <*> fmap getHref (o .: "self")
+
+instance FromJSON PullRequestCommit where
+    parseJSON = withObject "PullRequestCommit" $ \o -> PullRequestCommit
+        <$> o .: "label"
+        <*> o .: "ref"
+        <*> o .: "sha"
+        <*> o .: "user"
+        <*> o .: "repo"
+
+instance FromJSON PullRequestEvent where
+    parseJSON = withObject "PullRequestEvent" $ \o -> PullRequestEvent
+        <$> o .: "action"
+        <*> o .: "number"
+        <*> o .: "pull_request"
+        <*> o .: "repository"
+        <*> o .: "sender"
+
+instance FromJSON PullRequestEventType where
+    parseJSON = withText "PullRequestEventType" $ \t -> case T.toLower t of
+        "opened"                 -> pure PullRequestOpened
+        "closed"                 -> pure PullRequestClosed
+        "synchronize"            -> pure PullRequestSynchronized
+        "reopened"               -> pure PullRequestReopened
+        "assigned"               -> pure PullRequestAssigned
+        "unassigned"             -> pure PullRequestUnassigned
+        "labeled"                -> pure PullRequestLabeled
+        "unlabeled"              -> pure PullRequestUnlabeled
+        "review_requested"       -> pure PullRequestReviewRequested
+        "review_request_removed" -> pure PullRequestReviewRequestRemoved
+        "edited"                 -> pure PullRequestEdited
+        _                        -> fail $ "Unknown PullRequestEventType: " <> T.unpack t
+
+instance FromJSON PullRequestReference where
+    parseJSON = withObject "PullRequestReference" $ \o -> PullRequestReference
+        <$> o .:? "html_url"
+        <*> o .:? "patch_url"
+        <*> o .:? "diff_url"
+
+-- Helpers
+
+newtype Href a = Href { getHref :: a }
+
+instance FromJSON a => FromJSON (Href a) where
+    parseJSON = withObject "href object" $
+        \obj -> Href <$> obj .: "href"
+
+-- | Pull request merge results
+data MergeResult
+    = MergeSuccessful
+    | MergeCannotPerform
+    | MergeConflict
+  deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic)
diff --git a/src/GitHub/Data/RateLimit.hs b/src/GitHub/Data/RateLimit.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/RateLimit.hs
@@ -0,0 +1,61 @@
+module GitHub.Data.RateLimit where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Data.Time.Clock.System (SystemTime (..))
+
+import qualified Data.ByteString.Char8 as BS8
+import qualified Network.HTTP.Client as HTTP
+
+data Limits = Limits
+    { limitsMax       :: !Int
+    , limitsRemaining :: !Int
+    , limitsReset     :: !SystemTime
+    }
+  deriving (Show, Eq, Ord, Generic)
+
+instance NFData Limits
+instance Binary Limits
+
+instance FromJSON Limits where
+    parseJSON = withObject "Limits" $ \obj -> Limits
+        <$> obj .: "limit"
+        <*> obj .: "remaining"
+        <*> fmap (\t -> MkSystemTime t 0) (obj .: "reset")
+
+data RateLimit = RateLimit
+    { rateLimitCore    :: Limits
+    , rateLimitSearch  :: Limits
+    , rateLimitGraphQL :: Limits
+    }
+  deriving (Show, Eq, Ord, Generic)
+
+instance NFData RateLimit
+instance Binary RateLimit
+
+instance FromJSON RateLimit where
+    parseJSON = withObject "RateLimit" $ \obj -> do
+        resources <- obj .: "resources"
+        RateLimit
+            <$> resources .: "core"
+            <*> resources .: "search"
+            <*> resources .: "graphql"
+
+-------------------------------------------------------------------------------
+-- Extras
+-------------------------------------------------------------------------------
+
+-- | @since 0.24
+limitsFromHttpResponse :: HTTP.Response a -> Maybe Limits
+limitsFromHttpResponse res = do
+    let hdrs = HTTP.responseHeaders res
+    m <- lookup "X-RateLimit-Limit"     hdrs >>= readIntegral
+    r <- lookup "X-RateLimit-Remaining" hdrs >>= readIntegral
+    t <- lookup "X-RateLimit-Reset"     hdrs >>= readIntegral
+    return (Limits m r (MkSystemTime t 0))
+  where
+    readIntegral :: Num a => BS8.ByteString -> Maybe a
+    readIntegral bs = case BS8.readInt bs of
+        Just (n, bs') | BS8.null bs' -> Just (fromIntegral n)
+        _                            -> Nothing
diff --git a/src/GitHub/Data/Reactions.hs b/src/GitHub/Data/Reactions.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Reactions.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE InstanceSigs #-}
+module GitHub.Data.Reactions where
+
+import qualified Data.Text as T
+import GitHub.Data.Id (Id)
+import GitHub.Data.Definitions (SimpleUser)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data Reaction = Reaction
+  { reactionId :: Id Reaction
+  , reactionUser :: !(Maybe SimpleUser)
+  , reactionContent :: !ReactionContent
+  , reactionCreatedAt :: !UTCTime
+  }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Reaction
+instance Binary Reaction
+
+data NewReaction = NewReaction
+  { newReactionContent :: !ReactionContent
+  }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewReaction
+instance Binary NewReaction
+
+-- |
+-- <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#about-reactions>
+data ReactionContent
+  = PlusOne
+  | MinusOne
+  | Laugh
+  | Confused
+  | Heart
+  | Hooray
+  | Rocket
+  | Eyes
+  deriving (Show, Data, Eq, Ord, Enum, Bounded, Generic)
+
+instance NFData ReactionContent
+instance Binary ReactionContent
+
+-- JSON instances
+
+instance FromJSON Reaction where
+  parseJSON = withObject "Reaction" $ \o ->
+    Reaction
+      <$> o .: "id"
+      <*> o .:? "user"
+      <*> o .: "content"
+      <*> o .: "created_at"
+
+instance ToJSON NewReaction where
+  toJSON (NewReaction content) = object ["content" .= content]
+
+instance FromJSON ReactionContent where
+  parseJSON = withText "ReactionContent" $ \case
+    "+1" -> pure PlusOne
+    "-1" -> pure MinusOne
+    "laugh" -> pure Laugh
+    "confused" -> pure Confused
+    "heart" -> pure Heart
+    "hooray" -> pure Hooray
+    "rocket" -> pure Rocket
+    "eyes" -> pure Eyes
+    t -> fail $ "Unknown ReactionContent: " <> T.unpack t
+
+instance ToJSON ReactionContent where
+  toJSON PlusOne = String "+1"
+  toJSON MinusOne = String "-1"
+  toJSON Laugh = String "laugh"
+  toJSON Confused = String "confused"
+  toJSON Heart = String "heart"
+  toJSON Hooray = String "hooray"
+  toJSON Rocket = String "rocket"
+  toJSON Eyes = String "eyes"
diff --git a/src/GitHub/Data/Releases.hs b/src/GitHub/Data/Releases.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Releases.hs
@@ -0,0 +1,85 @@
+module GitHub.Data.Releases where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+data Release = Release
+    { releaseUrl             :: !URL
+    , releaseHtmlUrl         :: !URL
+    , releaseAssetsurl       :: !URL
+    , releaseUploadUrl       :: !URL
+    , releaseTarballUrl      :: !URL
+    , releaseZipballUrl      :: !URL
+    , releaseId              :: !(Id Release)
+    , releaseTagName         :: !Text
+    , releaseTargetCommitish :: !Text
+    , releaseName            :: !Text
+    , releaseBody            :: !Text
+    , releaseDraft           :: !Bool
+    , releasePrerelease      :: !Bool
+    , releaseCreatedAt       :: !UTCTime
+    , releasePublishedAt     :: !(Maybe UTCTime)
+    , releaseAuthor          :: !SimpleUser
+    , releaseAssets          :: !(Vector ReleaseAsset)
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON Release where
+    parseJSON = withObject "Event" $ \o -> Release
+        <$> o .: "url"
+        <*> o .: "html_url"
+        <*> o .: "assets_url"
+        <*> o .: "upload_url"
+        <*> o .: "tarball_url"
+        <*> o .: "zipball_url"
+        <*> o .: "id"
+        <*> o .: "tag_name"
+        <*> o .: "target_commitish"
+        <*> o .: "name"
+        <*> o .: "body"
+        <*> o .: "draft"
+        <*> o .: "prerelease"
+        <*> o .: "created_at"
+        <*> o .:? "published_at"
+        <*> o .: "author"
+        <*> o .: "assets"
+
+instance NFData Release
+instance Binary Release
+
+data ReleaseAsset = ReleaseAsset
+    { releaseAssetUrl                :: !URL
+    , releaseAssetBrowserDownloadUrl :: !Text
+    , releaseAssetId                 :: !(Id ReleaseAsset)
+    , releaseAssetName               :: !Text
+    , releaseAssetLabel              :: !(Maybe Text)
+    , releaseAssetState              :: !Text
+    , releaseAssetContentType        :: !Text
+    , releaseAssetSize               :: !Int
+    , releaseAssetDownloadCount      :: !Int
+    , releaseAssetCreatedAt          :: !UTCTime
+    , releaseAssetUpdatedAt          :: !UTCTime
+    , releaseAssetUploader           :: !SimpleUser
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON ReleaseAsset where
+    parseJSON = withObject "Event" $ \o -> ReleaseAsset
+        <$> o .: "url"
+        <*> o .: "browser_download_url"
+        <*> o .: "id"
+        <*> o .: "name"
+        <*> o .:? "label"
+        <*> o .: "state"
+        <*> o .: "content_type"
+        <*> o .: "size"
+        <*> o .: "download_count"
+        <*> o .: "created_at"
+        <*> o .: "updated_at"
+        <*> o .: "uploader"
+
+instance NFData ReleaseAsset
+instance Binary ReleaseAsset
diff --git a/src/GitHub/Data/Repos.hs b/src/GitHub/Data/Repos.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Repos.hs
@@ -0,0 +1,408 @@
+{-# LANGUAGE FlexibleInstances #-}
+
+-- |
+-- This module also exports
+-- @'FromJSON' a => 'FromJSON' ('HM.HashMap' 'Language' a)@
+-- orphan-ish instance for @aeson < 1@
+
+module GitHub.Data.Repos where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.Name        (Name)
+import GitHub.Data.Request     (IsPathPart (..))
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Text as T
+import Data.Aeson.Types (FromJSONKey (..), fromJSONKeyCoerce)
+
+data Repo = Repo
+    { repoId              :: !(Id Repo)
+    , repoName            :: !(Name Repo)
+    , repoOwner           :: !SimpleOwner
+    , repoPrivate         :: !Bool
+    , repoHtmlUrl         :: !URL
+    , repoDescription     :: !(Maybe Text)
+    , repoFork            :: !(Maybe Bool)
+    , repoUrl             :: !URL
+    , repoGitUrl          :: !(Maybe URL)
+    , repoSshUrl          :: !(Maybe URL)
+    , repoCloneUrl        :: !(Maybe URL)
+    , repoHooksUrl        :: !URL
+    , repoSvnUrl          :: !(Maybe URL)
+    , repoHomepage        :: !(Maybe Text)
+    , repoLanguage        :: !(Maybe Language)
+    , repoForksCount      :: !Int
+    , repoStargazersCount :: !Int
+    , repoWatchersCount   :: !Int
+    , repoSize            :: !(Maybe Int)
+    , repoDefaultBranch   :: !(Maybe Text)
+    , repoOpenIssuesCount :: !Int
+    , repoHasIssues       :: !(Maybe Bool)
+    , repoHasProjects     :: !(Maybe Bool)
+    , repoHasWiki         :: !(Maybe Bool)
+    , repoHasPages        :: !(Maybe Bool)
+    , repoHasDownloads    :: !(Maybe Bool)
+    , repoArchived        :: !Bool
+    , repoDisabled        :: !Bool
+    , repoPushedAt        :: !(Maybe UTCTime)   -- ^ this is Nothing for new repositories
+    , repoCreatedAt       :: !(Maybe UTCTime)
+    , repoUpdatedAt       :: !(Maybe UTCTime)
+    , repoPermissions     :: !(Maybe RepoPermissions) -- ^ Repository permissions as they relate to the authenticated user.
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Repo
+instance Binary Repo
+
+data CodeSearchRepo = CodeSearchRepo
+    { codeSearchRepoId              :: !(Id Repo)
+    , codeSearchRepoName            :: !(Name Repo)
+    , codeSearchRepoOwner           :: !SimpleOwner
+    , codeSearchRepoPrivate         :: !Bool
+    , codeSearchRepoHtmlUrl         :: !URL
+    , codeSearchRepoDescription     :: !(Maybe Text)
+    , codeSearchRepoFork            :: !(Maybe Bool)
+    , codeSearchRepoUrl             :: !URL
+    , codeSearchRepoGitUrl          :: !(Maybe URL)
+    , codeSearchRepoSshUrl          :: !(Maybe URL)
+    , codeSearchRepoCloneUrl        :: !(Maybe URL)
+    , codeSearchRepoHooksUrl        :: !URL
+    , codeSearchRepoSvnUrl          :: !(Maybe URL)
+    , codeSearchRepoHomepage        :: !(Maybe Text)
+    , codeSearchRepoLanguage        :: !(Maybe Language)
+    , codeSearchRepoSize            :: !(Maybe Int)
+    , codeSearchRepoDefaultBranch   :: !(Maybe Text)
+    , codeSearchRepoHasIssues       :: !(Maybe Bool)
+    , codeSearchRepoHasProjects     :: !(Maybe Bool)
+    , codeSearchRepoHasWiki         :: !(Maybe Bool)
+    , codeSearchRepoHasPages        :: !(Maybe Bool)
+    , codeSearchRepoHasDownloads    :: !(Maybe Bool)
+    , codeSearchRepoArchived        :: !Bool
+    , codeSearchRepoDisabled        :: !Bool
+    , codeSearchRepoPushedAt        :: !(Maybe UTCTime)   -- ^ this is Nothing for new repositories
+    , codeSearchRepoCreatedAt       :: !(Maybe UTCTime)
+    , codeSearchRepoUpdatedAt       :: !(Maybe UTCTime)
+    , codeSearchRepoPermissions     :: !(Maybe RepoPermissions) -- ^ Repository permissions as they relate to the authenticated user.
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData CodeSearchRepo
+instance Binary CodeSearchRepo
+
+-- | Repository permissions, as they relate to the authenticated user.
+--
+-- Returned by for example 'GitHub.Endpoints.Repos.currentUserReposR'
+data RepoPermissions = RepoPermissions
+    { repoPermissionAdmin :: !Bool
+    , repoPermissionPush :: !Bool
+    , repoPermissionPull :: !Bool
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoPermissions
+instance Binary RepoPermissions
+
+data RepoRef = RepoRef
+    { repoRefOwner :: !SimpleOwner
+    , repoRefRepo  :: !(Name Repo)
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoRef
+instance Binary RepoRef
+
+data NewRepo = NewRepo
+    { newRepoName              :: !(Name Repo)
+    , newRepoDescription       :: !(Maybe Text)
+    , newRepoHomepage          :: !(Maybe Text)
+    , newRepoPrivate           :: !(Maybe Bool)
+    , newRepoHasIssues         :: !(Maybe Bool)
+    , newRepoHasProjects       :: !(Maybe Bool)
+    , newRepoHasWiki           :: !(Maybe Bool)
+    , newRepoAutoInit          :: !(Maybe Bool)
+    , newRepoGitignoreTemplate :: !(Maybe Text)
+    , newRepoLicenseTemplate   :: !(Maybe Text)
+    , newRepoAllowSquashMerge  :: !(Maybe Bool)
+    , newRepoAllowMergeCommit  :: !(Maybe Bool)
+    , newRepoAllowRebaseMerge  :: !(Maybe Bool)
+    } deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData NewRepo
+instance Binary NewRepo
+
+newRepo :: Name Repo -> NewRepo
+newRepo name = NewRepo name Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
+
+data EditRepo = EditRepo
+    { editName             :: !(Maybe (Name Repo))
+    , editDescription      :: !(Maybe Text)
+    , editHomepage         :: !(Maybe Text)
+    , editPrivate          :: !(Maybe Bool)
+    , editHasIssues        :: !(Maybe Bool)
+    , editHasProjects      :: !(Maybe Bool)
+    , editHasWiki          :: !(Maybe Bool)
+    , editDefaultBranch    :: !(Maybe Text)
+    , editAllowSquashMerge :: !(Maybe Bool)
+    , editAllowMergeCommit :: !(Maybe Bool)
+    , editAllowRebaseMerge :: !(Maybe Bool)
+    , editArchived         :: !(Maybe Bool)
+    }
+    deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData EditRepo
+instance Binary EditRepo
+
+-- | Filter the list of the user's repos using any of these constructors.
+data RepoPublicity
+    = RepoPublicityAll     -- ^ All repos accessible to the user.
+    | RepoPublicityOwner   -- ^ Only repos owned by the user.
+    | RepoPublicityPublic  -- ^ Only public repos.
+    | RepoPublicityPrivate -- ^ Only private repos.
+    | RepoPublicityMember  -- ^ Only repos to which the user is a member but not an owner.
+    deriving (Show, Eq, Ord, Enum, Bounded, Data, Generic)
+
+-- | The value is the number of bytes of code written in that language.
+type Languages = HM.HashMap Language Int
+
+-- | A programming language.
+newtype Language = Language Text
+   deriving (Show, Data, Eq, Ord, Generic)
+
+getLanguage :: Language -> Text
+getLanguage (Language l) = l
+
+instance NFData Language
+instance Binary Language
+instance Hashable Language where
+    hashWithSalt salt (Language l) = hashWithSalt salt l
+instance IsString Language where
+    fromString = Language . fromString
+
+data Contributor
+    -- | An existing Github user, with their number of contributions, avatar
+    -- URL, login, URL, ID, and Gravatar ID.
+    = KnownContributor !Int !URL !(Name User) !URL !(Id User) !Text
+    -- | An unknown Github user with their number of contributions and recorded name.
+    | AnonymousContributor !Int !Text
+   deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Contributor
+instance Binary Contributor
+
+contributorToSimpleUser :: Contributor -> Maybe SimpleUser
+contributorToSimpleUser (AnonymousContributor _ _) = Nothing
+contributorToSimpleUser (KnownContributor _contributions avatarUrl name url uid _gravatarid) =
+    Just $ SimpleUser uid name avatarUrl url
+
+-- | The permission of a collaborator on a repository.
+-- See <https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level>
+data CollaboratorPermission
+    = CollaboratorPermissionAdmin
+    | CollaboratorPermissionWrite
+    | CollaboratorPermissionRead
+    | CollaboratorPermissionNone
+    deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData CollaboratorPermission
+instance Binary CollaboratorPermission
+
+-- | A collaborator and its permission on a repository.
+-- See <https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level>
+data CollaboratorWithPermission
+    = CollaboratorWithPermission SimpleUser CollaboratorPermission
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData CollaboratorWithPermission
+instance Binary CollaboratorWithPermission
+
+-- JSON instances
+
+instance FromJSON Repo where
+    parseJSON = withObject "Repo" $ \o -> Repo <$> o .: "id"
+        <*> o .: "name"
+        <*> o .: "owner"
+        <*> o .: "private"
+        <*> o .: "html_url"
+        <*> o .:? "description"
+        <*> o .: "fork"
+        <*> o .: "url"
+        <*> o .:? "git_url"
+        <*> o .:? "ssh_url"
+        <*> o .:? "clone_url"
+        <*> o .: "hooks_url"
+        <*> o .:? "svn_url"
+        <*> o .:? "homepage"
+        <*> o .:? "language"
+        <*> o .: "forks_count"
+        <*> o .: "stargazers_count"
+        <*> o .: "watchers_count"
+        <*> o .:? "size"
+        <*> o .:? "default_branch"
+        <*> o .: "open_issues_count"
+        <*> o .:? "has_issues"
+        <*> o .:? "has_projects"
+        <*> o .:? "has_wiki"
+        <*> o .:? "has_pages"
+        <*> o .:? "has_downloads"
+        <*> o .:? "archived" .!= False
+        <*> o .:? "disabled" .!= False
+        <*> o .:? "pushed_at"
+        <*> o .:? "created_at"
+        <*> o .:? "updated_at"
+        <*> o .:? "permissions"
+
+instance FromJSON CodeSearchRepo where
+    parseJSON = withObject "Repo" $ \o -> CodeSearchRepo <$> o .: "id"
+        <*> o .: "name"
+        <*> o .: "owner"
+        <*> o .: "private"
+        <*> o .: "html_url"
+        <*> o .:? "description"
+        <*> o .: "fork"
+        <*> o .: "url"
+        <*> o .:? "git_url"
+        <*> o .:? "ssh_url"
+        <*> o .:? "clone_url"
+        <*> o .: "hooks_url"
+        <*> o .:? "svn_url"
+        <*> o .:? "homepage"
+        <*> o .:? "language"
+        <*> o .:? "size"
+        <*> o .:? "default_branch"
+        <*> o .:? "has_issues"
+        <*> o .:? "has_projects"
+        <*> o .:? "has_wiki"
+        <*> o .:? "has_pages"
+        <*> o .:? "has_downloads"
+        <*> o .:? "archived" .!= False
+        <*> o .:? "disabled" .!= False
+        <*> o .:? "pushed_at"
+        <*> o .:? "created_at"
+        <*> o .:? "updated_at"
+        <*> o .:? "permissions"
+
+instance ToJSON NewRepo where
+  toJSON (NewRepo { newRepoName              = name
+                  , newRepoDescription       = description
+                  , newRepoHomepage          = homepage
+                  , newRepoPrivate           = private
+                  , newRepoHasIssues         = hasIssues
+                  , newRepoHasProjects       = hasProjects
+                  , newRepoHasWiki           = hasWiki
+                  , newRepoAutoInit          = autoInit
+                  , newRepoGitignoreTemplate = gitignoreTemplate
+                  , newRepoLicenseTemplate   = licenseTemplate
+                  , newRepoAllowSquashMerge  = allowSquashMerge
+                  , newRepoAllowMergeCommit  = allowMergeCommit
+                  , newRepoAllowRebaseMerge  = allowRebaseMerge
+                  }) = object
+                  [ "name"                .= name
+                  , "description"         .= description
+                  , "homepage"            .= homepage
+                  , "private"             .= private
+                  , "has_issues"          .= hasIssues
+                  , "has_projects"        .= hasProjects
+                  , "has_wiki"            .= hasWiki
+                  , "auto_init"           .= autoInit
+                  , "gitignore_template"  .= gitignoreTemplate
+                  , "license_template"    .= licenseTemplate
+                  , "allow_squash_merge"  .= allowSquashMerge
+                  , "allow_merge_commit"  .= allowMergeCommit
+                  , "allow_rebase_merge"  .= allowRebaseMerge
+                  ]
+
+instance ToJSON EditRepo where
+  toJSON (EditRepo { editName             = name
+                   , editDescription      = description
+                   , editHomepage         = homepage
+                   , editPrivate          = private
+                   , editHasIssues        = hasIssues
+                   , editHasProjects      = hasProjects
+                   , editHasWiki          = hasWiki
+                   , editDefaultBranch    = defaultBranch
+                   , editAllowSquashMerge = allowSquashMerge
+                   , editAllowMergeCommit = allowMergeCommit
+                   , editAllowRebaseMerge = allowRebaseMerge
+                   , editArchived         = archived
+                   }) = object
+                   [ "name"               .= name
+                   , "description"        .= description
+                   , "homepage"           .= homepage
+                   , "private"            .= private
+                   , "has_issues"         .= hasIssues
+                   , "has_projects"       .= hasProjects
+                   , "has_wiki"           .= hasWiki
+                   , "default_branch"     .= defaultBranch
+                   , "allow_squash_merge" .= allowSquashMerge
+                   , "allow_merge_commit" .= allowMergeCommit
+                   , "allow_rebase_merge" .= allowRebaseMerge
+                   , "archived"           .= archived
+                   ]
+
+instance FromJSON RepoPermissions where
+  parseJSON = withObject "RepoPermissions" $ \o -> RepoPermissions
+        <$> o .: "admin"
+        <*> o .: "push"
+        <*> o .: "pull"
+
+instance FromJSON RepoRef where
+    parseJSON = withObject "RepoRef" $ \o -> RepoRef
+        <$> o .: "owner"
+        <*> o .: "name"
+
+instance FromJSON Contributor where
+    parseJSON = withObject "Contributor" $ \o -> do
+        t <- o .: "type"
+        case (t :: Text) of
+            "Anonymous" -> AnonymousContributor
+                <$> o .: "contributions"
+                <*> o .: "name"
+            _  -> KnownContributor
+                <$> o .: "contributions"
+                <*> o .: "avatar_url"
+                <*> o .: "login"
+                <*> o .: "url"
+                <*> o .: "id"
+                <*> o .: "gravatar_id"
+
+instance FromJSON Language where
+    parseJSON = withText "Language" (pure . Language)
+
+instance ToJSON Language where
+    toJSON = toJSON . getLanguage
+
+instance FromJSONKey Language where
+    fromJSONKey = fromJSONKeyCoerce
+
+data ArchiveFormat
+    = ArchiveFormatTarball -- ^ ".tar.gz" format
+    | ArchiveFormatZipball -- ^ ".zip" format
+    deriving (Show, Eq, Ord, Enum, Bounded, Data, Generic)
+
+instance IsPathPart ArchiveFormat where
+    toPathPart af = case af of
+        ArchiveFormatTarball -> "tarball"
+        ArchiveFormatZipball -> "zipball"
+
+instance FromJSON CollaboratorPermission where
+    parseJSON = withText "CollaboratorPermission" $ \t -> case T.toLower t of
+        "admin" -> pure CollaboratorPermissionAdmin
+        "write" -> pure CollaboratorPermissionWrite
+        "read"  -> pure CollaboratorPermissionRead
+        "none"  -> pure CollaboratorPermissionNone
+        _       -> fail $ "Unknown CollaboratorPermission: " <> T.unpack t
+
+instance ToJSON CollaboratorPermission where
+    toJSON CollaboratorPermissionAdmin = "admin"
+    toJSON CollaboratorPermissionWrite = "write"
+    toJSON CollaboratorPermissionRead  = "read"
+    toJSON CollaboratorPermissionNone  = "none"
+
+instance FromJSON CollaboratorWithPermission where
+    parseJSON = withObject "CollaboratorWithPermission" $ \o -> CollaboratorWithPermission
+        <$> o .: "user"
+        <*> o .: "permission"
diff --git a/src/GitHub/Data/Request.hs b/src/GitHub/Data/Request.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Request.hs
@@ -0,0 +1,238 @@
+{-# LANGUAGE FlexibleContexts   #-}
+{-# LANGUAGE GADTs              #-}
+{-# LANGUAGE KindSignatures     #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+module GitHub.Data.Request (
+    -- * Request
+    Request,
+    GenRequest (..),
+    -- * Smart constructors
+    query, pagedQuery, command,
+    -- * Auxiliary types
+    RW(..),
+    CommandMethod(..),
+    toMethod,
+    FetchCount(..),
+    PageParams(..),
+    PageLinks(..),
+    MediaType (..),
+    Paths,
+    IsPathPart(..),
+    QueryString,
+    Count,
+    ) where
+
+import GitHub.Data.Definitions (Count, IssueNumber, QueryString, unIssueNumber)
+import GitHub.Data.Id          (Id, untagId)
+import GitHub.Data.Name        (Name, untagName)
+import GitHub.Internal.Prelude
+
+import qualified Data.ByteString.Lazy      as LBS
+import qualified Data.Text                 as T
+import qualified Network.HTTP.Types.Method as Method
+import Network.URI (URI)
+
+------------------------------------------------------------------------------
+-- Path parts
+------------------------------------------------------------------------------
+
+type Paths = [Text]
+
+class IsPathPart a where
+    toPathPart :: a -> Text
+
+instance IsPathPart (Name a) where
+    toPathPart = untagName
+
+instance IsPathPart (Id a) where
+    toPathPart = T.pack . show . untagId
+
+instance IsPathPart IssueNumber where
+    toPathPart = T.pack . show . unIssueNumber
+
+-------------------------------------------------------------------------------
+-- Command Method
+-------------------------------------------------------------------------------
+
+-- | Http method of requests with body.
+data CommandMethod
+    = Post
+    | Patch
+    | Put
+    | Delete
+  deriving (Eq, Ord, Read, Show, Enum, Bounded, Data, Generic)
+
+instance Hashable CommandMethod
+
+toMethod :: CommandMethod -> Method.Method
+toMethod Post   = Method.methodPost
+toMethod Patch  = Method.methodPatch
+toMethod Put    = Method.methodPut
+toMethod Delete = Method.methodDelete
+
+-------------------------------------------------------------------------------
+-- Fetch count
+-------------------------------------------------------------------------------
+
+-- | 'PagedQuery' returns just some results, using this data we can specify how
+-- many pages we want to fetch.
+data FetchCount =
+    FetchAtLeast !Word
+    | FetchAll
+    | FetchPage PageParams
+    deriving (Eq, Ord, Read, Show, Generic)
+
+
+-- | This instance is there mostly for 'fromInteger'.
+instance Num FetchCount where
+    fromInteger = FetchAtLeast . fromInteger
+
+    FetchAtLeast a + FetchAtLeast b = FetchAtLeast (a * b)
+    _ + _                           = FetchAll
+
+    FetchAtLeast a * FetchAtLeast b = FetchAtLeast (a * b)
+    _ * _                           = FetchAll
+
+    abs    = error "abs @FetchCount: not implemented"
+    signum = error "signum @FetchCount: not implemented"
+    negate = error "negate @FetchCount: not implemented"
+
+instance Hashable FetchCount
+instance Binary FetchCount
+instance NFData FetchCount
+
+-------------------------------------------------------------------------------
+-- PageParams
+-------------------------------------------------------------------------------
+
+-- | Params for specifying the precise page and items per page.
+data PageParams = PageParams {
+    pageParamsPerPage :: Maybe Int
+    , pageParamsPage :: Maybe Int
+    }
+    deriving (Eq, Ord, Read, Show, Generic)
+
+instance Hashable PageParams
+instance Binary PageParams
+instance NFData PageParams
+
+-------------------------------------------------------------------------------
+-- PageLinks
+-------------------------------------------------------------------------------
+
+-- | 'PagedQuery' returns just some results, using this data we can specify how
+-- many pages we want to fetch.
+data PageLinks = PageLinks {
+    pageLinksPrev :: Maybe URI
+    , pageLinksNext :: Maybe URI
+    , pageLinksLast :: Maybe URI
+    , pageLinksFirst :: Maybe URI
+    }
+    deriving (Eq, Ord, Show, Generic)
+
+instance NFData PageLinks
+
+-------------------------------------------------------------------------------
+-- MediaType
+-------------------------------------------------------------------------------
+
+data MediaType a
+    = MtJSON       -- ^ @application/vnd.github.v3+json@
+    | MtRaw        -- ^ @application/vnd.github.v3.raw@ <https://developer.github.com/v3/media/#raw-1>
+    | MtDiff       -- ^ @application/vnd.github.v3.diff@ <https://developer.github.com/v3/media/#diff>
+    | MtPatch      -- ^ @application/vnd.github.v3.patch@ <https://developer.github.com/v3/media/#patch>
+    | MtSha        -- ^ @application/vnd.github.v3.sha@ <https://developer.github.com/v3/media/#sha>
+    | MtStar       -- ^ @application/vnd.github.v3.star+json@ <https://developer.github.com/v3/activity/starring/#alternative-response-with-star-creation-timestamps-1>
+    | MtRedirect   -- ^ <https://developer.github.com/v3/repos/contents/#get-archive-link>
+    | MtStatus     -- ^ Parse status
+    | MtUnit       -- ^ Always succeeds
+    | MtPreview  a -- ^ Some other (preview) type; this is an extension point.
+  deriving (Eq, Ord, Read, Show, Data, Generic)
+
+------------------------------------------------------------------------------
+-- RW
+------------------------------------------------------------------------------
+
+-- | Type used as with @DataKinds@ to tag whether requests need authentication
+-- or aren't read-only.
+data RW
+    = RO  -- ^ /Read-only/, doesn't necessarily requires authentication
+    | RA  -- ^ /Read authenticated/
+    | RW  -- ^ /Read-write/, requires authentication
+  deriving (Eq, Ord, Read, Show, Enum, Bounded, Data, Generic)
+
+{-
+data SRO (rw :: RW) where
+    ROO :: SRO 'RO
+    ROA :: SRO 'RA
+
+-- | This class is used to describe read-only (but pontentially
+class    IReadOnly (rw :: RW) where iro :: SRO rw
+instance IReadOnly 'RO        where iro = ROO
+instance IReadOnly 'RA        where iro = ROA
+-}
+
+-------------------------------------------------------------------------------
+-- GitHub Request
+-------------------------------------------------------------------------------
+
+-- | Github request data type.
+--
+-- * @rw@ describes whether authentication is required. It's required for non-@GET@ requests.
+-- * @mt@ describes the media type, i.e. how the response should be interpreted.
+-- * @a@ is the result type
+--
+-- /Note:/ 'Request' is not 'Functor' on purpose.
+data GenRequest (mt :: MediaType *) (rw :: RW) a where
+    Query        :: Paths -> QueryString -> GenRequest mt rw a
+    PagedQuery   :: (a ~ t b, Foldable t, Semigroup a) => Paths -> QueryString -> FetchCount -> GenRequest mt rw a
+
+    -- | Command
+    Command
+        :: CommandMethod           -- ^ command
+        -> Paths                   -- ^ path
+        -> LBS.ByteString          -- ^ body
+        -> GenRequest mt 'RW a
+
+-- | Most requests ask for @JSON@.
+type Request = GenRequest 'MtJSON
+
+-------------------------------------------------------------------------------
+-- Smart constructors
+-------------------------------------------------------------------------------
+
+query :: Paths -> QueryString -> Request mt a
+query ps qs = Query ps qs
+
+pagedQuery :: FromJSON a => Paths -> QueryString -> FetchCount -> Request mt (Vector a)
+pagedQuery ps qs fc = PagedQuery ps qs fc
+
+command :: CommandMethod -> Paths -> LBS.ByteString -> Request 'RW a
+command m ps body = Command m ps body
+
+-------------------------------------------------------------------------------
+-- Instances
+-------------------------------------------------------------------------------
+
+deriving instance Eq (GenRequest rw mt a)
+deriving instance Ord (GenRequest rw mt a)
+deriving instance Show (GenRequest rw mt a)
+
+instance Hashable (GenRequest rw mt a) where
+    hashWithSalt salt (Query ps qs) =
+        salt `hashWithSalt` (0 :: Int)
+             `hashWithSalt` ps
+             `hashWithSalt` qs
+    hashWithSalt salt (PagedQuery ps qs l) =
+        salt `hashWithSalt` (1 :: Int)
+             `hashWithSalt` ps
+             `hashWithSalt` qs
+             `hashWithSalt` l
+    hashWithSalt salt (Command m ps body) =
+        salt `hashWithSalt` (2 :: Int)
+             `hashWithSalt` m
+             `hashWithSalt` ps
+             `hashWithSalt` body
+
+-- TODO: Binary
diff --git a/src/GitHub/Data/Reviews.hs b/src/GitHub/Data/Reviews.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Reviews.hs
@@ -0,0 +1,93 @@
+module GitHub.Data.Reviews where
+
+import GitHub.Data.Definitions (SimpleUser)
+import GitHub.Data.Id (Id)
+import GitHub.Data.URL (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text as T
+
+data ReviewState
+    = ReviewStatePending
+    | ReviewStateApproved
+    | ReviewStateDismissed
+    | ReviewStateCommented
+    | ReviewStateChangesRequested
+    deriving (Show, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData ReviewState
+instance Binary ReviewState
+
+instance FromJSON ReviewState where
+    parseJSON = withText "ReviewState" $ \t -> case T.toLower t of
+        "approved"          -> pure ReviewStateApproved
+        "pending"           -> pure ReviewStatePending
+        "dismissed"         -> pure ReviewStateDismissed
+        "commented"         -> pure ReviewStateCommented
+        "changes_requested" -> pure ReviewStateChangesRequested
+        _                   -> fail $ "Unknown ReviewState: " <> T.unpack t
+
+data Review = Review
+    { reviewBody :: !Text
+    , reviewCommitId :: !Text
+    , reviewState :: ReviewState
+    , reviewSubmittedAt :: !(Maybe UTCTime)
+    , reviewPullRequestUrl :: !URL
+    , reviewHtmlUrl :: !Text
+    , reviewUser :: !SimpleUser
+    , reviewId :: !(Id Review)
+    } deriving (Show, Generic)
+
+instance NFData Review
+instance Binary Review
+
+instance FromJSON Review where
+    parseJSON =
+        withObject "Review" $ \o ->
+            Review <$> o .: "body" <*> o .: "commit_id" <*> o .: "state" <*>
+            o .:? "submitted_at" <*>
+            o .:  "pull_request_url" <*>
+            o .:  "html_url" <*>
+            o .:  "user" <*>
+            o .:  "id"
+
+data ReviewComment = ReviewComment
+    { reviewCommentId :: !(Id ReviewComment)
+    , reviewCommentUser :: !SimpleUser
+    , reviewCommentBody :: !Text
+    , reviewCommentUrl :: !URL
+    , reviewCommentPullRequestReviewId :: !(Id Review)
+    , reviewCommentDiffHunk :: !Text
+    , reviewCommentPath :: !Text
+    , reviewCommentPosition :: !Int
+    , reviewCommentOriginalPosition :: !Int
+    , reviewCommentCommitId :: !Text
+    , reviewCommentOriginalCommitId :: !Text
+    , reviewCommentCreatedAt :: !UTCTime
+    , reviewCommentUpdatedAt :: !UTCTime
+    , reviewCommentHtmlUrl :: !URL
+    , reviewCommentPullRequestUrl :: !URL
+    } deriving (Show, Generic)
+
+instance NFData ReviewComment
+instance Binary ReviewComment
+
+instance FromJSON ReviewComment where
+    parseJSON =
+        withObject "ReviewComment" $ \o -> ReviewComment
+            <$> o .: "id"
+            <*> o .: "user"
+            <*> o .: "body"
+            <*> o .: "url"
+            <*> o .: "pull_request_review_id"
+            <*> o .: "diff_hunk"
+            <*> o .: "path"
+            <*> o .: "position"
+            <*> o .: "original_position"
+            <*> o .: "commit_id"
+            <*> o .: "original_commit_id"
+            <*> o .: "created_at"
+            <*> o .: "updated_at"
+            <*> o .: "html_url"
+            <*> o .: "pull_request_url"
diff --git a/src/GitHub/Data/Search.hs b/src/GitHub/Data/Search.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Search.hs
@@ -0,0 +1,54 @@
+module GitHub.Data.Search where
+
+import GitHub.Data.Repos       (CodeSearchRepo)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Vector as V
+
+data SearchResult' entities = SearchResult
+    { searchResultTotalCount :: !Int
+    , searchResultResults    :: !entities
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+type SearchResult entity = SearchResult' (V.Vector entity)
+
+instance NFData entities => NFData (SearchResult' entities)
+instance Binary entities => Binary (SearchResult' entities)
+
+instance (Monoid entities, FromJSON entities) => FromJSON (SearchResult' entities) where
+    parseJSON = withObject "SearchResult" $ \o -> SearchResult
+        <$> o .: "total_count"
+        <*> o .:? "items" .!= mempty
+
+instance Semigroup res => Semigroup (SearchResult' res) where
+    (SearchResult count res) <> (SearchResult count' res') = SearchResult (max count count') (res <> res')
+
+instance Foldable SearchResult' where
+    foldMap f (SearchResult _count results) = f results
+
+data Code = Code
+    { codeName    :: !Text
+    , codePath    :: !Text
+    , codeSha     :: !Text
+    , codeUrl     :: !URL
+    , codeGitUrl  :: !URL
+    , codeHtmlUrl :: !URL
+    , codeRepo    :: !CodeSearchRepo
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Code
+instance Binary Code
+
+instance FromJSON Code where
+    parseJSON = withObject "Code" $ \o -> Code
+        <$> o .: "name"
+        <*> o .: "path"
+        <*> o .: "sha"
+        <*> o .: "url"
+        <*> o .: "git_url"
+        <*> o .: "html_url"
+        <*> o .: "repository"
diff --git a/src/GitHub/Data/Statuses.hs b/src/GitHub/Data/Statuses.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Statuses.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE NoImplicitPrelude  #-}
+
+module GitHub.Data.Statuses where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Name        (Name)
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import GitHub.Data.GitData (Commit)
+import GitHub.Data.Repos   (RepoRef)
+
+import qualified Data.Text as T
+
+data StatusState
+    = StatusPending
+    | StatusSuccess
+    | StatusError
+    | StatusFailure
+  deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData StatusState
+instance Binary StatusState
+
+instance FromJSON StatusState where
+    parseJSON = withText "StatusState" $ \t -> case T.toLower t of
+        "pending" -> pure StatusPending
+        "success" -> pure StatusSuccess
+        "error"   -> pure StatusError
+        "failure" -> pure StatusFailure
+        _         -> fail $ "Unknown StatusState: " <> T.unpack t
+
+instance ToJSON StatusState where
+  toJSON StatusPending = String "pending"
+  toJSON StatusSuccess = String "success"
+  toJSON StatusError   = String "error"
+  toJSON StatusFailure = String "failure"
+
+
+data Status = Status
+    { statusCreatedAt   :: !UTCTime
+    , statusUpdatedAt   :: !UTCTime
+    , statusState       :: !StatusState
+    , statusTargetUrl   :: !(Maybe URL)
+    , statusDescription :: !(Maybe Text)
+    , statusId          :: !(Id Status)
+    , statusUrl         :: !URL
+    , statusContext     :: !(Maybe Text)
+    , statusCreator     :: !(Maybe SimpleUser)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON Status where
+  parseJSON = withObject "Status" $ \o -> Status
+      <$> o .: "created_at"
+      <*> o .: "updated_at"
+      <*> o .: "state"
+      <*> o .:? "target_url"
+      <*> o .:? "description"
+      <*> o .: "id"
+      <*> o .: "url"
+      <*> o .:? "context"
+      <*> o .:? "creator"
+
+
+data NewStatus = NewStatus
+    { newStatusState       :: !StatusState
+    , newStatusTargetUrl   :: !(Maybe URL)
+    , newStatusDescription :: !(Maybe Text)
+    , newStatusContext     :: !(Maybe Text)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData NewStatus
+instance Binary NewStatus
+
+instance ToJSON NewStatus where
+    toJSON (NewStatus s t d c) = object $ filter notNull $
+        [ "state"       .= s
+        , "target_url"  .= t
+        , "description" .= d
+        , "context"     .= c
+        ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _)    = True
+
+
+data CombinedStatus = CombinedStatus
+    { combinedStatusState      :: !StatusState
+    , combinedStatusSha        :: !(Name Commit)
+    , combinedStatusTotalCount :: !Int
+    , combinedStatusStatuses   :: !(Vector Status)
+    , combinedStatusRepository :: !RepoRef
+    , combinedStatusCommitUrl  :: !URL
+    , combinedStatusUrl        :: !URL
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance FromJSON CombinedStatus where
+    parseJSON = withObject "CombinedStatus" $ \o -> CombinedStatus
+        <$> o .: "state"
+        <*> o .: "sha"
+        <*> o .: "total_count"
+        <*> o .: "statuses"
+        <*> o .: "repository"
+        <*> o .: "commit_url"
+        <*> o .: "url"
diff --git a/src/GitHub/Data/Teams.hs b/src/GitHub/Data/Teams.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Teams.hs
@@ -0,0 +1,254 @@
+{-# LANGUAGE NoImplicitPrelude  #-}
+
+module GitHub.Data.Teams where
+
+import GitHub.Data.Definitions
+import GitHub.Data.Id          (Id)
+import GitHub.Data.Name        (Name)
+import GitHub.Data.Repos       (Repo)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text as T
+
+data Privacy
+    = PrivacyClosed
+    | PrivacySecret
+    deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData Privacy
+instance Binary Privacy
+
+data Permission
+    = PermissionPull
+    | PermissionPush
+    | PermissionAdmin
+    deriving (Show, Data, Enum, Bounded, Eq, Ord, Generic)
+
+instance NFData Permission
+instance Binary Permission
+
+data AddTeamRepoPermission = AddTeamRepoPermission
+    { addTeamRepoPermission :: !Permission
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData AddTeamRepoPermission
+instance Binary AddTeamRepoPermission
+
+data SimpleTeam = SimpleTeam
+    { simpleTeamId              :: !(Id Team)
+    , simpleTeamUrl             :: !URL
+    , simpleTeamName            :: !Text  -- TODO (0.15.0): unify this and 'simpleTeamSlug' as in 'Team'.
+    , simpleTeamSlug            :: !(Name Team)
+    , simpleTeamDescription     :: !(Maybe Text)
+    , simpleTeamPrivacy         :: !Privacy
+    , simpleTeamPermission      :: !Permission
+    , simpleTeamMembersUrl      :: !URL
+    , simpleTeamRepositoriesUrl :: !URL
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData SimpleTeam
+instance Binary SimpleTeam
+
+data Team = Team
+    { teamId              :: !(Id Team)
+    , teamUrl             :: !URL
+    , teamName            :: !Text
+    , teamSlug            :: !(Name Team)
+    , teamDescription     :: !(Maybe Text)
+    , teamPrivacy         :: !Privacy
+    , teamPermission      :: !Permission
+    , teamMembersUrl      :: !URL
+    , teamRepositoriesUrl :: !URL
+    , teamMembersCount    :: !Int
+    , teamReposCount      :: !Int
+    , teamOrganization    :: !SimpleOrganization
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Team
+instance Binary Team
+
+data CreateTeam = CreateTeam
+    { createTeamName        :: !(Name Team)
+    , createTeamDescription :: !(Maybe Text)
+    , createTeamRepoNames   :: !(Vector (Name Repo))
+    , createTeamPrivacy     :: !Privacy
+    , createTeamPermission  :: !Permission
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData CreateTeam
+instance Binary CreateTeam
+
+data EditTeam = EditTeam
+    { editTeamName        :: !(Name Team)
+    , editTeamDescription :: !(Maybe Text)
+    , editTeamPrivacy     :: !(Maybe Privacy)
+    , editTeamPermission  :: !(Maybe Permission)
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData EditTeam
+instance Binary  EditTeam
+
+data Role
+    = RoleMaintainer
+    | RoleMember
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData Role
+instance Binary Role
+
+data ReqState
+    = StatePending
+    | StateActive
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData ReqState
+instance Binary ReqState
+
+data TeamMembership = TeamMembership
+    { teamMembershipUrl      :: !URL
+    , teamMembershipRole     :: !Role
+    , teamMembershipReqState :: !ReqState
+    }
+    deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData TeamMembership
+instance Binary TeamMembership
+
+data CreateTeamMembership = CreateTeamMembership {
+  createTeamMembershipRole :: !Role
+} deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData CreateTeamMembership
+instance Binary CreateTeamMembership
+
+-- JSON Instances
+
+instance FromJSON SimpleTeam where
+    parseJSON = withObject "SimpleTeam" $ \o -> SimpleTeam
+        <$> o .: "id"
+        <*> o .: "url"
+        <*> o .: "name"
+        <*> o .: "slug"
+        <*> o .:?"description" .!= Nothing
+        <*> o .: "privacy"
+        <*> o .: "permission"
+        <*> o .: "members_url"
+        <*> o .: "repositories_url"
+
+instance FromJSON Team where
+    parseJSON = withObject "Team" $ \o -> Team
+        <$> o .: "id"
+        <*> o .: "url"
+        <*> o .: "name"
+        <*> o .: "slug"
+        <*> o .:?"description" .!= Nothing
+        <*> o .: "privacy"
+        <*> o .: "permission"
+        <*> o .: "members_url"
+        <*> o .: "repositories_url"
+        <*> o .: "members_count"
+        <*> o .: "repos_count"
+        <*> o .: "organization"
+
+instance ToJSON CreateTeam where
+    toJSON (CreateTeam name desc repo_names privacy permission) =
+        object $ filter notNull
+            [ "name"        .= name
+            , "description" .= desc
+            , "repo_names"  .= repo_names
+            , "privacy"     .= privacy
+            , "permission"  .= permission
+            ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _) = True
+
+instance ToJSON EditTeam where
+    toJSON (EditTeam name desc privacy permission) =
+        object $ filter notNull
+            [ "name"        .= name
+            , "description" .= desc
+            , "privacy"     .= privacy
+            , "permission"  .= permission
+            ]
+      where
+        notNull (_, Null) = False
+        notNull (_, _) = True
+
+instance FromJSON TeamMembership where
+    parseJSON = withObject "TeamMembership" $ \o -> TeamMembership
+        <$> o .: "url"
+        <*> o .: "role"
+        <*> o .: "state"
+
+instance FromJSON CreateTeamMembership where
+    parseJSON = withObject "CreateTeamMembership" $ \o -> CreateTeamMembership
+        <$> o .: "role"
+
+instance ToJSON CreateTeamMembership where
+    toJSON (CreateTeamMembership { createTeamMembershipRole = role }) =
+        object [ "role" .= role ]
+
+instance FromJSON AddTeamRepoPermission where
+    parseJSON = withObject "AddTeamRepoPermission" $ \o -> AddTeamRepoPermission
+        <$> o .: "permission"
+
+instance ToJSON AddTeamRepoPermission where
+    toJSON (AddTeamRepoPermission { addTeamRepoPermission = permission}) =
+        object [ "permission" .= permission ]
+
+instance FromJSON Role where
+    parseJSON = withText "Role" $ \t -> case T.toLower t of
+        "maintainer" -> pure RoleMaintainer
+        "member"     -> pure RoleMember
+        _            -> fail $ "Unknown Role: " <> T.unpack t
+
+instance ToJSON Role where
+    toJSON RoleMaintainer = String "maintainer"
+    toJSON RoleMember     = String "member"
+
+instance FromJSON Permission where
+    parseJSON = withText "Permission" $ \t -> case T.toLower t of
+        "pull"  -> pure PermissionPull
+        "push"  -> pure PermissionPush
+        "admin" -> pure PermissionAdmin
+        _       -> fail $ "Unknown Permission: " <> T.unpack t
+
+instance ToJSON Permission where
+    toJSON PermissionPull  = "pull"
+    toJSON PermissionPush  = "push"
+    toJSON PermissionAdmin = "admin"
+
+instance FromJSON Privacy where
+    parseJSON = withText "Privacy" $ \t -> case T.toLower t of
+        "secret" -> pure PrivacySecret
+        "closed" -> pure PrivacyClosed
+        _        -> fail $ "Unknown Privacy: " <> T.unpack t
+
+instance ToJSON Privacy where
+    toJSON PrivacySecret = String "secret"
+    toJSON PrivacyClosed = String "closed"
+
+instance FromJSON ReqState where
+    parseJSON = withText "ReqState" $ \t -> case T.toLower t of
+        "active"  -> pure StateActive
+        "pending" -> pure StatePending
+        _         -> fail $ "Unknown ReqState: " <> T.unpack t
+
+instance ToJSON ReqState where
+    toJSON StateActive  = String "active"
+    toJSON StatePending = String "pending"
+
+-- | Filters members returned by their role in the team.
+data TeamMemberRole
+    = TeamMemberRoleAll         -- ^ all members of the team.
+    | TeamMemberRoleMaintainer  -- ^ team maintainers
+    | TeamMemberRoleMember      -- ^ normal members of the team.
+    deriving (Show, Eq, Ord, Enum, Bounded, Data, Generic)
diff --git a/src/GitHub/Data/URL.hs b/src/GitHub/Data/URL.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/URL.hs
@@ -0,0 +1,25 @@
+module GitHub.Data.URL (
+    URL(..),
+    getUrl,
+    ) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Data representing URLs in responses.
+--
+-- /N.B./ syntactical validity is not verified.
+newtype URL = URL Text
+    deriving (Eq, Ord, Show, Generic, Data)
+
+getUrl :: URL -> Text
+getUrl (URL url) = url
+
+instance NFData URL
+instance Binary URL
+
+instance ToJSON URL where
+    toJSON (URL url) = toJSON url
+
+instance FromJSON URL where
+    parseJSON = withText "URL" (pure . URL)
diff --git a/src/GitHub/Data/Webhooks.hs b/src/GitHub/Data/Webhooks.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Webhooks.hs
@@ -0,0 +1,310 @@
+module GitHub.Data.Webhooks where
+
+import GitHub.Data.Id          (Id)
+import GitHub.Data.URL         (URL)
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Map as M
+import qualified Data.Text as T
+
+data RepoWebhook = RepoWebhook
+    { repoWebhookUrl          :: !URL
+    , repoWebhookTestUrl      :: !URL
+    , repoWebhookId           :: !(Id RepoWebhook)
+    , repoWebhookName         :: !Text
+    , repoWebhookActive       :: !Bool
+    , repoWebhookEvents       :: !(Vector RepoWebhookEvent)
+    , repoWebhookConfig       :: !(M.Map Text Text)
+    , repoWebhookLastResponse :: !RepoWebhookResponse
+    , repoWebhookUpdatedAt    :: !UTCTime
+    , repoWebhookCreatedAt    :: !UTCTime
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoWebhook
+instance Binary RepoWebhook
+
+-- | See <https://developer.github.com/webhooks/#events>.
+data RepoWebhookEvent
+    = WebhookWildcardEvent
+    | WebhookCheckRunEvent
+    | WebhookCheckSuiteEvent
+    | WebhookCodeScanningAlert
+    | WebhookCommitCommentEvent
+    | WebhookContentReferenceEvent
+    | WebhookCreateEvent
+    | WebhookDeleteEvent
+    | WebhookDeployKeyEvent
+    | WebhookDeploymentEvent
+    | WebhookDeploymentStatusEvent
+    | WebhookDiscussion
+    | WebhookDiscussionComment
+    | WebhookDownloadEvent
+    | WebhookFollowEvent
+    | WebhookForkEvent
+    | WebhookGistEvent
+    | WebhookGitHubAppAuthorizationEvent
+    | WebhookGollumEvent
+    | WebhookInstallationEvent
+    | WebhookInstallationRepositoriesEvent
+    | WebhookIssueCommentEvent
+    | WebhookIssuesEvent
+    | WebhookLabelEvent
+    | WebhookMarketplacePurchaseEvent
+    | WebhookMemberEvent
+    | WebhookMembershipEvent
+    | WebhookMetaEvent
+    | WebhookMilestoneEvent
+    | WebhookOrgBlockEvent
+    | WebhookOrganizationEvent
+    | WebhookPackage
+    | WebhookPageBuildEvent
+    | WebhookPingEvent
+    | WebhookProjectCardEvent
+    | WebhookProjectColumnEvent
+    | WebhookProjectEvent
+    | WebhookPublicEvent
+    | WebhookPullRequestEvent
+    | WebhookPullRequestReviewCommentEvent
+    | WebhookPullRequestReviewEvent
+    | WebhookPushEvent
+    | WebhookRegistryPackageEvent
+    | WebhookReleaseEvent
+    | WebhookRepositoryDispatch
+    | WebhookRepositoryEvent
+    | WebhookRepositoryImportEvent
+    | WebhookRepositoryVulnerabilityAlertEvent
+    | WebhookSecretScanningAlert
+    | WebhookSecurityAdvisoryEvent
+    | WebhookSponsorship
+    | WebhookStarEvent
+    | WebhookStatusEvent
+    | WebhookTeamAddEvent
+    | WebhookTeamEvent
+    | WebhookWatchEvent
+    | WebhookWorkflowDispatch
+    | WebhookWorkflowRun
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoWebhookEvent
+instance Binary RepoWebhookEvent
+
+data RepoWebhookResponse = RepoWebhookResponse
+    { repoWebhookResponseCode    :: !(Maybe Int)
+    , repoWebhookResponseStatus  :: !(Maybe Text)
+    , repoWebhookResponseMessage :: !(Maybe Text)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData RepoWebhookResponse
+instance Binary RepoWebhookResponse
+
+data PingEvent = PingEvent
+    { pingEventZen    :: !Text
+    , pingEventHook   :: !RepoWebhook
+    , pingEventHookId :: !(Id RepoWebhook)
+    }
+  deriving (Show, Data, Eq, Ord, Generic)
+
+instance NFData PingEvent
+instance Binary PingEvent
+
+data NewRepoWebhook = NewRepoWebhook
+    { newRepoWebhookName   :: !Text
+    , newRepoWebhookConfig :: !(M.Map Text Text)
+    , newRepoWebhookEvents :: !(Maybe (Vector RepoWebhookEvent))
+    , newRepoWebhookActive :: !(Maybe Bool)
+    }
+  deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData NewRepoWebhook
+instance Binary NewRepoWebhook
+
+data EditRepoWebhook = EditRepoWebhook
+    { editRepoWebhookConfig       :: !(Maybe (M.Map Text Text))
+    , editRepoWebhookEvents       :: !(Maybe (Vector RepoWebhookEvent))
+    , editRepoWebhookAddEvents    :: !(Maybe (Vector RepoWebhookEvent))
+    , editRepoWebhookRemoveEvents :: !(Maybe (Vector RepoWebhookEvent))
+    , editRepoWebhookActive       :: !(Maybe Bool)
+    }
+  deriving (Eq, Ord, Show, Data, Generic)
+
+instance NFData EditRepoWebhook
+instance Binary EditRepoWebhook
+
+-- JSON instances
+
+instance FromJSON RepoWebhookEvent where
+    parseJSON = withText "RepoWebhookEvent" $ \t -> case T.toLower t of
+        "*"                              -> pure WebhookWildcardEvent
+        "check_run"                      -> pure WebhookCheckRunEvent
+        "check_suite"                    -> pure WebhookCheckSuiteEvent
+        "code_scanning_alert"            -> pure WebhookCodeScanningAlert
+        "commit_comment"                 -> pure WebhookCommitCommentEvent
+        "content_reference"              -> pure WebhookContentReferenceEvent
+        "create"                         -> pure WebhookCreateEvent
+        "delete"                         -> pure WebhookDeleteEvent
+        "deploy_key"                     -> pure WebhookDeployKeyEvent
+        "deployment"                     -> pure WebhookDeploymentEvent
+        "deployment_status"              -> pure WebhookDeploymentStatusEvent
+        "discussion"                     -> pure WebhookDiscussion
+        "discussion_comment"             -> pure WebhookDiscussionComment
+        "download"                       -> pure WebhookDownloadEvent
+        "follow"                         -> pure WebhookFollowEvent
+        "fork"                           -> pure WebhookForkEvent
+        "gist"                           -> pure WebhookGistEvent
+        "github_app_authorization"       -> pure WebhookGitHubAppAuthorizationEvent
+        "gollum"                         -> pure WebhookGollumEvent
+        "installation"                   -> pure WebhookInstallationEvent
+        "installation_repositories"      -> pure WebhookInstallationRepositoriesEvent
+        "issue_comment"                  -> pure WebhookIssueCommentEvent
+        "issues"                         -> pure WebhookIssuesEvent
+        "label"                          -> pure WebhookLabelEvent
+        "marketplace_purchase"           -> pure WebhookMarketplacePurchaseEvent
+        "member"                         -> pure WebhookMemberEvent
+        "membership"                     -> pure WebhookMembershipEvent
+        "meta"                           -> pure WebhookMetaEvent
+        "milestone"                      -> pure WebhookMilestoneEvent
+        "org_block"                      -> pure WebhookOrgBlockEvent
+        "organization"                   -> pure WebhookOrganizationEvent
+        "package"                        -> pure WebhookPackage
+        "page_build"                     -> pure WebhookPageBuildEvent
+        "ping"                           -> pure WebhookPingEvent
+        "project"                        -> pure WebhookProjectEvent
+        "project_card"                   -> pure WebhookProjectCardEvent
+        "project_column"                 -> pure WebhookProjectColumnEvent
+        "public"                         -> pure WebhookPublicEvent
+        "pull_request"                   -> pure WebhookPullRequestEvent
+        "pull_request_review"            -> pure WebhookPullRequestReviewEvent
+        "pull_request_review_comment"    -> pure WebhookPullRequestReviewCommentEvent
+        "push"                           -> pure WebhookPushEvent
+        "registry_package"               -> pure WebhookRegistryPackageEvent
+        "release"                        -> pure WebhookReleaseEvent
+        "repository"                     -> pure WebhookRepositoryEvent
+        "repository_dispatch"            -> pure WebhookRepositoryDispatch
+        "repository_import"              -> pure WebhookRepositoryImportEvent
+        "repository_vulnerability_alert" -> pure WebhookRepositoryVulnerabilityAlertEvent
+        "secret_scanning_alert"          -> pure WebhookSecretScanningAlert
+        "security_advisory"              -> pure WebhookSecurityAdvisoryEvent
+        "sponsorship"                    -> pure WebhookSponsorship
+        "star"                           -> pure WebhookStarEvent
+        "status"                         -> pure WebhookStatusEvent
+        "team"                           -> pure WebhookTeamEvent
+        "team_add"                       -> pure WebhookTeamAddEvent
+        "watch"                          -> pure WebhookWatchEvent
+        "workflow_dispatch"              -> pure WebhookWorkflowDispatch
+        "workflow_run"                   -> pure WebhookWorkflowRun
+        _                                -> fail $ "Unknown RepoWebhookEvent: " <> T.unpack t
+
+instance ToJSON RepoWebhookEvent where
+    toJSON WebhookWildcardEvent                     = String "*"
+    toJSON WebhookCheckRunEvent                     = String "check_run"
+    toJSON WebhookCheckSuiteEvent                   = String "check_suite"
+    toJSON WebhookCodeScanningAlert                 = String "code_scanning_alert"
+    toJSON WebhookCommitCommentEvent                = String "commit_comment"
+    toJSON WebhookContentReferenceEvent             = String "content_reference"
+    toJSON WebhookCreateEvent                       = String "create"
+    toJSON WebhookDeleteEvent                       = String "delete"
+    toJSON WebhookDeployKeyEvent                    = String "deploy_key"
+    toJSON WebhookDeploymentEvent                   = String "deployment"
+    toJSON WebhookDeploymentStatusEvent             = String "deployment_status"
+    toJSON WebhookDiscussion                        = String "discussion"
+    toJSON WebhookDiscussionComment                 = String "discussion_comment"
+    toJSON WebhookDownloadEvent                     = String "download"
+    toJSON WebhookFollowEvent                       = String "follow"
+    toJSON WebhookForkEvent                         = String "fork"
+    toJSON WebhookGistEvent                         = String "gist"
+    toJSON WebhookGitHubAppAuthorizationEvent       = String "github_app_authorization"
+    toJSON WebhookGollumEvent                       = String "gollum"
+    toJSON WebhookInstallationEvent                 = String "installation"
+    toJSON WebhookInstallationRepositoriesEvent     = String "installation_repositories"
+    toJSON WebhookIssueCommentEvent                 = String "issue_comment"
+    toJSON WebhookIssuesEvent                       = String "issues"
+    toJSON WebhookLabelEvent                        = String "label"
+    toJSON WebhookMarketplacePurchaseEvent          = String "marketplace_purchase"
+    toJSON WebhookMemberEvent                       = String "member"
+    toJSON WebhookMembershipEvent                   = String "membership"
+    toJSON WebhookMetaEvent                         = String "meta"
+    toJSON WebhookMilestoneEvent                    = String "milestone"
+    toJSON WebhookOrgBlockEvent                     = String "org_block"
+    toJSON WebhookOrganizationEvent                 = String "organization"
+    toJSON WebhookPackage                           = String "package"
+    toJSON WebhookPageBuildEvent                    = String "page_build"
+    toJSON WebhookPingEvent                         = String "ping"
+    toJSON WebhookProjectCardEvent                  = String "project_card"
+    toJSON WebhookProjectColumnEvent                = String "project_column"
+    toJSON WebhookProjectEvent                      = String "project"
+    toJSON WebhookPublicEvent                       = String "public"
+    toJSON WebhookPullRequestEvent                  = String "pull_request"
+    toJSON WebhookPullRequestReviewCommentEvent     = String "pull_request_review_comment"
+    toJSON WebhookPullRequestReviewEvent            = String "pull_request_review"
+    toJSON WebhookPushEvent                         = String "push"
+    toJSON WebhookRegistryPackageEvent              = String "registry_package"
+    toJSON WebhookReleaseEvent                      = String "release"
+    toJSON WebhookRepositoryDispatch                = String "repository_dispatch"
+    toJSON WebhookRepositoryEvent                   = String "repository"
+    toJSON WebhookRepositoryImportEvent             = String "repository_import"
+    toJSON WebhookRepositoryVulnerabilityAlertEvent = String "repository_vulnerability_alert"
+    toJSON WebhookSecretScanningAlert               = String "secret_scanning_alert"
+    toJSON WebhookSecurityAdvisoryEvent             = String "security_advisory"
+    toJSON WebhookSponsorship                       = String "sponsorship"
+    toJSON WebhookStarEvent                         = String "star"
+    toJSON WebhookStatusEvent                       = String "status"
+    toJSON WebhookTeamAddEvent                      = String "team_add"
+    toJSON WebhookTeamEvent                         = String "team"
+    toJSON WebhookWatchEvent                        = String "watch"
+    toJSON WebhookWorkflowDispatch                  = String "workflow_dispatch"
+    toJSON WebhookWorkflowRun                       = String "workflow_run"
+
+instance FromJSON RepoWebhook where
+    parseJSON = withObject "RepoWebhook" $ \o -> RepoWebhook
+        <$> o .: "url"
+        <*> o .: "test_url"
+        <*> o .: "id"
+        <*> o .: "name"
+        <*> o .: "active"
+        <*> o .: "events"
+        <*> o .: "config"
+        <*> o .: "last_response"
+        <*> o .: "updated_at"
+        <*> o .: "created_at"
+
+instance FromJSON RepoWebhookResponse where
+    parseJSON = withObject "RepoWebhookResponse" $ \o -> RepoWebhookResponse
+        <$> o .: "code"
+        <*> o .:? "status"
+        <*> o .:? "message"
+
+instance ToJSON NewRepoWebhook where
+  toJSON (NewRepoWebhook { newRepoWebhookName = name
+                         , newRepoWebhookConfig = config
+                         , newRepoWebhookEvents = events
+                         , newRepoWebhookActive = active
+
+             }) = object
+             [ "name" .= name
+             , "config" .= config
+             , "events" .= events
+             , "active" .= active
+             ]
+
+instance ToJSON EditRepoWebhook where
+  toJSON (EditRepoWebhook { editRepoWebhookConfig = config
+                          , editRepoWebhookEvents = events
+                          , editRepoWebhookAddEvents = addEvents
+                          , editRepoWebhookRemoveEvents = removeEvents
+                          , editRepoWebhookActive = active
+             }) = object
+             [ "config" .= config
+             , "events" .= events
+             , "add_events" .= addEvents
+             , "remove_events" .= removeEvents
+             , "active" .= active
+             ]
+
+instance FromJSON PingEvent where
+    parseJSON = withObject "PingEvent" $ \o -> PingEvent
+        <$> o .: "zen"
+        <*> o .: "hook"
+        <*> o .: "hook_id"
diff --git a/src/GitHub/Data/Webhooks/Validate.hs b/src/GitHub/Data/Webhooks/Validate.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Data/Webhooks/Validate.hs
@@ -0,0 +1,34 @@
+-- |
+-- Verification of incomming webhook payloads, as described at
+-- <https://developer.github.com/webhooks/securing/>
+
+module GitHub.Data.Webhooks.Validate (
+  isValidPayload
+) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Crypto.Hash.SHA1 (hmac)
+import Data.ByteString  (ByteString)
+
+import qualified Data.ByteString.Base16 as Hex
+import qualified Data.Text.Encoding     as TE
+
+-- | Validates a given payload against a given HMAC hexdigest using a given
+-- secret.
+-- Returns 'True' iff the given hash is non-empty and it's a valid signature of
+-- the payload.
+isValidPayload
+  :: Text           -- ^ the secret
+  -> Maybe Text     -- ^ the hash provided by the remote party
+                    -- in @X-Hub-Signature@ (if any),
+                    -- including the 'sha1=...' prefix
+  -> ByteString     -- ^ the body
+  -> Bool
+isValidPayload secret shaOpt payload = maybe False (sign ==) shaOptBS
+  where
+    shaOptBS = TE.encodeUtf8 <$> shaOpt
+    hexDigest = Hex.encode
+    hm = hmac (TE.encodeUtf8 secret) payload
+    sign = "sha1=" <> hexDigest hm
diff --git a/src/GitHub/Endpoints/Actions/Artifacts.hs b/src/GitHub/Endpoints/Actions/Artifacts.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Actions/Artifacts.hs
@@ -0,0 +1,61 @@
+-- |
+-- The actions API as documented at
+-- <https://docs.github.com/en/rest/reference/actions>.
+
+module GitHub.Endpoints.Actions.Artifacts (
+    artifactsForR,
+    artifactR,
+    deleteArtifactR,
+    downloadArtifactR,
+    artifactsForWorkflowRunR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Network.URI             (URI)
+import Prelude ()
+
+-- | List artifacts for repository.
+-- See <https://docs.github.com/en/rest/reference/actions#list-artifacts-for-a-repository>
+artifactsForR
+    :: Name Owner
+    -> Name Repo
+    -> ArtifactMod
+    -> FetchCount
+    -> Request 'RA (WithTotalCount Artifact)
+artifactsForR user repo opts = PagedQuery
+    ["repos", toPathPart user, toPathPart repo, "actions", "artifacts"]
+    (artifactModToQueryString opts)
+
+-- | Get an artifact.
+-- See <https://docs.github.com/en/rest/reference/actions#get-an-artifact>
+artifactR :: Name Owner -> Name Repo -> Id Artifact -> Request 'RA Artifact
+artifactR user repo artid =
+    query ["repos", toPathPart user, toPathPart repo, "actions", "artifacts", toPathPart artid] []
+
+-- | Delete an artifact.
+-- See <https://docs.github.com/en/rest/reference/actions#delete-an-artifact>
+deleteArtifactR :: Name Owner -> Name Repo -> Id Comment -> GenRequest 'MtUnit 'RW ()
+deleteArtifactR user repo artid =
+    Command Delete parts mempty
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "actions", "artifacts", toPathPart artid]
+
+-- | Download an artifact.
+-- See <https://docs.github.com/en/rest/reference/actions#download-an-artifact>
+downloadArtifactR :: Name Owner -> Name Repo -> Id Artifact -> GenRequest 'MtRedirect 'RW URI
+downloadArtifactR user repo artid =
+    Query ["repos", toPathPart user, toPathPart repo, "actions", "artifacts", toPathPart artid, "zip"] []
+
+-- | List artifacts for a workflow run.
+-- See <https://docs.github.com/en/rest/reference/actions#list-workflow-run-artifacts>
+artifactsForWorkflowRunR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> FetchCount
+    -> Request 'RA (WithTotalCount Artifact)
+artifactsForWorkflowRunR user repo runid  = PagedQuery
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart runid, "artifacts"]
+    []
diff --git a/src/GitHub/Endpoints/Actions/Cache.hs b/src/GitHub/Endpoints/Actions/Cache.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Actions/Cache.hs
@@ -0,0 +1,66 @@
+-- |
+-- The actions API as documented at
+-- <https://docs.github.com/en/rest/reference/actions>.
+
+module GitHub.Endpoints.Actions.Cache (
+    cacheUsageOrganizationR,
+    cacheUsageByRepositoryR,
+    cacheUsageR,
+    cachesForRepoR,
+    deleteCacheR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Get Actions cache usage for the organization.
+-- See <https://docs.github.com/en/rest/actions/cache#get-github-actions-cache-usage-for-an-organization>
+cacheUsageOrganizationR
+    :: Name Organization
+    -> GenRequest 'MtJSON 'RA OrganizationCacheUsage
+cacheUsageOrganizationR org =
+    Query ["orgs", toPathPart org, "actions", "cache", "usage"] []
+
+-- | List repositories with GitHub Actions cache usage for an organization.
+-- See <https://docs.github.com/en/rest/actions/cache#list-repositories-with-github-actions-cache-usage-for-an-organization>
+cacheUsageByRepositoryR
+    :: Name Organization
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount RepositoryCacheUsage)
+cacheUsageByRepositoryR org =
+    PagedQuery ["orgs", toPathPart org, "actions", "cache", "usage-by-repository"] []
+
+-- | Get GitHub Actions cache usage for a repository.
+-- See <https://docs.github.com/en/rest/actions/cache#get-github-actions-cache-usage-for-a-repository>
+cacheUsageR
+    :: Name Owner
+    -> Name Repo
+    -> Request k RepositoryCacheUsage
+cacheUsageR user repo =
+    Query ["repos", toPathPart user, toPathPart repo, "actions", "cache", "usage"] []
+
+-- | List the GitHub Actions caches for a repository.
+-- See <https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository>
+cachesForRepoR
+    :: Name Owner
+    -> Name Repo
+    -> CacheMod
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount Cache)
+cachesForRepoR user repo opts = PagedQuery
+    ["repos", toPathPart user, toPathPart repo, "actions", "caches"]
+    (cacheModToQueryString opts)
+
+-- | Delete GitHub Actions cache for a repository.
+-- See <https://docs.github.com/en/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id>
+deleteCacheR
+    :: Name Owner
+    -> Name Repo
+    -> Id Cache
+    -> GenRequest 'MtUnit 'RW ()
+deleteCacheR user repo cacheid =
+    Command Delete parts mempty
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "actions", "caches", toPathPart cacheid]
diff --git a/src/GitHub/Endpoints/Actions/Secrets.hs b/src/GitHub/Endpoints/Actions/Secrets.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Actions/Secrets.hs
@@ -0,0 +1,221 @@
+-- |
+-- The actions API as documented at
+-- <https://docs.github.com/en/rest/reference/actions>.
+
+module GitHub.Endpoints.Actions.Secrets (
+    organizationSecretsR,
+    organizationPublicKeyR,
+    organizationSecretR,
+    setOrganizationSecretR,
+    deleteOrganizationSecretR,
+    organizationSelectedRepositoriesForSecretR,
+    setOrganizationSelectedRepositoriesForSecretR,
+    addOrganizationSelectedRepositoriesForSecretR,
+    removeOrganizationSelectedRepositoriesForSecretR,
+    repoSecretsR,
+    repoPublicKeyR,
+    repoSecretR,
+    setRepoSecretR,
+    deleteRepoSecretR,
+    environmentSecretsR,
+    environmentPublicKeyR,
+    environmentSecretR,
+    setEnvironmentSecretR,
+    deleteEnvironmentSecretR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List organization secrets.
+-- See <https://docs.github.com/en/rest/actions/secrets#list-organization-secrets>
+organizationSecretsR
+    :: Name Organization
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount OrganizationSecret)
+organizationSecretsR org =
+    PagedQuery ["orgs", toPathPart org, "actions", "secrets"] []
+
+-- | List organization secrets.
+-- See <https://docs.github.com/en/rest/actions/secrets#get-an-organization-public-key>
+organizationPublicKeyR
+    :: Name Organization
+    -> GenRequest 'MtJSON 'RA PublicKey
+organizationPublicKeyR org =
+    Query ["orgs", toPathPart org, "actions", "secrets", "public-key"] []
+
+-- | Get an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#get-an-organization-secret>
+organizationSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> GenRequest 'MtJSON 'RA OrganizationSecret
+organizationSecretR org name =
+    Query ["orgs", toPathPart org, "actions", "secrets", toPathPart name] []
+
+-- | Create or update an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#create-or-update-an-organization-secret>
+setOrganizationSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> SetSecret
+    -> GenRequest 'MtUnit 'RW ()
+setOrganizationSecretR org name =
+    Command Put ["orgs", toPathPart org, "actions", "secrets", toPathPart name] . encode
+
+-- | Delete an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#delete-an-organization-secret>
+deleteOrganizationSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> GenRequest 'MtUnit 'RW ()
+deleteOrganizationSecretR org name =
+    Command Delete parts mempty
+    where
+        parts = ["orgs", toPathPart org, "actions", "secrets", toPathPart name]
+
+-- | Get selected repositories for an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#list-selected-repositories-for-an-organization-secret>
+organizationSelectedRepositoriesForSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount SelectedRepo)
+organizationSelectedRepositoriesForSecretR org name =
+    PagedQuery ["orgs", toPathPart org, "actions", "secrets", toPathPart name, "repositories"] []
+
+-- | Set selected repositories for an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#set-selected-repositories-for-an-organization-secret>
+setOrganizationSelectedRepositoriesForSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> SetSelectedRepositories
+    -> GenRequest 'MtUnit 'RW ()
+setOrganizationSelectedRepositoriesForSecretR org name =
+    Command Put ["orgs", toPathPart org, "actions", "secrets", toPathPart name, "repositories"]  . encode
+
+-- | Add selected repository to an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#add-selected-repository-to-an-organization-secret>
+addOrganizationSelectedRepositoriesForSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> Id Repo
+    -> GenRequest 'MtUnit 'RW ()
+addOrganizationSelectedRepositoriesForSecretR org name repo =
+    Command Put ["orgs", toPathPart org, "actions", "secrets", toPathPart name, "repositories", toPathPart repo] mempty
+
+-- | Remove selected repository from an organization secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#remove-selected-repository-from-an-organization-secret>
+removeOrganizationSelectedRepositoriesForSecretR
+    :: Name Organization
+    -> Name OrganizationSecret
+    -> Id Repo
+    -> GenRequest 'MtUnit 'RW ()
+removeOrganizationSelectedRepositoriesForSecretR org name repo =
+    Command Delete ["orgs", toPathPart org, "actions", "secrets", toPathPart name, "repositories", toPathPart repo] mempty
+
+-- | List repository secrets.
+-- See <https://docs.github.com/en/rest/actions/secrets#list-repository-secrets>
+repoSecretsR
+    :: Name Owner
+    -> Name Repo
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount RepoSecret)
+repoSecretsR user repo =
+    PagedQuery ["repos", toPathPart user,  toPathPart repo, "actions", "secrets"] []
+
+-- | Get a repository public key.
+-- See <https://docs.github.com/en/rest/actions/secrets#get-a-repository-public-key>
+repoPublicKeyR
+    :: Name Owner
+    -> Name Organization
+    -> GenRequest 'MtJSON 'RA PublicKey
+repoPublicKeyR user org =
+    Query ["repos", toPathPart user, toPathPart org, "actions", "secrets", "public-key"] []
+
+-- | Get a repository secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#get-a-repository-secret>
+repoSecretR
+    :: Name Owner
+    -> Name Organization
+    -> Name RepoSecret
+    -> GenRequest 'MtJSON 'RA RepoSecret
+repoSecretR user org name =
+    Query ["repos", toPathPart user, toPathPart org, "actions", "secrets", toPathPart name] []
+
+-- | Create or update a repository secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#create-or-update-a-repository-secret>
+setRepoSecretR
+    :: Name Owner
+    -> Name Organization
+    -> Name RepoSecret
+    -> SetRepoSecret
+    -> GenRequest 'MtUnit 'RW ()
+setRepoSecretR user org name =
+    Command Put ["repos", toPathPart user, toPathPart org, "actions", "secrets", toPathPart name] . encode
+
+-- | Delete a repository secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#delete-a-repository-secret>
+deleteRepoSecretR
+    :: Name Owner
+    -> Name Organization
+    -> Name RepoSecret
+    -> GenRequest 'MtUnit 'RW ()
+deleteRepoSecretR user org name =
+    Command Delete parts mempty
+    where
+        parts = ["repos", toPathPart user, toPathPart org, "actions", "secrets", toPathPart name]
+
+-- | List environment secrets.
+-- See <https://docs.github.com/en/rest/actions/secrets#list-environment-secrets>
+environmentSecretsR
+    :: Id Repo
+    -> Name Environment
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount RepoSecret)
+environmentSecretsR repo env =
+    PagedQuery ["repositories", toPathPart repo, "environments", toPathPart env, "secrets"] []
+
+-- | Get an environment public key.
+-- See <https://docs.github.com/en/rest/actions/secrets#get-an-environment-public-key>
+environmentPublicKeyR
+    :: Id Repo
+    -> Name Environment
+    -> GenRequest 'MtJSON 'RA PublicKey
+environmentPublicKeyR repo env =
+    Query ["repositories", toPathPart repo, "environments", toPathPart env, "secrets", "public-key"] []
+
+-- | Get an environment secret
+-- See <https://docs.github.com/en/rest/actions/secrets#get-an-environment-secret>
+environmentSecretR
+    :: Id Repo
+    -> Name Environment
+    -> Name RepoSecret
+    -> GenRequest 'MtJSON 'RA RepoSecret
+environmentSecretR repo env name =
+    Query ["repositories", toPathPart repo, "environments", toPathPart env, "secrets", toPathPart name] []
+
+-- | Create or update an environment secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#create-or-update-an-environment-secret>
+setEnvironmentSecretR
+    :: Id Repo
+    -> Name Environment
+    -> Name RepoSecret
+    -> SetRepoSecret
+    -> GenRequest 'MtUnit 'RW ()
+setEnvironmentSecretR repo env name =
+    Command Put ["repositories", toPathPart repo, "environments", toPathPart env, "secrets", toPathPart name] . encode
+
+-- | Delete an environment secret.
+-- See <https://docs.github.com/en/rest/actions/secrets#delete-an-environment-secret>
+deleteEnvironmentSecretR
+    :: Id Repo
+    -> Name Environment
+    -> Name RepoSecret
+    -> GenRequest 'MtUnit 'RW ()
+deleteEnvironmentSecretR repo env name =
+    Command Delete parts mempty
+    where
+        parts = ["repositories", toPathPart repo, "environments", toPathPart env, "secrets", toPathPart name]
diff --git a/src/GitHub/Endpoints/Actions/WorkflowJobs.hs b/src/GitHub/Endpoints/Actions/WorkflowJobs.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Actions/WorkflowJobs.hs
@@ -0,0 +1,58 @@
+-- |
+-- The actions API as documented at
+-- <https://docs.github.com/en/rest/reference/actions>.
+
+module GitHub.Endpoints.Actions.WorkflowJobs (
+    jobR,
+    downloadJobLogsR,
+    jobsForWorkflowRunAttemptR,
+    jobsForWorkflowRunR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import Network.URI (URI)
+import Prelude ()
+
+-- | Get a job for a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-jobs#get-a-job-for-a-workflow-run>
+jobR
+    :: Name Owner
+    -> Name Repo
+    -> Id Job
+    -> Request 'RA Job
+jobR owner repo job =
+    Query ["repos", toPathPart owner, toPathPart repo, "actions", "jobs", toPathPart job] []
+
+-- | Download job logs for a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-jobs#download-job-logs-for-a-workflow-run>
+downloadJobLogsR
+    :: Name Owner
+    -> Name Repo
+    -> Id Job
+    -> GenRequest 'MtRedirect 'RO URI
+downloadJobLogsR owner repo job =
+    Query ["repos", toPathPart owner, toPathPart repo, "actions", "jobs", toPathPart job, "logs"] []
+
+-- | List jobs for a workflow run attempt.
+-- See <https://docs.github.com/en/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run-attempt>
+jobsForWorkflowRunAttemptR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> Id RunAttempt
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount Job)
+jobsForWorkflowRunAttemptR owner repo run attempt =
+    PagedQuery ["repos", toPathPart owner, toPathPart repo, "actions", "runs", toPathPart run, "attempts", toPathPart attempt, "jobs"] []
+
+-- | List jobs for a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run>
+jobsForWorkflowRunR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount Job)
+jobsForWorkflowRunR owner repo run =
+    PagedQuery ["repos", toPathPart owner, toPathPart repo, "actions", "runs", toPathPart run, "jobs"] []
diff --git a/src/GitHub/Endpoints/Actions/WorkflowRuns.hs b/src/GitHub/Endpoints/Actions/WorkflowRuns.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Actions/WorkflowRuns.hs
@@ -0,0 +1,181 @@
+module GitHub.Endpoints.Actions.WorkflowRuns (
+    reRunJobR,
+    workflowRunsR,
+    workflowRunR,
+    deleteWorkflowRunR,
+    workflowRunReviewHistoryR,
+    approveWorkflowRunR,
+    workflowRunAttemptR,
+    downloadWorkflowRunAttemptLogsR,
+    cancelWorkflowRunR,
+    downloadWorkflowRunLogsR,
+    deleteWorkflowRunLogsR,
+    reRunWorkflowR,
+    reRunFailedJobsR,
+    workflowRunsForWorkflowR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Network.URI             (URI)
+import Prelude ()
+
+-- | Re-run a job from a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#re-run-a-job-from-a-workflow-run>
+reRunJobR
+    :: Name Owner
+    -> Name Repo
+    -> Id Job
+    -> GenRequest 'MtUnit 'RW ()
+reRunJobR user repo job = Command Post
+    ["repos", toPathPart user, toPathPart repo, "actions", "jobs", toPathPart job, "rerun"]
+    mempty
+
+-- | List workflow runs for a repository.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository>
+workflowRunsR
+    :: Name Owner
+    -> Name Repo
+    -> WorkflowRunMod
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount WorkflowRun)
+workflowRunsR user repo runMod = PagedQuery
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs"]
+    (workflowRunModToQueryString runMod)
+
+-- | Get a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run>
+workflowRunR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtJSON 'RA WorkflowRun
+workflowRunR user repo run = Query
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run]
+    []
+
+-- | Delete a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#delete-a-workflow-run>
+deleteWorkflowRunR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtUnit 'RW ()
+deleteWorkflowRunR user repo run = Command Delete
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run]
+    mempty
+
+-- | Get the review history for a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#get-the-review-history-for-a-workflow-run>
+workflowRunReviewHistoryR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtJSON 'RA (Vector ReviewHistory)
+workflowRunReviewHistoryR user repo run = Query
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "approvals"]
+    []
+
+-- | Approve a workflow run for a fork pull request.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#approve-a-workflow-run-for-a-fork-pull-request>
+approveWorkflowRunR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtUnit 'RW ()
+approveWorkflowRunR user repo run = Command Post
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "approve"]
+    mempty
+
+-- | Get a workflow run attempt.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run-attempt>
+workflowRunAttemptR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> Id RunAttempt
+    -> GenRequest 'MtJSON 'RA WorkflowRun
+workflowRunAttemptR user repo run attempt = Query
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "attempts", toPathPart attempt]
+    []
+
+-- | Download workflow run attempt logs.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#download-workflow-run-attempt-logs>
+downloadWorkflowRunAttemptLogsR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> Id RunAttempt
+    -> GenRequest 'MtRedirect 'RO URI
+downloadWorkflowRunAttemptLogsR user repo run attempt = Query
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "attempts", toPathPart attempt, "logs"]
+    []
+
+-- | Cancel a workflow run.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#cancel-a-workflow-run>
+cancelWorkflowRunR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtUnit 'RW ()
+cancelWorkflowRunR user repo run = Command Post
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "cancel"]
+    mempty
+
+-- | Download workflow run logs.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#download-workflow-run-logs>
+downloadWorkflowRunLogsR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtRedirect 'RA URI
+downloadWorkflowRunLogsR user repo run = Query
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "logs"]
+    []
+
+-- | Delete workflow run logs.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#delete-workflow-run-logs>
+deleteWorkflowRunLogsR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    ->  GenRequest 'MtUnit 'RW ()
+deleteWorkflowRunLogsR user repo run = Command Delete
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "logs"]
+    mempty
+
+-- | Re-run a workflow.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#re-run-a-workflow>
+reRunWorkflowR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtUnit 'RW ()
+reRunWorkflowR user repo run = Command Post
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "rerun"]
+    mempty
+
+-- | Re-run failed jobs from a workflow run.
+-- See <https://docs.github.com/en/rest/actions/re-run-failed-jobs-from-a-workflow-run>
+reRunFailedJobsR
+    :: Name Owner
+    -> Name Repo
+    -> Id WorkflowRun
+    -> GenRequest 'MtUnit 'RW ()
+reRunFailedJobsR user repo run = Command Post
+    ["repos", toPathPart user, toPathPart repo, "actions", "runs", toPathPart run, "rerun-failed-jobs"]
+    mempty
+
+-- | List workflow runs for a workflow.
+-- See <https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-workflow>
+workflowRunsForWorkflowR
+    :: (IsPathPart idOrName) => Name Owner
+    -> Name Repo
+    -> idOrName
+    -> WorkflowRunMod
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount WorkflowRun)
+workflowRunsForWorkflowR user repo idOrName runMod = PagedQuery
+    ["repos", toPathPart user, toPathPart repo, "actions", "workflows", toPathPart idOrName, "runs"]
+     (workflowRunModToQueryString runMod)
diff --git a/src/GitHub/Endpoints/Actions/Workflows.hs b/src/GitHub/Endpoints/Actions/Workflows.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Actions/Workflows.hs
@@ -0,0 +1,68 @@
+module GitHub.Endpoints.Actions.Workflows (
+    repositoryWorkflowsR,
+    workflowR,
+    disableWorkflowR,
+    triggerWorkflowR,
+    enableWorkflowR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List repository workflows.
+-- See <https://docs.github.com/en/rest/actions/workflows#list-repository-workflows>
+repositoryWorkflowsR
+    :: Name Owner
+    -> Name Repo
+    -> FetchCount
+    -> GenRequest 'MtJSON 'RA (WithTotalCount Workflow)
+repositoryWorkflowsR user repo  = PagedQuery
+    ["repos", toPathPart user, toPathPart repo, "actions", "workflows"]
+    []
+
+-- | Get a workflow.
+-- See <https://docs.github.com/en/rest/actions/workflows#get-a-workflow>
+workflowR
+    :: (IsPathPart idOrName) => Name Owner
+    -> Name Repo
+    -> idOrName
+    -> GenRequest 'MtJSON 'RA  Workflow
+workflowR user repo idOrName  = Query
+    ["repos", toPathPart user, toPathPart repo, "actions", "workflows", toPathPart idOrName]
+    []
+
+-- | Disable a workflow.
+-- See <https://docs.github.com/en/rest/actions/workflows#disable-a-workflow>
+disableWorkflowR
+    :: (IsPathPart idOrName) => Name Owner
+    -> Name Repo
+    -> idOrName
+    -> GenRequest 'MtUnit 'RW  ()
+disableWorkflowR user repo idOrName  = Command Put
+    ["repos", toPathPart user, toPathPart repo, "actions", "workflows", toPathPart idOrName, "disable"]
+    mempty
+
+-- | Create a workflow dispatch event.
+-- See <https://docs.github.com/en/rest/actions/workflows#create-a-workflow-dispatch-event>
+triggerWorkflowR
+    :: (ToJSON a, IsPathPart idOrName) => Name Owner
+    -> Name Repo
+    -> idOrName
+    -> CreateWorkflowDispatchEvent a
+    -> GenRequest 'MtUnit 'RW  ()
+triggerWorkflowR user repo idOrName  = Command Post
+    ["repos", toPathPart user, toPathPart repo, "actions", "workflows", toPathPart idOrName, "dispatches"]
+    . encode
+
+-- | Enable a workflow.
+-- See <https://docs.github.com/en/rest/actions/workflows#enable-a-workflow>
+enableWorkflowR
+    :: (IsPathPart idOrName) => Name Owner
+    -> Name Repo
+    -> idOrName
+    -> GenRequest 'MtUnit 'RW  ()
+enableWorkflowR user repo idOrName  = Command Put
+    ["repos", toPathPart user, toPathPart repo, "actions", "workflows", toPathPart idOrName, "enable"]
+    mempty
diff --git a/src/GitHub/Endpoints/Activity/Events.hs b/src/GitHub/Endpoints/Activity/Events.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Activity/Events.hs
@@ -0,0 +1,25 @@
+-- |
+-- The events API as described on <https://developer.github.com/v3/activity/events/>.
+
+module GitHub.Endpoints.Activity.Events (
+    -- * Events
+    repositoryEventsR,
+    userEventsR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List repository events.
+-- See <https://developer.github.com/v3/activity/events/#list-repository-events>
+repositoryEventsR :: Name Owner -> Name Repo -> FetchCount -> Request 'RO (Vector Event)
+repositoryEventsR user repo =
+    pagedQuery  ["repos", toPathPart user, toPathPart repo, "events"] []
+
+-- | List user public events.
+-- See <https://developer.github.com/v3/activity/events/#list-public-events-performed-by-a-user>
+userEventsR :: Name User -> FetchCount -> Request 'RO (Vector Event)
+userEventsR user =
+    pagedQuery ["users", toPathPart user, "events", "public"] []
diff --git a/src/GitHub/Endpoints/Activity/Notifications.hs b/src/GitHub/Endpoints/Activity/Notifications.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Activity/Notifications.hs
@@ -0,0 +1,32 @@
+-- |
+-- The repo watching API as described on
+-- <https://developer.github.com/v3/activity/notifications/>.
+
+module GitHub.Endpoints.Activity.Notifications (
+    getNotificationsR,
+    markNotificationAsReadR,
+    markAllNotificationsAsReadR,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List your notifications.
+-- See <https://developer.github.com/v3/activity/notifications/#list-your-notifications>
+getNotificationsR :: FetchCount -> Request 'RA (Vector Notification)
+getNotificationsR = pagedQuery ["notifications"] []
+
+-- | Mark a thread as read.
+-- See <https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read>
+markNotificationAsReadR :: Id Notification -> GenRequest 'MtUnit 'RW ()
+markNotificationAsReadR nid = Command
+    Patch
+    ["notifications", "threads", toPathPart nid]
+    (encode ())
+
+-- | Mark as read.
+-- See <https://developer.github.com/v3/activity/notifications/#mark-as-read>
+markAllNotificationsAsReadR :: GenRequest 'MtUnit 'RW ()
+markAllNotificationsAsReadR =
+    Command Put ["notifications"] $ encode emptyObject
diff --git a/src/GitHub/Endpoints/Activity/Starring.hs b/src/GitHub/Endpoints/Activity/Starring.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Activity/Starring.hs
@@ -0,0 +1,54 @@
+-- |
+-- The repo starring API as described on
+-- <https://developer.github.com/v3/activity/starring/>.
+
+module GitHub.Endpoints.Activity.Starring (
+    stargazersForR,
+    reposStarredByR,
+    myStarredR,
+    myStarredAcceptStarR,
+    starRepoR,
+    unstarRepoR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Auth
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List Stargazers.
+-- See <https://developer.github.com/v3/activity/starring/#list-stargazers>
+stargazersForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector SimpleUser)
+stargazersForR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "stargazers"] []
+
+-- | List repositories being starred.
+-- See <https://developer.github.com/v3/activity/starring/#list-repositories-being-starred>
+reposStarredByR :: Name Owner -> FetchCount -> Request k (Vector Repo)
+reposStarredByR user =
+    pagedQuery ["users", toPathPart user, "starred"] []
+
+-- | All the repos starred by the authenticated user.
+-- See <https://developer.github.com/v3/activity/starring/#list-repositories-being-starred>
+myStarredR :: FetchCount -> Request 'RA (Vector Repo)
+myStarredR = pagedQuery ["user", "starred"] []
+
+-- | All the repos starred by the authenticated user.
+-- See <https://developer.github.com/v3/activity/starring/#alternative-response-with-star-creation-timestamps-1>
+myStarredAcceptStarR :: FetchCount -> GenRequest 'MtStar 'RA (Vector RepoStarred)
+myStarredAcceptStarR = PagedQuery ["user", "starred"] []
+
+-- | Star a repo by the authenticated user.
+-- See <https://developer.github.com/v3/activity/starring/#star-a-repository>
+starRepoR :: Name Owner -> Name Repo -> GenRequest 'MtUnit 'RW ()
+starRepoR user repo = Command Put paths mempty
+  where
+    paths = ["user", "starred", toPathPart user, toPathPart repo]
+
+-- | Unstar a repo by the authenticated user.
+-- See <https://developer.github.com/v3/activity/starring/#unstar-a-repository>
+unstarRepoR :: Name Owner -> Name Repo -> GenRequest 'MtUnit 'RW ()
+unstarRepoR user repo = Command Delete paths mempty
+  where
+    paths = ["user", "starred", toPathPart user, toPathPart repo]
diff --git a/src/GitHub/Endpoints/Activity/Watching.hs b/src/GitHub/Endpoints/Activity/Watching.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Activity/Watching.hs
@@ -0,0 +1,33 @@
+-- |
+-- The repo watching API as described on
+-- <https://developer.github.com/v3/activity/watching/>.
+
+module GitHub.Endpoints.Activity.Watching (
+    watchersForR,
+    reposWatchedByR,
+    unwatchRepoR,
+    module GitHub.Data,
+) where
+
+import GitHub.Auth
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List watchers.
+-- See <https://developer.github.com/v3/activity/watching/#list-watchers>
+watchersForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector SimpleUser)
+watchersForR user repo limit =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "watchers"] [] limit
+
+-- | List repositories being watched.
+-- See <https://developer.github.com/v3/activity/watching/#list-repositories-being-watched>
+reposWatchedByR :: Name Owner -> FetchCount -> Request k (Vector Repo)
+reposWatchedByR user =
+    pagedQuery ["users", toPathPart user, "subscriptions"] []
+
+-- | Stop watching repository.
+-- See <https://docs.github.com/en/rest/reference/activity#delete-a-repository-subscription>
+unwatchRepoR :: Name Owner -> Name Repo -> Request 'RW ()
+unwatchRepoR owner repo =
+    command Delete ["repos", toPathPart owner, toPathPart repo, "subscription"] mempty
diff --git a/src/GitHub/Endpoints/Enterprise/Organizations.hs b/src/GitHub/Endpoints/Enterprise/Organizations.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Enterprise/Organizations.hs
@@ -0,0 +1,25 @@
+-- |
+-- The GitHub Enterprise orgs API as described on <https://developer.github.com/enterprise/v3/enterprise-admin/orgs/>.
+
+module GitHub.Endpoints.Enterprise.Organizations (
+    createOrganizationR,
+    renameOrganizationR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Data.Enterprise
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Create an organization.
+-- See <https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#create-an-organization>
+createOrganizationR :: CreateOrganization -> Request 'RW SimpleOrganization
+createOrganizationR =
+    command Post ["admin", "organizations"] . encode
+
+-- | Rename an organization.
+-- See <https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#rename-an-organization>
+renameOrganizationR :: Name Organization -> RenameOrganization -> Request 'RW RenameOrganizationResponse
+renameOrganizationR org =
+    command Patch ["admin", "organizations", toPathPart org] . encode
diff --git a/src/GitHub/Endpoints/Gists.hs b/src/GitHub/Endpoints/Gists.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Gists.hs
@@ -0,0 +1,47 @@
+-- |
+-- The gists API as described at <http://developer.github.com/v3/gists/>.
+
+module GitHub.Endpoints.Gists (
+    gistsR,
+    gistR,
+    createGistR,
+    starGistR,
+    unstarGistR,
+    deleteGistR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List gists.
+-- See <https://developer.github.com/v3/gists/#list-gists>
+gistsR :: Name Owner -> FetchCount -> Request k (Vector Gist)
+gistsR user = pagedQuery ["users", toPathPart user, "gists"] []
+
+-- | Query a single gist.
+-- See <https://developer.github.com/v3/gists/#get-a-single-gist>
+gistR :: Name Gist -> Request k Gist
+gistR gid =
+    query ["gists", toPathPart gid] []
+
+-- | Create a new gist
+-- See <https://docs.github.com/rest/reference/gists#create-a-gist>
+createGistR :: NewGist -> Request 'RW Gist
+createGistR ngist = command Post ["gists"] (encode ngist)
+
+-- | Star a gist by the authenticated user.
+-- See <https://developer.github.com/v3/gists/#star-a-gist>
+starGistR :: Name Gist -> GenRequest 'MtUnit 'RW ()
+starGistR gid = Command Put ["gists", toPathPart gid, "star"] mempty
+
+-- | Unstar a gist by the authenticated user.
+-- See <https://developer.github.com/v3/gists/#unstar-a-gist>
+unstarGistR :: Name Gist -> GenRequest 'MtUnit 'RW ()
+unstarGistR gid = Command Delete ["gists", toPathPart gid, "star"] mempty
+
+-- | Delete a gist by the authenticated user.
+-- See <https://developer.github.com/v3/gists/#delete-a-gist>
+deleteGistR :: Name Gist -> GenRequest 'MtUnit 'RW ()
+deleteGistR gid = Command Delete ["gists", toPathPart gid] mempty
diff --git a/src/GitHub/Endpoints/Gists/Comments.hs b/src/GitHub/Endpoints/Gists/Comments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Gists/Comments.hs
@@ -0,0 +1,25 @@
+-- |
+-- The loving comments people have left on Gists, described on
+-- <http://developer.github.com/v3/gists/comments/>.
+
+module GitHub.Endpoints.Gists.Comments (
+    commentsOnR,
+    gistCommentR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List comments on a gist.
+-- See <https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist>
+commentsOnR :: Name Gist -> FetchCount -> Request k (Vector GistComment)
+commentsOnR gid =
+    pagedQuery ["gists", toPathPart gid, "comments"] []
+
+-- | Query a single comment.
+-- See <https://developer.github.com/v3/gists/comments/#get-a-single-comment>
+gistCommentR :: Id GistComment -> Request k GistComment
+gistCommentR cid =
+    query ["gists", "comments", toPathPart cid] []
diff --git a/src/GitHub/Endpoints/GitData/Blobs.hs b/src/GitHub/Endpoints/GitData/Blobs.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/GitData/Blobs.hs
@@ -0,0 +1,17 @@
+-- |
+-- The API for dealing with git blobs from Github repos, as described in
+-- <http://developer.github.com/v3/git/blobs/>.
+
+module GitHub.Endpoints.GitData.Blobs (
+    blobR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import Prelude ()
+
+-- | Query a blob.
+-- See <https://developer.github.com/v3/git/blobs/#get-a-blob>
+blobR :: Name Owner -> Name Repo -> Name Blob -> Request k Blob
+blobR user repo sha =
+    query ["repos", toPathPart user, toPathPart repo, "git", "blobs", toPathPart sha] []
diff --git a/src/GitHub/Endpoints/GitData/Commits.hs b/src/GitHub/Endpoints/GitData/Commits.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/GitData/Commits.hs
@@ -0,0 +1,17 @@
+-- |
+-- The API for underlying git commits of a Github repo, as described on
+-- <http://developer.github.com/v3/git/commits/>.
+
+module GitHub.Endpoints.GitData.Commits (
+    gitCommitR,
+    module GitHub.Data,
+) where
+
+import GitHub.Data
+import Prelude ()
+
+-- | Query a commit.
+-- See <https://developer.github.com/v3/git/commits/#get-a-commit>
+gitCommitR :: Name Owner -> Name Repo -> Name GitCommit -> Request k GitCommit
+gitCommitR user repo sha =
+    query ["repos", toPathPart user, toPathPart repo, "git", "commits", toPathPart sha] []
diff --git a/src/GitHub/Endpoints/GitData/References.hs b/src/GitHub/Endpoints/GitData/References.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/GitData/References.hs
@@ -0,0 +1,47 @@
+-- |
+-- The underlying git references on a Github repo, exposed for the world to
+-- see. The git internals documentation will also prove handy for understanding
+-- these. API documentation at <http://developer.github.com/v3/git/refs/>.
+
+module GitHub.Endpoints.GitData.References (
+    referenceR,
+    referencesR,
+    createReferenceR,
+    deleteReferenceR,
+    namespacedReferencesR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | A single reference -- | Query a reference.
+-- See <https://developer.github.com/v3/git/refs/#get-a-reference>
+referenceR :: Name Owner -> Name Repo -> Name GitReference -> Request k GitReference
+referenceR user repo ref =
+    query ["repos", toPathPart user, toPathPart repo, "git", "refs", toPathPart ref] []
+
+-- | Query all References.
+-- See <https://developer.github.com/v3/git/refs/#get-all-references>
+referencesR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector GitReference)
+referencesR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "git", "refs"] []
+
+-- | Create a reference.
+-- See <https://developer.github.com/v3/git/refs/#create-a-reference>
+createReferenceR :: Name Owner -> Name Repo -> NewGitReference -> Request 'RW GitReference
+createReferenceR user repo newRef =
+     command Post  ["repos", toPathPart user, toPathPart repo , "git", "refs"] (encode newRef)
+
+-- | Delete a reference.
+-- See <https://developer.github.com/v3/git/refs/#delete-a-reference>
+deleteReferenceR :: Name Owner -> Name Repo -> Name GitReference -> GenRequest 'MtUnit 'RW ()
+deleteReferenceR user repo ref =
+    Command Delete ["repos", toPathPart user, toPathPart repo , "git", "refs", toPathPart ref] mempty
+
+-- | Query namespaced references.
+-- See <https://developer.github.com/v3/git/refs/#get-all-references>
+namespacedReferencesR :: Name Owner -> Name Repo -> Text -> Request k [GitReference]
+namespacedReferencesR user repo namespace =
+    query ["repos", toPathPart user, toPathPart repo, "git", "refs", namespace] []
diff --git a/src/GitHub/Endpoints/GitData/Trees.hs b/src/GitHub/Endpoints/GitData/Trees.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/GitData/Trees.hs
@@ -0,0 +1,25 @@
+-- |
+-- The underlying tree of SHA1s and files that make up a git repo. The API is
+-- described on <http://developer.github.com/v3/git/trees/>.
+
+module GitHub.Endpoints.GitData.Trees (
+    treeR,
+    nestedTreeR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Query a Tree.
+-- See <https://developer.github.com/v3/git/trees/#get-a-tree>
+treeR :: Name Owner -> Name Repo -> Name Tree -> Request k Tree
+treeR user repo sha =
+    query  ["repos", toPathPart user, toPathPart repo, "git", "trees", toPathPart sha] []
+
+-- | Query a Tree Recursively.
+-- See <https://developer.github.com/v3/git/trees/#get-a-tree-recursively>
+nestedTreeR :: Name Owner -> Name Repo -> Name Tree -> Request k Tree
+nestedTreeR user repo sha =
+    query  ["repos", toPathPart user, toPathPart repo, "git", "trees", toPathPart sha] [("recursive", Just "1")]
diff --git a/src/GitHub/Endpoints/Issues.hs b/src/GitHub/Endpoints/Issues.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Issues.hs
@@ -0,0 +1,64 @@
+-- |
+-- The issues API as described on <http://developer.github.com/v3/issues/>.
+
+module GitHub.Endpoints.Issues (
+    currentUserIssuesR,
+    organizationIssuesR,
+    issueR,
+    issuesForRepoR,
+    createIssueR,
+    newIssue,
+    editIssueR,
+    editOfIssue,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | See <https://developer.github.com/v3/issues/#list-issues>.
+currentUserIssuesR :: IssueMod -> FetchCount -> Request 'RA (Vector Issue)
+currentUserIssuesR opts =
+    pagedQuery ["user", "issues"] (issueModToQueryString opts)
+
+-- | See <https://developer.github.com/v3/issues/#list-issues>.
+organizationIssuesR :: Name Organization -> IssueMod -> FetchCount -> Request k (Vector Issue)
+organizationIssuesR org opts =
+    pagedQuery ["orgs", toPathPart org, "issues"] (issueModToQueryString opts)
+
+-- | Query a single issue.
+-- See <https://developer.github.com/v3/issues/#get-a-single-issue>
+issueR :: Name Owner -> Name Repo -> IssueNumber -> Request k Issue
+issueR user reqRepoName reqIssueNumber =
+    query ["repos", toPathPart user, toPathPart reqRepoName, "issues", toPathPart reqIssueNumber] []
+
+-- | List issues for a repository.
+-- See <https://developer.github.com/v3/issues/#list-issues-for-a-repository>
+issuesForRepoR :: Name Owner -> Name Repo -> IssueRepoMod -> FetchCount -> Request k (Vector Issue)
+issuesForRepoR user reqRepoName opts =
+    pagedQuery ["repos", toPathPart user, toPathPart reqRepoName, "issues"] qs
+  where
+    qs = issueRepoModToQueryString opts
+
+-- Creating new issues.
+
+newIssue :: Text -> NewIssue
+newIssue title = NewIssue title Nothing mempty Nothing Nothing
+
+-- | Create an issue.
+-- See <https://developer.github.com/v3/issues/#create-an-issue>
+createIssueR :: Name Owner -> Name Repo -> NewIssue -> Request 'RW Issue
+createIssueR user repo =
+    command Post ["repos", toPathPart user, toPathPart repo, "issues"] . encode
+
+-- Editing issues.
+
+editOfIssue :: EditIssue
+editOfIssue = EditIssue Nothing Nothing Nothing Nothing Nothing Nothing
+
+-- | Edit an issue.
+-- See <https://developer.github.com/v3/issues/#edit-an-issue>
+editIssueR :: Name Owner -> Name Repo -> IssueNumber -> EditIssue -> Request 'RW Issue
+editIssueR user repo iss =
+    command Patch ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iss] . encode
diff --git a/src/GitHub/Endpoints/Issues/Comments.hs b/src/GitHub/Endpoints/Issues/Comments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Issues/Comments.hs
@@ -0,0 +1,52 @@
+-- |
+-- The Github issue comments API from
+-- <http://developer.github.com/v3/issues/comments/>.
+
+module GitHub.Endpoints.Issues.Comments (
+    commentR,
+    commentsR,
+    createCommentR,
+    deleteCommentR,
+    editCommentR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Query a single comment.
+-- See <https://developer.github.com/v3/issues/comments/#get-a-single-comment>
+commentR :: Name Owner -> Name Repo -> Id Comment -> Request k IssueComment
+commentR user repo cid =
+    query ["repos", toPathPart user, toPathPart repo, "issues", "comments", toPathPart cid] []
+
+-- | List comments on an issue.
+-- See <https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue>
+commentsR :: Name Owner -> Name Repo -> IssueNumber -> FetchCount -> Request k (Vector IssueComment)
+commentsR user repo iid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "comments"] []
+
+-- | Create a comment.
+-- See <https://developer.github.com/v3/issues/comments/#create-a-comment>
+createCommentR :: Name Owner -> Name Repo -> IssueNumber -> Text -> Request 'RW Comment
+createCommentR user repo iss body =
+    command Post parts (encode $ NewComment body)
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iss, "comments"]
+
+-- | Edit a comment.
+-- See <https://developer.github.com/v3/issues/comments/#edit-a-comment>
+editCommentR :: Name Owner -> Name Repo -> Id Comment -> Text -> Request 'RW Comment
+editCommentR user repo commid body =
+    command Patch parts (encode $ EditComment body)
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "issues", "comments", toPathPart commid]
+
+-- | Delete a comment.
+-- See <https://developer.github.com/v3/issues/comments/#delete-a-comment>
+deleteCommentR :: Name Owner -> Name Repo -> Id Comment -> GenRequest 'MtUnit 'RW ()
+deleteCommentR user repo commid =
+    Command Delete parts mempty
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "issues", "comments", toPathPart commid]
diff --git a/src/GitHub/Endpoints/Issues/Events.hs b/src/GitHub/Endpoints/Issues/Events.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Issues/Events.hs
@@ -0,0 +1,32 @@
+-- |
+-- The Github issue events API, which is described on
+-- <http://developer.github.com/v3/issues/events/>
+
+module GitHub.Endpoints.Issues.Events (
+    eventsForIssueR,
+    eventsForRepoR,
+    eventR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List events for an issue.
+-- See <https://developer.github.com/v3/issues/events/#list-events-for-an-issue>
+eventsForIssueR :: Name Owner -> Name Repo -> Id Issue -> FetchCount -> Request k (Vector IssueEvent)
+eventsForIssueR user repo iid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "events"] []
+
+-- | List events for a repository.
+-- See <https://developer.github.com/v3/issues/events/#list-events-for-a-repository>
+eventsForRepoR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector IssueEvent)
+eventsForRepoR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "issues", "events"] []
+
+-- | Query a single event.
+-- See <https://developer.github.com/v3/issues/events/#get-a-single-event>
+eventR :: Name Owner -> Name Repo -> Id IssueEvent -> Request k IssueEvent
+eventR user repo eid =
+    query ["repos", toPathPart user, toPathPart repo, "issues", "events", toPathPart eid] []
diff --git a/src/GitHub/Endpoints/Issues/Labels.hs b/src/GitHub/Endpoints/Issues/Labels.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Issues/Labels.hs
@@ -0,0 +1,108 @@
+-- |
+-- The API for dealing with labels on Github issues as described on
+-- <http://developer.github.com/v3/issues/labels/>.
+
+module GitHub.Endpoints.Issues.Labels (
+    labelsOnRepoR,
+    labelR,
+    createLabelR,
+    updateLabelR,
+    deleteLabelR,
+    labelsOnIssueR,
+    addLabelsToIssueR,
+    removeLabelFromIssueR,
+    replaceAllLabelsForIssueR,
+    removeAllLabelsFromIssueR,
+    labelsOnMilestoneR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List all labels for this repository.
+-- See <https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository>
+labelsOnRepoR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector IssueLabel)
+labelsOnRepoR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "labels"] []
+
+-- | Query a single label.
+-- See <https://developer.github.com/v3/issues/labels/#get-a-single-label>
+labelR :: Name Owner -> Name Repo -> Name IssueLabel -> Request k IssueLabel
+labelR user repo lbl =
+    query ["repos", toPathPart user, toPathPart repo, "labels", toPathPart lbl] []
+
+-- | Create a label.
+-- See <https://developer.github.com/v3/issues/labels/#create-a-label>
+createLabelR :: Name Owner -> Name Repo -> NewIssueLabel -> Request 'RW IssueLabel
+createLabelR user repo =
+    command Post ["repos", toPathPart user, toPathPart repo, "labels"] . encode
+
+-- | Update a label.
+-- See <https://developer.github.com/v3/issues/labels/#update-a-label>
+updateLabelR :: Name Owner
+             -> Name Repo
+             -> Name IssueLabel   -- ^ old label name
+             -> UpdateIssueLabel   -- ^ new label
+             -> Request 'RW IssueLabel
+updateLabelR user repo oldLbl =
+    command Patch ["repos", toPathPart user, toPathPart repo, "labels", toPathPart oldLbl] . encode
+
+-- | Delete a label.
+-- See <https://developer.github.com/v3/issues/labels/#delete-a-label>
+deleteLabelR :: Name Owner -> Name Repo -> Name IssueLabel -> GenRequest 'MtUnit 'RW ()
+deleteLabelR user repo lbl =
+    Command Delete ["repos", toPathPart user, toPathPart repo, "labels", toPathPart lbl] mempty
+
+-- | List labels on an issue.
+-- See <https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue>
+labelsOnIssueR :: Name Owner -> Name Repo -> Id Issue -> FetchCount -> Request k (Vector IssueLabel)
+labelsOnIssueR user repo iid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "labels"] []
+
+-- | Add lables to an issue.
+-- See <https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue>
+addLabelsToIssueR :: Foldable f
+                  => Name Owner
+                  -> Name Repo
+                  -> Id Issue
+                  -> f (Name IssueLabel)
+                  -> Request 'RW (Vector IssueLabel)
+addLabelsToIssueR user repo iid lbls =
+    command Post paths (encode $ toList lbls)
+  where
+    paths = ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "labels"]
+
+-- | Remove a label from an issue.
+-- See <https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue>
+removeLabelFromIssueR :: Name Owner -> Name Repo -> Id Issue -> Name IssueLabel -> GenRequest 'MtUnit 'RW ()
+removeLabelFromIssueR user repo iid lbl =
+    Command Delete ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "labels", toPathPart lbl] mempty
+
+-- | Replace all labels on an issue.
+-- See <https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue>
+--
+-- Sending an empty list will remove all labels from the issue.
+replaceAllLabelsForIssueR :: Foldable f
+                          => Name Owner
+                          -> Name Repo
+                          -> Id Issue
+                          -> f (Name IssueLabel)
+                          -> Request 'RW (Vector IssueLabel)
+replaceAllLabelsForIssueR user repo iid lbls =
+    command Put paths (encode $ toList lbls)
+  where
+    paths = ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "labels"]
+
+-- | Remove all labels from an issue.
+-- See <https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue>
+removeAllLabelsFromIssueR :: Name Owner -> Name Repo -> Id Issue -> GenRequest 'MtUnit 'RW ()
+removeAllLabelsFromIssueR user repo iid =
+    Command Delete ["repos", toPathPart user, toPathPart repo, "issues", toPathPart iid, "labels"] mempty
+
+-- | Query labels for every issue in a milestone.
+-- See <https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone>
+labelsOnMilestoneR :: Name Owner -> Name Repo -> Id Milestone -> FetchCount -> Request k (Vector IssueLabel)
+labelsOnMilestoneR user repo mid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "milestones", toPathPart mid, "labels"] []
diff --git a/src/GitHub/Endpoints/Issues/Milestones.hs b/src/GitHub/Endpoints/Issues/Milestones.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Issues/Milestones.hs
@@ -0,0 +1,47 @@
+-- |
+-- The milestones API as described on
+-- <http://developer.github.com/v3/issues/milestones/>.
+
+module GitHub.Endpoints.Issues.Milestones (
+    milestonesR,
+    milestoneR,
+    createMilestoneR,
+    updateMilestoneR,
+    deleteMilestoneR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List milestones for a repository.
+-- See <https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository>
+milestonesR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Milestone)
+milestonesR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "milestones"] []
+
+-- | Query a single milestone.
+-- See <https://developer.github.com/v3/issues/milestones/#get-a-single-milestone>
+milestoneR :: Name Owner -> Name Repo -> Id Milestone -> Request k Milestone
+milestoneR user repo mid =
+    query ["repos", toPathPart user, toPathPart repo, "milestones", toPathPart mid] []
+
+-- | Create a milestone.
+-- See <https://developer.github.com/v3/issues/milestones/#create-a-milestone>
+createMilestoneR :: Name Owner -> Name Repo -> NewMilestone -> Request 'RW Milestone
+createMilestoneR user repo =
+    command Post ["repos", toPathPart user, toPathPart repo, "milestones"] . encode
+
+-- | Update a milestone.
+-- See <https://developer.github.com/v3/issues/milestones/#update-a-milestone>
+updateMilestoneR :: Name Owner -> Name Repo -> Id Milestone -> UpdateMilestone -> Request 'RW Milestone
+updateMilestoneR user repo mid =
+    command Patch ["repos", toPathPart user, toPathPart repo, "milestones", toPathPart mid ] . encode
+
+-- | Delete a milestone.
+-- See <https://developer.github.com/v3/issues/milestones/#delete-a-milestone>
+deleteMilestoneR :: Name Owner -> Name Repo -> Id Milestone -> GenRequest 'MtUnit 'RW ()
+deleteMilestoneR user repo mid =
+    Command Delete
+        ["repos", toPathPart user, toPathPart repo, "milestones", toPathPart mid] mempty
diff --git a/src/GitHub/Endpoints/Organizations.hs b/src/GitHub/Endpoints/Organizations.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Organizations.hs
@@ -0,0 +1,28 @@
+-- |
+-- The orgs API as described on <http://developer.github.com/v3/orgs/>.
+
+module GitHub.Endpoints.Organizations (
+    publicOrganizationsForR,
+    publicOrganizationR,
+    organizationsR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List all user organizations.
+-- See <https://developer.github.com/v3/orgs/#list-your-organizations>
+organizationsR :: FetchCount -> Request k (Vector SimpleOrganization)
+organizationsR = pagedQuery ["user", "orgs"] []
+
+-- | List public user organizations.
+-- See <https://developer.github.com/v3/orgs/#list-user-organizations>
+publicOrganizationsForR :: Name User -> FetchCount -> Request k (Vector SimpleOrganization)
+publicOrganizationsForR user = pagedQuery ["users", toPathPart user, "orgs"] []
+
+-- | Query an organization.
+-- See <https://developer.github.com/v3/orgs/#get-an-organization>
+publicOrganizationR :: Name Organization -> Request k Organization
+publicOrganizationR reqOrganizationName = query ["orgs", toPathPart reqOrganizationName] []
diff --git a/src/GitHub/Endpoints/Organizations/Members.hs b/src/GitHub/Endpoints/Organizations/Members.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Organizations/Members.hs
@@ -0,0 +1,58 @@
+-- |
+-- The organization members API as described on
+-- <http://developer.github.com/v3/orgs/members/>.
+
+module GitHub.Endpoints.Organizations.Members (
+    membersOfR,
+    membersOfWithR,
+    isMemberOfR,
+    orgInvitationsR,
+    orgMembershipR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | All the users who are members of the specified organization.
+--
+-- See <https://developer.github.com/v3/orgs/members/#members-list>
+membersOfR :: Name Organization -> FetchCount -> Request k (Vector SimpleUser)
+membersOfR organization =
+    pagedQuery ["orgs", toPathPart organization, "members"] []
+
+-- | 'membersOfR' with filters.
+--
+-- See <https://developer.github.com/v3/orgs/members/#members-list>
+membersOfWithR :: Name Organization -> OrgMemberFilter -> OrgMemberRole -> FetchCount -> Request k (Vector SimpleUser)
+membersOfWithR org f r =
+    pagedQuery ["orgs", toPathPart org, "members"] [("filter", Just f'), ("role", Just r')]
+  where
+    f' = case f of
+        OrgMemberFilter2faDisabled -> "2fa_disabled"
+        OrgMemberFilterAll         -> "all"
+    r' = case r of
+        OrgMemberRoleAll    -> "all"
+        OrgMemberRoleAdmin  -> "admin"
+        OrgMemberRoleMember -> "member"
+
+-- | Check if a user is a member of an organization.
+--
+-- See <https://developer.github.com/v3/orgs/members/#check-membership>
+isMemberOfR :: Name User -> Name Organization -> GenRequest 'MtStatus rw Bool
+isMemberOfR user org =
+    Query [ "orgs", toPathPart org, "members", toPathPart user ] []
+
+-- | List pending organization invitations
+--
+-- See <https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations>
+orgInvitationsR :: Name Organization -> FetchCount -> Request 'RA (Vector Invitation)
+orgInvitationsR org = pagedQuery ["orgs", toPathPart org, "invitations"] []
+
+-- | Get user membership information in an organization
+--
+-- See <https://docs.github.com/en/rest/orgs/members#get-organization-membership-for-a-user>
+orgMembershipR :: Name User -> Name Organization -> Request 'RA Membership
+orgMembershipR user org =
+    Query [ "orgs", toPathPart org, "memberships", toPathPart user ] []
diff --git a/src/GitHub/Endpoints/Organizations/OutsideCollaborators.hs b/src/GitHub/Endpoints/Organizations/OutsideCollaborators.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Organizations/OutsideCollaborators.hs
@@ -0,0 +1,18 @@
+-- |
+-- The organization members API as described on
+-- <https://developer.github.com/v3/orgs/outside_collaborators/>.
+
+module GitHub.Endpoints.Organizations.OutsideCollaborators (
+    outsideCollaboratorsR,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | All the users who are outside collaborators of the specified organization.
+--
+-- See <https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators>
+outsideCollaboratorsR :: Name Organization -> FetchCount -> Request k (Vector SimpleUser)
+outsideCollaboratorsR organization =
+    pagedQuery ["orgs", toPathPart organization, "outside_collaborators"] []
diff --git a/src/GitHub/Endpoints/Organizations/Teams.hs b/src/GitHub/Endpoints/Organizations/Teams.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Organizations/Teams.hs
@@ -0,0 +1,101 @@
+-- |
+-- The Owner teams API as described on
+-- <http://developer.github.com/v3/orgs/teams/>.
+
+module GitHub.Endpoints.Organizations.Teams (
+    teamsOfR,
+    teamInfoForR,
+    createTeamForR,
+    editTeamR,
+    deleteTeamR,
+    listTeamMembersR,
+    listTeamReposR,
+    addOrUpdateTeamRepoR,
+    teamMembershipInfoForR,
+    addTeamMembershipForR,
+    deleteTeamMembershipForR,
+    listTeamsCurrentR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List teams.
+-- See <https://developer.github.com/v3/orgs/teams/#list-teams>
+teamsOfR :: Name Organization -> FetchCount -> Request k (Vector SimpleTeam)
+teamsOfR org =
+    pagedQuery ["orgs", toPathPart org, "teams"] []
+
+-- | Query team.
+-- See <https://developer.github.com/v3/orgs/teams/#get-team>
+teamInfoForR  :: Id Team -> Request k Team
+teamInfoForR tid =
+    query ["teams", toPathPart tid] []
+
+-- | Create team.
+-- See <https://developer.github.com/v3/orgs/teams/#create-team>
+createTeamForR :: Name Organization -> CreateTeam -> Request 'RW Team
+createTeamForR org cteam =
+    command Post ["orgs", toPathPart org, "teams"] (encode cteam)
+
+-- | Edit team.
+-- See <https://developer.github.com/v3/orgs/teams/#edit-team>
+editTeamR :: Id Team -> EditTeam -> Request 'RW Team
+editTeamR tid eteam =
+    command Patch ["teams", toPathPart tid] (encode eteam)
+
+--
+-- See <https://developer.github.com/v3/orgs/teams/#delete-team>
+deleteTeamR :: Id Team -> GenRequest 'MtUnit 'RW ()
+deleteTeamR tid =
+    Command Delete ["teams", toPathPart tid] mempty
+
+-- | List team members.
+--
+-- See <https://developer.github.com/v3/orgs/teams/#list-team-members>
+listTeamMembersR :: Id Team -> TeamMemberRole -> FetchCount -> Request 'RA (Vector SimpleUser)
+listTeamMembersR tid r =
+    pagedQuery ["teams", toPathPart tid, "members"] [("role", Just r')]
+  where
+    r' = case r of
+        TeamMemberRoleAll         -> "all"
+        TeamMemberRoleMaintainer  -> "maintainer"
+        TeamMemberRoleMember      -> "member"
+
+-- | Query team repositories.
+-- See <https://developer.github.com/v3/orgs/teams/#list-team-repos>
+listTeamReposR :: Id Team -> FetchCount -> Request k (Vector Repo)
+listTeamReposR tid  =
+    pagedQuery ["teams", toPathPart tid, "repos"] []
+
+-- | Add or update a team repository.
+-- See <https://developer.github.com/v3/orgs/teams/#add-or-update-team-repository>
+addOrUpdateTeamRepoR :: Id Team -> Name Organization -> Name Repo -> Permission -> GenRequest 'MtUnit 'RW ()
+addOrUpdateTeamRepoR tid org repo permission =
+    Command Put ["teams", toPathPart tid, "repos", toPathPart org, toPathPart repo] (encode $ AddTeamRepoPermission permission)
+
+-- | Query team membership.
+-- See <https://developer.github.com/v3/orgs/teams/#get-team-membership
+teamMembershipInfoForR :: Id Team -> Name Owner -> Request k TeamMembership
+teamMembershipInfoForR tid user =
+    query ["teams", toPathPart tid, "memberships", toPathPart user] []
+
+-- | Add team membership.
+-- See <https://developer.github.com/v3/orgs/teams/#add-team-membership>
+addTeamMembershipForR :: Id Team -> Name Owner -> Role -> Request 'RW TeamMembership
+addTeamMembershipForR tid user role =
+    command Put ["teams", toPathPart tid, "memberships", toPathPart user] (encode $ CreateTeamMembership role)
+
+-- | Remove team membership.
+-- See <https://developer.github.com/v3/orgs/teams/#remove-team-membership>
+deleteTeamMembershipForR :: Id Team -> Name Owner -> GenRequest 'MtUnit 'RW ()
+deleteTeamMembershipForR tid user =
+    Command Delete ["teams", toPathPart tid, "memberships", toPathPart user] mempty
+
+-- | List user teams.
+-- See <https://developer.github.com/v3/orgs/teams/#list-user-teams>
+listTeamsCurrentR :: FetchCount -> Request 'RA (Vector Team)
+listTeamsCurrentR =
+    pagedQuery ["user", "teams"] []
diff --git a/src/GitHub/Endpoints/PullRequests.hs b/src/GitHub/Endpoints/PullRequests.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/PullRequests.hs
@@ -0,0 +1,101 @@
+-- |
+-- The pull requests API as documented at
+-- <http://developer.github.com/v3/pulls/>.
+
+module GitHub.Endpoints.PullRequests (
+    pullRequestsForR,
+    pullRequestR,
+    pullRequestDiffR,
+    pullRequestPatchR,
+    createPullRequestR,
+    updatePullRequestR,
+    pullRequestCommitsR,
+    pullRequestFilesR,
+    isPullRequestMergedR,
+    mergePullRequestR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+import Data.ByteString.Lazy (ByteString)
+
+-- | List pull requests.
+-- See <https://developer.github.com/v3/pulls/#list-pull-requests>
+pullRequestsForR
+    :: Name Owner
+    -> Name Repo
+    -> PullRequestMod
+    -> FetchCount
+    -> Request k (Vector SimplePullRequest)
+pullRequestsForR user repo opts = pagedQuery
+    ["repos", toPathPart user, toPathPart repo, "pulls"]
+    (prModToQueryString opts)
+
+-- | Query a single pull request to obtain the diff
+-- See <https://developer.github.com/v3/pulls/#get-a-single-pull-request>
+pullRequestDiffR :: Name Owner -> Name Repo -> IssueNumber -> GenRequest 'MtDiff rw ByteString
+pullRequestDiffR user repo prid =
+    Query ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid] []
+
+-- | Query a single pull request to obtain the patch
+-- See <https://developer.github.com/v3/pulls/#get-a-single-pull-request>
+pullRequestPatchR :: Name Owner -> Name Repo -> IssueNumber -> GenRequest 'MtPatch rw ByteString
+pullRequestPatchR user repo prid =
+    Query ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid] []
+
+-- | Query a single pull request.
+-- See <https://developer.github.com/v3/pulls/#get-a-single-pull-request>
+pullRequestR :: Name Owner -> Name Repo -> IssueNumber -> Request k PullRequest
+pullRequestR user repo prid =
+    query ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid] []
+
+-- | Create a pull request.
+-- See <https://developer.github.com/v3/pulls/#create-a-pull-request>
+createPullRequestR :: Name Owner
+                   -> Name Repo
+                   -> CreatePullRequest
+                   -> Request 'RW PullRequest
+createPullRequestR user repo cpr =
+    command Post ["repos", toPathPart user, toPathPart repo, "pulls"] (encode cpr)
+
+-- | Update a pull request.
+-- See <https://developer.github.com/v3/pulls/#update-a-pull-request>
+updatePullRequestR :: Name Owner
+                   -> Name Repo
+                   -> IssueNumber
+                   -> EditPullRequest
+                   -> Request 'RW PullRequest
+updatePullRequestR user repo prid epr =
+    command Patch ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid] (encode epr)
+
+-- | List commits on a pull request.
+-- See <https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request>
+pullRequestCommitsR :: Name Owner -> Name Repo -> IssueNumber -> FetchCount -> Request k (Vector Commit)
+pullRequestCommitsR user repo prid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid, "commits"] []
+
+-- | List pull requests files.
+-- See <https://developer.github.com/v3/pulls/#list-pull-requests-files>
+pullRequestFilesR :: Name Owner -> Name Repo -> IssueNumber -> FetchCount -> Request k (Vector File)
+pullRequestFilesR user repo prid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid, "files"] []
+
+-- | Query if a pull request has been merged.
+-- See <https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged>
+isPullRequestMergedR :: Name Owner -> Name Repo -> IssueNumber -> GenRequest 'MtStatus rw Bool
+isPullRequestMergedR user repo prid =
+    Query ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid, "merge"] []
+
+-- | Merge a pull request (Merge Button).
+-- https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button
+mergePullRequestR :: Name Owner -> Name Repo -> IssueNumber -> Maybe Text -> GenRequest 'MtStatus 'RW MergeResult
+mergePullRequestR user repo prid commitMessage =
+    Command Put paths (encode $ buildCommitMessageMap commitMessage)
+  where
+    paths = ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid, "merge"]
+
+    buildCommitMessageMap :: Maybe Text -> Value
+    buildCommitMessageMap (Just msg) = object ["commit_message" .= msg ]
+    buildCommitMessageMap Nothing    = object []
diff --git a/src/GitHub/Endpoints/PullRequests/Comments.hs b/src/GitHub/Endpoints/PullRequests/Comments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/PullRequests/Comments.hs
@@ -0,0 +1,46 @@
+-- |
+-- The pull request review comments API as described at
+-- <http://developer.github.com/v3/pulls/comments/>.
+
+module GitHub.Endpoints.PullRequests.Comments (
+    pullRequestCommentsR,
+    pullRequestCommentR,
+    createPullCommentR,
+    createPullCommentReplyR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List comments on a pull request.
+-- See <https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request>
+pullRequestCommentsR :: Name Owner -> Name Repo -> IssueNumber -> FetchCount -> Request k (Vector Comment)
+pullRequestCommentsR user repo prid =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart prid, "comments"] []
+
+-- | Query a single comment.
+-- See <https://developer.github.com/v3/pulls/comments/#get-a-single-comment>
+pullRequestCommentR :: Name Owner -> Name Repo -> Id Comment -> Request k Comment
+pullRequestCommentR user repo cid =
+    query ["repos", toPathPart user, toPathPart repo, "pulls", "comments", toPathPart cid] []
+
+-- | Create a comment.
+--
+-- See <https://developer.github.com/v3/pulls/comments/#create-a-comment>
+createPullCommentR :: Name Owner -> Name Repo -> IssueNumber -> Text -> Text -> Int -> Text -> Request 'RW Comment
+createPullCommentR user repo iss commit path position body =
+    command Post parts (encode $ NewPullComment commit path position body)
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart iss, "comments"]
+
+-- | Create a comment reply.
+--
+-- See <https://developer.github.com/v3/pulls/comments/#create-a-review-comment-reply>
+createPullCommentReplyR :: Name Owner -> Name Repo -> IssueNumber -> Id Comment -> Text -> Request 'RW Comment
+createPullCommentReplyR user repo iss cid body =
+    command Post parts (encode $ PullCommentReply body)
+  where
+    parts = ["repos", toPathPart user, toPathPart repo, "pulls", toPathPart iss
+            , "comments", toPathPart cid, "replies"]
diff --git a/src/GitHub/Endpoints/PullRequests/Reviews.hs b/src/GitHub/Endpoints/PullRequests/Reviews.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/PullRequests/Reviews.hs
@@ -0,0 +1,73 @@
+-- |
+-- The reviews API as described on <http://developer.github.com/v3/pulls/reviews/>.
+
+module GitHub.Endpoints.PullRequests.Reviews
+    ( pullRequestReviewsR
+    , pullRequestReviewR
+    , pullRequestReviewCommentsR
+    , module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List reviews for a pull request.
+-- See <https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request>
+pullRequestReviewsR
+    :: Name Owner
+    -> Name Repo
+    -> IssueNumber
+    -> FetchCount
+    -> Request k (Vector Review)
+pullRequestReviewsR owner repo prid =
+    pagedQuery
+        [ "repos"
+        , toPathPart owner
+        , toPathPart repo
+        , "pulls"
+        , toPathPart prid
+        , "reviews"
+        ]
+        []
+
+-- | Query a single pull request review.
+-- see <https://developer.github.com/v3/pulls/reviews/#get-a-single-review>
+pullRequestReviewR
+    :: Name Owner
+    -> Name Repo
+    -> IssueNumber
+    -> Id Review
+    -> Request k Review
+pullRequestReviewR owner repo prid rid =
+    query
+        [ "repos"
+        , toPathPart owner
+        , toPathPart repo
+        , "pulls"
+        , toPathPart prid
+        , "reviews"
+        , toPathPart rid
+        ]
+        []
+
+-- | Query the comments for a single pull request review.
+-- see <https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review>
+pullRequestReviewCommentsR
+    :: Name Owner
+    -> Name Repo
+    -> IssueNumber
+    -> Id Review
+    -> Request k [ReviewComment]
+pullRequestReviewCommentsR owner repo prid rid =
+    query
+        [ "repos"
+        , toPathPart owner
+        , toPathPart repo
+        , "pulls"
+        , toPathPart prid
+        , "reviews"
+        , toPathPart rid
+        , "comments"
+        ]
+        []
diff --git a/src/GitHub/Endpoints/RateLimit.hs b/src/GitHub/Endpoints/RateLimit.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/RateLimit.hs
@@ -0,0 +1,16 @@
+-- |
+-- The Github RateLimit API, as described at
+-- <http://developer.github.com/v3/rate_limit/>.
+
+module GitHub.Endpoints.RateLimit (
+    rateLimitR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import Prelude ()
+
+-- | Get your current rate limit status.
+-- <https://developer.github.com/v3/rate_limit/#get-your-current-rate-limit-status>
+rateLimitR :: Request k RateLimit
+rateLimitR = query ["rate_limit"] []
diff --git a/src/GitHub/Endpoints/Reactions.hs b/src/GitHub/Endpoints/Reactions.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Reactions.hs
@@ -0,0 +1,60 @@
+-- |
+-- The Reactions API as described at
+-- <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28>.
+module GitHub.Endpoints.Reactions (
+  issueReactionsR,
+  createIssueReactionR,
+  deleteIssueReactionR,
+  commentReactionsR,
+  createCommentReactionR,
+  deleteCommentReactionR,
+  module GitHub.Data,
+) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List reactions for an issue.
+-- See <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-an-issue>
+issueReactionsR :: Name Owner -> Name Repo -> Id Issue -> FetchCount -> Request k (Vector Reaction)
+issueReactionsR owner repo iid =
+  pagedQuery ["repos", toPathPart owner, toPathPart repo, "issues", toPathPart iid, "reactions"] []
+
+-- | Create reaction for an issue comment.
+-- See <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue>
+createIssueReactionR :: Name Owner -> Name Repo -> Id Issue -> ReactionContent -> Request 'RW Reaction
+createIssueReactionR owner repo iid content =
+    command Post parts (encode $ NewReaction content)
+  where
+    parts = ["repos", toPathPart owner, toPathPart repo, "issues", toPathPart iid, "reactions"]
+
+-- | Delete an issue comment reaction.
+-- See <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-reaction>
+deleteIssueReactionR :: Name Owner -> Name Repo -> Id Issue -> Id Reaction -> GenRequest 'MtUnit 'RW ()
+deleteIssueReactionR owner repo iid rid =
+    Command Delete parts mempty
+  where
+    parts = ["repos", toPathPart owner, toPathPart repo, "issues", toPathPart iid, "reactions", toPathPart rid]
+
+-- | List reactions for an issue comment.
+-- See <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-an-issue-comment>
+commentReactionsR :: Name Owner -> Name Repo -> Id Comment -> FetchCount -> Request k (Vector Reaction)
+commentReactionsR owner repo cid =
+  pagedQuery ["repos", toPathPart owner, toPathPart repo, "issues", "comments", toPathPart cid, "reactions"] []
+
+-- | Create reaction for an issue comment.
+-- See https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue-comment
+createCommentReactionR :: Name Owner -> Name Repo -> Id Comment -> ReactionContent -> Request 'RW Reaction
+createCommentReactionR owner repo cid content =
+    command Post parts (encode $ NewReaction content)
+  where
+    parts = ["repos", toPathPart owner, toPathPart repo, "issues", "comments", toPathPart cid, "reactions"]
+
+-- | Delete an issue comment reaction.
+-- See <https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-comment-reaction>
+deleteCommentReactionR :: Name Owner -> Name Repo -> Id Comment -> Id Reaction -> GenRequest 'MtUnit 'RW ()
+deleteCommentReactionR owner repo cid rid =
+    Command Delete parts mempty
+  where
+    parts = ["repos", toPathPart owner, toPathPart repo, "issues", "comments", toPathPart cid, "reactions", toPathPart rid]
diff --git a/src/GitHub/Endpoints/Repos.hs b/src/GitHub/Endpoints/Repos.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos.hs
@@ -0,0 +1,140 @@
+-- |
+-- The Github Repos API, as documented at
+-- <http://developer.github.com/v3/repos/>
+
+module GitHub.Endpoints.Repos (
+    -- * Querying repositories
+    currentUserReposR,
+    userReposR,
+    organizationReposR,
+    repositoryR,
+    contributorsR,
+    languagesForR,
+    tagsForR,
+    branchesForR,
+
+    -- ** Create
+    createRepoR,
+    createOrganizationRepoR,
+    forkExistingRepoR,
+
+    -- ** Edit
+    editRepoR,
+
+    -- ** Delete
+    deleteRepoR,
+
+    -- * Data
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+repoPublicityQueryString :: RepoPublicity -> QueryString
+repoPublicityQueryString RepoPublicityAll     = [("type", Just "all")]
+repoPublicityQueryString RepoPublicityOwner   = [("type", Just "owner")]
+repoPublicityQueryString RepoPublicityMember  = [("type", Just "member")]
+repoPublicityQueryString RepoPublicityPublic  = [("type", Just "public")]
+repoPublicityQueryString RepoPublicityPrivate = [("type", Just "private")]
+
+-- | List your repositories.
+-- See <https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user>
+currentUserReposR :: RepoPublicity -> FetchCount -> Request k (Vector Repo)
+currentUserReposR publicity =
+    pagedQuery  ["user", "repos"] qs
+  where
+    qs = repoPublicityQueryString publicity
+
+-- | List user repositories.
+-- See <https://docs.github.com/en/rest/reference/repos#list-repositories-for-a-user>
+userReposR :: Name Owner -> RepoPublicity -> FetchCount -> Request k(Vector Repo)
+userReposR user publicity =
+    pagedQuery  ["users", toPathPart user, "repos"] qs
+  where
+    qs = repoPublicityQueryString publicity
+
+-- | List organization repositories.
+-- See <https://docs.github.com/en/rest/reference/repos#list-organization-repositories>
+organizationReposR
+    :: Name Organization
+    -> RepoPublicity
+    -> FetchCount
+    -> Request k (Vector Repo)
+organizationReposR org publicity =
+    pagedQuery ["orgs", toPathPart org, "repos"] qs
+  where
+    qs = repoPublicityQueryString publicity
+
+-- | Query single repository.
+-- See <https://developer.github.com/v3/repos/#get>
+repositoryR :: Name Owner -> Name Repo -> Request k Repo
+repositoryR user repo =
+    query ["repos", toPathPart user, toPathPart repo] []
+
+-- | Create a new repository.
+-- See <https://developer.github.com/v3/repos/#create>
+createRepoR :: NewRepo -> Request 'RW Repo
+createRepoR nrepo =
+    command Post ["user", "repos"] (encode nrepo)
+
+-- | Fork an existing repository.
+-- See <https://developer.github.com/v3/repos/forks/#create-a-fork>
+-- TODO: The third paramater (an optional Organisation) is not used yet.
+forkExistingRepoR :: Name Owner -> Name Repo -> Maybe (Name Owner) -> Request 'RW Repo
+forkExistingRepoR owner repo _morg =
+    command Post ["repos", toPathPart owner, toPathPart repo, "forks" ] mempty
+
+-- | Create a new repository for an organization.
+-- See <https://developer.github.com/v3/repos/#create>
+createOrganizationRepoR :: Name Organization -> NewRepo -> Request 'RW Repo
+createOrganizationRepoR org nrepo =
+    command Post ["orgs", toPathPart org, "repos"] (encode nrepo)
+
+-- | Edit an existing repository.
+-- See <https://developer.github.com/v3/repos/#edit>
+editRepoR :: Name Owner -> Name Repo -> EditRepo -> Request 'RW Repo
+editRepoR user repo body =
+    command Patch ["repos", toPathPart user, toPathPart repo] (encode b)
+  where
+    -- if no name is given, use curent name
+    b = body {editName = editName body <|> Just repo}
+
+-- | List contributors.
+-- See <https://developer.github.com/v3/repos/#list-contributors>
+contributorsR
+    :: Name Owner
+    -> Name Repo
+    -> Bool              -- ^ Include anonymous
+    -> FetchCount
+    -> Request k (Vector Contributor)
+contributorsR user repo anon =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "contributors"] qs
+  where
+    qs | anon      = [("anon", Just "true")]
+       | otherwise = []
+
+-- | List languages.
+-- See <https://developer.github.com/v3/repos/#list-languages>
+languagesForR :: Name Owner -> Name Repo -> Request k Languages
+languagesForR user repo =
+    query ["repos", toPathPart user, toPathPart repo, "languages"] []
+
+-- | List tags.
+-- See <https://developer.github.com/v3/repos/#list-tags>
+tagsForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Tag)
+tagsForR user repo =
+    pagedQuery  ["repos", toPathPart user, toPathPart repo, "tags"] []
+
+-- | List branches.
+-- See <https://developer.github.com/v3/repos/#list-branches>
+branchesForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Branch)
+branchesForR user repo =
+    pagedQuery  ["repos", toPathPart user, toPathPart repo, "branches"] []
+
+-- | Delete a repository,.
+-- See <https://developer.github.com/v3/repos/#delete-a-repository>
+deleteRepoR :: Name Owner -> Name Repo -> GenRequest 'MtUnit 'RW ()
+deleteRepoR user repo =
+    Command Delete ["repos", toPathPart user, toPathPart repo] mempty
diff --git a/src/GitHub/Endpoints/Repos/Collaborators.hs b/src/GitHub/Endpoints/Repos/Collaborators.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Collaborators.hs
@@ -0,0 +1,51 @@
+-- |
+-- The repo collaborators API as described on
+-- <http://developer.github.com/v3/repos/collaborators/>.
+
+module GitHub.Endpoints.Repos.Collaborators (
+    collaboratorsOnR,
+    collaboratorPermissionOnR,
+    isCollaboratorOnR,
+    addCollaboratorR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List collaborators.
+-- See <https://developer.github.com/v3/repos/collaborators/#list-collaborators>
+collaboratorsOnR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector SimpleUser)
+collaboratorsOnR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "collaborators"] []
+
+-- | Review a user's permission level.
+-- <https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level>
+collaboratorPermissionOnR
+    :: Name Owner        -- ^ Repository owner
+    -> Name Repo         -- ^ Repository name
+    -> Name User         -- ^ Collaborator to check permissions of.
+    -> GenRequest 'MtJSON rw CollaboratorWithPermission
+collaboratorPermissionOnR owner repo coll =
+    query ["repos", toPathPart owner, toPathPart repo, "collaborators", toPathPart coll, "permission"] []
+
+-- | Check if a user is a collaborator.
+-- See <https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator>
+isCollaboratorOnR
+    :: Name Owner  -- ^ Repository owner
+    -> Name Repo         -- ^ Repository name
+    -> Name User         -- ^ Collaborator?
+    -> GenRequest 'MtStatus rw Bool
+isCollaboratorOnR user repo coll =
+    Query ["repos", toPathPart user, toPathPart repo, "collaborators", toPathPart coll] []
+
+-- | Invite a user as a collaborator.
+-- See <https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator>
+addCollaboratorR
+    :: Name Owner        -- ^ Repository owner
+    -> Name Repo         -- ^ Repository name
+    -> Name User         -- ^ Collaborator to add
+    -> GenRequest 'MtJSON 'RW (Maybe RepoInvitation)
+addCollaboratorR owner repo coll =
+    Command Put ["repos", toPathPart owner, toPathPart repo, "collaborators", toPathPart coll] mempty
diff --git a/src/GitHub/Endpoints/Repos/Comments.hs b/src/GitHub/Endpoints/Repos/Comments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Comments.hs
@@ -0,0 +1,32 @@
+-- |
+-- The repo commits API as described on
+-- <http://developer.github.com/v3/repos/comments/>.
+
+module GitHub.Endpoints.Repos.Comments (
+    commentsForR,
+    commitCommentsForR,
+    commitCommentForR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List commit comments for a repository.
+-- See <https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository>
+commentsForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Comment)
+commentsForR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "comments"] []
+
+-- | List comments for a single commit.
+-- See <https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit>
+commitCommentsForR :: Name Owner -> Name Repo -> Name Commit -> FetchCount -> Request k (Vector Comment)
+commitCommentsForR user repo sha =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "commits", toPathPart sha, "comments"] []
+
+-- | Query a single commit comment.
+-- See <https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment>
+commitCommentForR :: Name Owner -> Name Repo -> Id Comment -> Request k Comment
+commitCommentForR user repo cid =
+    query ["repos", toPathPart user, toPathPart repo, "comments", toPathPart cid] []
diff --git a/src/GitHub/Endpoints/Repos/Commits.hs b/src/GitHub/Endpoints/Repos/Commits.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Commits.hs
@@ -0,0 +1,52 @@
+-- |
+-- The repo commits API as described on
+-- <http://developer.github.com/v3/repos/commits/>.
+
+module GitHub.Endpoints.Repos.Commits (
+    CommitQueryOption(..),
+    commitsForR,
+    commitsWithOptionsForR,
+    commitR,
+    diffR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.ByteString    as BS
+import qualified Data.Text          as T
+import qualified Data.Text.Encoding as TE
+
+renderCommitQueryOption :: CommitQueryOption -> (BS.ByteString, Maybe BS.ByteString)
+renderCommitQueryOption (CommitQuerySha sha)      = ("sha", Just $ TE.encodeUtf8 sha)
+renderCommitQueryOption (CommitQueryPath path)     = ("path", Just $ TE.encodeUtf8 path)
+renderCommitQueryOption (CommitQueryAuthor author) = ("author", Just $ TE.encodeUtf8 author)
+renderCommitQueryOption (CommitQuerySince date)    = ("since", Just $ TE.encodeUtf8 . T.pack $ formatISO8601 date)
+renderCommitQueryOption (CommitQueryUntil date)    = ("until", Just $ TE.encodeUtf8 . T.pack $ formatISO8601 date)
+
+-- | List commits on a repository.
+-- See <https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository>
+commitsForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Commit)
+commitsForR user repo limit = commitsWithOptionsForR user repo limit []
+
+-- | List commits on a repository.
+-- See <https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository>
+commitsWithOptionsForR :: Name Owner -> Name Repo -> FetchCount -> [CommitQueryOption] -> Request k (Vector Commit)
+commitsWithOptionsForR user repo limit opts =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "commits"] qs limit
+  where
+    qs = map renderCommitQueryOption opts
+
+-- | Query a single commit.
+-- See <https://developer.github.com/v3/repos/commits/#get-a-single-commit>
+commitR :: Name Owner -> Name Repo -> Name Commit -> Request k Commit
+commitR user repo sha =
+    query ["repos", toPathPart user, toPathPart repo, "commits", toPathPart sha] []
+
+-- | Compare two commits.
+-- See <https://developer.github.com/v3/repos/commits/#compare-two-commits>
+diffR :: Name Owner -> Name Repo -> Name Commit -> Name Commit -> Request k Diff
+diffR user repo base headref =
+    query ["repos", toPathPart user, toPathPart repo, "compare", toPathPart base <> "..." <> toPathPart headref] []
diff --git a/src/GitHub/Endpoints/Repos/Contents.hs b/src/GitHub/Endpoints/Repos/Contents.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Contents.hs
@@ -0,0 +1,86 @@
+-- |
+-- The Github Repo Contents API, as documented at
+-- <https://developer.github.com/v3/repos/contents/>
+
+module GitHub.Endpoints.Repos.Contents (
+    -- * Querying contents
+    contentsForR,
+    readmeForR,
+    archiveForR,
+
+    -- ** Create
+    createFileR,
+
+    -- ** Update
+    updateFileR,
+
+    -- ** Delete
+    deleteFileR,
+
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Data.Maybe (maybeToList)
+import qualified Data.Text.Encoding as TE
+import Network.URI (URI)
+
+contentsForR
+    :: Name Owner
+    -> Name Repo
+    -> Text            -- ^ file or directory
+    -> Maybe Text      -- ^ Git commit
+    -> Request k Content
+contentsForR user repo path ref =
+    query ["repos", toPathPart user, toPathPart repo, "contents", path] qs
+  where
+    qs =  maybe [] (\r -> [("ref", Just . TE.encodeUtf8 $ r)]) ref
+
+readmeForR :: Name Owner -> Name Repo -> Request k Content
+readmeForR user repo =
+    query ["repos", toPathPart user, toPathPart repo, "readme"] []
+
+-- | Get archive link.
+-- See <https://developer.github.com/v3/repos/contents/#get-archive-link>
+archiveForR
+    :: Name Owner
+    -> Name Repo
+    -> ArchiveFormat   -- ^ The type of archive to retrieve
+    -> Maybe Text      -- ^ Git commit
+    -> GenRequest 'MtRedirect rw URI
+archiveForR user repo format ref = Query path []
+  where
+    path = ["repos", toPathPart user, toPathPart repo, toPathPart format] <> maybeToList ref
+
+-- | Create a file.
+-- See <https://developer.github.com/v3/repos/contents/#create-a-file>
+createFileR
+    :: Name Owner
+    -> Name Repo
+    -> CreateFile
+    -> Request 'RW ContentResult
+createFileR user repo body =
+    command Put ["repos", toPathPart user, toPathPart repo, "contents", createFilePath body] (encode body)
+
+-- | Update a file.
+-- See <https://developer.github.com/v3/repos/contents/#update-a-file>
+updateFileR
+    :: Name Owner
+    -> Name Repo
+    -> UpdateFile
+    -> Request 'RW ContentResult
+updateFileR user repo body =
+    command Put ["repos", toPathPart user, toPathPart repo, "contents", updateFilePath body] (encode body)
+
+-- | Delete a file.
+-- See <https://developer.github.com/v3/repos/contents/#delete-a-file>
+deleteFileR
+    :: Name Owner
+    -> Name Repo
+    -> DeleteFile
+    -> GenRequest 'MtUnit 'RW ()
+deleteFileR user repo body =
+    Command Delete ["repos", toPathPart user, toPathPart repo, "contents", deleteFilePath body] (encode body)
diff --git a/src/GitHub/Endpoints/Repos/DeployKeys.hs b/src/GitHub/Endpoints/Repos/DeployKeys.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/DeployKeys.hs
@@ -0,0 +1,46 @@
+-----------------------------------------------------------------------------
+-- |
+-- License     :  BSD-3-Clause
+-- Maintainer  :  Todd Mohney <toddmohney@gmail.com>
+--
+-- The deploy keys API, as described at
+-- <https://developer.github.com/v3/repos/keys>
+module GitHub.Endpoints.Repos.DeployKeys (
+    -- * Querying deploy keys
+    deployKeysForR,
+    deployKeyForR,
+
+    -- ** Create
+    createRepoDeployKeyR,
+
+    -- ** Delete
+    deleteRepoDeployKeyR,
+) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Querying deploy keys.
+-- See <https://developer.github.com/v3/repos/keys/#list-deploy-keys>
+deployKeysForR :: Name Owner -> Name Repo -> FetchCount -> Request 'RA (Vector RepoDeployKey)
+deployKeysForR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "keys"] []
+
+-- | Querying a deploy key.
+-- See <https://developer.github.com/v3/repos/keys/#get-a-deploy-key>
+deployKeyForR :: Name Owner -> Name Repo -> Id RepoDeployKey -> Request 'RA RepoDeployKey
+deployKeyForR user repo keyId =
+    query ["repos", toPathPart user, toPathPart repo, "keys", toPathPart keyId] []
+
+-- | Create a deploy key.
+-- See <https://developer.github.com/v3/repos/keys/#add-a-new-deploy-key>.
+createRepoDeployKeyR :: Name Owner -> Name Repo -> NewRepoDeployKey -> Request 'RW RepoDeployKey
+createRepoDeployKeyR user repo key =
+    command Post ["repos", toPathPart user, toPathPart repo, "keys"] (encode key)
+
+-- | Delete a deploy key.
+-- See <https://developer.github.com/v3/repos/keys/#remove-a-deploy-key>
+deleteRepoDeployKeyR :: Name Owner -> Name Repo -> Id RepoDeployKey -> GenRequest 'MtUnit 'RW ()
+deleteRepoDeployKeyR user repo keyId =
+    Command Delete ["repos", toPathPart user, toPathPart repo, "keys", toPathPart keyId] mempty
diff --git a/src/GitHub/Endpoints/Repos/Deployments.hs b/src/GitHub/Endpoints/Repos/Deployments.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Deployments.hs
@@ -0,0 +1,72 @@
+-- | The deployments API, as described at <https://developer.github.com/v3/repos/deployments/>
+module GitHub.Endpoints.Repos.Deployments
+    ( deploymentsWithOptionsForR
+    , createDeploymentR
+
+    , deploymentStatusesForR
+    , createDeploymentStatusR
+
+    , module GitHub.Data
+    ) where
+
+import Control.Arrow (second)
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+
+-- | List deployments.
+-- See <https://developer.github.com/v3/repos/deployments/#list-deployments>
+deploymentsWithOptionsForR
+    :: FromJSON a
+    => Name Owner
+    -> Name Repo
+    -> FetchCount
+    -> [DeploymentQueryOption]
+    -> Request 'RA (Vector (Deployment a))
+deploymentsWithOptionsForR owner repo limit opts =
+    pagedQuery (deployPaths owner repo)
+        (map (second Just . renderDeploymentQueryOption) opts)
+        limit
+
+-- | Create a deployment.
+-- See <https://developer.github.com/v3/repos/deployments/#create-a-deployment>
+createDeploymentR
+    :: ( ToJSON   a
+       , FromJSON a
+       )
+    => Name Owner
+    -> Name Repo
+    -> CreateDeployment a
+    -> Request 'RW (Deployment a)
+createDeploymentR owner repo =
+    command Post (deployPaths owner repo) . encode
+
+-- | List deployment statuses.
+-- See <https://developer.github.com/v3/repos/deployments/#list-deployment-statuses>
+deploymentStatusesForR
+    :: Name Owner
+    -> Name Repo
+    -> Id (Deployment a)
+    -> FetchCount
+    -> Request 'RA (Vector DeploymentStatus)
+deploymentStatusesForR owner repo deploy =
+    pagedQuery (statusesPaths owner repo deploy) []
+
+-- | Create a deployment status.
+-- See <https://developer.github.com/v3/repos/deployments/#list-deployment-statuses>
+createDeploymentStatusR
+    :: Name Owner
+    -> Name Repo
+    -> Id (Deployment a)
+    -> CreateDeploymentStatus
+    -> Request 'RW DeploymentStatus
+createDeploymentStatusR owner repo deploy =
+    command Post (statusesPaths owner repo deploy) . encode
+
+statusesPaths :: Name Owner -> Name Repo -> Id (Deployment a) -> Paths
+statusesPaths owner repo deploy =
+    deployPaths owner repo ++ [toPathPart deploy, "statuses"]
+
+deployPaths :: Name Owner -> Name Repo -> Paths
+deployPaths owner repo =
+    ["repos", toPathPart owner, toPathPart repo, "deployments"]
diff --git a/src/GitHub/Endpoints/Repos/Forks.hs b/src/GitHub/Endpoints/Repos/Forks.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Forks.hs
@@ -0,0 +1,18 @@
+-- |
+-- Hot forking action, as described at
+-- <http://developer.github.com/v3/repos/forks/>.
+
+module GitHub.Endpoints.Repos.Forks (
+    forksForR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List forks.
+-- See <https://developer.github.com/v3/repos/forks/#list-forks>
+forksForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Repo)
+forksForR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "forks"] []
diff --git a/src/GitHub/Endpoints/Repos/Invitations.hs b/src/GitHub/Endpoints/Repos/Invitations.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Invitations.hs
@@ -0,0 +1,32 @@
+-- |
+-- The repo invitations API as described on
+-- <https://developer.github.com/v3/repos/invitations/>.
+
+module GitHub.Endpoints.Repos.Invitations (
+    listInvitationsOnR,
+    listInvitationsForR,
+    acceptInvitationFromR
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List open invitations of a repository
+-- See <https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository>
+listInvitationsOnR :: Name Owner -> Name Repo -> FetchCount -> GenRequest 'MtJSON k (Vector RepoInvitation)
+listInvitationsOnR user repo =
+    PagedQuery ["repos", toPathPart user, toPathPart repo, "invitations"] []
+
+-- | List a user's repository invitations
+-- See <https://developer.github.com/v3/repos/invitations/#list-a-users-repository-invitations>
+listInvitationsForR :: FetchCount -> Request k (Vector RepoInvitation)
+listInvitationsForR =
+    pagedQuery ["user", "repository_invitations"] []
+
+
+-- | Accept a repository invitation
+-- See <https://developer.github.com/v3/repos/invitations/#accept-a-repository-invitation>
+acceptInvitationFromR :: Id RepoInvitation -> GenRequest 'MtUnit 'RW ()
+acceptInvitationFromR invId =
+    Command Patch ["user", "repository_invitations", toPathPart invId] mempty
diff --git a/src/GitHub/Endpoints/Repos/Releases.hs b/src/GitHub/Endpoints/Repos/Releases.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Releases.hs
@@ -0,0 +1,49 @@
+-- The Release API, as described at
+-- <https://developer.github.com/v3/repos/releases/>.
+module GitHub.Endpoints.Repos.Releases (
+    releasesR,
+    releaseR,
+    latestReleaseR,
+    releaseByTagNameR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List releases for a repository.
+-- See <https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository>
+releasesR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector Release)
+releasesR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "releases"] []
+
+-- | Get a single release.
+-- See <https://developer.github.com/v3/repos/releases/#get-a-single-release>
+releaseR :: Name Owner -> Name Repo -> Id Release -> Request k Release
+releaseR user repo reqReleaseId =
+    query ["repos", toPathPart user, toPathPart repo, "releases", toPathPart reqReleaseId ] []
+
+-- | Get the latest release.
+-- See <https://developer.github.com/v3/repos/releases/#get-the-latest-release>
+latestReleaseR :: Name Owner -> Name Repo -> Request k Release
+latestReleaseR user repo =
+    query ["repos", toPathPart user, toPathPart repo, "releases", "latest" ] []
+
+-- | Get a release by tag name
+-- See <https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name>
+releaseByTagNameR :: Name Owner -> Name Repo -> Text -> Request k Release
+releaseByTagNameR user repo reqTagName =
+    query ["repos", toPathPart user, toPathPart repo, "releases", "tags" , reqTagName ] []
+
+{-
+--  TODO: implement the following:
+    https://developer.github.com/v3/repos/releases/#create-a-release
+    https://developer.github.com/v3/repos/releases/#edit-a-release
+    https://developer.github.com/v3/repos/releases/#delete-a-release
+    https://developer.github.com/v3/repos/releases/#list-assets-for-a-release
+    https://developer.github.com/v3/repos/releases/#upload-a-release-asset
+    https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
+    https://developer.github.com/v3/repos/releases/#edit-a-release-asset
+    https://developer.github.com/v3/repos/releases/#delete-a-release-asset
+-}
diff --git a/src/GitHub/Endpoints/Repos/Statuses.hs b/src/GitHub/Endpoints/Repos/Statuses.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Statuses.hs
@@ -0,0 +1,34 @@
+-- |
+-- The repo statuses API as described on
+-- <https://developer.github.com/v3/repos/statuses/>.
+
+module GitHub.Endpoints.Repos.Statuses (
+    createStatusR,
+    statusesForR,
+    statusForR,
+    module GitHub.Data
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Create a new status
+-- See <https://developer.github.com/v3/repos/statuses/#create-a-status>
+createStatusR :: Name Owner -> Name Repo -> Name Commit -> NewStatus -> Request 'RW Status
+createStatusR owner repo sha =
+    command Post parts . encode
+    where
+        parts = ["repos", toPathPart owner, toPathPart repo, "statuses", toPathPart sha]
+
+-- | All statuses for a commit
+-- See <https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref>
+statusesForR :: Name Owner -> Name Repo -> Name Commit -> FetchCount -> Request 'RW (Vector Status)
+statusesForR user repo sha =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "commits", toPathPart sha, "statuses"] []
+
+-- | The combined status for a specific commit
+-- See <https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref>
+statusForR :: Name Owner -> Name Repo -> Name Commit -> Request 'RW CombinedStatus
+statusForR user repo sha =
+    query ["repos", toPathPart user, toPathPart repo, "commits", toPathPart sha, "status"] []
diff --git a/src/GitHub/Endpoints/Repos/Webhooks.hs b/src/GitHub/Endpoints/Repos/Webhooks.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Repos/Webhooks.hs
@@ -0,0 +1,75 @@
+-- |
+-- The webhooks API, as described at
+-- <https://developer.github.com/v3/repos/hooks/>
+-- <https://developer.github.com/webhooks>
+
+module GitHub.Endpoints.Repos.Webhooks (
+    -- * Querying repositories
+    webhooksForR,
+    webhookForR,
+
+    -- ** Create
+    createRepoWebhookR,
+
+    -- ** Edit
+    editRepoWebhookR,
+
+    -- ** Test
+    testPushRepoWebhookR,
+    pingRepoWebhookR,
+
+    -- ** Delete
+    deleteRepoWebhookR,
+) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List hooks.
+-- See <https://developer.github.com/v3/repos/hooks/#list-hooks>
+webhooksForR :: Name Owner -> Name Repo -> FetchCount -> Request k (Vector RepoWebhook)
+webhooksForR user repo =
+    pagedQuery ["repos", toPathPart user, toPathPart repo, "hooks"] []
+-- See <https://developer.github.com/v3/repos/hooks/#get-single-hook>
+webhookForR :: Name Owner -> Name Repo -> Id RepoWebhook -> Request k RepoWebhook
+webhookForR user repo hookId =
+    query ["repos", toPathPart user, toPathPart repo, "hooks", toPathPart hookId] []
+
+-- | Create a hook.
+-- See <https://developer.github.com/v3/repos/hooks/#create-a-hook>
+createRepoWebhookR :: Name Owner -> Name Repo -> NewRepoWebhook -> Request 'RW RepoWebhook
+createRepoWebhookR user repo hook =
+    command Post ["repos", toPathPart user, toPathPart repo, "hooks"] (encode hook)
+
+-- | Edit a hook.
+-- See <https://developer.github.com/v3/repos/hooks/#edit-a-hook>
+editRepoWebhookR :: Name Owner -> Name Repo -> Id RepoWebhook -> EditRepoWebhook -> Request 'RW RepoWebhook
+editRepoWebhookR user repo hookId hookEdit =
+    command Patch ["repos", toPathPart user, toPathPart repo, "hooks", toPathPart hookId] (encode hookEdit)
+
+-- | Test a push hook.
+-- See <https://developer.github.com/v3/repos/hooks/#test-a-push-hook>
+testPushRepoWebhookR :: Name Owner -> Name Repo -> Id RepoWebhook -> GenRequest 'MtStatus 'RW Bool
+testPushRepoWebhookR user repo hookId =
+    Command Post (createWebhookOpPath user repo hookId $ Just "tests") (encode ())
+
+-- | Ping a hook.
+-- See <https://developer.github.com/v3/repos/hooks/#ping-a-hook>
+pingRepoWebhookR :: Name Owner -> Name Repo -> Id RepoWebhook -> GenRequest 'MtStatus 'RW Bool
+pingRepoWebhookR user repo hookId =
+    Command Post (createWebhookOpPath user repo hookId $ Just "pings") (encode ())
+
+-- | Delete a hook.
+-- See <https://developer.github.com/v3/repos/hooks/#delete-a-hook>
+deleteRepoWebhookR :: Name Owner -> Name Repo -> Id RepoWebhook -> GenRequest 'MtUnit 'RW ()
+deleteRepoWebhookR user repo hookId =
+    Command Delete (createWebhookOpPath user repo hookId Nothing) mempty
+
+createBaseWebhookPath :: Name Owner -> Name Repo -> Id RepoWebhook -> Paths
+createBaseWebhookPath user repo hookId =
+    ["repos", toPathPart user, toPathPart repo, "hooks", toPathPart hookId]
+
+createWebhookOpPath :: Name Owner -> Name Repo -> Id RepoWebhook -> Maybe Text -> Paths
+createWebhookOpPath owner reqName webhookId Nothing = createBaseWebhookPath owner reqName webhookId
+createWebhookOpPath owner reqName webhookId (Just operation) = createBaseWebhookPath owner reqName webhookId ++ [operation]
diff --git a/src/GitHub/Endpoints/Search.hs b/src/GitHub/Endpoints/Search.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Search.hs
@@ -0,0 +1,41 @@
+-- |
+-- The Github Search API, as described at
+-- <http://developer.github.com/v3/search/>.
+
+module GitHub.Endpoints.Search(
+    searchReposR,
+    searchCodeR,
+    searchIssuesR,
+    searchUsersR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import qualified Data.Text.Encoding as TE
+
+-- | Search repositories.
+-- See <https://developer.github.com/v3/search/#search-repositories>
+searchReposR :: Text -> FetchCount -> Request k (SearchResult Repo)
+searchReposR searchString =
+    PagedQuery ["search", "repositories"] [("q", Just $ TE.encodeUtf8 searchString)]
+
+-- | Search code.
+-- See <https://developer.github.com/v3/search/#search-code>
+searchCodeR :: Text -> FetchCount -> Request k (SearchResult Code)
+searchCodeR searchString =
+    PagedQuery ["search", "code"] [("q", Just $ TE.encodeUtf8 searchString)]
+
+-- | Search issues.
+-- See <https://developer.github.com/v3/search/#search-issues>
+searchIssuesR :: Text -> FetchCount -> Request k (SearchResult Issue)
+searchIssuesR searchString =
+    PagedQuery ["search", "issues"] [("q", Just $ TE.encodeUtf8 searchString)]
+
+-- | Search users.
+-- See <https://developer.github.com/v3/search/#search-code>
+searchUsersR :: Text -> FetchCount -> Request k (SearchResult SimpleUser)
+searchUsersR searchString =
+    PagedQuery ["search", "users"] [("q", Just $ TE.encodeUtf8 searchString)]
diff --git a/src/GitHub/Endpoints/Users.hs b/src/GitHub/Endpoints/Users.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Users.hs
@@ -0,0 +1,35 @@
+-- |
+-- The Github Users API, as described at
+-- <http://developer.github.com/v3/users/>.
+
+module GitHub.Endpoints.Users (
+    userInfoForR,
+    ownerInfoForR,
+    userInfoCurrentR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import Prelude ()
+
+-- | Query a single user.
+-- See <https://developer.github.com/v3/users/#get-a-single-user>
+--
+-- >>> github' userInfoForR "mike-burns"
+--
+-- or
+--
+-- >>> github userInfoForR (OAuth "github-token") "mike-burns"
+--
+userInfoForR :: Name User -> Request k User
+userInfoForR user = query ["users", toPathPart user] []
+
+-- | Query a single user or an organization.
+-- See <https://developer.github.com/v3/users/#get-a-single-user>
+ownerInfoForR :: Name Owner -> Request k Owner
+ownerInfoForR owner = query ["users", toPathPart owner] []
+
+-- | Query the authenticated user.
+-- See <https://developer.github.com/v3/users/#get-the-authenticated-user>
+userInfoCurrentR :: Request 'RA User
+userInfoCurrentR = query ["user"] []
diff --git a/src/GitHub/Endpoints/Users/Emails.hs b/src/GitHub/Endpoints/Users/Emails.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Users/Emails.hs
@@ -0,0 +1,25 @@
+-- |
+-- The user emails API as described on
+-- <http://developer.github.com/v3/users/emails/>.
+
+module GitHub.Endpoints.Users.Emails (
+    currentUserEmailsR,
+    currentUserPublicEmailsR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List email addresses.
+-- See <https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user>
+currentUserEmailsR :: FetchCount -> Request 'RA (Vector Email)
+currentUserEmailsR =
+    pagedQuery ["user", "emails"] []
+
+-- | List public email addresses.
+-- See <https://developer.github.com/v3/users/emails/#list-public-email-addresses-for-a-user>
+currentUserPublicEmailsR :: FetchCount -> Request 'RA (Vector Email)
+currentUserPublicEmailsR =
+    pagedQuery ["user", "public_emails"] []
diff --git a/src/GitHub/Endpoints/Users/Followers.hs b/src/GitHub/Endpoints/Users/Followers.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Users/Followers.hs
@@ -0,0 +1,25 @@
+-- |
+-- The user followers API as described on
+-- <http://developer.github.com/v3/users/followers/>.
+
+module GitHub.Endpoints.Users.Followers (
+    usersFollowingR,
+    usersFollowedByR,
+    module GitHub.Data,
+    ) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | List followers of a user.
+-- See <https://developer.github.com/v3/users/followers/#list-followers-of-a-user>
+usersFollowingR :: Name User -> FetchCount -> Request k (Vector SimpleUser)
+usersFollowingR user =
+    pagedQuery ["users", toPathPart user, "followers"] []
+
+-- | List users followed by another user.
+-- See <https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user>
+usersFollowedByR :: Name User -> FetchCount -> Request k (Vector SimpleUser)
+usersFollowedByR user =
+    pagedQuery ["users", toPathPart user, "following"] []
diff --git a/src/GitHub/Endpoints/Users/PublicSSHKeys.hs b/src/GitHub/Endpoints/Users/PublicSSHKeys.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Endpoints/Users/PublicSSHKeys.hs
@@ -0,0 +1,53 @@
+-----------------------------------------------------------------------------
+-- |
+-- License     :  BSD-3-Clause
+-- Maintainer  :  Todd Mohney <toddmohney@gmail.com>
+--
+-- The public keys API, as described at
+-- <https://developer.github.com/v3/users/keys/>
+module GitHub.Endpoints.Users.PublicSSHKeys (
+    -- * Querying public SSH keys
+    publicSSHKeysR,
+    publicSSHKeysForR,
+    publicSSHKeyR,
+
+    -- ** Create
+    createUserPublicSSHKeyR,
+
+    -- ** Delete
+    deleteUserPublicSSHKeyR,
+) where
+
+import GitHub.Data
+import GitHub.Internal.Prelude
+import Prelude ()
+
+-- | Querying public SSH keys.
+-- See <https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user>
+publicSSHKeysForR :: Name Owner -> FetchCount -> Request 'RO (Vector PublicSSHKeyBasic)
+publicSSHKeysForR user =
+    pagedQuery ["users", toPathPart user, "keys"] []
+
+-- | Querying the authenticated users' public SSH keys
+-- See <https://developer.github.com/v3/users/keys/#list-your-public-keys>
+publicSSHKeysR :: Request 'RA (Vector PublicSSHKey)
+publicSSHKeysR =
+    query ["user", "keys"] []
+
+-- | Querying a public SSH key.
+-- See <https://developer.github.com/v3/users/keys/#get-a-single-public-key>
+publicSSHKeyR :: Id PublicSSHKey -> Request 'RA PublicSSHKey
+publicSSHKeyR keyId =
+    query ["user", "keys", toPathPart keyId] []
+
+-- | Create a public SSH key.
+-- See <https://developer.github.com/v3/users/keys/#create-a-public-key>.
+createUserPublicSSHKeyR :: NewPublicSSHKey -> Request 'RW PublicSSHKey
+createUserPublicSSHKeyR key =
+    command Post ["user", "keys"] (encode key)
+
+-- | Delete a public SSH key.
+-- See <https://developer.github.com/v3/users/keys/#delete-a-public-key>
+deleteUserPublicSSHKeyR :: Id PublicSSHKey -> GenRequest 'MtUnit 'RW ()
+deleteUserPublicSSHKeyR keyId =
+    Command Delete ["user", "keys", toPathPart keyId] mempty
diff --git a/src/GitHub/Enterprise.hs b/src/GitHub/Enterprise.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Enterprise.hs
@@ -0,0 +1,19 @@
+-- |
+-- This module re-exports all request constructors and data definitions for
+-- working with GitHub Enterprise.
+
+module GitHub.Enterprise (
+    -- * Enterprise Admin
+    -- | See <https://developer.github.com/enterprise/v3/enterprise-admin/>
+
+    -- ** Organizations
+    -- | See <https://developer.github.com/enterprise/v3/enterprise-admin/orgs/>
+    createOrganizationR,
+    renameOrganizationR,
+
+    -- * Data definitions
+    module GitHub.Data.Enterprise,
+    ) where
+
+import GitHub.Data.Enterprise
+import GitHub.Endpoints.Enterprise.Organizations
diff --git a/src/GitHub/Internal/Prelude.hs b/src/GitHub/Internal/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Internal/Prelude.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+-- |
+-- This module may change between minor releases. Do not rely on its contents.
+
+module GitHub.Internal.Prelude ( module X ) where
+
+import Control.Applicative      as X ((<|>))
+import Control.DeepSeq          as X (NFData (..))
+import Data.Aeson               as X
+       (FromJSON (..), Object, ToJSON (..), Value (..), encode, object,
+       withObject, withText, (.!=), (.:), (.:?), (.=))
+import Data.Aeson.Types         as X (emptyObject, typeMismatch)
+import Data.Binary              as X (Binary)
+import Data.Binary.Instances    as X ()
+import Data.Data                as X (Data)
+import Data.Foldable            as X (toList)
+import Data.Hashable            as X (Hashable (..))
+import Data.HashMap.Strict      as X (HashMap)
+import Data.List                as X (intercalate)
+import Data.Maybe               as X (catMaybes)
+import Data.Semigroup           as X (Semigroup (..))
+import Data.String              as X (IsString (..))
+import Data.Text                as X (Text, pack, unpack)
+import Data.Time                as X (UTCTime)
+import Data.Time.ISO8601        as X (formatISO8601)
+import Data.Vector              as X (Vector)
+import GHC.Generics             as X (Generic)
+import Prelude.Compat           as X
+import Data.Functor.Compat      as X ((<&>))
diff --git a/src/GitHub/Request.hs b/src/GitHub/Request.hs
new file mode 100644
--- /dev/null
+++ b/src/GitHub/Request.hs
@@ -0,0 +1,588 @@
+{-# LANGUAGE CPP                    #-}
+{-# LANGUAGE FlexibleContexts       #-}
+{-# LANGUAGE FlexibleInstances      #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE GADTs                  #-}
+{-# LANGUAGE KindSignatures         #-}
+{-# LANGUAGE ScopedTypeVariables    #-}
+{-# LANGUAGE UndecidableInstances   #-}
+
+-- |
+-- This module provides data types and helper methods, which makes possible
+-- to build alternative API request intepreters in addition to provided
+-- 'IO' functions.
+--
+-- Simple example using @operational@ package. See @samples\/Operational\/Operational.hs@
+--
+-- > type GithubMonad a = Program (GH.Request 'False) a
+-- >
+-- > -- | Intepret GithubMonad value into IO
+-- > runMonad :: Manager -> GH.Auth -> GithubMonad a -> ExceptT GH.Error IO a
+-- > runMonad mgr auth m = case view m of
+-- >    Return a   -> return a
+-- >    req :>>= k -> do
+-- >        b <- ExceptT $ GH.executeRequestWithMgr mgr auth req
+-- >        runMonad mgr auth (k b)
+-- >
+-- > -- | Lift request into Monad
+-- > githubRequest :: GH.Request 'False a -> GithubMonad a
+-- > githubRequest = singleton
+
+module GitHub.Request (
+    -- * A convenient execution of requests
+    github,
+    github',
+    GitHubRW,
+    GitHubRO,
+    -- * Types
+    Request,
+    GenRequest (..),
+    CommandMethod(..),
+    toMethod,
+    Paths,
+    QueryString,
+    -- * Request execution in IO
+    executeRequest,
+    executeRequestWithMgr,
+    executeRequestWithMgrAndRes,
+    executeRequest',
+    executeRequestWithMgr',
+    executeRequestMaybe,
+    unsafeDropAuthRequirements,
+    -- * Helpers
+    Accept (..),
+    ParseResponse (..),
+    makeHttpRequest,
+    parseStatus,
+    parsePageLinks,
+    StatusMap,
+    getNextUrl,
+    performPagedRequest,
+    parseResponseJSON,
+    -- ** Preview
+    PreviewAccept (..),
+    PreviewParseResponse (..),
+    -- * SSL
+    -- | This always exist, independently of @openssl@ configuration flag.
+    -- They change accordingly, to make use of the library simpler.
+    withOpenSSL,
+    tlsManagerSettings,
+    ) where
+
+import GitHub.Internal.Prelude
+import Prelude ()
+
+import Control.Monad.Error.Class (MonadError (..))
+
+import Control.Monad              (when)
+import Control.Monad.Catch        (MonadCatch (..), MonadThrow)
+import Control.Monad.Trans.Class  (lift)
+import Control.Monad.Trans.Except (ExceptT (..), runExceptT)
+import Data.Aeson                 (eitherDecode)
+import Data.List                  (find)
+import Data.Maybe                 (fromMaybe)
+import Data.Tagged                (Tagged (..))
+import Data.Version               (showVersion)
+
+import Network.HTTP.Client
+       (HttpException (..), Manager, RequestBody (..), Response (..), getUri,
+       httpLbs, method, newManager, redirectCount, requestBody, requestHeaders,
+       setQueryString, setRequestIgnoreStatus)
+import Network.HTTP.Link.Parser (parseLinkHeaderBS)
+import Network.HTTP.Link.Types  (Link(..), LinkParam (..), href, linkParams)
+import Network.HTTP.Types       (Method, RequestHeaders, Status (..))
+import Network.URI
+       (URI, escapeURIString, isUnescapedInURIComponent, parseURIReference,
+       relativeTo)
+
+import qualified Data.ByteString              as BS
+import           Data.ByteString.Builder      (intDec, toLazyByteString)
+import qualified Data.ByteString.Lazy         as LBS
+import qualified Data.Text                    as T
+import qualified Data.Text.Encoding           as TE
+import qualified Network.HTTP.Client          as HTTP
+import qualified Network.HTTP.Client.Internal as HTTP
+
+#ifdef MIN_VERSION_http_client_tls
+import Network.HTTP.Client.TLS (tlsManagerSettings)
+#else
+import Network.HTTP.Client.OpenSSL (opensslManagerSettings, withOpenSSL)
+
+import qualified OpenSSL.Session          as SSL
+import qualified OpenSSL.X509.SystemStore as SSL
+#endif
+
+import GitHub.Auth              (AuthMethod, endpoint, setAuthRequest)
+import GitHub.Data              (Error (..))
+import GitHub.Data.PullRequests (MergeResult (..))
+import GitHub.Data.Request
+
+import Paths_github (version)
+
+-------------------------------------------------------------------------------
+-- Convenience
+-------------------------------------------------------------------------------
+
+-- | A convenience function to turn functions returning @'Request' rw x@,
+-- into functions returning @IO (Either 'Error' x)@.
+--
+-- >>> :t \auth -> github auth userInfoForR
+-- \auth -> github auth userInfoForR
+--   :: AuthMethod am => am -> Name User -> IO (Either Error User)
+--
+-- >>> :t github pullRequestsForR
+-- \auth -> github auth pullRequestsForR
+--   :: AuthMethod am =>
+--      am
+--      -> Name Owner
+--      -> Name Repo
+--      -> PullRequestMod
+--      -> FetchCount
+--      -> IO (Either Error (Data.Vector.Vector SimplePullRequest))
+--
+github :: (AuthMethod am, GitHubRW req res) => am -> req -> res
+github = githubImpl
+
+-- | Like 'github'' but for 'RO' i.e. read-only requests.
+-- Note that GitHub has low request limit for non-authenticated requests.
+--
+-- >>> :t github' userInfoForR
+-- github' userInfoForR :: Name User -> IO (Either Error User)
+--
+github' :: GitHubRO req res => req -> res
+github' = githubImpl'
+
+-- | A type-class implementing 'github'.
+class GitHubRW req res | req -> res where
+    githubImpl :: AuthMethod am => am -> req -> res
+
+-- | A type-class implementing 'github''.
+class GitHubRO req res | req -> res where
+    githubImpl' :: req -> res
+
+instance (ParseResponse mt req, res ~ Either Error req) => GitHubRW (GenRequest mt rw req) (IO res) where
+    githubImpl = executeRequest
+
+instance (ParseResponse mt req, res ~ Either Error req, rw ~ 'RO) => GitHubRO (GenRequest mt rw req) (IO res) where
+    githubImpl' = executeRequest'
+
+instance GitHubRW req res => GitHubRW (a -> req) (a -> res) where
+    githubImpl am req x = githubImpl am (req x)
+
+instance GitHubRO req res => GitHubRO (a -> req) (a -> res) where
+    githubImpl' req x = githubImpl' (req x)
+
+-------------------------------------------------------------------------------
+-- Execution
+-------------------------------------------------------------------------------
+
+#ifdef MIN_VERSION_http_client_tls
+withOpenSSL :: IO a -> IO a
+withOpenSSL = id
+#else
+tlsManagerSettings :: HTTP.ManagerSettings
+tlsManagerSettings = opensslManagerSettings $ do
+    ctx <- SSL.context
+    SSL.contextAddOption ctx SSL.SSL_OP_NO_SSLv2
+    SSL.contextAddOption ctx SSL.SSL_OP_NO_SSLv3
+    SSL.contextAddOption ctx SSL.SSL_OP_NO_TLSv1
+    SSL.contextSetCiphers ctx "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
+    SSL.contextLoadSystemCerts ctx
+    SSL.contextSetVerificationMode ctx $ SSL.VerifyPeer True True Nothing
+    return ctx
+#endif
+
+-- | Execute 'Request' in 'IO'
+executeRequest
+    :: (AuthMethod am, ParseResponse mt a)
+    => am
+    -> GenRequest mt rw a
+    -> IO (Either Error a)
+executeRequest auth req = withOpenSSL $ do
+    manager <- newManager tlsManagerSettings
+    executeRequestWithMgr manager auth req
+
+-- | Like 'executeRequest' but with provided 'Manager'.
+executeRequestWithMgr
+    :: (AuthMethod am, ParseResponse mt a)
+    => Manager
+    -> am
+    -> GenRequest mt rw a
+    -> IO (Either Error a)
+executeRequestWithMgr mgr auth req =
+    fmap (fmap responseBody) (executeRequestWithMgrAndRes mgr auth req)
+
+-- | Execute request and return the last received 'HTTP.Response'.
+--
+-- @since 0.24
+executeRequestWithMgrAndRes
+    :: (AuthMethod am, ParseResponse mt a)
+    => Manager
+    -> am
+    -> GenRequest mt rw a
+    -> IO (Either Error (HTTP.Response a))
+executeRequestWithMgrAndRes mgr auth req = runExceptT $ do
+    httpReq <- makeHttpRequest (Just auth) req
+    performHttpReq httpReq req
+  where
+    httpLbs' :: HTTP.Request -> ExceptT Error IO (HTTP.Response LBS.ByteString)
+    httpLbs' req' = lift (httpLbs req' mgr) `catch` onHttpException
+
+    performHttpReq :: forall rw mt b. ParseResponse mt b => HTTP.Request -> GenRequest mt rw b -> ExceptT Error IO (HTTP.Response b)
+    performHttpReq httpReq Query {} = do
+        res <- httpLbs' httpReq
+        (<$ res) <$> unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
+
+    performHttpReq httpReq (PagedQuery _ _ (FetchPage _)) = do
+        (res, _pageLinks) <- unTagged (performPerPageRequest httpLbs' httpReq :: Tagged mt (ExceptT Error IO (HTTP.Response b, PageLinks)))
+        return res
+    performHttpReq httpReq (PagedQuery _ _ FetchAll) =
+        unTagged (performPagedRequest httpLbs' (const True) httpReq :: Tagged mt (ExceptT Error IO (HTTP.Response b)))
+    performHttpReq httpReq (PagedQuery _ _ (FetchAtLeast j)) =
+        unTagged (performPagedRequest httpLbs' (\v -> length v < fromIntegral j) httpReq :: Tagged mt (ExceptT Error IO (HTTP.Response b)))
+
+    performHttpReq httpReq (Command _ _ _) = do
+        res <- httpLbs' httpReq
+        (<$ res) <$> unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
+
+-- | Like 'executeRequest' but without authentication.
+executeRequest' :: ParseResponse mt a => GenRequest mt 'RO a -> IO (Either Error a)
+executeRequest' req = withOpenSSL $ do
+    manager <- newManager tlsManagerSettings
+    executeRequestWithMgr' manager req
+
+-- | Like 'executeRequestWithMgr' but without authentication.
+executeRequestWithMgr'
+    :: ParseResponse mt a
+    => Manager
+    -> GenRequest mt 'RO a
+    -> IO (Either Error a)
+executeRequestWithMgr' mgr = executeRequestWithMgr mgr ()
+
+-- | Helper for picking between 'executeRequest' and 'executeRequest''.
+--
+-- The use is discouraged.
+executeRequestMaybe
+    :: (AuthMethod am, ParseResponse mt a)
+    => Maybe am
+    -> GenRequest mt 'RO a
+    -> IO (Either Error a)
+executeRequestMaybe = maybe executeRequest' executeRequest
+
+-- | Partial function to drop authentication need.
+unsafeDropAuthRequirements :: GenRequest mt rw' a -> GenRequest mt rw a
+unsafeDropAuthRequirements (Query ps qs) = Query ps qs
+unsafeDropAuthRequirements r             =
+    error $ "Trying to drop authenatication from" ++ show r
+
+-------------------------------------------------------------------------------
+-- Parse response
+-------------------------------------------------------------------------------
+
+class Accept (mt :: MediaType *) where
+    contentType :: Tagged mt BS.ByteString
+    contentType = Tagged "application/json" -- default is JSON
+
+    modifyRequest :: Tagged mt (HTTP.Request -> HTTP.Request)
+    modifyRequest = Tagged id
+
+class Accept mt => ParseResponse (mt :: MediaType *) a where
+    parseResponse
+        :: MonadError Error m
+        => HTTP.Request -> HTTP.Response LBS.ByteString
+        -> Tagged mt (m a)
+
+-------------------------------------------------------------------------------
+-- JSON (+ star)
+-------------------------------------------------------------------------------
+
+-- | Parse API response.
+--
+-- @
+-- parseResponse :: 'FromJSON' a => 'HTTP.Response' 'LBS.ByteString' -> 'Either' 'Error' a
+-- @
+parseResponseJSON :: (FromJSON a, MonadError Error m) => HTTP.Response LBS.ByteString -> m a
+parseResponseJSON res = case eitherDecode (responseBody res) of
+    Right x  -> return x
+    Left err -> throwError . ParseError . T.pack $ err
+
+instance Accept 'MtJSON where
+    contentType = Tagged "application/vnd.github.v3+json"
+
+instance FromJSON a => ParseResponse 'MtJSON a where
+    parseResponse _ res = Tagged (parseResponseJSON res)
+
+instance Accept 'MtStar where
+    contentType = Tagged "application/vnd.github.v3.star+json"
+
+instance FromJSON a => ParseResponse 'MtStar a where
+    parseResponse _ res = Tagged (parseResponseJSON res)
+
+-------------------------------------------------------------------------------
+-- Raw / Diff / Patch / Sha
+-------------------------------------------------------------------------------
+
+instance Accept 'MtRaw   where contentType = Tagged "application/vnd.github.v3.raw"
+instance Accept 'MtDiff  where contentType = Tagged "application/vnd.github.v3.diff"
+instance Accept 'MtPatch where contentType = Tagged "application/vnd.github.v3.patch"
+instance Accept 'MtSha   where contentType = Tagged "application/vnd.github.v3.sha"
+
+instance a ~ LBS.ByteString => ParseResponse 'MtRaw   a where parseResponse _ = Tagged . return . responseBody
+instance a ~ LBS.ByteString => ParseResponse 'MtDiff  a where parseResponse _ = Tagged . return . responseBody
+instance a ~ LBS.ByteString => ParseResponse 'MtPatch a where parseResponse _ = Tagged . return . responseBody
+instance a ~ LBS.ByteString => ParseResponse 'MtSha   a where parseResponse _ = Tagged . return . responseBody
+
+-------------------------------------------------------------------------------
+-- Redirect
+-------------------------------------------------------------------------------
+
+instance Accept 'MtRedirect where
+    modifyRequest = Tagged $ \req ->
+        setRequestIgnoreStatus $ req { redirectCount = 0 }
+
+instance b ~ URI => ParseResponse 'MtRedirect b where
+    parseResponse req = Tagged . parseRedirect (getUri req)
+
+-- | Helper for handling of 'RequestRedirect'.
+--
+-- @
+-- parseRedirect :: 'HTTP.Response' 'LBS.ByteString' -> 'Either' 'Error' a
+-- @
+parseRedirect :: MonadError Error m => URI -> HTTP.Response LBS.ByteString -> m URI
+parseRedirect originalUri rsp = do
+    let status = responseStatus rsp
+    when (statusCode status /= 302) $
+        throwError $ ParseError $ "invalid status: " <> T.pack (show status)
+    loc <- maybe noLocation return $ lookup "Location" $ responseHeaders rsp
+    case parseURIReference $ T.unpack $ TE.decodeUtf8 loc of
+        Nothing -> throwError $ ParseError $
+            "location header does not contain a URI: " <> T.pack (show loc)
+        Just uri -> return $ uri `relativeTo` originalUri
+  where
+    noLocation = throwError $ ParseError "no location header in response"
+
+-------------------------------------------------------------------------------
+-- Extension point
+-------------------------------------------------------------------------------
+
+class PreviewAccept p where
+    previewContentType :: Tagged ('MtPreview p) BS.ByteString
+
+    previewModifyRequest :: Tagged ('MtPreview p) (HTTP.Request -> HTTP.Request)
+    previewModifyRequest = Tagged id
+
+class PreviewAccept p => PreviewParseResponse p a where
+    previewParseResponse
+        :: MonadError Error m
+        => HTTP.Request -> HTTP.Response LBS.ByteString
+        -> Tagged ('MtPreview p) (m a)
+
+instance PreviewAccept p => Accept ('MtPreview p) where
+    contentType   = previewContentType
+    modifyRequest = previewModifyRequest
+
+instance PreviewParseResponse p a => ParseResponse ('MtPreview p) a where
+    parseResponse = previewParseResponse
+
+-------------------------------------------------------------------------------
+-- Status
+-------------------------------------------------------------------------------
+
+instance Accept 'MtStatus where
+    modifyRequest = Tagged setRequestIgnoreStatus
+
+instance HasStatusMap a => ParseResponse 'MtStatus a where
+    parseResponse _ = Tagged . parseStatus statusMap . responseStatus
+
+type StatusMap a = [(Int, a)]
+
+class HasStatusMap a where
+    statusMap :: StatusMap a
+
+instance HasStatusMap Bool where
+    statusMap =
+        [ (204, True)
+        , (404, False)
+        ]
+
+instance HasStatusMap MergeResult where
+    statusMap =
+        [ (200, MergeSuccessful)
+        , (405, MergeCannotPerform)
+        , (409, MergeConflict)
+        ]
+
+-- | Helper for handling of 'RequestStatus'.
+--
+-- @
+-- parseStatus :: 'StatusMap' a -> 'Status' -> 'Either' 'Error' a
+-- @
+parseStatus :: MonadError Error m => StatusMap a -> Status -> m a
+parseStatus m (Status sci _) =
+    maybe err return $ lookup sci m
+  where
+    err = throwError $ JsonError $ "invalid status: " <> T.pack (show sci)
+
+-------------------------------------------------------------------------------
+-- Unit
+-------------------------------------------------------------------------------
+
+-- | Note: we don't ignore response status.
+--
+-- We only accept any response body.
+instance Accept 'MtUnit where
+
+instance a ~ () => ParseResponse 'MtUnit a where
+    parseResponse _ _ = Tagged (return ())
+
+------------------------------------------------------------------------------
+-- Tools
+------------------------------------------------------------------------------
+
+-- | Create @http-client@ 'Request'.
+--
+-- * for 'PagedQuery', the initial request is created.
+-- * for 'Status', the 'Request' for underlying 'Request' is created,
+--   status checking is modifying accordingly.
+--
+makeHttpRequest
+    :: forall am mt rw a m. (AuthMethod am, MonadThrow m, Accept mt)
+    => Maybe am
+    -> GenRequest mt rw a
+    -> m HTTP.Request
+makeHttpRequest auth r = case r of
+    Query paths qs -> do
+        req <- parseUrl' $ url paths
+        return
+            $ setReqHeaders
+            . unTagged (modifyRequest :: Tagged mt (HTTP.Request -> HTTP.Request))
+            . maybe id setAuthRequest auth
+            . setQueryString (qs <> extraQueryItems)
+            $ req
+    PagedQuery paths qs _ -> do
+        req <- parseUrl' $ url paths
+        return
+            $ setReqHeaders
+            . unTagged (modifyRequest :: Tagged mt (HTTP.Request -> HTTP.Request))
+            . maybe id setAuthRequest auth
+            . setQueryString (qs <> extraQueryItems)
+            $ req
+    Command m paths body -> do
+        req <- parseUrl' $ url paths
+        return
+            $ setReqHeaders
+            . unTagged (modifyRequest :: Tagged mt (HTTP.Request -> HTTP.Request))
+            . maybe id setAuthRequest auth
+            . setBody body
+            . setMethod (toMethod m)
+            $ req
+  where
+    parseUrl' :: MonadThrow m => String -> m HTTP.Request
+    parseUrl' = HTTP.parseUrlThrow
+
+    url :: Paths -> String
+    url paths = maybe "https://api.github.com" T.unpack (endpoint =<< auth) ++ "/" ++ intercalate "/" paths' where
+        paths' = map (escapeURIString isUnescapedInURIComponent . T.unpack) paths
+
+    setReqHeaders :: HTTP.Request -> HTTP.Request
+    setReqHeaders req = req { requestHeaders = reqHeaders <> requestHeaders req }
+
+    setMethod :: Method -> HTTP.Request -> HTTP.Request
+    setMethod m req = req { method = m }
+
+    reqHeaders :: RequestHeaders
+    reqHeaders = [("User-Agent", "github.hs/" <> fromString (showVersion version))] -- Version
+        <> [("Accept", unTagged (contentType :: Tagged mt BS.ByteString))]
+
+    setBody :: LBS.ByteString -> HTTP.Request -> HTTP.Request
+    setBody body req = req { requestBody = RequestBodyLBS body }
+
+    extraQueryItems :: [(BS.ByteString, Maybe BS.ByteString)]
+    extraQueryItems = case r of
+      PagedQuery _ _ (FetchPage pp) -> catMaybes [
+          (\page -> ("page", Just (LBS.toStrict $ toLazyByteString $ intDec page))) <$> pageParamsPage pp
+          , (\perPage -> ("per_page", Just (LBS.toStrict $ toLazyByteString $ intDec perPage))) <$> pageParamsPerPage pp
+          ]
+      _ -> []
+
+-- | Query @Link@ header with @rel=next@ from the request headers.
+getNextUrl :: HTTP.Response a -> Maybe URI
+getNextUrl req = do
+    linkHeader <- lookup "Link" (responseHeaders req)
+    links <- parseLinkHeaderBS linkHeader
+    nextURI <- find isRelNext links
+    return $ href nextURI
+  where
+    -- isRelNext :: Link -> Bool or Link uri -> Bool
+    isRelNext = any (== relNextLinkParam) . linkParams
+
+    relNextLinkParam :: (LinkParam, Text)
+    relNextLinkParam = (Rel, "next")
+
+-- | Helper for making paginated requests. Responses, @a@ are combined monoidally.
+--
+-- The result is wrapped in the last received 'HTTP.Response'.
+--
+-- @
+-- performPagedRequest :: ('FromJSON' a, 'Semigroup' a)
+--                     => ('HTTP.Request' -> 'ExceptT' 'Error' 'IO' ('HTTP.Response' 'LBS.ByteString'))
+--                     -> (a -> 'Bool')
+--                     -> 'HTTP.Request'
+--                     -> 'ExceptT' 'Error' 'IO' ('HTTP.Response' a)
+-- @
+performPagedRequest
+    :: forall a m mt. (ParseResponse mt a, Semigroup a, MonadCatch m, MonadError Error m)
+    => (HTTP.Request -> m (HTTP.Response LBS.ByteString))  -- ^ `httpLbs` analogue
+    -> (a -> Bool)                                         -- ^ predicate to continue iteration
+    -> HTTP.Request                                        -- ^ initial request
+    -> Tagged mt (m (HTTP.Response a))
+performPagedRequest httpLbs' predicate initReq = Tagged $ do
+    res <- httpLbs' initReq
+    m <- unTagged (parseResponse initReq res :: Tagged mt (m a))
+    go m res initReq
+  where
+    go :: a -> HTTP.Response LBS.ByteString -> HTTP.Request -> m (HTTP.Response a)
+    go acc res req =
+        case (predicate acc, getNextUrl res) of
+            (True, Just uri) -> do
+                req' <- HTTP.setUri req uri
+                res' <- httpLbs' req'
+                m <- unTagged (parseResponse req' res' :: Tagged mt (m a))
+                go (acc <> m) res' req'
+            (_, _)           -> return (acc <$ res)
+
+-- | Helper for requesting a single page, as specified by 'PageParams'.
+--
+-- This parses and returns the 'PageLinks' alongside the HTTP response.
+performPerPageRequest
+    :: forall a m mt. (ParseResponse mt a, MonadCatch m, MonadError Error m)
+    => (HTTP.Request -> m (HTTP.Response LBS.ByteString))  -- ^ `httpLbs` analogue
+    -> HTTP.Request                                        -- ^ initial request
+    -> Tagged mt (m (HTTP.Response a, PageLinks))
+performPerPageRequest httpLbs' initReq = Tagged $ do
+    res <- httpLbs' initReq
+    m <- unTagged (parseResponse initReq res :: Tagged mt (m a))
+    return (m <$ res, parsePageLinks res)
+
+-- | Parse the 'PageLinks' from an HTTP response, where the information is
+-- encoded in the Link header.
+parsePageLinks :: HTTP.Response a -> PageLinks
+parsePageLinks res = PageLinks {
+    pageLinksPrev = linkToUri <$> find (elem (Rel, "prev") . linkParams) links
+    , pageLinksNext = linkToUri <$> find (elem (Rel, "next") . linkParams) links
+    , pageLinksLast = linkToUri <$> find (elem (Rel, "last") . linkParams) links
+    , pageLinksFirst = linkToUri <$> find (elem (Rel, "first") . linkParams) links
+    }
+    where
+        links :: [Link URI]
+        links = fromMaybe [] (lookup "Link" (responseHeaders res) >>= parseLinkHeaderBS)
+
+        linkToUri :: Link URI -> URI
+        linkToUri (Link uri _) = uri
+
+-------------------------------------------------------------------------------
+-- Internal
+-------------------------------------------------------------------------------
+
+onHttpException :: MonadError Error m => HttpException -> m a
+onHttpException = throwError . HTTPError
