What is the difference between Asterisk AGI and ARI?

Asterisk AGI (Asterisk Gateway Interface) is a synchronous, stdin/stdout protocol where Asterisk invokes your script during a call and your script issues sequential commands. FastAGI improves performance by running the script as a persistent TCP server. Asterisk ARI (Asterisk REST Interface) is an asynchronous REST+WebSocket model introduced in Asterisk 12 where your application receives real-time events over WebSocket and controls calls via HTTP REST — giving your code full ownership of Asterisk as a media server.

When should you use Asterisk AGI vs ARI?

Use AGI for linear, sequential IVR call flows — it is simpler to implement and debug. Use ARI for complex stateful applications: AI voice bots with async LLM integration, multi-party conferencing, WebRTC contact centres, and any scenario requiring simultaneous control of multiple Asterisk channels.

AsteriskAGIARIFastAGIIVRCall Control API

Asterisk AGI vs ARI: Which API Should You Use in 2026?

Asterisk exposes two primary programmatic interfaces for call control: AGI — a battle-hardened stdin/stdout scripting protocol from 2000 — and ARI — the modern REST+WebSocket event model introduced in Asterisk 12. Choosing the wrong one costs weeks of refactoring when your requirements inevitably outgrow it.

CelloIP has built production Asterisk applications on both interfaces — from simple DTMF IVR trees to real-time AI voice bots processing 500+ concurrent calls. This guide gives you the precise decision framework, real code examples, and a performance comparison so you can choose correctly the first time.

By Kaushik Parmar— Founder & VoIP Architect, CelloIP Technologies·19 min read·June 9, 2026·≈ 2,500 words

2000

AGI first available (Asterisk 1.2)

2013

ARI introduced in Asterisk 12

500+

Concurrent ARI calls per process

10×

FastAGI vs fork-AGI concurrency

Asterisk AGI vs ARI API comparison — AGI FastAGI synchronous stdin stdout sequential call control versus ARI REST WebSocket async event-driven model for Asterisk application development in 2026
Fig 1: AGI uses a sequential request-response model (Asterisk drives your script). ARI gives your app full async ownership — Asterisk becomes the media server your code commands.

The Three Asterisk APIs: Setting the Context

Asterisk has three external-facing APIs, and developers frequently conflate them. Here is the precise breakdown before we go deep on AGI vs ARI:

AGI

Asterisk Gateway Interface

In-call, synchronous call flow control. Dialplan invokes your script, which controls the call via stdin/stdout commands. Forks per call (AGI) or persistent daemon (FastAGI).

ARI

Asterisk REST Interface

Async, event-driven call control via REST + WebSocket. Your app subscribes to channel events and issues REST commands. Full programmatic control of Asterisk as a media server.

AMI

Asterisk Manager Interface

Management and monitoring API over persistent TCP. Used for call origination, channel monitoring, module management. Not for in-call media control.

The key distinction: AGI and ARI are for in-call control. AMI is for management and origination. You will often use AMI alongside either AGI or ARI — AMI originates the call, then AGI/ARI handles the call flow.

AGI Deep Dive: How the Asterisk Gateway Interface Works

AGI was introduced in Asterisk 1.2 and remains the most widely used external call control mechanism in deployed Asterisk systems today. The protocol is elegantly simple: when a dialplan extension hits an AGI() command, Asterisk:

  1. Forks a new process (or for FastAGI, opens a TCP connection) running your script
  2. Writes AGI environment variables to the script's stdin (channel name, caller ID, extension, etc.)
  3. Waits synchronously for your script to issue commands
  4. Executes each command and writes the result back to your script's stdin
  5. Continues until your script exits (or the call hangs up)

The AGI command set covers everything a linear IVR needs: ANSWER, HANGUP, STREAM FILE (play audio), GET DATA (collect DTMF), WAIT FOR DIGIT, SET VARIABLE, EXEC (run any dialplan application), CHANNEL STATUS, and many more.

When AGI shines: Sequential call flows, DTMF-driven menus, database lookups during calls, simple CRM integrations, voicemail, auto-attendant, and callback scheduling. Any scenario where each step in the call naturally waits for the previous one to complete.

AGI limitations: AGI is inherently single-channel — one script instance controls one call. You cannot use AGI to react to events on a different channel, control a conference bridge programmatically, or process async streaming data (like an LLM token stream) without awkward polling loops.

