axle · integrations
Wire axle into the tools your team uses
Scan results are useful only when they reach the human who can act on them. axle ships with PR-comment integration out of the box, and the JSON output is structured so you can route it anywhere — Slack, Linear, Jira, your team's own dashboard. This page documents the recipes.
Already-supported (out of the box)
GitHub PRs
Built-inThe axle GitHub Action posts a sticky comment on every pull request with the full violation breakdown. AI-fix diffs (with with-ai-fixes: true) appear inline in the same comment. No setup beyond adding the workflow file.
Vercel / Netlify / Cloudflare Pages
Built-inEach hosting provider has a dedicated plugin (axle-vercel-plugin, axle-netlify-plugin, axle-cloudflare-plugin) that runs against the preview URL on every deploy and fails the build on serious-severity regressions. See the docs for setup.
Recipes (DIY for now — first-class integration on the roadmap)
Slack — notify on scan failure
Recipeaxle's GitHub Action exposes the scan output as job outputs. Pipe it into Slack with the standard slackapi/slack-github-action:
- uses: asafamos/axle-action@v1
id: axle
with:
url: ${{ secrets.PREVIEW_URL }}
fail-on: serious
- if: ${{ failure() && steps.axle.outputs.failing == 'true' }}
uses: slackapi/slack-github-action@v2
with:
channel-id: 'C0XXXXXXX'
payload: |
{
"text": ":warning: a11y regression on ${{ github.event.pull_request.html_url }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*axle CI failed on PR #${{ github.event.pull_request.number }}*\nPRevent merge until WCAG violations resolved."
}
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}Linear — auto-create issues from violations
RecipeParse the JSON output and POST to Linear's GraphQL API. Each serious or critical violation becomes a Linear issue tagged a11y. Use the issue title pattern [a11y] <rule_id> on <page_path> so duplicates are auto-merged when the rule appears on the same path next scan:
- uses: asafamos/axle-action@v1
id: axle
with:
url: ${{ secrets.PREVIEW_URL }}
json-out: axle-report.json
- if: ${{ steps.axle.outputs.failing == 'true' }}
run: |
cat axle-report.json | jq -c '.violations[] | select(.impact == "serious" or .impact == "critical")' | while read v; do
title="[a11y] $(echo "$v" | jq -r '.id') on /${{ github.head_ref }}"
body=$(echo "$v" | jq -r '"WCAG: " + (.tags | join(", ")) + "\n\n" + .help + "\n\nFix: " + .helpUrl')
curl -s -X POST "https://api.linear.app/graphql" \
-H "Authorization: ${{ secrets.LINEAR_API_KEY }}" \
-H "Content-Type: application/json" \
-d "{\"query\":\"mutation { issueCreate(input: { teamId: \\\"${{ secrets.LINEAR_TEAM_ID }}\\\", title: \\\"$title\\\", description: \\\"$body\\\", labelIds: [\\\"${{ secrets.LINEAR_A11Y_LABEL }}\\\"] }) { success } }\"}"
doneJira — same idea, REST API
RecipeJira's REST API (POST /rest/api/3/issue) takes a similar JSON payload. Authenticate with a Jira API token + email pair as Basic auth. The same violation-to-issue script structure works — just swap the Linear GraphQL call for the Jira REST call. Email asaf@amoss.co.il if you want a starter template for your specific Jira workflow.
GitLab CI
RecipeUse the axle-cli npm package directly in.gitlab-ci.yml:
a11y:
image: node:20
stage: test
script:
- npx axle-cli scan $PREVIEW_URL --fail-on serious
- mv axle-report.json gitlab-a11y-report.json
artifacts:
when: always
reports:
junit: gitlab-a11y-report.json
paths:
- gitlab-a11y-report.jsonWhat's on the roadmap as first-class integrations
- Native Slack app — install once, axle posts directly without GitHub Action plumbing
- Native Linear / Jira / Asana app — same idea, no copy-paste
- Webhook endpoint per axle account — POST scan results to your own URL
- Datadog / Honeycomb / Sentry export — accessibility regressions as observability events
- Storybook addon — scan stories during dev
Vote / request priority by emailing asaf@amoss.co.il. The thing customers ask for first is the thing we build first.