Notification API

Overview

The Avaya Infinity™ Notification API lets third-party systems receive real-time event notifications for agent activity and interaction lifecycle changes via webhooks. Instead of polling for state changes, you register a subscription once and Avaya Infinity pushes events to your endpoint as they happen.

Common use cases include:

  • Agent activity monitoring — Track agent login/logout and ready/not-ready state transitions to power workforce management dashboards or real-time reporting tools.
  • Interaction lifecycle tracking — Trigger downstream workflows or CRM updates when interactions are created, transferred, or completed.
  • Audit logging and compliance — Capture a continuous event stream for immutable audit trails without polling the platform.
  • Proactive customer notifications — Use interaction completion events to automatically send follow-up messages or satisfaction surveys.

Subscriptions are time-limited and must be renewed before they expire to remain active. The API follows standard REST conventions and uses OAuth 2.0 Bearer token authentication.


Before You Begin

Find Your Customer Subdomain

All Notification API requests are scoped to your Avaya Infinity tenant. Your subdomain appears in your Infinity portal URL:

https://core.avaya1234.ec.avayacloud.com/app/core-config-ui/
                  ^^^^^^^^^^^
                  your subdomain

All API requests use this base URL pattern:

https://core.{customerId}.ec.avayacloud.com/api/events/v1

Request API Credentials

Contact Avaya Support to request a client_id and client_secret provisioned with the EVENT_NOTIFICATION OAuth 2.0 scope. These credentials are separate from those used for the Workflow or Custom Messaging APIs.

Obtain an Access Token

Exchange your credentials for a Bearer token using the client credentials flow:

curl -X POST \
  https://core.{customerId}.ec.avayacloud.com/auth/realms/avaya/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&scope=EVENT_NOTIFICATION"

Response:

{
  "access_token": "eyJhbGciOiJSUzI1...",
  "expires_in": 900,
  "refresh_expires_in": 9000,
  "refresh_token": "eyJhbGciOiJIUzI1...",
  "token_type": "bearer",
  "scope": "EVENT_NOTIFICATION"
}

Include this token in the Authorization header of every subsequent request:

Authorization: Bearer {access_token}

Note: The account/tenant ID is derived automatically from the organization field in your JWT token. You do not need to pass it separately in the request body.


Key Concepts

Event Families

Subscriptions are organized around event families. Each family groups a related category of events. When creating a subscription, specify one or more families and optionally filter to specific event types within those families.

FamilyDescription
AGENTAgent state changes: login, logout, ready, not-ready
INTERACTIONInteraction lifecycle events: created, completed, transferred
QUEUEQueue-level events
ALLAll families — must be specified alone, not combined with others

You can subscribe to up to 3 families in a single subscription. If you specify ALL, it must be the only value in the array.

Event Types

Within each family, subscribe to specific event types or use ALL to receive every event in that family. You can specify up to 25 event types per subscription.

Agent family events:

EventDescription
Agent.LoggedInAgent has logged into the system
Agent.LoggedOutAgent has logged out
Agent.ReadyAgent has set their state to Ready
Agent.NotReadyAgent has set their state to Not Ready

Interaction family events:

EventDescription
Interaction.CreatedA new interaction has been created
Interaction.CompletedAn interaction has ended
Interaction.TransferredAn interaction has been transferred to another agent or queue

Transport

The only supported transport type is WEBHOOK. When an event occurs, Avaya Infinity makes an HTTP POST (or PUT) request to the endpoint URL you configure in your subscription.

You can optionally specify an authToken and authTokenHeader. When set, Avaya will include the token value under the specified header name in every webhook delivery — use this on your server to verify that requests genuinely originate from Avaya Infinity.

Subscription Lifecycle

Subscriptions are time-limited. The expiresIn field (seconds) counts down to expiry. If a subscription expires it transitions to INACTIVE and stops delivering events. Call the Renew endpoint before expiry to keep it active — an INACTIVE subscription can also be reactivated the same way.

  CREATE ──────► ACTIVE ──────► (renewed before expiry) ──────► ACTIVE
                   │                                                ▲
                   │ (not renewed in time)                          │
                   ▼                                                │
               INACTIVE ──────────────────────────────────────────┘
                             (reactivated via Renew endpoint)

