Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions github/copilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,152 @@ import (
// GitHub API docs: https://docs.github.com/rest/copilot?apiVersion=2022-11-28
type CopilotService service

// CopilotSpace represents a Copilot Space.
type CopilotSpace struct {
ID int64 `json:"id"`
Number int `json:"number"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
GeneralInstructions *string `json:"general_instructions,omitempty"`
Owner User `json:"owner"`
Creator User `json:"creator"`
CreatedAt Timestamp `json:"created_at"`
UpdatedAt Timestamp `json:"updated_at"`
HTMLURL string `json:"html_url"`
APIURL string `json:"api_url"`
BaseRole string `json:"base_role"`
ResourcesAttributes []*CopilotSpaceResource `json:"resources_attributes,omitempty"`
}

// CopilotSpaceResource represents a resource attached to a Copilot Space.
type CopilotSpaceResource struct {
ID *int64 `json:"id,omitempty"`
ResourceType *string `json:"resource_type,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`

@alexandear alexandear Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's introduce a separate struct that represents the Metadata

Image
                      metadata:
                        type: object
                        description: Metadata specific to the resource type.
                        properties:
                          repository_id:
                            type: integer
                            description: Repository ID for repository or file resources.
                          file_path:
                            type: string
                            description: File path for file resources.
                          text:
                            type: string
                            description: Text content for free text resources.
                          name:
                            type: string
                            description: Name for the resource.
                          number:
                            type: integer
                            description: Issue or PR number.

}

// CopilotSpaceRequest represents a request to create or update a Copilot Space.
type CopilotSpaceRequest struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
GeneralInstructions *string `json:"general_instructions,omitempty"`
BaseRole *string `json:"base_role,omitempty"`
ResourcesAttributes []*CopilotSpaceResource `json:"resources_attributes,omitempty"`
}
Comment on lines +48 to +55

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use this struct for both create and update. I propose introducing the following:

// CreateOrganizationCopilotSpaceRequest represents a request to create a Copilot Space.
type CreateOrganizationCopilotSpaceRequest struct {
	Name                string                  `json:"name"`
	Description         *string                 `json:"description,omitempty"`
	GeneralInstructions *string                 `json:"general_instructions,omitempty"`
	BaseRole            *string                 `json:"base_role,omitempty"`
	ResourcesAttributes []*CopilotSpaceResource `json:"resources_attributes,omitempty"`
}

// UpdateOrganizationCopilotSpaceRequest represents a request to update a Copilot Space.
type UpdateOrganizationCopilotSpaceRequest struct {
	Name                *string                 `json:"name,omitempty"`
	Description         *string                 `json:"description,omitempty"`
	GeneralInstructions *string                 `json:"general_instructions,omitempty"`
	BaseRole            *string                 `json:"base_role,omitempty"`
	ResourcesAttributes []*CopilotSpaceResource `json:"resources_attributes,omitempty"`
}


// CopilotSpacesList represents a list of Copilot Spaces.
type CopilotSpacesList struct {
Spaces []*CopilotSpace `json:"spaces"`
}

// ListOrganizationCopilotSpaces lists Copilot Spaces for an organization.
//
// GitHub API docs: https://docs.github.com/rest/copilot-spaces/copilot-spaces?apiVersion=2022-11-28#list-organization-copilot-spaces
//
//meta:operation GET /orgs/{org}/copilot-spaces
func (s *CopilotService) ListOrganizationCopilotSpaces(ctx context.Context, org string, opts *ListCursorOptions) (*CopilotSpacesList, *Response, error) {
u := fmt.Sprintf("orgs/%v/copilot-spaces", org)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest(ctx, "GET", u, nil)
if err != nil {
return nil, nil, err
}

var spaces *CopilotSpacesList
resp, err := s.client.Do(req, &spaces)
if err != nil {
return nil, resp, err
}

return spaces, resp, nil
}

