How to Listen for Events

Warning: Beta software This API is in beta stage and may be subject to change. Therefore, we do not recommend using this in production.

Interested in this feature? Please reach out to mobility-services-developer@cisco.com

Overview

The event API allows you to subscribe to a variety of different events generated by Cisco's systems.

The examples will start a subscription to voice and voicemail events, which includes call initiated, call ended and new voicemail received.

Prerequisites

  1. An OAuth 2.0 client
  2. A client access token

Required Scope

Event type Required scope
CONSENT_REVOKE_EVENT
HANDSET_UPDATE_EVENT events.handset_update.subscribe
LOCATION_UPDATE_EVENT events.location.subscribe
ROAMING_EVENT events.roaming.subscribe
SMS_EVENT events.sms.subscribe
SMS_DELIVERY_EVENT events.sms_delivery_report.subscribe
VOICE_EVENT events.voice.subscribe
VOICEMAIL_EVENT events.voicemail.subscribe

This event will fire when the OAuth2.0 consent is revoked for a subscription.

It allows the application to cleanup user state. This may either be via our revoke flow or from customer support.

Handset Update

This event will fire when a SIM card is used in a new device.

It will contain the previous and current IMEI.

Location Update

This event will fire when the handset is registering at a new location.

Roaming

This event will fire on the first location update in a new country.

It will contain the country code and name for the previous and current country.

SMS

This event will fire every time an SMS is sent or received by the handset.

This contains the from and to address in addition to the actual content of the SMS.

SMS Delivery Report

This event will fire when a SMS delivery report is generated.

This contains the from and to address, and the status for the SMS sent.

Voice

This will fire for when a call is initiated, ringing, answered, sent to voicemail or hung up. It will contain from and to number, call ID and which type of call event it is.

Voicemail

This will fire when a voicemail is left for a subscription.

It contains from and to number and the ID of the voicemail. The actual content of the voicemail may be retrieved by a separate API using this ID.

Code Dependencies

<dependencies>
    <dependency>
      <groupId>com.wgtwo.api.v0.grpc</groupId>
      <artifactId>events</artifactId>
      <version>0.1.6</version>
  </dependency>
    <dependency>
        <groupId>com.github.working-group-two.wgtwoapis</groupId>
        <artifactId>utils-grpc</artifactId>
        <version>cca7093</version>
    </dependency>
</depenencies>

Code, Listen for Events

If targeting production, you would need to add authentication to the sample code.


#!/usr/bin/env bash
grpcurl \
  -d '
  {
    "type": ["VOICE_EVENT"]
  }
  ' \
  sandbox.api.shamrock.wgtwo.com:443 \
  wgtwo.events.v0.EventsService.Subscribe

package com.example.events

import com.wgtwo.api.v0.events.EventsProto
import com.wgtwo.api.v0.events.EventsServiceGrpc
import io.grpc.ManagedChannelBuilder
import io.grpc.stub.StreamObserver