Best practice: Run a background job that monitors expiresIn across your subscriptions and calls the Renew endpoint proactively — for example, when expiresIn drops below 300 seconds (5 minutes).


Data Models

All request and response bodies conform to the schemas below. Understanding these models upfront will help you build confidently without needing to refer back to the raw spec.

CreateSubscription — Request Body

Used when creating a new subscription (POST /subscriptions).

FieldTypeRequiredConstraintsDescription
familyarray of stringsYes1–3 items; or ["ALL"] aloneEvent families to subscribe to. See Event Families.
eventsarray of stringsYes1–25 items; max 256 chars eachSpecific event types within the family, or ["ALL"] for everything.
transportTransport objectYesSee belowWebhook delivery configuration.

Transport — Webhook Configuration

Used in CreateSubscription and PatchSubscription.

FieldTypeRequiredConstraintsDescription
typestringYesEnum: WEBHOOKTransport type. Currently only WEBHOOK is supported.
methodstringYesEnum: POST, PUT; default POSTHTTP method used to deliver events to your endpoint.
endpointstringYesURI format; max 2048 charsYour webhook URL. Must be HTTPS in production.
authTokenstringNo1–256 charsToken Avaya will send in the auth header for webhook verification. Must be used together with authTokenHeader.
authTokenHeaderstringNo1–256 chars; default auth-tokenHTTP header name for the auth token. Must be used together with authToken.

Subscription — Response Object

Returned by Create, Get, Update, Renew, and List operations.

FieldTypeRead-onlyDescription
subscriptionIdstring (UUID)YesUnique 36-character identifier. Pattern: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
statusstringYesCurrent status: ACTIVE, INACTIVE, or PENDING
createdAtstring (ISO 8601)YesTimestamp when the subscription was created. Example: 2020-08-01T14:25:23.162Z
expiresAtstring (ISO 8601)YesTimestamp when the subscription expires. Example: 2020-08-08T14:25:23.177Z
expiresIninteger (int64)YesSeconds remaining before the subscription expires. Example: 900
familyarray of stringsNoEvent families this subscription covers.
eventsarray of stringsNoEvent types this subscription filters on.
transportTransportResponse objectNoWebhook configuration (see below).

Note: The subscriptionId is only available from API responses — it is not visible in the Avaya Infinity Admin portal. Save it from the POST /subscriptions response body when you create a subscription. If you lose track of a subscription's ID, retrieve it via GET /subscriptions and match on the endpoint URL in the transport object.

TransportResponse — Webhook Configuration (in Responses)

Returned as part of the Subscription object.

FieldTypeDescription
typestringTransport type. Currently WEBHOOK.
methodstringHTTP method: POST or PUT.
endpointstringConfigured webhook URL.
authTokenstringConfigured auth token value (if set).
authTokenHeaderstringConfigured auth token header name (if set). Default: auth-token.

PatchSubscription — Request Body

Used when partially updating a subscription (PATCH /subscriptions/{subscriptionId}). All fields are optional — only fields you include will be modified.

FieldTypeConstraintsDescription
familyarray of strings1–3 items; or ["ALL"] aloneUpdated event families.
eventsarray of strings1–25 itemsUpdated event types.
transportTransport objectSee Transport aboveUpdated webhook configuration.

RenewSubscription — Request Body

Used when renewing a subscription (POST /subscriptions/{subscriptionId}:renew). The entire body is optional — omit it to renew with no changes.

FieldTypeConstraintsDescription
transport.authTokenstring0–256 charsNew auth token value to rotate at renewal time.

SubscriptionPage — Paginated List Response

Returned by GET /subscriptions.

FieldTypeDescription
pagination.pageNumberintegerCurrent page number (1-indexed).
pagination.pageSizeintegerRecords per page (max 25).
pagination.totalintegerTotal number of subscriptions across all pages.
pagination.totalPagesintegerTotal pages available.
subscriptionsarray of SubscriptionThe subscriptions on this page.
links.prevstringURL of the previous page; blank if on the first page.
links.nextstringURL of the next page; blank if on the last page.