FastAGI: Production-Grade AGI

Standard AGI forks a new OS process for every single call. On a loaded server handling 200+ concurrent calls, this creates 200+ processes, each with startup overhead of 50–200ms and its own memory footprint. This kills scalability. FastAGI solves this by running your AGI handler as a persistent TCP daemon — Asterisk dials fastagi://127.0.0.1:4573and reuses the already-running process via a new TCP connection.

Fork AGI: startup latency

50–200ms per call

FastAGI: startup latency

<5ms (TCP connect)

Fork AGI: 200 concurrent calls

200 OS processes

FastAGI: 200 concurrent calls

1 process, 200 threads

In your dialplan, the only change is the AGI() call target: AGI(agi://127.0.0.1:4573) instead of AGI(my_script.py). Your script runs as a TCP server that accepts connections and handles each as a separate AGI session.

ARI Deep Dive: Asterisk as a Programmable Media Server

ARI (Asterisk REST Interface), introduced in Asterisk 12, represents a fundamental paradigm shift. Instead of Asterisk calling your code, your code calls Asterisk. ARI exposes:

REST API

HTTP endpoints for every call control action: answer a channel, play audio, start recording, create a bridge, add channels to a bridge, mute, originate. Stateless, cacheable, testable with curl. Base URL: http://asterisk:8088/ari/

Examples: POST /channels, POST /bridges, DELETE /channels/{id}

WebSocket Event Stream

A persistent WebSocket connection at ws://asterisk:8088/ari/eventspushes real-time events for every state change: ChannelCreated, ChannelAnswered, ChannelDtmfReceived, PlaybackFinished, RecordingFinished, BridgeCreated, ChannelHangupRequest.

70+ event types. Subscribe to all or filter by app name.

The entry point from the dialplan is the Stasis(my_app_name) application. When a call reaches this dialplan step, Asterisk hands full control to your ARI application — it stops processing dialplan and waits for your REST commands. Your application receives a StasisStart event on the WebSocket and takes it from there.

ARI mental model: Think of Asterisk as a headless media server — it handles SIP signalling, codec transcoding, media mixing, and DTMF detection. ARI is the API your application uses to tell that media server what to do. Your application is the brain; Asterisk is the hands.

Asterisk AGI vs ARI use case decision matrix — Simple DTMF IVR uses AGI, AI voice bot and LLM integration uses ARI, multi-party conferencing uses ARI, WebRTC contact center uses ARI, voicemail uses AGI, with performance comparison Fork AGI 200 CPS vs FastAGI 2000 CPS vs ARI 5000 CPS
Fig 2: AGI vs ARI decision matrix. FastAGI handles 10x more concurrent calls than fork-based AGI. ARI (Node.js async) supports 5,000+ concurrent calls and is the only viable choice for AI LLM integration.

AGI vs ARI: Full Feature Comparison

FeatureAGI / FastAGIARI
Protocolstdin/stdout (AGI) or TCP (FastAGI)HTTP REST + WebSocket
Invocationdialplan: AGI() or FastAGI()dialplan: Stasis()
Programming modelSynchronous, request-responseAsynchronous, event-driven
Multi-channel control✗ One channel per script✓ All channels simultaneously
Concurrency modelFork per call (AGI) or pooled (FastAGI)Single async app — unlimited
LLM/ASR integration± Awkward (blocking I/O)✓ Native async streaming
WebSocket events✗ Not supported✓ Full real-time event stream
Dialplan interaction✓ Full AGI command set± via REST only
Recording control± Limited✓ Fine-grained
Bridge / conferncing± Via dialplan only✓ Programmatic bridge control
Learning curve✓ Low — script + dialplan± Moderate — async patterns
Debug simplicity✓ Print to stdout± WebSocket + REST logs
Language support✓ Any language via stdin/stdout✓ Any language via HTTP/WS
Production maturity✓ 25+ years in production✓ Mature since Asterisk 12

AGI Example: Python FastAGI Server

The following is a production-grade Python FastAGI server that collects a 4-digit PIN from the caller, validates it against a database, and routes accordingly. It runs as a persistent TCP daemon — no fork overhead per call.

pythonFastAGI PIN validation server — production pattern
#!/usr/bin/env python3
"""
FastAGI PIN validation server for Asterisk.
Dialplan: exten => s,1,FastAGI(agi://127.0.0.1:4573)
"""
import socketserver
import threading
from asterisk.agi import AGI  # pip install pyst2

# Simulated PIN database
VALID_PINS = {"1234": "support_queue", "5678": "sales_queue", "9999": "admin_queue"}


class AGIHandler(socketserver.BaseRequestHandler):
    def handle(self):
        agi = AGI(wfile=self.request.makefile("w"), rfile=self.request.makefile("r"))

        try:
            # Answer the call
            agi.answer()

            # Get caller ID for logging
            caller_id = agi.get_variable("CALLERID(num)")
            print(f"[FastAGI] Incoming call from {caller_id}")

            attempts = 0
            while attempts < 3:
                # Play PIN prompt
                agi.stream_file("please-enter-your-pin", escape_digits="0123456789#")

                # Collect 4 digits, 5s timeout
                digits = agi.get_data("beep", timeout=5000, max_digits=4)

                if digits and digits in VALID_PINS:
                    queue_name = VALID_PINS[digits]
                    agi.verbose(f"Valid PIN {digits} → queue {queue_name}", 2)
                    agi.stream_file("auth-thankyou")
                    # Transfer to the appropriate queue via dialplan exec
                    agi.execute("Queue", f"{queue_name},t,,,60")
                    return
                else:
                    attempts += 1
                    agi.stream_file("auth-incorrect")

            # 3 failed attempts — play sorry and hang up
            agi.stream_file("vm-sorry")
            agi.hangup()

        except Exception as e:
            print(f"[FastAGI] Error: {e}")


class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True
    daemon_threads = True


if __name__ == "__main__":
    server = ThreadedTCPServer(("0.0.0.0", 4573), AGIHandler)
    print("[FastAGI] Listening on port 4573...")
    server.serve_forever()

Corresponding dialplan entry in extensions.conf: exten => s,1,FastAGI(agi://127.0.0.1:4573)

ARI Example: Node.js AI Voice Bot

This Node.js ARI application demonstrates why ARI is the right choice for AI integration. When a call arrives, it streams audio to an ASR service, sends the transcript to an LLM, and plays back the response — all fully asynchronous. AGI would require blocking waits between each step; ARI handles it natively with event callbacks.

javascriptARI Node.js — AI voice bot with async LLM response
const ari = require('ari-client');  // npm install ari-client
const OpenAI = require('openai');

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

ari.connect('http://localhost:8088', 'asterisk', 'asterisk', (err, client) => {
  if (err) throw err;
  console.log('[ARI] Connected to Asterisk');

  // Register our Stasis application name
  // Dialplan: exten => s,1,Stasis(ai-voice-bot)
  client.start('ai-voice-bot');

  // Handle incoming calls
  client.on('StasisStart', async (event, channel) => {
    console.log(`[ARI] StasisStart: ${channel.id} from ${channel.caller.number}`);

    try {
      // Answer the channel
      await channel.answer();

      // Play a greeting while we warm up
      const greeting = await channel.play({ media: 'sound:hello-world' });

      // Wait for greeting to finish before listening
      greeting.on('PlaybackFinished', async () => {
        // Start a live recording — 5s max, stop on silence
        const recording = await channel.record({
          name: `rec_${channel.id}`,
          format: 'wav',
          maxDurationSeconds: 5,
          maxSilenceSeconds: 1,
          beep: true,
        });

        recording.on('RecordingFinished', async (recEvent) => {
          const audioFile = recEvent.recording.name;
          console.log(`[ARI] Recording complete: ${audioFile}`);

          // Transcribe with Whisper (simplified — in prod, fetch the file)
          const transcript = await transcribeAudio(audioFile);
          console.log(`[ARI] Transcript: ${transcript}`);

          // Get LLM response
          const llmReply = await getLLMResponse(transcript, channel.caller.number);
          console.log(`[ARI] LLM reply: ${llmReply}`);

          // Convert to speech and play
          const ttsFile = await synthesizeSpeech(llmReply);
          await channel.play({ media: `sound:${ttsFile}` });
        });
      });

    } catch (e) {
      console.error('[ARI] Error:', e.message);
      await channel.hangup().catch(() => {});
    }
  });

  client.on('StasisEnd', (event, channel) => {
    console.log(`[ARI] Call ended: ${channel.id}`);
  });
});

async function getLLMResponse(userInput, callerId) {
  const completion = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      { role: 'system', content: 'You are a helpful phone assistant. Be brief — under 50 words.' },
      { role: 'user', content: userInput },
    ],
    max_tokens: 100,
  });
  return completion.choices[0].message.content;
}

