Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Configure CI/CD pipelines for testing Lindy AI agent integrations. Use when setting up automated testing, configuring GitHub Actions for webhook receiver tests, or validating agent connectivity in CI. Trigger with phrases like "lindy CI", "lindy GitHub Actions", "lindy automated tests", "CI lindy pipeline".
.claude/skills/jeremylongshore-lindy-ci-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 126% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 115% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 25% | 0% |
Lindy agents run on Lindy's managed platform — CI/CD tests your integration code: webhook receivers, callback handlers, and application logic that interacts with Lindy agents. Test webhook signature verification, payload processing, and error handling without hitting live Lindy endpoints.
lindy-install-auth setupbashgh secret set LINDY_API_KEY --body "lnd_live_xxxxxxxxxxxx" gh secret set LINDY_WEBHOOK_SECRET --body "whsec_xxxxxxxxxxxx"
yaml# .github/workflows/lindy-integration.yml name: Lindy Integration Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - name: Run unit tests run: npm test env: LINDY_WEBHOOK_SECRET: ${{ secrets.LINDY_WEBHOOK_SECRET }} - name: Validate webhook handler run: npm run test:webhook - name: Connectivity check (non-blocking) continue-on-error: true run: | curl -s -o /dev/null -w "%{http_code}" \ -X POST "https://public.lindy.ai/api/v1/webhooks/health" \ -H "Authorization: Bearer ${{ secrets.LINDY_API_KEY }}"
typescript// __tests__/webhook-handler.test.ts import { describe, it, expect, vi } from 'vitest'; import request from 'supertest'; import { app } from '../src/server'; describe('Lindy Webhook Handler', () => { const VALID_SECRET = process.env.LINDY_WEBHOOK_SECRET || 'test-secret'; it('rejects requests without auth header', async () => { const res = await request(app) .post('/lindy/callback') .send({ event: 'test' }); expect(res.status).toBe(401); }); it('rejects requests with wrong auth token', async () => { const res = await request(app) .post('/lindy/callback') .set('Authorization', 'Bearer wrong-token') .send({ event: 'test' }); expect(res.status).toBe(401); }); it('accepts requests with valid auth token', async () => { const res = await request(app) .post('/lindy/callback') .set('Authorization', `Bearer ${VALID_SECRET}`) .set('Content-Type', 'application/json') .send({ taskId: 'task_123', status: 'completed', result: { summary: 'Test result' }, }); expect(res.status).toBe(200); expect(res.body.received).toBe(true); }); it('handles malformed payload gracefully', async () => { const res = await request(app) .post('/lindy/callback') .set('Authorization', `Bearer ${VALID_SECRET}`) .set('Content-Type', 'application/json') .send('not-json'); expect(res.status).toBeLessThan(500); }); it('processes webhook payload fields correctly', async () => { const payload = { taskId: 'task_456', status: 'completed', result: { classification: 'billing', sentiment: 'neutral', summary: 'Customer asks about invoice #789', }, }; const res = await request(app) .post('/lindy/callback') .set('Authorization', `Bearer ${VALID_SECRET}`) .set('Content-Type', 'application/json') .send(payload); expect(res.status).toBe(200); }); });
typescript// __tests__/connectivity.test.ts import { describe, it, expect } from 'vitest'; describe('Lindy Connectivity (smoke)', () => { it('can reach Lindy webhook endpoint', async () => { const webhookUrl = process.env.LINDY_WEBHOOK_URL; if (!webhookUrl) { console.warn('LINDY_WEBHOOK_URL not set, skipping connectivity test'); return; } const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.LINDY_WEBHOOK_SECRET}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ test: true, ci: true }), }); expect(response.ok).toBe(true); }); });
yaml# Add to existing workflow - name: Post test results if: always() uses: actions/github-script@v7 with: script: | const status = '${{ job.status }}' === 'success' ? 'Passed' : 'Failed'; if (context.payload.pull_request) { github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, body: `Lindy Integration Tests: **${status}**` }); }
| Category | What It Tests | Requires Live Lindy? | |----------|-------------|---------------------| | Auth verification | Webhook signature checking | No | | Payload processing | Data extraction and transformation | No | | Error handling | Malformed input, edge cases | No | | Connectivity | Webhook endpoint reachability | Yes (non-blocking) | | End-to-end | Full trigger -> callback cycle | Yes |
| Issue | Cause | Solution | |-------|-------|----------| | Secret not found in CI | Not configured | gh secret set LINDY_WEBHOOK_SECRET | | Connectivity test fails | Lindy endpoint unreachable in CI | Mark as continue-on-error: true | | Tests timeout | Network latency | Set timeout-minutes: 10 on job | | Flaky live tests | Lindy processing delay | Add retry logic or mock external calls |
Proceed to lindy-deploy-integration for deployment automation.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 17,694 | 11,613 | -34% | 1 | 1 | 0% | 2,316 | 3,115 | +34% | 0 | 0 | — |
case-02 | fail→fail | 30,243 | 19,424 | -36% | 1 | 1 | 0% | 5,259 | 4,786 | -9% | 0 | 0 | — |
case-03 | fail→fail | 24,060 | 16,290 | -32% | 1 | 1 | 0% | 3,743 | 4,026 | +8% | 0 | 0 | — |
case-04 | pass→fail | 24,306 | 21,235 | -13% | 1 | 1 | 0% | 3,409 | 4,835 | +42% | 0 | 0 | — |
case-05 | pass→pass | 19,948 | 17,053 | -15% | 1 | 1 | 0% | 2,589 | 3,792 | +46% | 0 | 0 | — |
case-06 | fail→fail | 15,550 | 12,749 | -18% | 1 | 1 | 0% | 1,641 | 3,180 | +94% | 0 | 0 | — |
case-07 | fail→pass | 10,949 | 8,217 | -25% | 1 | 1 | 0% | 1,033 | 2,334 | +126% | 0 | 0 | — |
case-08 | pass→pass | 13,892 | 8,347 | -40% | 1 | 1 | 0% | 1,697 | 2,335 | +38% | 0 | 0 | — |
case-09 | pass→pass | 15,607 | 13,006 | -17% | 1 | 1 | 0% | 1,593 | 2,903 | +82% | 0 | 0 | — |
case-10 | pass→pass | 14,134 | 8,100 | -43% | 1 | 1 | 0% | 1,279 | 2,178 | +70% | 0 | 0 | — |
case-19 | fail→pass | 15,984 | 2,837 | -82% | 1 | 1 | 0% | 1,425 | 2,319 | +63% | 0 | 0 | — |
case-11 | fail→pass | 11,600 | 3,657 | -68% | 1 | 1 | 0% | 1,171 | 2,519 | +115% | 0 | 0 | — |
case-12 | pass→pass | 10,247 | 7,498 | -27% | 1 | 1 | 0% | 1,352 | 2,268 | +68% | 0 | 0 | — |
case-13 | fail→pass | 12,825 | 8,055 | -37% | 1 | 1 | 0% | 1,873 | 2,334 | +25% | 0 | 0 | — |
case-14 | pass→pass | 7,018 | 3,026 | -57% | 1 | 1 | 0% | 1,096 | 2,127 | +94% | 0 | 0 | — |
case-15 | pass→pass | 9,314 | 7,459 | -20% | 1 | 1 | 0% | 1,348 | 2,160 | +60% | 0 | 0 | — |
case-16 | pass→pass | 5,906 | 7,485 | +27% | 1 | 1 | 0% | 955 | 2,218 | +132% | 0 | 0 | — |
case-17 | fail→pass | 12,689 | 8,049 | -37% | 1 | 1 | 0% | 1,059 | 2,144 | +102% | 0 | 0 | — |
case-18 | pass→pass | 14,671 | 10,310 | -30% | 1 | 1 | 0% | 2,293 | 3,243 | +41% | 0 | 0 | — |
case-20 | fail→pass | 16,869 | 2,926 | -83% | 1 | 1 | 0% | 1,418 | 2,106 | +49% | 0 | 0 | — |
case-21 | pass→pass | 7,887 | 2,704 | -66% | 1 | 1 | 0% | 1,381 | 2,255 | +63% | 0 | 0 | — |
case-22 | pass→pass | 12,240 | 3,958 | -68% | 1 | 1 | 0% | 1,281 | 2,500 | +95% | 0 | 0 | — |
case-23 | fail→pass | 19,517 | 7,675 | -61% | 1 | 1 | 0% | 2,142 | 2,284 | +7% | 0 | 0 | — |
case-24 | fail→pass | 13,008 | 8,247 | -37% | 1 | 1 | 0% | 1,133 | 2,212 | +95% | 0 | 0 | — |
case-25 | fail→pass | 11,227 | 8,384 | -25% | 1 | 1 | 0% | 1,157 | 2,195 | +90% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 25 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.