Problem — Error Response

All 4xx and 5xx responses use the RFC 7807 Problem Detail format with Content-Type: application/problem+json.

FieldTypeRequiredDescription
typestring (URI)YesURI identifying the problem type.
titlestringYesShort human-readable summary.
statusintegerYesHTTP status code.
detailstringNoHuman-readable explanation of this specific occurrence.
instancestring (URI)NoURI identifying this specific occurrence.
violationsarrayNoList of field-level violations (used with 400 responses).
violations[].fieldstringField name that caused the violation.
violations[].messagestringHuman-readable explanation of the violation.
violations[].codeintegerMachine-readable violation code. Use this in error handling logic — messages may change between releases, codes do not.

Limits & Constraints

LimitValue
Max families per subscription3 (or ALL alone)
Max event types per subscription25 (or ALL)
Max endpoint URL length2048 characters
Max authToken length256 characters
Max authTokenHeader length256 characters
subscriptionId format36-character UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
List pageSize default10
List pageSize maximum25
List pageNumber maximum1000

Rate Limiting

All endpoints implement DOS protection and rate limiting. The subscription management endpoints (create, list, get, update, renew, delete) are designed for infrequent administrative calls — not high-frequency automation.

If you receive a 429 Too Many Requests response, do not retry immediately. Implement exponential backoff with jitter: start with a 1-second delay, doubling on each attempt up to a maximum of 60 seconds.

Common causes of hitting rate limits:

  • A renewal loop running on too short an interval (check once per minute, not once per second)
  • Listing all subscriptions on every webhook delivery to look up subscription details (cache the subscription list instead)
  • Re-creating subscriptions on every application restart rather than reusing existing active ones

Best practice: Create subscriptions once and manage them with the Renew endpoint. Do not delete and recreate subscriptions on a regular cadence.


API Reference Summary

Base URL: https://core.{customerId}.ec.avayacloud.com/api/events/v1

MethodEndpointOperation
POST/subscriptionsCreate a new subscription
GET/subscriptionsList all subscriptions (paginated)
GET/subscriptions/{subscriptionId}Get a subscription by ID
PATCH/subscriptions/{subscriptionId}Update a subscription
POST/subscriptions/{subscriptionId}:renewRenew a subscription
DELETE/subscriptions/{subscriptionId}Delete a subscription

For the interactive API explorer, see the API Reference.


Endpoints

Create Subscription

POST /subscriptions

Registers your webhook endpoint and selects the event families and types you want to receive. Returns 201 Created with the full subscription object including the subscriptionId you'll use for all subsequent operations. The new subscription is immediately ACTIVE and begins delivering events.

cURL:

curl -X POST \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "family": ["AGENT", "INTERACTION"],
    "events": [
      "Agent.LoggedIn",
      "Agent.LoggedOut",
      "Agent.Ready",
      "Agent.NotReady",
      "Interaction.Created",
      "Interaction.Completed"
    ],
    "transport": {
      "type": "WEBHOOK",
      "method": "POST",
      "endpoint": "https://your-system.example.com/webhooks/avaya-events",
      "authToken": "your-secret-verification-token",
      "authTokenHeader": "x-avaya-auth"
    }
  }'

Node.js:

const response = await fetch(
  'https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      family: ['AGENT', 'INTERACTION'],
      events: [
        'Agent.LoggedIn', 'Agent.LoggedOut', 'Agent.Ready', 'Agent.NotReady',
        'Interaction.Created', 'Interaction.Completed'
      ],
      transport: {
        type: 'WEBHOOK',
        method: 'POST',
        endpoint: 'https://your-system.example.com/webhooks/avaya-events',
        authToken: 'your-secret-verification-token',
        authTokenHeader: 'x-avaya-auth'
      }
    })
  }
);
const subscription = await response.json();
console.log('Subscription ID:', subscription.subscriptionId);

Python:

import requests

