Repository: google/go-github
Pull request #2826 (2023-07-04 11:00:03)

Add support for `personal_access_token_request` webhook event type
This PR adds support for the [`personal_access_token_request`](https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads#personal_access_token_request) webhook event.

> This event occurs when there is activity relating to a request for a fine-grained personal access token to access resources that belong to a resource owner that requires approval for token access.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/google/go-github/pull/2826?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#2826](https://app.codecov.io/gh/google/go-github/pull/2826?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (cf895e5) into [master](https://app.codecov.io/gh/google/go-github/commit/9f7124cc0903ad531ba469d2f1403fb3e39645cf?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (9f7124c) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #2826   +/-   ##
=======================================
  Coverage   98.06%   98.06%           
=======================================
  Files         136      136           
  Lines       12279    12281    +2     
=======================================
+ Hits        12041    12043    +2     
  Misses        162      162           
  Partials       76       76           
```


| [Impacted Files](https://app.codecov.io/gh/google/go-github/pull/2826?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Coverage Δ | |
|---|---|---|
| [github/event\_types.go](https://app.codecov.io/gh/google/go-github/pull/2826?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL2V2ZW50X3R5cGVzLmdv) | `100.00% <ø> (ø)` | |
| [github/messages.go](https://app.codecov.io/gh/google/go-github/pull/2826?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL21lc3NhZ2VzLmdv) | `100.00% <ø> (ø)` | |
| [github/event.go](https://app.codecov.io/gh/google/go-github/pull/2826?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL2V2ZW50Lmdv) | `100.00% <100.00%> (ø)` | |


joaopenteado: Thanks for the very quick response, @gmlewis!
I've just committed the documentation changes requested. Let me know if you want any additional changes.
joaopenteado: @gmlewis upon further testing, I've actually found that this webhook delivers an map in the API. This isn't very well documented IMO but could be inferred I guess. I've updated the event type to fix this. Here's an excerpt from a webhook event I tested:

```json
{
  "action": "created",
  "personal_access_token_request": {
    "id": 11233,
    "owner": { /* ... */ },
    "repository_selection": "all",
    "repository_count": null,
    "repositories": null,
    "permissions_added": {
      "organization": {
        "organization_announcement_banners": "write",
        "organization_events": "read"
      },
      "repository": {
        "codespaces_lifecycle_admin": "read",
        "metadata": "read",
        "security_events": "write"
      }
    },
    "permissions_upgraded": {},
    "permissions_result": {
      "organization": {
        "organization_announcement_banners": "write",
        "organization_events": "read"
      },
      "repository": {
        "codespaces_lifecycle_admin": "read",
        "metadata": "read",
        "security_events": "write"
      }
    },
    "created_at": "2023-07-07T00:37:57+09:00",
    "token_expired": false,
    "token_expires_at": "2023-08-05T15:37:56Z",
    "token_last_used_at": null
  },
  "organization": { /* ... */ },
  "sender": { /* ... */},
  "installation": {
    "id": 1111111,
    "node_id": "xxxxxxxxxxxxxx"
  }
```

Patch:
diff --git a/github/event.go b/github/event.go
index 4ee25603..4ee60e33 100644
--- a/github/event.go
+++ b/github/event.go
@@ -93,6 +93,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
 		payload = &PackageEvent{}
 	case "PageBuildEvent":
 		payload = &PageBuildEvent{}
+	case "PersonalAccessTokenRequestEvent":
+		payload = &PersonalAccessTokenRequestEvent{}
 	case "PingEvent":
 		payload = &PingEvent{}
 	case "ProjectEvent":
diff --git a/github/event_types.go b/github/event_types.go
index 6a13b286..fca897af 100644
--- a/github/event_types.go
+++ b/github/event_types.go
@@ -774,6 +774,74 @@ type PageBuildEvent struct {
 	Installation *Installation `json:"installation,omitempty"`
 }
 
+// PersonalAccessTokenRequestEvent occurs when there is activity relating to a
+// request for a fine-grained personal access token to access resources that
+// belong to a resource owner that requires approval for token access.
+// The webhook event name is "personal_access_token_request".
+//
+// GitHub API docs: https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads#personal_access_token_request
+type PersonalAccessTokenRequestEvent struct {
+	// Action is the action that was performed. Possible values are:
+	// "approved", "cancelled", "created" or "denied"
+	Action                     *string                     `json:"action,omitempty"`
+	PersonalAccessTokenRequest *PersonalAccessTokenRequest `json:"personal_access_token_request,omitempty"`
+	Org                        *Organization               `json:"organization,omitempty"`
+	Sender                     *User                       `json:"sender,omitempty"`
+	Installation               *Installation               `json:"installation,omitempty"`
+}
+
+// PersonalAccessTokenRequest contains the details of a PersonalAccessTokenRequestEvent.
+type PersonalAccessTokenRequest struct {
+	// Unique identifier of the request for access via fine-grained personal
+	// access token. Used as the pat_request_id parameter in the list and review
+	// API calls.
+	ID    *int64 `json:"id,omitempty"`
+	Owner *User  `json:"owner,omitempty"`
+
+	// New requested permissions, categorized by type of permission.
+	PermissionsAdded *PersonalAccessTokenPermissions `json:"permissions_added,omitempty"`
+
+	// Requested permissions that elevate access for a previously approved
+	// request for access, categorized by type of permission.
+	PermissionsUpgraded *PersonalAccessTokenPermissions `json:"permissions_upgraded,omitempty"`
+
+	// Permissions requested, categorized by type of permission.
+	// This field incorporates permissions_added and permissions_upgraded.
+	PermissionsResult *PersonalAccessTokenPermissions `json:"permissions_result,omitempty"`
+
+	// Type of repository selection requested. Possible values are:
+	// "none", "all" or "subset"
+	RepositorySelection *string `json:"repository_selection,omitempty"`
+
+	// The number of repositories the token is requesting access to.
+	// This field is only populated when repository_selection is subset.
+	RepositoryCount *int64 `json:"repository_count,omitempty"`
+
+	// An array of repository objects the token is requesting access to.
+	// This field is only populated when repository_selection is subset.
+	Repositories []*Repository `json:"repositories,omitempty"`
+
+	// Date and time when the request for access was created.
+	CreatedAt *Timestamp `json:"created_at,omitempty"`
+
+	// Whether the associated fine-grained personal access token has expired.
+	TokenExpired *bool `json:"token_expired,omitempty"`
+
+	// Date and time when the associated fine-grained personal access token expires.
+	TokenExpiresAt *Timestamp `json:"token_expires_at,omitempty"`
+
+	// Date and time when the associated fine-grained personal access token was last used for authentication.
+	TokenLastUsedAt *Timestamp `json:"token_last_used_at,omitempty"`
+}
+
+// PersonalAccessTokenPermissions represents the original or newly requested
+// scope of permissions for a fine-grained personal access token within a PersonalAccessTokenRequest.
+type PersonalAccessTokenPermissions struct {
+	Org   map[string]string `json:"organization,omitempty"`
+	Repo  map[string]string `json:"repository,omitempty"`
+	Other map[string]string `json:"other,omitempty"`
+}
+
 // PingEvent is triggered when a Webhook is added to GitHub.
 //
 // GitHub API docs: https://developer.github.com/webhooks/#ping-event
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 41cd582c..19d7bd47 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -13134,6 +13134,158 @@ func (p *PagesUpdate) GetSource() *PagesSource {
 	return p.Source
 }
 
+// GetOrg returns the Org map if it's non-nil, an empty map otherwise.
+func (p *PersonalAccessTokenPermissions) GetOrg() map[string]string {
+	if p == nil || p.Org == nil {
+		return map[string]string{}
+	}
+	return p.Org
+}
+
+// GetOther returns the Other map if it's non-nil, an empty map otherwise.
+func (p *PersonalAccessTokenPermissions) GetOther() map[string]string {
+	if p == nil || p.Other == nil {
+		return map[string]string{}
+	}
+	return p.Other
+}
+
+// GetRepo returns the Repo map if it's non-nil, an empty map otherwise.
+func (p *PersonalAccessTokenPermissions) GetRepo() map[string]string {
+	if p == nil || p.Repo == nil {
+		return map[string]string{}
+	}
+	return p.Repo
+}
+
+// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetCreatedAt() Timestamp {
+	if p == nil || p.CreatedAt == nil {
+		return Timestamp{}
+	}
+	return *p.CreatedAt
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetID() int64 {
+	if p == nil || p.ID == nil {
+		return 0
+	}
+	return *p.ID
+}
+
+// GetOwner returns the Owner field.
+func (p *PersonalAccessTokenRequest) GetOwner() *User {
+	if p == nil {
+		return nil
+	}
+	return p.Owner
+}
+
+// GetPermissionsAdded returns the PermissionsAdded field.
+func (p *PersonalAccessTokenRequest) GetPermissionsAdded() *PersonalAccessTokenPermissions {
+	if p == nil {
+		return nil
+	}
+	return p.PermissionsAdded
+}
+
+// GetPermissionsResult returns the PermissionsResult field.
+func (p *PersonalAccessTokenRequest) GetPermissionsResult() *PersonalAccessTokenPermissions {
+	if p == nil {
+		return nil
+	}
+	return p.PermissionsResult
+}
+
+// GetPermissionsUpgraded returns the PermissionsUpgraded field.
+func (p *PersonalAccessTokenRequest) GetPermissionsUpgraded() *PersonalAccessTokenPermissions {
+	if p == nil {
+		return nil
+	}
+	return p.PermissionsUpgraded
+}
+
+// GetRepositoryCount returns the RepositoryCount field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetRepositoryCount() int64 {
+	if p == nil || p.RepositoryCount == nil {
+		return 0
+	}
+	return *p.RepositoryCount
+}
+
+// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetRepositorySelection() string {
+	if p == nil || p.RepositorySelection == nil {
+		return ""
+	}
+	return *p.RepositorySelection
+}
+
+// GetTokenExpired returns the TokenExpired field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetTokenExpired() bool {
+	if p == nil || p.TokenExpired == nil {
+		return false
+	}
+	return *p.TokenExpired
+}
+
+// GetTokenExpiresAt returns the TokenExpiresAt field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetTokenExpiresAt() Timestamp {
+	if p == nil || p.TokenExpiresAt == nil {
+		return Timestamp{}
+	}
+	return *p.TokenExpiresAt
+}
+
+// GetTokenLastUsedAt returns the TokenLastUsedAt field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequest) GetTokenLastUsedAt() Timestamp {
+	if p == nil || p.TokenLastUsedAt == nil {
+		return Timestamp{}
+	}
+	return *p.TokenLastUsedAt
+}
+
+// GetAction returns the Action field if it's non-nil, zero value otherwise.
+func (p *PersonalAccessTokenRequestEvent) GetAction() string {
+	if p == nil || p.Action == nil {
+		return ""
+	}
+	return *p.Action
+}
+
+// GetInstallation returns the Installation field.
+func (p *PersonalAccessTokenRequestEvent) GetInstallation() *Installation {
+	if p == nil {
+		return nil
+	}
+	return p.Installation
+}
+
+// GetOrg returns the Org field.
+func (p *PersonalAccessTokenRequestEvent) GetOrg() *Organization {
+	if p == nil {
+		return nil
+	}
+	return p.Org
+}
+
+// GetPersonalAccessTokenRequest returns the PersonalAccessTokenRequest field.
+func (p *PersonalAccessTokenRequestEvent) GetPersonalAccessTokenRequest() *PersonalAccessTokenRequest {
+	if p == nil {
+		return nil
+	}
+	return p.PersonalAccessTokenRequest
+}
+
+// GetSender returns the Sender field.
+func (p *PersonalAccessTokenRequestEvent) GetSender() *User {
+	if p == nil {
+		return nil
+	}
+	return p.Sender
+}
+
 // GetHook returns the Hook field.
 func (p *PingEvent) GetHook() *Hook {
 	if p == nil {
@@ -18806,6 +18958,14 @@ func (r *ReviewersRequest) GetNodeID() string {
 	return *r.NodeID
 }
 
+// GetReason returns the Reason field if it's non-nil, zero value otherwise.
+func (r *ReviewPersonalAccessTokenRequestOptions) GetReason() string {
+	if r == nil || r.Reason == nil {
+		return ""
+	}
+	return *r.Reason
+}
+
 // GetDescription returns the Description field if it's non-nil, zero value otherwise.
 func (r *Rule) GetDescription() string {
 	if r == nil || r.Description == nil {
diff --git a/github/messages.go b/github/messages.go
index bb5ae3f3..e61ef1c0 100644
--- a/github/messages.go
+++ b/github/messages.go
@@ -76,6 +76,7 @@ var (
 		"org_block":                      "OrgBlockEvent",
 		"package":                        "PackageEvent",
 		"page_build":                     "PageBuildEvent",
+		"personal_access_token_request":  "PersonalAccessTokenRequestEvent",
 		"ping":                           "PingEvent",
 		"project":                        "ProjectEvent",
 		"project_card":                   "ProjectCardEvent",
diff --git a/github/orgs_personal_access_tokens.go b/github/orgs_personal_access_tokens.go
new file mode 100644
index 00000000..c30ff284
--- /dev/null
+++ b/github/orgs_personal_access_tokens.go
@@ -0,0 +1,34 @@
+// Copyright 2023 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+)
+
+// ReviewPersonalAccessTokenRequestOptions specifies the parameters to the ReviewPersonalAccessTokenRequest method.
+type ReviewPersonalAccessTokenRequestOptions struct {
+	Action string  `json:"action"`
+	Reason *string `json:"reason,omitempty"`
+}
+
+// ReviewPersonalAccessTokenRequest approves or denies a pending request to access organization resources via a fine-grained personal access token.
+// Only GitHub Apps can call this API, using the `organization_personal_access_token_requests: write` permission.
+// `action` can be one of `approve` or `deny`.
+//
+// GitHub API docs: https://docs.github.com/en/rest/orgs/personal-access-tokens?apiVersion=2022-11-28#review-a-request-to-access-organization-resources-with-a-fine-grained-personal-access-token
+func (s *OrganizationsService) ReviewPersonalAccessTokenRequest(ctx context.Context, org string, requestID int64, opts ReviewPersonalAccessTokenRequestOptions) (*Response, error) {
+	u := fmt.Sprintf("orgs/%v/personal-access-token-requests/%v", org, requestID)
+
+	req, err := s.client.NewRequest(http.MethodPost, u, &opts)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}


Test patch:
diff --git a/github/event_types_test.go b/github/event_types_test.go
index db284036..298477fe 100644
--- a/github/event_types_test.go
+++ b/github/event_types_test.go
@@ -7089,6 +7089,80 @@ func TestPackageEvent_Marshal(t *testing.T) {
 	testJSONMarshal(t, u, want)
 }
 
+func TestPersonalAccessTokenRequestEvent_Marshal(t *testing.T) {
+	testJSONMarshal(t, &PersonalAccessTokenRequestEvent{}, "{}")
+
+	event := &PersonalAccessTokenRequestEvent{
+		Action: String("a"),
+		PersonalAccessTokenRequest: &PersonalAccessTokenRequest{
+			ID:    Int64(1),
+			Owner: &User{Login: String("l")},
+			PermissionsAdded: &PersonalAccessTokenPermissions{
+				Org:  map[string]string{"organization_events": "read"},
+				Repo: map[string]string{"security_events": "write"},
+			},
+			CreatedAt:           &Timestamp{referenceTime},
+			TokenExpired:        Bool(false),
+			TokenExpiresAt:      &Timestamp{referenceTime},
+			TokenLastUsedAt:     &Timestamp{referenceTime},
+			RepositoryCount:     Int64(1),
+			RepositorySelection: String("rs"),
+			Repositories: []*Repository{
+				{
+					Name: String("n"),
+				},
+			},
+		},
+		Org: &Organization{Name: String("n")},
+		Sender: &User{
+			Login: String("l"),
+		},
+		Installation: &Installation{
+			ID: Int64(1),
+		},
+	}
+
+	want := `{
+		"action": "a",
+		"personal_access_token_request": {
+			"id": 1,
+			"owner": {
+				"login": "l"
+			},
+			"permissions_added": {
+				"organization": {
+					"organization_events": "read"
+				},
+				"repository": {
+					"security_events": "write"
+				}
+			},
+			"created_at": ` + referenceTimeStr + `,
+			"token_expired": false,
+			"token_expires_at": ` + referenceTimeStr + `,
+			"token_last_used_at": ` + referenceTimeStr + `,
+			"repository_count": 1,
+			"repository_selection": "rs",
+			"repositories": [
+				{
+					"name": "n"
+				}
+			]
+		},
+		"organization": {
+			"name": "n"
+		},
+		"sender": {
+			"login": "l"
+		},
+		"installation": {
+			"id": 1
+		}
+	}`
+
+	testJSONMarshal(t, event, want)
+}
+
 func TestPingEvent_Marshal(t *testing.T) {
 	testJSONMarshal(t, &PingEvent{}, "{}")
 
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 993a1248..5cdf5ff5 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -15422,6 +15422,172 @@ func TestPagesUpdate_GetSource(tt *testing.T) {
 	p.GetSource()
 }
 
+func TestPersonalAccessTokenPermissions_GetOrg(tt *testing.T) {
+	zeroValue := map[string]string{}
+	p := &PersonalAccessTokenPermissions{Org: zeroValue}
+	p.GetOrg()
+	p = &PersonalAccessTokenPermissions{}
+	p.GetOrg()
+	p = nil
+	p.GetOrg()
+}
+
+func TestPersonalAccessTokenPermissions_GetOther(tt *testing.T) {
+	zeroValue := map[string]string{}
+	p := &PersonalAccessTokenPermissions{Other: zeroValue}
+	p.GetOther()
+	p = &PersonalAccessTokenPermissions{}
+	p.GetOther()
+	p = nil
+	p.GetOther()
+}
+
+func TestPersonalAccessTokenPermissions_GetRepo(tt *testing.T) {
+	zeroValue := map[string]string{}
+	p := &PersonalAccessTokenPermissions{Repo: zeroValue}
+	p.GetRepo()
+	p = &PersonalAccessTokenPermissions{}
+	p.GetRepo()
+	p = nil
+	p.GetRepo()
+}
+
+func TestPersonalAccessTokenRequest_GetCreatedAt(tt *testing.T) {
+	var zeroValue Timestamp
+	p := &PersonalAccessTokenRequest{CreatedAt: &zeroValue}
+	p.GetCreatedAt()
+	p = &PersonalAccessTokenRequest{}
+	p.GetCreatedAt()
+	p = nil
+	p.GetCreatedAt()
+}
+
+func TestPersonalAccessTokenRequest_GetID(tt *testing.T) {
+	var zeroValue int64
+	p := &PersonalAccessTokenRequest{ID: &zeroValue}
+	p.GetID()
+	p = &PersonalAccessTokenRequest{}
+	p.GetID()
+	p = nil
+	p.GetID()
+}
+
+func TestPersonalAccessTokenRequest_GetOwner(tt *testing.T) {
+	p := &PersonalAccessTokenRequest{}
+	p.GetOwner()
+	p = nil
+	p.GetOwner()
+}
+
+func TestPersonalAccessTokenRequest_GetPermissionsAdded(tt *testing.T) {
+	p := &PersonalAccessTokenRequest{}
+	p.GetPermissionsAdded()
+	p = nil
+	p.GetPermissionsAdded()
+}
+
+func TestPersonalAccessTokenRequest_GetPermissionsResult(tt *testing.T) {
+	p := &PersonalAccessTokenRequest{}
+	p.GetPermissionsResult()
+	p = nil
+	p.GetPermissionsResult()
+}
+
+func TestPersonalAccessTokenRequest_GetPermissionsUpgraded(tt *testing.T) {
+	p := &PersonalAccessTokenRequest{}
+	p.GetPermissionsUpgraded()
+	p = nil
+	p.GetPermissionsUpgraded()
+}
+
+func TestPersonalAccessTokenRequest_GetRepositoryCount(tt *testing.T) {
+	var zeroValue int64
+	p := &PersonalAccessTokenRequest{RepositoryCount: &zeroValue}
+	p.GetRepositoryCount()
+	p = &PersonalAccessTokenRequest{}
+	p.GetRepositoryCount()
+	p = nil
+	p.GetRepositoryCount()
+}
+
+func TestPersonalAccessTokenRequest_GetRepositorySelection(tt *testing.T) {
+	var zeroValue string
+	p := &PersonalAccessTokenRequest{RepositorySelection: &zeroValue}
+	p.GetRepositorySelection()
+	p = &PersonalAccessTokenRequest{}
+	p.GetRepositorySelection()
+	p = nil
+	p.GetRepositorySelection()
+}
+
+func TestPersonalAccessTokenRequest_GetTokenExpired(tt *testing.T) {
+	var zeroValue bool
+	p := &PersonalAccessTokenRequest{TokenExpired: &zeroValue}
+	p.GetTokenExpired()
+	p = &PersonalAccessTokenRequest{}
+	p.GetTokenExpired()
+	p = nil
+	p.GetTokenExpired()
+}
+
+func TestPersonalAccessTokenRequest_GetTokenExpiresAt(tt *testing.T) {
+	var zeroValue Timestamp
+	p := &PersonalAccessTokenRequest{TokenExpiresAt: &zeroValue}
+	p.GetTokenExpiresAt()
+	p = &PersonalAccessTokenRequest{}
+	p.GetTokenExpiresAt()
+	p = nil
+	p.GetTokenExpiresAt()
+}
+
+func TestPersonalAccessTokenRequest_GetTokenLastUsedAt(tt *testing.T) {
+	var zeroValue Timestamp
+	p := &PersonalAccessTokenRequest{TokenLastUsedAt: &zeroValue}
+	p.GetTokenLastUsedAt()
+	p = &PersonalAccessTokenRequest{}
+	p.GetTokenLastUsedAt()
+	p = nil
+	p.GetTokenLastUsedAt()
+}
+
+func TestPersonalAccessTokenRequestEvent_GetAction(tt *testing.T) {
+	var zeroValue string
+	p := &PersonalAccessTokenRequestEvent{Action: &zeroValue}
+	p.GetAction()
+	p = &PersonalAccessTokenRequestEvent{}
+	p.GetAction()
+	p = nil
+	p.GetAction()
+}
+
+func TestPersonalAccessTokenRequestEvent_GetInstallation(tt *testing.T) {
+	p := &PersonalAccessTokenRequestEvent{}
+	p.GetInstallation()
+	p = nil
+	p.GetInstallation()
+}
+
+func TestPersonalAccessTokenRequestEvent_GetOrg(tt *testing.T) {
+	p := &PersonalAccessTokenRequestEvent{}
+	p.GetOrg()
+	p = nil
+	p.GetOrg()
+}
+
+func TestPersonalAccessTokenRequestEvent_GetPersonalAccessTokenRequest(tt *testing.T) {
+	p := &PersonalAccessTokenRequestEvent{}
+	p.GetPersonalAccessTokenRequest()
+	p = nil
+	p.GetPersonalAccessTokenRequest()
+}
+
+func TestPersonalAccessTokenRequestEvent_GetSender(tt *testing.T) {
+	p := &PersonalAccessTokenRequestEvent{}
+	p.GetSender()
+	p = nil
+	p.GetSender()
+}
+
 func TestPingEvent_GetHook(tt *testing.T) {
 	p := &PingEvent{}
 	p.GetHook()
@@ -21933,6 +22099,16 @@ func TestReviewersRequest_GetNodeID(tt *testing.T) {
 	r.GetNodeID()
 }
 
+func TestReviewPersonalAccessTokenRequestOptions_GetReason(tt *testing.T) {
+	var zeroValue string
+	r := &ReviewPersonalAccessTokenRequestOptions{Reason: &zeroValue}
+	r.GetReason()
+	r = &ReviewPersonalAccessTokenRequestOptions{}
+	r.GetReason()
+	r = nil
+	r.GetReason()
+}
+
 func TestRule_GetDescription(tt *testing.T) {
 	var zeroValue string
 	r := &Rule{Description: &zeroValue}
diff --git a/github/messages_test.go b/github/messages_test.go
index 80fb3e5f..5230b87a 100644
--- a/github/messages_test.go
+++ b/github/messages_test.go
@@ -380,6 +380,10 @@ func TestParseWebHook(t *testing.T) {
 			payload:     &PageBuildEvent{},
 			messageType: "page_build",
 		},
+		{
+			payload:     &PersonalAccessTokenRequestEvent{},
+			messageType: "personal_access_token_request",
+		},
 		{
 			payload:     &PingEvent{},
 			messageType: "ping",
diff --git a/github/orgs_personal_access_tokens_test.go b/github/orgs_personal_access_tokens_test.go
new file mode 100644
index 00000000..93ee1b3d
--- /dev/null
+++ b/github/orgs_personal_access_tokens_test.go
@@ -0,0 +1,73 @@
+// Copyright 2023 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestOrganizationsService_ReviewPersonalAccessTokenRequest(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := ReviewPersonalAccessTokenRequestOptions{
+		Action: "a",
+		Reason: String("r"),
+	}
+
+	mux.HandleFunc("/orgs/o/personal-access-token-requests/1", func(w http.ResponseWriter, r *http.Request) {
+		v := new(ReviewPersonalAccessTokenRequestOptions)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, http.MethodPost)
+		if !cmp.Equal(v, &input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ctx := context.Background()
+	res, err := client.Organizations.ReviewPersonalAccessTokenRequest(ctx, "o", 1, input)
+	if err != nil {
+		t.Errorf("Organizations.ReviewPersonalAccessTokenRequest returned error: %v", err)
+	}
+
+	if res.StatusCode != http.StatusNoContent {
+		t.Errorf("Organizations.ReviewPersonalAccessTokenRequest returned %v, want %v", res.StatusCode, http.StatusNoContent)
+	}
+
+	const methodName = "ReviewPersonalAccessTokenRequest"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Organizations.ReviewPersonalAccessTokenRequest(ctx, "\n", 0, input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Organizations.ReviewPersonalAccessTokenRequest(ctx, "o", 1, input)
+	})
+}
+
+func TestReviewPersonalAccessTokenRequestOptions_Marshal(t *testing.T) {
+	testJSONMarshal(t, &ReviewPersonalAccessTokenRequestOptions{}, "{}")
+
+	u := &ReviewPersonalAccessTokenRequestOptions{
+		Action: "a",
+		Reason: String("r"),
+	}
+
+	want := `{
+		"action": "a",
+		"reason": "r"
+	}`
+
+	testJSONMarshal(t, u, want)
+}