Skip to content
Back to skills

Elevenlabs Ci Integration

ASecurity

'Configure CI/CD pipelines for ElevenLabs with mocked unit tests and

  • 2,688 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 29, 2026
ai-agentstypescriptgobashnodegitapici/cdsecurity

Works with

  • claude code
  • cli
  • api

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned May 29, 2026

npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill elevenlabs-ci-integration --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Elevenlabs Ci Integration?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Elevenlabs Ci Integration
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremylongshore-elevenlabs-ci-integration/badge)](https://www.skillsdirectory.com/skills/jeremylongshore-elevenlabs-ci-integration)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: elevenlabs-ci-integration
description: 'Configure CI/CD pipelines for ElevenLabs with mocked unit tests and
  gated integration tests.

  Use when setting up GitHub Actions for TTS projects, configuring CI test strategies,

  or automating ElevenLabs integration validation.

  Trigger: "elevenlabs CI", "elevenlabs GitHub Actions",

  "elevenlabs automated tests", "CI elevenlabs", "elevenlabs pipeline".

  '
allowed-tools: Read, Write, Edit, Bash(gh:*)
version: 1.0.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- voice
- ai
- elevenlabs
- ci
- github-actions
compatibility: Designed for Claude Code
---
# ElevenLabs CI Integration

## Overview

Set up CI/CD pipelines that test ElevenLabs integrations without burning character quota on every PR. Uses a two-tier strategy: mocked unit tests on every push, gated integration tests on demand.

## Prerequisites

- GitHub repository with Actions enabled
- ElevenLabs API key for integration tests
- npm/pnpm project with vitest configured

## Instructions

### Step 1: GitHub Actions Workflow

```yaml
# .github/workflows/elevenlabs-tests.yml
name: ElevenLabs Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  # Tier 1: Always runs — no API key needed, no quota cost
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
      - run: npm ci
      - run: npm test -- --coverage
        env:
          # Mock mode — no real API calls
          ELEVENLABS_API_KEY: "sk_test_mock_key_for_ci"

  # Tier 2: Only on main or manual trigger — uses real API
  integration-tests:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
    needs: unit-tests
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
      - run: npm ci

      # Check quota before running integration tests
      - name: Check ElevenLabs quota
        env:
          ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }}
        run: |
          REMAINING=$(curl -s https://api.elevenlabs.io/v1/user \
            -H "xi-api-key: ${ELEVENLABS_API_KEY}" | \
            jq '.subscription | (.character_limit - .character_count)')
          echo "Characters remaining: $REMAINING"
          if [ "$REMAINING" -lt 5000 ]; then
            echo "::warning::Low ElevenLabs quota ($REMAINING chars). Skipping integration tests."
            echo "SKIP_INTEGRATION=true" >> $GITHUB_ENV
          fi

      - name: Run integration tests
        if: env.SKIP_INTEGRATION != 'true'
        env:
          ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }}
          ELEVENLABS_INTEGRATION: "1"
        run: npm run test:integration
```

### Step 2: Configure Repository Secrets

```bash
# Store API key as GitHub secret (use a test/dev key, NOT production)
gh secret set ELEVENLABS_API_KEY --body "sk_your_test_key_here"

# Optional: webhook secret for webhook tests
gh secret set ELEVENLABS_WEBHOOK_SECRET --body "whsec_your_secret_here"
```

### Step 3: Unit Test with SDK Mock

```typescript
// tests/unit/tts-service.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";

// Mock the entire SDK — no API calls, no quota usage
vi.mock("@elevenlabs/elevenlabs-js", () => ({
  ElevenLabsClient: vi.fn().mockImplementation(() => ({
    textToSpeech: {
      convert: vi.fn().mockResolvedValue(
        new ReadableStream({
          start(controller) {
            controller.enqueue(new Uint8Array([0xFF, 0xFB, 0x90, 0x00])); // MP3 header
            controller.close();
          },
        })
      ),
      stream: vi.fn().mockImplementation(async function* () {
        yield new Uint8Array([0xFF, 0xFB, 0x90, 0x00]);
      }),
    },
    voices: {
      getAll: vi.fn().mockResolvedValue({
        voices: [
          { voice_id: "21m00Tcm4TlvDq8ikWAM", name: "Rachel", category: "premade" },
        ],
      }),
    },
    user: {
      get: vi.fn().mockResolvedValue({
        subscription: { tier: "pro", character_count: 1000, character_limit: 500000 },
      }),
    },
  })),
}));

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

describe("TTS Service", () => {
  let client: InstanceType<typeof ElevenLabsClient>;

  beforeEach(() => {
    client = new ElevenLabsClient();
  });

  it("should call TTS with correct parameters", async () => {
    await client.textToSpeech.convert("21m00Tcm4TlvDq8ikWAM", {
      text: "Test speech",
      model_id: "eleven_multilingual_v2",
      voice_settings: { stability: 0.5, similarity_boost: 0.75 },
    });

    expect(client.textToSpeech.convert).toHaveBeenCalledWith(
      "21m00Tcm4TlvDq8ikWAM",
      expect.objectContaining({
        text: "Test speech",
        model_id: "eleven_multilingual_v2",
      })
    );
  });

  it("should handle voice listing", async () => {
    const result = await client.voices.getAll();
    expect(result.voices).toHaveLength(1);
    expect(result.voices[0].name).toBe("Rachel");
  });
});
```

### Step 4: Integration Test (Gated)

```typescript
// tests/integration/tts-smoke.test.ts
import { describe, it, expect } from "vitest";

const SKIP = !process.env.ELEVENLABS_INTEGRATION;

describe.skipIf(SKIP)("ElevenLabs Integration", () => {
  it("should generate audio from text", async () => {
    const { ElevenLabsClient } = await import("@elevenlabs/elevenlabs-js");
    const client = new ElevenLabsClient();

    // Use Flash model + short text to minimize quota usage
    const audio = await client.textToSpeech.convert("21m00Tcm4TlvDq8ikWAM", {
      text: "CI test.",  // 8 characters = 4 credits (Flash)
      model_id: "eleven_flash_v2_5",
      output_format: "mp3_22050_32",
    });

    expect(audio).toBeDefined();
  }, 30_000);

  it("should list voices", async () => {
    const { ElevenLabsClient } = await import("@elevenlabs/elevenlabs-js");
    const client = new ElevenLabsClient();
    const { voices } = await client.voices.getAll();
    expect(voices.length).toBeGreaterThan(0);
  });
});
```

### Step 5: Package Scripts

```json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest --watch",
    "test:integration": "ELEVENLABS_INTEGRATION=1 vitest run tests/integration/",
    "test:ci": "vitest run --coverage --reporter=junit --outputFile=test-results.xml"
  }
}
```

## CI Strategy Summary

| Tier | When | API Key | Quota Cost | Coverage |
|------|------|---------|------------|----------|
| Unit tests | Every push/PR | Mock key | 0 characters | SDK integration patterns |
| Integration | Main + manual | Real test key | ~50 chars | End-to-end TTS verification |
| Quota check | Before integration | Real test key | 0 (GET only) | Prevents surprise billing |

## Error Handling

| Issue | Cause | Solution |
|-------|-------|----------|
| Secret not found in CI | Missing repository secret | `gh secret set ELEVENLABS_API_KEY` |
| Integration tests timeout | Slow TTS generation | Increase test timeout to 30s; use Flash model |
| Quota depleted in CI | Too many integration runs | Use quota guard; limit to main branch only |
| Mock drift | SDK API changed | Update mocks when upgrading SDK |

## Resources

- [GitHub Actions Secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions)
- [Vitest CI Configuration](https://vitest.dev/guide/ci.html)
- [ElevenLabs JS SDK](https://github.com/elevenlabs/elevenlabs-js)

## Next Steps

For deployment patterns, see `elevenlabs-deploy-integration`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.