response = requests.post(
    'https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions',
    headers={
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json'
    },
    json={
        'family': ['AGENT', 'INTERACTION'],
        'events': [
            'Agent.LoggedIn', 'Agent.LoggedOut', 'Agent.Ready', 'Agent.NotReady',
            'Interaction.Created', 'Interaction.Completed'
        ],
        'transport': {
            'type': 'WEBHOOK',
            'method': 'POST',
            'endpoint': 'https://your-system.example.com/webhooks/avaya-events',
            'authToken': 'your-secret-verification-token',
            'authTokenHeader': 'x-avaya-auth'
        }
    }
)
subscription = response.json()
print('Subscription ID:', subscription['subscriptionId'])

Response (201 Created):

{
  "subscriptionId": "fdbec917-e76e-4645-8120-4eac46f29487",
  "createdAt": "2020-08-01T14:25:23.162Z",
  "expiresAt": "2020-08-08T14:25:23.177Z",
  "expiresIn": 900,
  "status": "ACTIVE",
  "family": ["AGENT", "INTERACTION"],
  "events": [
    "Agent.LoggedIn",
    "Agent.LoggedOut",
    "Agent.Ready",
    "Agent.NotReady",
    "Interaction.Created",
    "Interaction.Completed"
  ],
  "transport": {
    "type": "WEBHOOK",
    "method": "POST",
    "endpoint": "https://your-system.example.com/webhooks/avaya-events",
    "authToken": "your-secret-verification-token",
    "authTokenHeader": "x-avaya-auth"
  }
}

Save the subscriptionId — you will need it to manage this subscription going forward.


List Subscriptions

GET /subscriptions

Returns a paginated list of all subscriptions for your account.

Query parameters:

ParameterTypeDefaultMaximumDescription
pageNumberinteger11000Page to retrieve (1-indexed)
pageSizeinteger1025Number of records per page

cURL:

curl -X GET \
  "https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions?pageNumber=1&pageSize=10" \
  -H "Authorization: Bearer {access_token}"

Response (200 OK):

{
  "pagination": {
    "pageNumber": 1,
    "pageSize": 10,
    "total": 3,
    "totalPages": 1
  },
  "subscriptions": [
    {
      "subscriptionId": "fdbec917-e76e-4645-8120-4eac46f29487",
      "createdAt": "2020-08-01T14:25:23.162Z",
      "expiresAt": "2020-08-08T14:25:23.177Z",
      "expiresIn": 604,
      "status": "ACTIVE",
      "family": ["AGENT"],
      "events": ["ALL"],
      "transport": {
        "type": "WEBHOOK",
        "method": "POST",
        "endpoint": "https://your-system.example.com/webhooks/avaya-events",
        "authTokenHeader": "x-avaya-auth"
      }
    }
  ],
  "links": {
    "prev": "",
    "next": ""
  }
}

Use links.next and links.prev to navigate between pages.


Get Subscription

GET /subscriptions/{subscriptionId}

Returns full details of a single subscription. Returns 404 Not Found if the ID does not exist.

cURL:

curl -X GET \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/fdbec917-e76e-4645-8120-4eac46f29487 \
  -H "Authorization: Bearer {access_token}"

Update Subscription

PATCH /subscriptions/{subscriptionId}

Partially updates a subscription. Only fields present in the request body are modified — omitted fields remain unchanged.

cURL:

curl -X PATCH \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/fdbec917-e76e-4645-8120-4eac46f29487 \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      "Agent.LoggedIn",
      "Agent.LoggedOut",
      "Agent.Ready",
      "Agent.NotReady",
      "Interaction.Created",
      "Interaction.Completed",
      "Interaction.Transferred"
    ]
  }'

Returns 200 OK with the full updated subscription object. Returns 404 Not Found if the ID does not exist.


Renew Subscription

POST /subscriptions/{subscriptionId}:renew

Resets the subscription expiry timer, keeping it ACTIVE. Also reactivates an INACTIVE subscription — event delivery resumes immediately. Optionally rotate your authToken at the same time.

cURL — renew without changes:

curl -X POST \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/fdbec917-e76e-4645-8120-4eac46f29487:renew \
  -H "Authorization: Bearer {access_token}"

cURL — renew and rotate the auth token:

curl -X POST \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/fdbec917-e76e-4645-8120-4eac46f29487:renew \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "transport": {
      "authToken": "your-new-rotated-token"
    }
  }'

Returns 200 OK with the refreshed subscription object, including updated expiresAt and expiresIn. Returns 404 Not Found if the ID does not exist.


Delete Subscription

DELETE /subscriptions/{subscriptionId}

Permanently removes a subscription. Event delivery stops immediately. This action cannot be undone.

cURL:

curl -X DELETE \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/fdbec917-e76e-4645-8120-4eac46f29487 \
  -H "Authorization: Bearer {access_token}"

Returns 204 No Content on success. Returns 404 Not Found if the ID does not exist.


Receiving Webhook Events

How Delivery Works

When a subscribed event occurs, Avaya Infinity makes an HTTP request to your configured endpoint. Your server must return a 2xx response to acknowledge receipt.

BehaviourDetail
Delivery guaranteeAt-least-once — the same event may be delivered more than once
Event orderingEvents may arrive out of order — use event timestamps to sort if sequence matters
Retry on failureYes — Avaya Infinity retries delivery when your endpoint returns a non-2xx response or does not respond in time
AcknowledgementReturn any 2xx status code to confirm receipt

Design for idempotency. Because delivery is at-least-once, your handler must process the same event more than once without unintended side effects. See Handling Duplicate Events below.

Webhook Request Headers

Every delivery includes the following HTTP headers:

HeaderDescription
Content-Typeapplication/json
{authTokenHeader}Your configured auth token value (e.g. x-avaya-auth: your-secret-token) — only present if authToken was configured on the subscription

Verifying Webhook Authenticity

Always validate the auth token header before processing any payload. This confirms the request originates from Avaya Infinity and not a third party.

Node.js / Express:

app.post('/webhooks/avaya-events', (req, res) => {
  const receivedToken = req.headers['x-avaya-auth'];
  const expectedToken = process.env.AVAYA_WEBHOOK_AUTH_TOKEN;

  if (!receivedToken || receivedToken !== expectedToken) {
    return res.status(401).send('Unauthorized');
  }

  // Acknowledge immediately — process asynchronously to avoid timeouts
  res.status(200).send('OK');
  processEventAsync(req.body);
});

Python / Flask:

from flask import Flask, request, abort
import os

app = Flask(__name__)

@app.route('/webhooks/avaya-events', methods=['POST'])
def handle_event():
    received_token = request.headers.get('x-avaya-auth')
    expected_token = os.environ.get('AVAYA_WEBHOOK_AUTH_TOKEN')

    if not received_token or received_token != expected_token:
        abort(401)

    event = request.get_json()
    # Acknowledge immediately — process asynchronously
    process_event_async(event)
    return '', 200

Best practice: Respond with 200 immediately and handle heavy processing (database writes, downstream API calls) in a background job or queue. Slow responses will be treated as failures and trigger retries.

Handling Duplicate Events

To safely handle events your endpoint receives more than once:

  1. Extract a unique identifier from the event payload (a combination of event type, timestamp, and entity ID works well).
  2. Check whether you have already processed an event with that identifier — for example using Redis SET NX with a short TTL, or a deduplication column in your database.
  3. If already processed, discard the event and still return 200. Do not return an error — that would trigger further retries.
  4. Otherwise, record the identifier as processed and handle the event normally.

Event Payload Structure

Every webhook delivery is a JSON object with a consistent top-level structure. The eventType field identifies which event occurred; the payload object contains the event-specific data.

Top-level fields present on all events:

FieldTypeDescription
eventTypestringThe event type, e.g. Agent.LoggedIn, Interaction.Created.
timestampstring (ISO 8601)When the event occurred. Use this to sort out-of-order deliveries.
accountIdstringYour account/tenant identifier.
payloadobjectEvent-specific data. See examples below.

Example: Agent.LoggedIn

{
  "eventType": "Agent.LoggedIn",
  "timestamp": "2025-11-03T13:26:24.000Z",
  "accountId": "1234567890",
  "payload": {
    "agentId": "0020005ca02e4654dfde78ab12",
    "agentName": "Jane Smith",
    "loginTime": "2025-11-03T13:26:24.000Z"
  }
}