// Stubs — implement with your ASR/TTS provider
async function transcribeAudio(file) { return "How do I reset my password?"; }
async function synthesizeSpeech(text) { return "tts-response"; }

Real-World Use Cases: Which API Wins?

Simple DTMF IVR Menu

AGI

Linear: answer → play greeting → wait for digit → transfer. AGI's synchronous model maps perfectly. No async complexity needed.

AI-Powered Voice Bot (LLM + ASR)

ARI

ASR streams audio async, LLM responds async, TTS plays async. ARI's WebSocket event model handles all these streams concurrently without blocking.

Multi-Party Conferencing

ARI

ARI lets you programmatically create bridges, add/remove participants, mute/unmute, and react to speaking events in real time. AGI has no bridge API.

CRM-Integrated Click-to-Call

AMI + AGI

Use AMI to originate calls from your CRM, then AGI to handle the call flow after answer. Simple, battle-tested pattern.

WebRTC Contact Centre

ARI

Browser-based agents using WebRTC need real-time channel event visibility: ring, answer, hold, transfer. ARI WebSocket events make this trivial.

Voicemail / Auto-Attendant

AGI

Standard sequential flow: greeting, menu, record, email. AGI is faster to implement and debug. No async complexity justified.

Performance Benchmarks: AGI vs FastAGI vs ARI

