GitHub Actions · Setup guide
Give issues and PRs
a starting label.
Classify GitHub issues and pull requests with the Jev model through Vercel AI Gateway. Deploy the API, add a workflow, and automatically label new issues and PRs.
01 / Connect
Set up your repository
Deploy the API
Use the deploy button above. Set
LABELER_API_KEYin the deployment environment to a secret of your choice. The API uses Vercel AI Gateway with Vercel OIDC, or anAI_GATEWAY_API_KEYyou configure.Add your Actions configuration
In the repository you want to label, open Settings → Secrets and variables → Actions.
Variable
LABELER_URLYour deployment base URL, such ashttps://your-app.vercel.app, without/api/classify-issue.Secret
LABELER_API_KEYThe same secret you set in your deployment.Add the workflow
Copy an example below into
.github/workflows/classify-issues.ymland commit it to your default branch.
02 / Automate
Copy your workflow
The workflow includes starter labels such as bug, enhancement, and documentation. Edit the label names and descriptions to match your repository.
name: Classify issues
on:
issues:
types: [opened]
permissions:
issues: write
jobs:
classify:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v8
env:
LABELER_URL: ${{ vars.LABELER_URL }}
LABELER_API_KEY: ${{ secrets.LABELER_API_KEY }}
with:
script: |
const { title, body } = context.payload.issue;
// Edit these names and descriptions to match your repository.
const labels = {
bug: "Existing behavior is broken or differs from expected behavior.",
enhancement: "A request for new functionality or an improvement.",
documentation: "Improvements or additions to documentation.",
question: "A usage question or an issue with insufficient information to determine its intent.",
duplicate: "Explicitly established as already covered by another issue or pull request.",
"good first issue": "Explicitly identified as suitable for new contributors.",
"help wanted": "Explicitly asks for additional contributor attention.",
invalid: "Clearly established as not a valid issue.",
wontfix: "An explicit maintainer decision that the work will not proceed.",
};
const classifierUrl = new URL("/api/classify-issue", process.env.LABELER_URL);
const response = await fetch(classifierUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.LABELER_API_KEY}`,
},
body: JSON.stringify({ title, body, labels }),
signal: AbortSignal.timeout(60000),
});
if (!response.ok) {
throw new Error(`Labeler returned HTTP ${response.status}`);
}
const { classification } = await response.json();
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.issue.number,
labels: [classification],
});
The workflow sends a labels object mapping label names to descriptions in the JSON body. The API chooses only from those labels.
{
"title": "The save button does nothing",
"body": "Clicking Save leaves my changes unsaved.",
"labels": {
"bug": "Broken behavior",
"enhancement": "New functionality"
}
}03 / Verify
Open an issue to try it
The workflow sends the issue title, body, and label criteria to POST /api/classify-issue and adds the returned label. Existing labels stay in place. Check the run in your repository's Actions tab.
If a run fails, confirm the endpoint URL and matching secrets. An HTTP 401 means the API secret did not match; 502 means classification failed.