Example: Agent.NotReady

{
  "eventType": "Agent.NotReady",
  "timestamp": "2025-11-03T14:05:10.000Z",
  "accountId": "1234567890",
  "payload": {
    "agentId": "0020005ca02e4654dfde78ab12",
    "agentName": "Jane Smith",
    "reason": "Break"
  }
}

Example: Interaction.Created

{
  "eventType": "Interaction.Created",
  "timestamp": "2025-11-03T13:30:00.000Z",
  "accountId": "1234567890",
  "payload": {
    "interactionId": "004d01000069d6b91753dcd4c0",
    "channel": "voice",
    "queueId": "003d010227ce56f0fe85da8098",
    "direction": "inbound",
    "createdAt": "2025-11-03T13:30:00.000Z"
  }
}

Example: Interaction.Completed

{
  "eventType": "Interaction.Completed",
  "timestamp": "2025-11-03T13:45:22.000Z",
  "accountId": "1234567890",
  "payload": {
    "interactionId": "004d01000069d6b91753dcd4c0",
    "channel": "voice",
    "queueId": "003d010227ce56f0fe85da8098",
    "completedAt": "2025-11-03T13:45:22.000Z"
  }
}

Tip: To capture the exact payload shapes your tenant produces before building your integration, use a webhook inspection service such as webhook.site as your subscription endpoint and trigger real events in Avaya Infinity™. See Using a Webhook Inspection Service in the Local Development & Testing section.


Common Scenarios

A Newly Created Subscription Is Immediately ACTIVE

When you successfully call POST /subscriptions, the subscription is immediately ACTIVE — there is no pending or provisioning state to wait for. Event delivery begins at once. The response body includes expiresAt and expiresIn so you can schedule your first renewal.

Your Webhook Endpoint Is Temporarily Unavailable

If your endpoint returns a non-2xx response or does not respond within the timeout window, Avaya Infinity will retry delivery automatically. The same event may arrive multiple times as a result. Your handler must be idempotent — processing the same event twice should have no unintended side effects. See Handling Duplicate Events.

If your endpoint is down for an extended period and the subscription expires while it is unreachable, the subscription transitions to INACTIVE and retries stop. Call the Renew endpoint once your endpoint is restored to reactivate it.

A Subscription Has Gone INACTIVE

An INACTIVE subscription appears in GET /subscriptions with "status": "INACTIVE" and an expiresIn of 0. It is no longer delivering events. To reactivate it, call the Renew endpoint:

curl -X POST \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/fdbec917-e76e-4645-8120-4eac46f29487:renew \
  -H "Authorization: Bearer {access_token}"

The subscription returns to ACTIVE immediately and a new expiresAt is set. No events that arrived during the inactive window are replayed — delivery is live-only from the point of reactivation.

Events Are Arriving Out of Order

This is expected. The Notification API does not guarantee delivery order. If your integration depends on event sequence — for example, to track agent state transitions accurately — always use the timestamp field in the event payload to sort events, not the order in which they arrive at your endpoint.


Common Integration Patterns

Pattern 1: Agent Activity Dashboard

Subscribe to the AGENT family to power a real-time supervisor dashboard showing agent states.

curl -X POST \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "family": ["AGENT"],
    "events": ["ALL"],
    "transport": {
      "type": "WEBHOOK",
      "method": "POST",
      "endpoint": "https://dashboard.example.com/api/agent-events",
      "authToken": "dashboard-webhook-secret",
      "authTokenHeader": "x-avaya-auth"
    }
  }'

On your backend, maintain a map of agent IDs to their current states, updating on each event. Push changes to connected browsers via WebSocket or Server-Sent Events.


Pattern 2: CRM Interaction Sync

Subscribe to INTERACTION events to automatically create and close CRM records.

curl -X POST \
  https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "family": ["INTERACTION"],
    "events": ["Interaction.Created", "Interaction.Completed", "Interaction.Transferred"],
    "transport": {
      "type": "WEBHOOK",
      "method": "POST",
      "endpoint": "https://crm.example.com/webhooks/interactions",
      "authToken": "crm-webhook-secret",
      "authTokenHeader": "x-avaya-auth"
    }
  }'