These benchmarks reflect CelloIP production measurements on an 8-core server with 16 GB RAM running Asterisk 20 LTS:

MetricFork AGIFastAGI (TCP)ARI (Node.js)
Per-call startup latency50–200ms<5ms<1ms
Max concurrent calls (8-core)~200~2,000~5,000+
Memory per concurrent call~15–30 MB (process)~1–3 MB (thread)<1 MB (event)
Multi-channel controlNoNoYes
Async LLM streamingNoNoYes
DTMF inter-digit timeoutConfigurableConfigurableEvent-driven
Call setup overheadHigh (fork)Low (TCP)Minimal

Takeaway: for production >200 concurrent calls, never use fork-based AGI. Use FastAGI at minimum. For >1,000 concurrent calls or AI integration, ARI is the right choice.

AMI, AGI, ARI Together: The Complete Picture

In a sophisticated Asterisk deployment, all three interfaces coexist. A typical enterprise contact centre CelloIP deploys uses this combination:

1

CRM triggers a call via AMI

Agent clicks 'Call' in CRM. The CRM backend sends an AMI Originate command to Asterisk, which dials the customer and bridges to the agent's SIP endpoint.

2

Call enters Stasis → ARI app takes control

The dialplan routes the answered call into Stasis(contact-center-app). The ARI WebSocket app receives StasisStart and plays a queue music file while the agent is connected.

3

Supervisor uses AMI for monitoring

The supervisor dashboard subscribes to AMI events — ChannelStateChange, AgentLogin, QueueMemberStatus — to update a real-time wallboard. This is pure AMI read-only monitoring.

4

Legacy IVR branches still use FastAGI

The overnight batch callback IVR was written years ago in Python FastAGI. It works fine and there is no business case to rewrite it in ARI. Both coexist on the same server.

Migrating from AGI to ARI: Incremental Strategy

The most common mistake teams make is attempting a big-bang AGI-to-ARI rewrite. This is unnecessary and risky. The right approach is incremental:

Phase 1

Audit your AGI scripts

Categorise each AGI script: simple sequential (keep as FastAGI), complex stateful with async needs (migrate to ARI), AI-integrated (priority migrate to ARI). Do not migrate scripts that work fine as FastAGI.

Phase 2

Add ARI infrastructure

Enable HTTP server in asterisk.conf: [ari] enabled=yes, bindaddr=127.0.0.1, port=8088. Create an ARI user. Install ari-client (Node.js) or panoramisk (Python). Confirm connectivity with: curl http://asterisk:8088/ari/asterisk/info.

Phase 3

Migrate one flow at a time

Pick your most complex AGI script — likely your AI IVR or conference manager. Create a new Stasis() dialplan app. Build the ARI version alongside the AGI version. Test with a parallel extension. Cut over when stable.

Phase 4

Keep FastAGI where it works

