DEV Community

Jason Shouldice
Jason Shouldice

Posted on • Originally published at vicistack.com

A Go Client for VICIdial's API That Actually Handles the Response Format

VICIdial's API is powerful but its response format is chaotic. Some functions return pipe-delimited fields. Some return newline-separated records. Some return a mix. Authentication is via query parameters, not headers. Errors return HTTP 200. Building a proper client library means parsing all of this once so you never think about it again.

We chose Go for our production VICIdial integrations for three reasons: goroutines handle concurrent polling of 200 agents without thread pool management, a single 8MB binary deploys by copying it to the server, and when processing thousands of lead records for scoring, Go never becomes the bottleneck.

The Core Client

The client struct wraps http.Client with connection pooling, adds VICIdial authentication to every request, and parses the SUCCESS:/ERROR: response format into Go errors.

client := vicidial.NewClient(vicidial.Config{
    BaseURL: "https://dialer.example.com",
    User:    "api_user",
    Pass:    "api_pass",
    Timeout: 30 * time.Second,
    Source:  "CRM_SYNC",
})
Enter fullscreen mode Exit fullscreen mode

Every API call flows through a single doRequest method that handles URL construction, credential injection, response reading, and error detection. VICIdial returns API errors with HTTP 200 status codes, so you can't just check resp.StatusCode -- the client has to parse the body and look for ERROR: prefixes.

Lead Management

The most common integration. Adding leads, searching by phone, updating dispositions, scheduling callbacks. The batch import method uses goroutines with a semaphore channel to control concurrency:

results, errs := client.AddLeadsBatch(ctx, leads, 10) // 10 concurrent
Enter fullscreen mode Exit fullscreen mode

This imports 1,000 leads in under 30 seconds vs. 5+ minutes sequentially.

Real-Time Monitoring

Where Go's concurrency model really pays off. Polling campaign stats every 5 seconds across multiple campaigns, all concurrent:

client.Monitor(ctx, vicidial.MonitorConfig{
    CampaignIDs:  []string{"SALES_B2B", "SOLAR_OB", "INSURANCE"},
    PollInterval: 5 * time.Second,
    OnUpdate: func(stats []vicidial.CampaignStats) {
        // Update dashboard, check thresholds, etc.
    },
})
Enter fullscreen mode Exit fullscreen mode

We built a terminal dashboard with this that shows agents logged in, agents in call, calls today, drops, drop percentage, and hopper level -- updated every 5 seconds.

Drop Rate Protection

The killer feature: automated TCPA compliance. A goroutine monitors the drop rate and automatically reduces the dial level when it approaches 3%.

client.DropRateGuard(ctx, "SALES", 2.5, 30) // max 2.5%, check every 30s
Enter fullscreen mode Exit fullscreen mode

When the drop rate crosses 2.5%, the dial level ratchets down by 0.5 automatically. No human needs to watch the real-time report.

Deployment

Two patterns work well:

CLI tool -- build a binary that ops uses for ad-hoc operations. vicli stats SALES, vicli search 5551234567, vicli dial-level SALES 3.0. Cross-compile with GOOS=linux GOARCH=amd64 go build, scp to the server.

HTTP proxy -- expose VICIdial's API as a proper REST API with JSON responses, Bearer token auth, and standard error codes. Your web team integrates with POST /api/leads instead of learning VICIdial's query-parameter-based API.

For the complete library with all Go source files, tests with mock servers, the terminal dashboard, the drop rate guard, and the HTTP proxy, see the full guide at ViciStack.

Originally published at https://vicistack.com/blog/vicidial-golang-api-client/

Top comments (0)