Configure Lindy AI CI/CD integration with GitHub Actions and testing. Use when setting up automated testing, configuring CI pipelines, or integrating Lindy tests into your build process. Trigger with...
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 setupgh secret set LINDY_API_KEY --body "lnd_live_xxxxxxxxxxxx"
gh secret set LINDY_WEBHOOK_SECRET --body "whsec_xxxxxxxxxxxx"
# .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 }}"
// __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);
});
});
// __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);
});
});
# 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 |
Produce a CI integration record containing the workflow trigger, test fixtures, required secrets by name only, failure ownership, and the command or dashboard evidence proving that the automation ran against a non-production agent.
For a pull request, trigger a disposable Lindy test agent with fixed fixture input, wait for its terminal status, and fail the workflow when the expected action is absent or the agent reports an error. Keep the production agent ID out of the workflow and promote only after the fixture result is reviewed.
Proceed to lindy-deploy-integration for deployment automation.