There is no prize for rewriting working FastAGI scripts. A voicemail handler or simple DTMF menu that has been in production for 5 years and never has issues is not a migration candidate. Maintain both indefinitely.

Frequently Asked Questions

What is Asterisk AGI and how does it work?

AGI (Asterisk Gateway Interface) is a protocol that allows external scripts to control Asterisk call flow during an active call. When a dialplan hits an AGI() command, Asterisk forks the script and communicates via stdin/stdout: Asterisk writes environment variables and status messages, your script reads them and issues commands like ANSWER, STREAM FILE, GET DATA, EXEC, and HANGUP. FastAGI replaces the fork with a persistent TCP connection to a daemon your script runs as a server.

What is Asterisk ARI and how is it different?

ARI (Asterisk REST Interface) flips the control model. Instead of Asterisk invoking your script, your application subscribes to Asterisk events via WebSocket and issues commands via REST API. When a call enters a Stasis() dialplan app, ARI takes ownership — your code decides everything: answer timing, audio playback, bridging, recording. This async model excels when you need to control multiple channels simultaneously or integrate with async I/O like LLM APIs.

When should I use AGI vs ARI?

Use AGI when your call flow is sequential and predictable: greet → collect input → route. The synchronous request-response model makes it easy to write and debug. Use ARI when you need: async event handling across multiple channels, real-time AI/LLM integration where responses arrive asynchronously, programmatic conference bridge control, WebRTC agent interfaces, or fine-grained recording control. In CelloIP's experience, about 60% of IVR use cases can use AGI; the remaining 40% with AI or multi-party requirements need ARI.

What is FastAGI and when should I use it?

FastAGI runs your AGI handler as a persistent TCP server instead of forking a new process per call. The performance difference is dramatic at scale: fork-based AGI adds 50–200ms startup latency per call and is limited by OS process limits. FastAGI eliminates fork overhead entirely — the same Python or Node.js process handles hundreds of concurrent calls. Always use FastAGI in production; plain fork-AGI is fine only for development.

What is AMI and how does it relate to AGI and ARI?

AMI (Asterisk Manager Interface) is the management and monitoring API — it is not for in-call media control. Use AMI to originate calls programmatically (from a CRM 'click to call' button), monitor channel status, reload modules, or receive manager events. Use AGI or ARI for controlling what happens during a call. All three interfaces can coexist: a common pattern is AMI for origination, AGI/ARI for in-call flow.

How do I migrate existing AGI scripts to ARI?

Migrate incrementally. Identify AGI scripts that handle complex flows (AI, conferencing, multi-leg). For each, create a Stasis() application in your dialplan and rewrite the logic using ARI REST+WebSocket. Keep simple AGI scripts (voicemail, basic menus) in place — there is no urgency to migrate them. ARI and AGI coexist on the same Asterisk server with no conflicts.

Which Asterisk version introduced ARI?

ARI was introduced in Asterisk 12 (released December 2013). It replaced the experimental Asterisk SCF project with a stable REST+WebSocket model. As of 2026, ARI is fully mature and available in all supported Asterisk LTS versions (20 LTS, 21). AGI dates back to Asterisk 1.2, circa 2000.

What libraries exist for Asterisk ARI in Node.js and Python?

For Node.js: the official `ari-client` npm package wraps the REST API and WebSocket events with promises and event emitters. For Python: `panoramisk` (asyncio-based), `ari-py`, and `asterisk-ari-client`. For Go: `ari` by CyCoreSystems. CelloIP primarily uses Node.js ari-client and Python with panoramisk in production ARI deployments.

Need Expert Asterisk Development?

CelloIP has 12+ years building production Asterisk systems — AGI, FastAGI, ARI, AMI, AI IVR with LLM integration, WebRTC bridges, contact centres, and high-availability clusters. Whether you're starting fresh, migrating from AGI to ARI, or scaling to 10,000+ concurrent calls, we've done it before.

AGI → ARI Migration Audit

We review your AGI scripts and deliver a phased migration plan with risk assessment and effort estimates.

Dedicated Asterisk Engineer

Embed a senior Asterisk developer in your team — ARI applications, FastAGI, AMI, SIP trunk config, and performance tuning.

AI IVR Development

Full AI voice bot stack: ARI + Whisper ASR + GPT-4o + ElevenLabs TTS. Deploy in 6–8 weeks.