Lindy ci integration
'Configure CI/CD pipelines for testing Lindy AI agent integrations.From its SKILL.md
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill lindy-ci-integrationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.6 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it
Lindy CI Integration
Overview
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.
Prerequisites
- GitHub repository with Actions enabled
- Lindy API key and webhook secret stored as GitHub secrets
- Node.js project with webhook receiver code
- Completed
lindy-install-authsetup
Instructions
Step 1: Store Secrets in GitHub
gh secret set LINDY_API_KEY --body "lnd_live_xxxxxxxxxxxx"
gh secret set LINDY_WEBHOOK_SECRET --body "whsec_xxxxxxxxxxxx"
Step 2: Create GitHub Actions Workflow
# .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 }}"
Step 3: Write Webhook Handler Tests
// __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);
});
});
Step 4: Add Smoke Test for Live Connectivity
// __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);
});
});
Step 5: PR Status Check
# 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}**`
});
}
Test Categories
| 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 |
Error Handling
| 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 |
Resources
Next Steps
Proceed to lindy-deploy-integration for deployment automation.
What ships with it: 1 file
4.1 KB alongside SKILL.md
references/
- implementation-guide.md4.1 KB