diff --git a/github/actions_workflow_jobs.go b/github/actions_workflow_jobs.go
new file mode 100644
index 00000000000..d9adad7de4d
--- /dev/null
+++ b/github/actions_workflow_jobs.go
@@ -0,0 +1,137 @@
+// Copyright 2020 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"
+	"net/url"
+)
+
+// TaskStep represents a single task step from a sequence of tasks of a job.
+type TaskStep struct {
+	Name        *string    `json:"name,omitempty"`
+	Status      *string    `json:"status,omitempty"`
+	Conclusion  *string    `json:"conclusion,omitempty"`
+	Number      *int64     `json:"number,omitempty"`
+	StartedAt   *Timestamp `json:"started_at,omitempty"`
+	CompletedAt *Timestamp `json:"completed_at,omitempty"`
+}
+
+// WorkflowJob represents a repository action workflow job.
+type WorkflowJob struct {
+	ID          *int64      `json:"id,omitempty"`
+	RunID       *int64      `json:"run_id,omitempty"`
+	RunURL      *string     `json:"run_url,omitempty"`
+	NodeID      *string     `json:"node_id,omitempty"`
+	HeadSHA     *string     `json:"head_sha,omitempty"`
+	URL         *string     `json:"url,omitempty"`
+	HTMLURL     *string     `json:"html_url,omitempty"`
+	Status      *string     `json:"status,omitempty"`
+	Conclusion  *string     `json:"conclusion,omitempty"`
+	StartedAt   *Timestamp  `json:"started_at,omitempty"`
+	CompletedAt *Timestamp  `json:"completed_at,omitempty"`
+	Name        *string     `json:"name,omitempty"`
+	Steps       []*TaskStep `json:"steps,omitempty"`
+	CheckRunURL *string     `json:"check_run_url,omitempty"`
+}
+
+// Jobs represents a slice of repository action workflow job.
+type Jobs struct {
+	TotalCount *int           `json:"total_count,omitempty"`
+	Jobs       []*WorkflowJob `json:"jobs,omitempty"`
+}
+
+// ListWorkflowJobs lists all jobs for a workflow run.
+//
+// GitHub API docs: https://developer.github.com/v3/actions/workflow_jobs/#list-jobs-for-a-workflow-run
+func (s *ActionsService) ListWorkflowJobs(ctx context.Context, owner, repo string, runID int64, opts *ListOptions) (*Jobs, *Response, error) {
+	u := fmt.Sprintf("repos/%s/%s/actions/runs/%v/jobs", owner, repo, runID)
+	u, err := addOptions(u, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	jobs := new(Jobs)
+	resp, err := s.client.Do(ctx, req, &jobs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return jobs, resp, nil
+}
+
+// GetWorkflowJobByID gets a specific job in a workflow run by ID.
+//
+// GitHub API docs: https://developer.github.com/v3/actions/workflow_jobs/#list-jobs-for-a-workflow-run
+func (s *ActionsService) GetWorkflowJobByID(ctx context.Context, owner, repo string, jobID int64) (*WorkflowJob, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v", owner, repo, jobID)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(WorkflowJob)
+	resp, err := s.client.Do(ctx, req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, nil
+}
+
+// GetWorkflowJobLogs gets a redirect URL to download a plain text file of logs for a workflow job.
+//
+// GitHub API docs: https://developer.github.com/v3/actions/workflow_jobs/#list-workflow-job-logs
+func (s *ActionsService) GetWorkflowJobLogs(ctx context.Context, owner, repo string, jobID int64, followRedirects bool) (*url.URL, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v/logs", owner, repo, jobID)
+
+	resp, err := s.getWorkflowJobLogsFromURL(ctx, u, followRedirects)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if resp.StatusCode != http.StatusFound {
+		return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
+	}
+	parsedURL, err := url.Parse(resp.Header.Get("Location"))
+	return parsedURL, newResponse(resp), err
+}
+
+func (s *ActionsService) getWorkflowJobLogsFromURL(ctx context.Context, u string, followRedirects bool) (*http.Response, error) {
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var resp *http.Response
+	// Use http.DefaultTransport if no custom Transport is configured
+	req = withContext(ctx, req)
+	if s.client.client.Transport == nil {
+		resp, err = http.DefaultTransport.RoundTrip(req)
+	} else {
+		resp, err = s.client.client.Transport.RoundTrip(req)
+	}
+	if err != nil {
+		return nil, err
+	}
+	resp.Body.Close()
+
+	// If redirect response is returned, follow it
+	if followRedirects && resp.StatusCode == http.StatusMovedPermanently {
+		u = resp.Header.Get("Location")
+		resp, err = s.getWorkflowJobLogsFromURL(ctx, u, false)
+	}
+	return resp, err
+
+}
diff --git a/github/actions_workflow_jobs_test.go b/github/actions_workflow_jobs_test.go
new file mode 100644
index 00000000000..36cf7a82ea4
--- /dev/null
+++ b/github/actions_workflow_jobs_test.go
@@ -0,0 +1,136 @@
+// Copyright 2020 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"
+	"net/url"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestActionsService_ListWorkflowJobs(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testFormValues(t, r, values{"per_page": "2", "page": "2"})
+		fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}]}`)
+	})
+
+	opts := &ListOptions{Page: 2, PerPage: 2}
+	jobs, _, err := client.Actions.ListWorkflowJobs(context.Background(), "o", "r", 29679449, opts)
+	if err != nil {
+		t.Errorf("Actions.ListWorkflowJobs returned error: %v", err)
+	}
+
+	want := &Jobs{
+		TotalCount: Int(4),
+		Jobs: []*WorkflowJob{
+			{ID: Int64(399444496), RunID: Int64(29679449), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
+			{ID: Int64(399444497), RunID: Int64(29679449), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
+		},
+	}
+	if !reflect.DeepEqual(jobs, want) {
+		t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want)
+	}
+}
+
+func TestActionsService_GetWorkflowJobByID(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/jobs/399444496", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"id":399444496,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}`)
+	})
+
+	job, _, err := client.Actions.GetWorkflowJobByID(context.Background(), "o", "r", 399444496)
+	if err != nil {
+		t.Errorf("Actions.GetWorkflowJobByID returned error: %v", err)
+	}
+
+	want := &WorkflowJob{
+		ID:          Int64(399444496),
+		StartedAt:   &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)},
+		CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)},
+	}
+	if !reflect.DeepEqual(job, want) {
+		t.Errorf("Actions.GetWorkflowJobByID returned %+v, want %+v", job, want)
+	}
+}
+
+func TestActionsService_GetWorkflowJobLogs(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
+	})
+
+	url, resp, err := client.Actions.GetWorkflowJobLogs(context.Background(), "o", "r", 399444496, true)
+	if err != nil {
+		t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err)
+	}
+	if resp.StatusCode != http.StatusFound {
+		t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound)
+	}
+	want := "http://github.com/a"
+	if url.String() != want {
+		t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want)
+	}
+}
+
+func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently)
+	})
+
+	_, resp, _ := client.Actions.GetWorkflowJobLogs(context.Background(), "o", "r", 399444496, false)
+	if resp.StatusCode != http.StatusMovedPermanently {
+		t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
+	}
+}
+
+func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_followRedirects(t *testing.T) {
+	client, mux, serverURL, teardown := setup()
+	defer teardown()
+
+	// Mock a redirect link, which leads to an archive link
+	mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
+		http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
+	})
+
+	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
+	})
+
+	url, resp, err := client.Actions.GetWorkflowJobLogs(context.Background(), "o", "r", 399444496, true)
+	if err != nil {
+		t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err)
+	}
+
+	if resp.StatusCode != http.StatusFound {
+		t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound)
+	}
+
+	want := "http://github.com/a"
+	if url.String() != want {
+		t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want)
+	}
+}
diff --git a/github/github-accessors.go b/github/github-accessors.go
index a1f3e0c19a3..ecd6a762535 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -4972,6 +4972,14 @@ func (i *IssueStats) GetTotalIssues() int {
 	return *i.TotalIssues
 }
 
+// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise.
+func (j *Jobs) GetTotalCount() int {
+	if j == nil || j.TotalCount == nil {
+		return 0
+	}
+	return *j.TotalCount
+}
+
 // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
 func (k *Key) GetCreatedAt() Timestamp {
 	if k == nil || k.CreatedAt == nil {
@@ -11916,6 +11924,54 @@ func (t *Tag) GetVerification() *SignatureVerification {
 	return t.Verification
 }
 
+// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise.
+func (t *TaskStep) GetCompletedAt() Timestamp {
+	if t == nil || t.CompletedAt == nil {
+		return Timestamp{}
+	}
+	return *t.CompletedAt
+}
+
+// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise.
+func (t *TaskStep) GetConclusion() string {
+	if t == nil || t.Conclusion == nil {
+		return ""
+	}
+	return *t.Conclusion
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (t *TaskStep) GetName() string {
+	if t == nil || t.Name == nil {
+		return ""
+	}
+	return *t.Name
+}
+
+// GetNumber returns the Number field if it's non-nil, zero value otherwise.
+func (t *TaskStep) GetNumber() int64 {
+	if t == nil || t.Number == nil {
+		return 0
+	}
+	return *t.Number
+}
+
+// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise.
+func (t *TaskStep) GetStartedAt() Timestamp {
+	if t == nil || t.StartedAt == nil {
+		return Timestamp{}
+	}
+	return *t.StartedAt
+}
+
+// GetStatus returns the Status field if it's non-nil, zero value otherwise.
+func (t *TaskStep) GetStatus() string {
+	if t == nil || t.Status == nil {
+		return ""
+	}
+	return *t.Status
+}
+
 // GetDescription returns the Description field if it's non-nil, zero value otherwise.
 func (t *Team) GetDescription() string {
 	if t == nil || t.Description == nil {
@@ -13932,6 +13988,110 @@ func (w *Workflow) GetURL() string {
 	return *w.URL
 }
 
+// GetCheckRunURL returns the CheckRunURL field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetCheckRunURL() string {
+	if w == nil || w.CheckRunURL == nil {
+		return ""
+	}
+	return *w.CheckRunURL
+}
+
+// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetCompletedAt() Timestamp {
+	if w == nil || w.CompletedAt == nil {
+		return Timestamp{}
+	}
+	return *w.CompletedAt
+}
+
+// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetConclusion() string {
+	if w == nil || w.Conclusion == nil {
+		return ""
+	}
+	return *w.Conclusion
+}
+
+// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetHeadSHA() string {
+	if w == nil || w.HeadSHA == nil {
+		return ""
+	}
+	return *w.HeadSHA
+}
+
+// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetHTMLURL() string {
+	if w == nil || w.HTMLURL == nil {
+		return ""
+	}
+	return *w.HTMLURL
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetID() int64 {
+	if w == nil || w.ID == nil {
+		return 0
+	}
+	return *w.ID
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetName() string {
+	if w == nil || w.Name == nil {
+		return ""
+	}
+	return *w.Name
+}
+
+// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetNodeID() string {
+	if w == nil || w.NodeID == nil {
+		return ""
+	}
+	return *w.NodeID
+}
+
+// GetRunID returns the RunID field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetRunID() int64 {
+	if w == nil || w.RunID == nil {
+		return 0
+	}
+	return *w.RunID
+}
+
+// GetRunURL returns the RunURL field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetRunURL() string {
+	if w == nil || w.RunURL == nil {
+		return ""
+	}
+	return *w.RunURL
+}
+
+// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetStartedAt() Timestamp {
+	if w == nil || w.StartedAt == nil {
+		return Timestamp{}
+	}
+	return *w.StartedAt
+}
+
+// GetStatus returns the Status field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetStatus() string {
+	if w == nil || w.Status == nil {
+		return ""
+	}
+	return *w.Status
+}
+
+// GetURL returns the URL field if it's non-nil, zero value otherwise.
+func (w *WorkflowJob) GetURL() string {
+	if w == nil || w.URL == nil {
+		return ""
+	}
+	return *w.URL
+}
+
 // GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise.
 func (w *Workflows) GetTotalCount() int {
 	if w == nil || w.TotalCount == nil {