// GetOrganizationCopilotSpace gets a Copilot Space for an organization.
//
// GitHub API docs: https://docs.github.com/rest/copilot-spaces/copilot-spaces?apiVersion=2022-11-28#get-an-organization-copilot-space
//
//meta:operation GET /orgs/{org}/copilot-spaces/{space_number}
func (s *CopilotService) GetOrganizationCopilotSpace(ctx context.Context, org string, spaceNumber int) (*CopilotSpace, *Response, error) {
u := fmt.Sprintf("orgs/%v/copilot-spaces/%v", org, spaceNumber)

req, err := s.client.NewRequest(ctx, "GET", u, nil)
if err != nil {
return nil, nil, err
}

var space *CopilotSpace
resp, err := s.client.Do(req, &space)
if err != nil {
return nil, resp, err
}

return space, resp, nil
}

// CreateOrganizationCopilotSpace creates a Copilot Space for an organization.
//
// GitHub API docs: https://docs.github.com/rest/copilot-spaces/copilot-spaces?apiVersion=2022-11-28#create-an-organization-copilot-space
//
//meta:operation POST /orgs/{org}/copilot-spaces
func (s *CopilotService) CreateOrganizationCopilotSpace(ctx context.Context, org string, body CopilotSpaceRequest) (*CopilotSpace, *Response, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use separate body structs for CreateOrganizationCopilotSpace and UpdateOrganizationCopilotSpace.

Body parameters for these endpoints differ and we can't use the single CopilotSpaceRequest for both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair. I dug deeper onto separate parameters create and update accept and found that update 's resources_attributes have one more nested param called _destroy, which Create endpoint does not.

update: forgot to send this message earlier, but the curl example shows a _destroy param to delete an item from the collection, but it is not shown in the list of params shown as required or not. Is this a github docs error?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2026-07-14 at 3 51 41 PM

u := fmt.Sprintf("orgs/%v/copilot-spaces", org)

req, err := s.client.NewRequest(ctx, "POST", u, body)
if err != nil {
return nil, nil, err
}

var space *CopilotSpace
resp, err := s.client.Do(req, &space)
if err != nil {
return nil, resp, err
}

return space, resp, nil
}

// UpdateOrganizationCopilotSpace updates a Copilot Space for an organization.
//
// GitHub API docs: https://docs.github.com/rest/copilot-spaces/copilot-spaces?apiVersion=2022-11-28#set-an-organization-copilot-space
//
//meta:operation PUT /orgs/{org}/copilot-spaces/{space_number}
func (s *CopilotService) UpdateOrganizationCopilotSpace(ctx context.Context, org string, spaceNumber int, body CopilotSpaceRequest) (*CopilotSpace, *Response, error) {
u := fmt.Sprintf("orgs/%v/copilot-spaces/%v", org, spaceNumber)

req, err := s.client.NewRequest(ctx, "PUT", u, body)
if err != nil {
return nil, nil, err
}

var space *CopilotSpace
resp, err := s.client.Do(req, &space)
if err != nil {
return nil, resp, err
}

return space, resp, nil
}

// DeleteOrganizationCopilotSpace deletes a Copilot Space for an organization.
//
// GitHub API docs: https://docs.github.com/rest/copilot-spaces/copilot-spaces?apiVersion=2022-11-28#delete-an-organization-copilot-space
//
//meta:operation DELETE /orgs/{org}/copilot-spaces/{space_number}
func (s *CopilotService) DeleteOrganizationCopilotSpace(ctx context.Context, org string, spaceNumber int) (*Response, error) {
u := fmt.Sprintf("orgs/%v/copilot-spaces/%v", org, spaceNumber)

req, err := s.client.NewRequest(ctx, "DELETE", u, nil)
if err != nil {
return nil, err
}

return s.client.Do(req, nil)
}

// CopilotOrganizationDetails represents the details of an organization's Copilot for Business subscription.
type CopilotOrganizationDetails struct {
SeatBreakdown *CopilotSeatBreakdown `json:"seat_breakdown"`
Expand Down
Loading
Loading