Pattern 3: Proactive Subscription Renewal

Avoid expiry by running a background job that monitors expiresIn and renews subscriptions proactively.

const RENEWAL_THRESHOLD_SECONDS = 300; // Renew when less than 5 minutes remain
const POLL_INTERVAL_MS = 60_000;       // Check every minute

async function renewExpiringSubscriptions(accessToken) {
  const res = await fetch(
    'https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions?pageSize=25',
    { headers: { Authorization: `Bearer ${accessToken}` } }
  );
  const { subscriptions } = await res.json();

  for (const sub of subscriptions) {
    if (sub.status === 'ACTIVE' && sub.expiresIn < RENEWAL_THRESHOLD_SECONDS) {
      await fetch(
        `https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/${sub.subscriptionId}:renew`,
        { method: 'POST', headers: { Authorization: `Bearer ${accessToken}` } }
      );
      console.log(`Renewed subscription ${sub.subscriptionId}`);
    }
  }
}

setInterval(() => renewExpiringSubscriptions(getAccessToken()), POLL_INTERVAL_MS);

Pattern 4: Webhook Auth Token Rotation

Rotate your webhook auth token on a regular schedule — for example monthly — without disrupting event delivery, by combining renewal with a token update in a single call.

async function rotateWebhookToken(subscriptionId, newToken, accessToken) {
  const res = await fetch(
    `https://core.avaya1234.ec.avayacloud.com/api/events/v1/subscriptions/${subscriptionId}:renew`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ transport: { authToken: newToken } })
    }
  );
  if (res.ok) {
    // Update the expected token in your webhook handler config before retiring the old one
    await updateWebhookSecret(newToken);
    console.log('Webhook token rotated successfully');
  }
}

Error Handling

All error responses use the RFC 7807 Problem Detail format with Content-Type: application/problem+json.

HTTP StatusTitleCommon Causes
400 Bad RequestConstraint ViolationMissing required fields; ALL combined with other families; invalid family value; empty events array; authToken and authTokenHeader not provided together
401 UnauthorizedUnauthorizedExpired, malformed, or missing Bearer token
403 ForbiddenForbiddenToken lacks EVENT_NOTIFICATION scope; account mismatch
404 Not FoundResource Not FoundInvalid or already-deleted subscriptionId
500 Internal Server ErrorServer ErrorPlatform error — retry with exponential backoff

Example 400 response:

{
  "type": "https://developers.avayacloud.com/onecloud-ccaas/docs/error-handling#constraint-violation",
  "title": "Constraint Violation",
  "status": 400,
  "detail": "A problem that indicates a syntactically correct, yet semantically illegal request.",
  "violations": [
    {
      "field": "family",
      "message": "must not be null",
      "code": 20002
    }
  ]
}

Use violations[].code — not violations[].message — in your error handling logic. Codes are stable across releases; messages may change.

Recommended retry strategy:

  • 400, 401, 403, 404 — do not retry; resolve the underlying issue first.
  • 429 Too Many Requests — back off and retry using exponential backoff with jitter.
  • 500 — retry with exponential backoff. Start with a 1-second delay, doubling on each attempt up to a maximum of 60 seconds.

Local Development & Testing

Exposing a Local Endpoint

Avaya Infinity needs a publicly accessible HTTPS URL to deliver webhooks. During local development your server is typically not reachable from the internet. Use a tunnelling tool to create a temporary public URL forwarding to your local machine.

Using ngrok:

# Expose your local port 3000
ngrok http 3000

ngrok will display a forwarding URL such as https://abc123.ngrok.io. Use this as your subscription endpoint while testing. Keep the process running — the URL changes on each restart with a free plan.

Using localtunnel:

npx localtunnel --port 3000

Using a Webhook Inspection Service

Before writing any handler code, capture and inspect real payloads from Avaya Infinity using a webhook inspection service such as webhook.site or RequestBin. These services give you a unique URL and display every incoming request in real time — headers, body, and all.

  1. Open webhook.site and copy your unique URL.
  2. Create a subscription using that URL as the endpoint.
  3. Trigger activity in Avaya Infinity (agent login, create an interaction, etc.).
  4. Inspect the full payload in the webhook.site interface.