fun main() {
    val channel = ManagedChannelBuilder.forAddress("sandbox.api.shamrock.wgtwo.com", 443).build()
    val stub = EventsServiceGrpc.newStub(channel)
    val request = EventsProto.SubscribeEventsRequest.newBuilder()
        .addType(EventsProto.EventType.VOICE_EVENT)
        .addType(EventsProto.EventType.VOICEMAIL_EVENT)
        .build()
    stub.subscribe(
        request,
        object : StreamObserver<EventsProto.SubscribeEventsResponse> {
            override fun onNext(response: EventsProto.SubscribeEventsResponse) {
                println("Received event:
${response.event}")
            }

            override fun onError(throwable: Throwable) {
                println("Got error: ${throwable.message}")
                // TODO: Reconnect
            }

            override fun onCompleted() {
                println("Connection closed by the server")
            }
        },
    )
    println("subscribed to events, waiting.")
    // Wait for stream to close
    try {
        Thread.currentThread().join()
    } catch (e: InterruptedException) {
    }
}

Example Results, Listen for Events

{
  "event": {
    "timestamp": "2024-08-02T10:08:55.668065022Z",
    "voiceEvent": {
      "callId": "e70b8117-6e3d-49e1-83f6-0e7f932e5bb2",
      "type": "CALL_INITIATED",
      "fromNumber": {
        "e164": "+4799948352"
      },
      "toNumber": {
        "e164": "+46724450024"
      },
      "callerIdHidden": true
    },
    "owner": {
      "phoneNumber": {
        "e164": "+46724450024"
      },
      "sub": "66cd5d7e9d9bc07ae7f042ceb65ad0ba72047bf25ade95b8924d52fb705fd7ac630fa762d4419050d0f982aed86a33b7d4171a3fd4965c53d59dcf040a808c1a"
    }
  }
}
subscribed to events, waiting.
Received event:
timestamp {
  seconds: 1722599642
  nanos: 160869380
}
voicemail_event {
  voicemail_id: "764ba4ba-9d09-11eb-892b-37b0c646aeaa"
  type: NEW_VOICEMAIL
  from_number {
    e164: "+46161433210"
  }
  to_number {
    e164: "+4751972385"
  }
}
owner {
  phone_number {
    e164: "+46724450024"
  }
  sub: "66cd5d7e9d9bc07ae7f042ceb65ad0ba72047bf25ade95b8924d52fb705fd7ac630fa762d4419050d0f982aed86a33b7d4171a3fd4965c53d59dcf040a808c1a"
}

Manual Acknowledge

In the below example we enable manual acknowledgement, and set a custom ack timeout. This provides more control and flexibility when handling events.

Code, Manual Acknowledge


package com.example.events

import com.google.protobuf.util.Durations
import com.wgtwo.api.v0.events.EventsProto
import com.wgtwo.api.v0.events.EventsServiceGrpc
import io.grpc.ManagedChannelBuilder
import io.grpc.stub.StreamObserver

fun main() {
    val channel = ManagedChannelBuilder.forAddress("sandbox.api.shamrock.wgtwo.com", 443).build()
    val stub: EventsServiceGrpc.EventsServiceStub = EventsServiceGrpc.newStub(channel)
    val request = EventsProto.SubscribeEventsRequest.newBuilder()
        .addType(EventsProto.EventType.VOICE_EVENT)
        .addType(EventsProto.EventType.VOICEMAIL_EVENT)
        .setManualAck(
            EventsProto.ManualAckConfig.newBuilder()
                .setEnable(true)
                .setTimeout(Durations.fromSeconds(15))
                .build(),
        )
        .build()
    stub.subscribe(
        request,
        object : StreamObserver<EventsProto.SubscribeEventsResponse> {
            override fun onNext(response: EventsProto.SubscribeEventsResponse) {
                println("Received event:
${response.event}")
                acknowledge(response.event, stub)
            }

            override fun onError(throwable: Throwable) {
                println("Got error: ${throwable.message}")
                // TODO: Reconnect
            }

            override fun onCompleted() {
                println("Connection closed by the server")
            }
        },
    )
    println("subscribed to events, waiting.")
    // Wait for stream to close
    try {
        Thread.currentThread().join()
    } catch (e: InterruptedException) {
    }
}

fun acknowledge(event: EventsProto.Event, stub: EventsServiceGrpc.EventsServiceStub) {
    val request = EventsProto.AckRequest.newBuilder()
        .setSequence(event.metadata.sequence)
        .setInbox(event.metadata.ackInbox)
        .build()
    stub.ack(
        request,
        object : StreamObserver<EventsProto.AckResponse> {
            override fun onNext(response: EventsProto.AckResponse) {
                println("Event successfully acknowledged")
            }

            override fun onError(throwable: Throwable) {
                println("Error acknowledging event: ${throwable.message}")
            }

            override fun onCompleted() {}
        },
    )
}

Example Results, Manual Acknowledge

subscribed to events, waiting.
Received event:
timestamp {
  seconds: 1722599642
  nanos: 160869380
}
voicemail_event {
  voicemail_id: "764ba4ba-9d09-11eb-892b-37b0c646aeaa"
  type: NEW_VOICEMAIL
  from_number {
    e164: "+46161433210"
  }
  to_number {
    e164: "+4751972385"
  }
}
owner {
  phone_number {
    e164: "+46724450024"
  }
  sub: "66cd5d7e9d9bc07ae7f042ceb65ad0ba72047bf25ade95b8924d52fb705fd7ac630fa762d4419050d0f982aed86a33b7d4171a3fd4965c53d59dcf040a808c1a"
}

Read More