# GitHub Open and update issues, comment on pull requests and start workflows in your repositories. URL: https://docs.qaldy.com/connectors/github/ - **Status:** [[Preview]] - **Shelf:** Developer - **How you connect:** API key - **Regions:** global (not available to Kazakhstan-only workspaces) - **The service’s own API reference:** https://docs.github.com/en/rest Apps open and update issues, comment on issues and pull requests, and start workflows in your repositories with a **fine-grained personal access token**. GitHub is reached through the HTTP API connector with a preset: the gate calls only `api.github.com`, under `/repos`, with `GET`, `POST` and `PATCH`. The token is sealed to the gate and apps never see it. > **Status: Preview through the HTTP API preset.** It follows GitHub's REST API reference > (version `2022-11-28`) and is tested against a stand-in server. It has not been run against a > live repository, so a first call may need a fix. ## Connect it 1. On GitHub: **Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token** ([github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new)). 2. **Repository access: only the repositories the apps use.** Under permissions give **Issues** and **Pull requests** *read and write*. Add **Contents** *write* only if an app starts workflows with a repository event (`repository_dispatch` needs it). Nothing else. 3. Copy the token (it starts with `github_pat_`; GitHub shows it once). Set an expiry you will remember: an expired token shows as **Expired — reconnect**. 4. Qaldy → **Connectors** → GitHub → **Connect GitHub** → paste the token → **Seal and connect**. **Test connection** calls `GET /rate_limit`, which answers for any valid token and does not count against your rate limit. It proves the token is accepted, not that it can see a repository. ## What the gate allows | | | |---|---| | Host | `api.github.com` (pinned, every resolved address public) | | Methods | `GET`, `POST`, `PATCH` (no `PUT`, no `DELETE`: an app can't merge, delete or change settings) | | Paths | everything under `/repos` | | Key | `Authorization: Bearer `, added by the gate | | Fixed headers | `Accept: application/vnd.github+json`, `X-GitHub-Api-Version: 2022-11-28`, `User-Agent: qaldy-cloud`; an app can't override them | ## Use it from an app ```json { "id": "http", "actions": ["request"], "scope": "conn_k3v9…" } ``` ```ts import { github } from '@qaldy/sdk' const gh = github(ctx.connectors.http('conn_k3v9…')) const { issues } = await gh.listIssues({ repo: 'acme/shop', state: 'open', limit: 20 }) await gh.createIssue({ repo: 'acme/shop', title: 'Printer is out of toner', labels: ['ops'] }) await gh.commentOnIssue({ repo: 'acme/shop', number: 42, body: 'Ordered, arrives Friday.' }) await gh.dispatchEvent({ repo: 'acme/shop', eventType: 'rebuild', payload: { reason: 'prices' } }) ``` The helper checks the input before anything is sent (`repo` must be `owner/name`) and decodes the reply. Every call still goes through the gate as an ordinary HTTP request held to the fence above. ## Start an automation from GitHub The other direction is a **webhook**: GitHub calls Qaldy when an issue is opened. 1. Automations → the automation → **What starts it → Webhook → Who calls it → GitHub → Set up webhook**. Copy the secret (shown once). 2. In the repository: **Settings → Webhooks → Add webhook**. Payload URL: the address shown; content type `application/json`; secret: the one you copied; events: **Issues** (add what the app handles). The console's example shows the same thing as one `curl` or Node call. 3. GitHub signs each delivery with `X-Hub-Signature-256` (HMAC-SHA256 over the raw body). Qaldy checks it, and `X-GitHub-Delivery` makes a redelivery a repeat, not a second run. GitHub sends a `ping` event first; the automation receives it like any other body, so an app that only handles issues should ignore a body with no `issue`. See [webhooks.md](https://docs.qaldy.com/connectors/webhooks/). The catalog still lists "An issue is opened" as a *planned* trigger of the connector: the webhook above works today but lives on the automation, not on the connection. ## When it breaks | You see | Why | Do | |---|---|---| | `401` | The token expired, was revoked or mistyped | Replace the token | | `404 Not Found` on a repository you can open | The token isn't granted that repository (GitHub answers 404 to hide it) | Add the repository to the token | | `403 Resource not accessible by personal access token` | A permission is missing (Issues, Pull requests, Contents) | Edit the token's permissions on GitHub | | `422 Validation Failed` | The input is wrong (an unknown label, a closed discussion) | Fix the app's input | | `403` with `rate limit` | GitHub's hourly quota | Wait, or poll less often | ## Gaps - Not run against a live repository (hence Preview). - No `PUT` or `DELETE`, so no merging, no deleting, no repository settings: by design. - No GitHub App installation tokens; a personal token belongs to the person who made it, so a workspace should make it from a machine user or a shared account.