Custom GPT - Action to Confluence API

Hi - I am attempting to add an action to one of my custom GPTs in ChatGPT to connect to the Atlassian Confluence cloud to read content on the site. I’ve set up the oAuth 2.0 authentication/authorization, scopes, etc. I have also been building out the OpenAI schema for the API; however, I believe I am receiving errors in parsing. Any guidance would be appreciated.


{
  "openapi": "3.1.0",
  "info": {
    "title": "Confluence",
    "description": "API integration with Confluence Cloud for accessing and managing content within Confluence spaces at XXXX.",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://XXXXX.atlassian.net/wiki/rest/api"
    }
  ],
  "paths": {

    "/labels/{id}/pages": {
      "get": {
        "summary": "Get pages for label",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier for the label",
            "schema": {
              "type": "string"
            }
          }],
        "responses": {
          "200": {
            "description": "Successful response with pages for the label",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageResponse"
                }
              }
            }
          }
        }
      }
    },
    "/pages": {
      "get": {
        "summary": "Get pages",
        "responses": {
          "200": {
            "description": "Successful response with list of pages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageResponse"
                }
              }
            }
          }
        }
      }
    },
    "/pages/{id}": {
      "get": {
        "summary": "Get page by id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier for the page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with page details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              }
            }
          }
        }
      }
    },
    "/spaces/{id}/pages": {
      "get": {
        "summary": "Get pages in space",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier for the space",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with pages in the space",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageResponse"
                }
              }
            }
          }
        }
      }
    },
  "/attachments/{id}/footer-comments": {
    "get": {
      "summary": "Get attachment comments",
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The unique identifier for the attachment",
          "schema": {
            "type": "string"
          }
        }
      ],
      "responses": {
        "200": {
          "description": "Successful response with comments for the attachment",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommentResponse"
              }
            }
          }
        }
      }
    }
  },
  "/spaces": {
    "get": {
      "summary": "Get spaces",
      "description": "Returns all spaces. The results will be sorted by id ascending.",
      "responses": {
        "200": {
          "description": "Successful response with list of spaces",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Space"
              }
            }
          }
        }
      }
    }
  },
  "/spaces/{id}": {
    "get": {
      "summary": "Get space by id",
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "The unique identifier for the space",
          "schema": {
            "type": "string"
          }
        }
      ],
      "responses": {
        "200": {
          "description": "Successful response with space details",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Space"
              }
            }
          }
        }
      }
    }
  },

  "/footer-comments/{comment-id}": {
    "get": {
      "summary": "Get footer comment by id",
      "parameters": [
        {
          "name": "comment-id",
          "in": "path",
          "required": true,
          "description": "The unique identifier for the footer comment",
          "schema": {
            "type": "string"
          }
        }
      ],
      "responses": {
        "200": {
          "description": "Successful response with footer comment details",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Comment"
              }
            }
          }
        }
      }
    }
  },

  "components": {
    "schemas": {

      "PageResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Page"
            }
          },
          "_links": {
            "type": "object",
            "properties": {
              "next": {
                "type": "string"
              }
            }
          }
        }
      },
      "Page": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "status": { "type": "string" },
          "title": { "type": "string" },
          "spaceId": { "type": "string" },
          "parentId": { "type": "string" },
          "parentType": { "type": "string" },
          "position": { "type": "integer" },
          "authorId": { "type": "string" },
          "ownerId": { "type": "string" },
          "lastOwnerId": { "type": "string" },
          "createdAt": { "type": "string" },
          "version": {
            "type": "object",
            "properties": {
              "createdAt": { "type": "string" },
              "message": { "type": "string" },
              "number": { "type": "integer" },
              "minorEdit": { "type": "boolean" },
              "authorId": { "type": "string" }
            }
          },
          "body": {
            "type": "object",
            "properties": {
              "storage": { "type": "object" },
              "atlas_doc_format": { "type": "object" }
            }
          },
          "_links": {
            "type": "object",
            "properties": {
              "webui": { "type": "string" },
              "editui": { "type": "string" },
              "tinyui": { "type": "string" }
            }
          }
        }
      }
    },
    "/pages/{id}": {
      "get": {
        "summary": "Get page by id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier for the page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with page details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              }
            }
          }
        }
      }
    },
    "/spaces/{id}/pages": {
      "get": {
        "summary": "Get pages in space",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier for the space",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with pages in the space",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageResponse"
                }
              }
            }
          }
        }
      }
    },

    "CommentResponse": {
      "type": "object",
      "properties": {
        "results": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Comment"
          }
        },
        "_links": {
          "type": "object",
          "properties": {
            "next": {
              "type": "string"
            }
          }
        }
      }
    },
    "Comment": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "status": { "type": "string" },
        "title": { "type": "string" },
        "parentCommentId": { "type": "string" },
        "version": {
          "type": "object",
          "properties": {
            "createdAt": { "type": "string" },
            "message": { "type": "string" },
            "number": { "type": "integer" },
            "minorEdit": { "type": "boolean" },
            "authorId": { "type": "string" }
          }
        },
        "body": {
          "type": "object",
          "properties": {
            "storage": { "type": "object" },
            "atlas_doc_format": { "type": "object" }
          }
        },
        "resolutionStatus": { "type": "string" },
        "properties": {
          "type": "object",
          "properties": {
            "inline-marker-ref": { "type": "string" },
            "inline-original-selection": { "type": "string" }
          }
        },
        "_links": {
          "type": "object",
          "properties": {
            "webui": { "type": "string" }
            }
          }
        }
      },
  "Space": {
    "type": "object",
    "properties": {
      "id": { "type": "string" },
      "key": { "type": "string" },
      "name": { "type": "string" },
      "type": { "type": "string" },
      "status": { "type": "string" },
      "authorId": { "type": "string" },
      "createdAt": { "type": "string" },
      "homepageId": { "type": "string" },
      "description": {
        "type": "object",
        "properties": {
          "plain": {
            "type": "object",
            "properties": {
              "representation": { "type": "string" },
              "value": { "type": "string" }
            }
          },
          "view": {
            "type": "object",
            "properties": {
              "representation": { "type": "string" },
              "value": { "type": "string" }
            }
          }
        }
      },
      "icon": {
        "type": "object",
        "properties": {
          "path": { "type": "string" },
          "apiDownloadLink": { "type": "string" }
        }
      },
      "_links": {
        "type": "object",
        "properties": {
          "webui": { "type": "string" }
        }
      }
    },
    "PageResponse": {
      "type": "object",
      "properties": {
        "results": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Page"
          }
        },
        "_links": {
          "type": "object",
          "properties": {
            "next": {
              "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
1 Like

components is one level too high in your schema, and operationId is required on each get as a top-level key. You can just name each operationId something related to the path, so /pages/{id} might have an operationId of page-by-id (you name it anything you want though)

1 Like

Could you please share the steps to set up the oAuth 2.0 authentication/authorization, scopes in confluence. Many Thanks

1 Like

I’m also interested on getting OAuth working with my Atlassian integration.

2 Likes

were you able to connect to confluence via custom GPT?

I have tried via API key basic option, format base64 email:apitoken but no luck

curious, If you were able to find the way?

2 Likes

I had to do some major trimming/filtering with jq to get a spec file that I could test. In my case, Jira Service Management

Even then I struggle to debug.

Im getting stuck with the oauth.

If i make the schema for api(.)atlassian, i can oauth fine and retrieve /me

But if i make a schema for my knowledge base at subdomain(.)atlassian, the auth wont work when trying to reach any endpoints there.

Ive copied new callback urls, and the auth url, hasn’t fixed it though.

Note;
This one had me stuck for a while, so i hope it helps someone else.
Your callback url for the action may change. I think this might be based off when your URL changes inside of your schema, or possibly your endpoints. This will need to he updated at atlassian developer console to retrieve the new auth url with the correct callback url.

If i figure it out, ill post my results of how to get it working.

DISCLAIMER: I work at Superface

We have created something similar for Jira – Connect GPTs to Jira to connect your GPT to Jira including the authentication for your end-users.

I reckon this should be very similar to Confluence, let me know if you would need a help setting it up.