This is the fastest way to understand the exact shape of event payloads before building your integration, and requires no local setup at all.

Testing Your Auth Verification Logic Locally

Simulate a webhook delivery from Avaya Infinity to your local server using cURL:

curl -X POST http://localhost:3000/webhooks/avaya-events \
  -H "Content-Type: application/json" \
  -H "x-avaya-auth: your-secret-verification-token" \
  -d '{
    "eventType": "Agent.Ready",
    "timestamp": "2024-11-15T10:30:00.000Z"
  }'

Adjust the header name and token value to match what you configured in your subscription. This lets you verify your auth validation works correctly before connecting a live subscription.


Troubleshooting

My webhook endpoint is not receiving events

  • Confirm your subscription status is ACTIVE via GET /subscriptions/{subscriptionId}. If INACTIVE, the subscription has expired — call the Renew endpoint to reactivate it.
  • Verify that your endpoint URL is publicly accessible over HTTPS. Avaya Infinity cannot reach localhost, private network addresses, or plain HTTP in production.
  • Check that your server is returning a 2xx HTTP response. Any non-2xx response is treated as a delivery failure and will trigger retries.
  • Ensure no firewall, load balancer, or WAF is blocking inbound POST requests to your endpoint URL.

My subscription keeps going INACTIVE

  • Subscription expiry is tied to the lifetime of the access token used to create it. Implement a proactive renewal job — see Pattern 3: Proactive Subscription Renewal.
  • Verify that your renewal job is using a fresh, valid Bearer token. A renewal call with an expired token will fail with 401.

I am receiving duplicate events

  • This is expected — the API guarantees at-least-once delivery. Implement idempotency in your handler. See Handling Duplicate Events.

I am getting 401 Unauthorized on API calls

  • Your Bearer token may have expired. Tokens have a short lifetime (typically 900 seconds). Re-authenticate using the client credentials flow to get a fresh token before retrying.
  • Double-check the header format: Authorization: Bearer {token} — there must be a space after Bearer, and the token must not be wrapped in quotes.

I am getting 403 Forbidden on API calls

  • Confirm your credentials were provisioned with the EVENT_NOTIFICATION scope. Contact Avaya Support if unsure.
  • Ensure the customerId subdomain in your base URL matches the account your token was issued for.

My Create Subscription call returns 400

  • Check the violations array in the response body — it identifies the exact field and constraint that failed.
  • Common causes: family includes ALL alongside other values; events array is empty; transport.endpoint is not a valid URI; authToken or authTokenHeader is provided without the other.

Security Guidelines

  • Never expose your client_secret or Bearer token in client-side applications, mobile apps, or public repositories.
  • Always configure authToken and authTokenHeader and validate the header on every incoming webhook request to prevent spoofed deliveries.
  • Use HTTPS endpoints only for your webhook URL. Plain HTTP must not be used in production.
  • Rotate your authToken regularly using the Renew endpoint — for example monthly. See Pattern 4: Webhook Auth Token Rotation.
  • Respond quickly. Acknowledge the webhook with 200 immediately and process asynchronously to prevent timeouts that would trigger unnecessary retries.
  • Validate and sanitise all incoming event data before passing it to downstream systems — treat webhook payloads as untrusted input.
  • Do not log raw event payloads in production environments if they may contain PII or sensitive customer data.

Where Next?

Now that your subscription is set up and events are flowing to your webhook, here are some logical next steps:

  • Trigger workflows in response to events — Use the Workflow Sessions API to start an Avaya Infinity workflow when an Interaction.Created event arrives, passing interaction context as input variables.
  • Send messages back to customers — Combine Notification events with the Custom Messaging API to send follow-up messages when an Interaction.Completed event is received.
  • Try the interactive API explorer — The Notification API Reference lets you call each endpoint directly against your live tenant from the browser.
  • Review authentication — For a refresher on token management and scopes, see How to Authenticate with Avaya Infinity™ APIs.

Related Resources