Asttapi was the first open source TAPI driver for use with Asterisk. It has been hugely popular on the sourceforge web site. Because we have neglected it a little its popularity is declining a little. It stills serves as a good reference of how to write a TAPI driver for Windows.

This is because we fell in love with other projects, such as Freeswitch which our babblevoice platform is developed on top of. I would highly recommend checking out Freeswitch if you need your own internal implementation, or if you want to get rid of the hassle of looking after your own PBX then put it into the cloud in babblevoice.

Check out our page on our next TAPI driver designed for Freeswitch.

If you want to use Asttapi, please feel free to, it is still on the sourceforge web site. It still works and it is still free.

Although, we do not actively support it any more, we hope the following information may be of use.

Configure Asttapi

AstTapi works by connecting to the Manager on the Asterisk server. You must configure your manager.conf correctly to do this.

I have an entry which looks like this




Reload the system.

To test this from your Windows machine, drop into a DOS box: telnet 5038 You will then see: Asterisk Call Manager/1.2 Install the TAPI driver onto your workstation.

To configure it, open your control panel, Phone and Modem Options, Advanced, Select AstTapi, then configure. You can now enter the host, user name and password as you have just configured above. Enter the user channel, this is the phone which will get called when you wish to initiate a call – your phone. Make sure at this point you have the Asterisk manager window running; you can see manager's logging on and problems which occur here easily.

This is now set up to make calls – from Outlook for example. The application now needs configuring; just choose the Asterisk line in the application.

To initiate screen popping (which by the way Outlook on its own is not capable of) you also need to enter the line name. The line name is the identifier which we recognize the call is intended for us. The line name has to be placed into the dial plan.

The line name could be a string you define yourself to track calls, or it could be something like the channel name. Using the channel name you could use string formatting in Asterisk to handle a lot of users simply. The dial plan is now used to signal TAPI events, there were several reasons for this

  • Different applications required different events – it is a very open standard!
  • It is quite difficult to map manager events effectively to TAPI events
  • It simplifies the TSP enormously, now it doesn't have to track the state of a call, this is now only handled in one place – in Asterisk

We use the UserEvent function of Asterisk to signal the event to our TAPI driver. The event takes on the form UserEvent(TAPI|TAPIEVENT: )

The event data is recognisable to any TAPI programmer. We use the following events to signal a new inbound call to the application

  • LINE_NEWCALL line_1

The phone rings for a while (note the line_1 is the identifier which is configured in AstTapi)


You talk for a while


A word about LINE_NEWCALL LINE_NEWCALL has extra meaning in AstTapi. In normal TAPI it should only ever be presented when a new call comes in that the TAPI application does not now about. The AstTapi TSP filters this, so that if it is received regarding calls the application initiated we will filter it out. This is intended to make the dial plan slightly simpler to implement.

AstTapi also uses this event, to track calls with, once it receives this event it uses Asterisk's unique id's to track the call.

It also uses the event LINE_CALLSTATE LINECALLSTATE_IDLE to remove this call from its list of calls to track. If you do not enter this into the dial plan, then not only is your application not going to now when the line is idle again, but also you will have a slow memory leak in the TSP as it will not know when to free memory allocated to a call initiated earlier.


This event tells the TAPI application that the call is connected. But from a dial plan point of view is quite tricky to get in a place when the call is truly connected. We also have to manage calls which are one to many, that is 1 call comes in and 2 phones ring. For example we may have in our dial plan

exten => 100,1,dial(sip/bob&sip/bobbett) First off we handle sending the LINECALLSTATE_CONNECTED event in this fashion: exten => 100,1,dial(sip/bob&sip/bobbett||M(tapi^${UNIQUEID})) We have to pass the unique ID across to the macro which is called when the call is connected, as the macro is run as the second leg of the call which has a new unique id.


          exten => s,1,UserEvent(TAPI|TAPIEVENT: [~${ARG1}&sip/bob] LINE_CALLSTATE LINECALLSTATE_CONNECTED)
          exten => s,1,UserEvent(TAPI|TAPIEVENT: [~${ARG1}&!sip/bob] LINE_CALLSTATE LINECALLSTATE_HANGUP)

You will note a few things here

  • Our TAPI events are sent by way of a specially formatted UserEvent
  • We can simply send the TAPI command, and AstTapi will track the call for you
  • Or we can signal AstTapi which call it is referring to
  • We can also describe which line we intend the call for if the call is being tracked by a number of lines.

After the TAPI|TAPIEVENT string we can optionally place a [] which the contents take on the format

If the character '~' is used the string following it is the Asterisk unique ID we intend the signal for (by default AstTapi traces the Asterisk unique ID generated when the NEWCALL is signalled).

Any string which is a line identifier means that this TAPI event is specifically for that line.

Any string proceeded by a '!' means that this event is specifically excluded for that line (and by default intended for all others if a line is not specified – see the previous comment).

We can have multiple definitions here which are separated by the '&' character.

Putting it all together

For inbound calls we will extend the standard extension macro to include our TAPI events

          exten => s,1,UserEvent(TAPI|TAPIEVENT [~${ARG1}&sip/bob] LINE_CALLSTATE LINECALLSTATE_CONNECTED)
          exten => s,1,UserEvent(TAPI|TAPIEVENT [~${ARG1}&!sip/bob] LINE_CALLSTATE LINECALLSTATE_HANGUP)


          ;Our TAPI events
          exten => s,1,UserEvent(TAPI|TAPIEVENT: LINE_NEWCALL ${ARG3})
          exten => s,n,UserEvent(TAPI|TAPIEVENT: SET CALLERID ${CALLERID})

          ;The normal macro
          exten => s,n,Dial(${ARG1},20,TM(tapi^${UNIQUEID}))
          exten => s,2,Goto(s-${DIALSTATUS},1)                            ; Jump based on status (NOANSWER,BUSY,CHANUNAVAIL,CONGESTION,ANSWER)
          exten => s-NOANSWER,1,Voicemail(u${ARG1})               ; If unavailable, send to voicemail w/ unavail announce
          exten => s-NOANSWER,2,Goto(default,s,1)                 ; If they press #, return to start
          exten => s-BUSY,1,Voicemail(b${ARG1})                   ; If busy, send to voicemail w/ busy announce
          exten => s-BUSY,2,Goto(default,s,1)                             ; If they press #, return to start
          exten => _s-.,1,Goto(s-NOANSWER,1)                              ; Treat anything else as no answer
          exten => a,1,VoicemailMain(${ARG1})                             ; If they press *, send the user into VoicemailMain

We also have to remember to have the 'h' extension in the context the call came in on: exten => h,1,UserEvent(TAPI|TAPIEVENT: LINE_CALLSTATE LINECALLSTATE_IDLE)

We also have to remember that Outlook and other diallers expect to see events to know about the progress of the call they have requested. Here we have to modify the way outgoing calls are made

          ;  Local seven digit dialling accessed through the trunk interface
          exten => _9NXXXXXX,1,UserEvent(TAPI|TAPIEVENT: LINE_NEWCALL line_1)
          exten => _9NXXXXXX,1,Dial(${TRUNK}/${EXTEN:${TRUNKMSD}}||M(tapi^${UNIQUEID}))


  • Because the TSP filters the LINE_NEWCALL event, this means this can be used in the situation where either the TAPI application initiates the call, or if it is a call which is initiated by the actual phone
  • Remember this must also pick up on the ‘h’ extension to signal when the call is idle again