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
organizationfield 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.
| Family | Description |
|---|---|
AGENT | Agent state changes: login, logout, ready, not-ready |
INTERACTION | Interaction lifecycle events: created, completed, transferred |
QUEUE | Queue-level events |
ALL | All 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:
| Event | Description |
|---|---|
Agent.LoggedIn | Agent has logged into the system |
Agent.LoggedOut | Agent has logged out |
Agent.Ready | Agent has set their state to Ready |
Agent.NotReady | Agent has set their state to Not Ready |
Interaction family events:
| Event | Description |
|---|---|
Interaction.Created | A new interaction has been created |
Interaction.Completed | An interaction has ended |
Interaction.Transferred | An 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
expiresInacross your subscriptions and calls the Renew endpoint proactively — for example, whenexpiresIndrops 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
CreateSubscription — Request BodyUsed when creating a new subscription (POST /subscriptions).
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
family | array of strings | Yes | 1–3 items; or ["ALL"] alone | Event families to subscribe to. See Event Families. |
events | array of strings | Yes | 1–25 items; max 256 chars each | Specific event types within the family, or ["ALL"] for everything. |
transport | Transport object | Yes | See below | Webhook delivery configuration. |
Transport — Webhook Configuration
Transport — Webhook ConfigurationUsed in CreateSubscription and PatchSubscription.
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
type | string | Yes | Enum: WEBHOOK | Transport type. Currently only WEBHOOK is supported. |
method | string | Yes | Enum: POST, PUT; default POST | HTTP method used to deliver events to your endpoint. |
endpoint | string | Yes | URI format; max 2048 chars | Your webhook URL. Must be HTTPS in production. |
authToken | string | No | 1–256 chars | Token Avaya will send in the auth header for webhook verification. Must be used together with authTokenHeader. |
authTokenHeader | string | No | 1–256 chars; default auth-token | HTTP header name for the auth token. Must be used together with authToken. |
Subscription — Response Object
Subscription — Response ObjectReturned by Create, Get, Update, Renew, and List operations.
| Field | Type | Read-only | Description |
|---|---|---|---|
subscriptionId | string (UUID) | Yes | Unique 36-character identifier. Pattern: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
status | string | Yes | Current status: ACTIVE, INACTIVE, or PENDING |
createdAt | string (ISO 8601) | Yes | Timestamp when the subscription was created. Example: 2020-08-01T14:25:23.162Z |
expiresAt | string (ISO 8601) | Yes | Timestamp when the subscription expires. Example: 2020-08-08T14:25:23.177Z |
expiresIn | integer (int64) | Yes | Seconds remaining before the subscription expires. Example: 900 |
family | array of strings | No | Event families this subscription covers. |
events | array of strings | No | Event types this subscription filters on. |
transport | TransportResponse object | No | Webhook configuration (see below). |
Note: The
subscriptionIdis only available from API responses — it is not visible in the Avaya Infinity Admin portal. Save it from thePOST /subscriptionsresponse body when you create a subscription. If you lose track of a subscription's ID, retrieve it viaGET /subscriptionsand match on theendpointURL in thetransportobject.
TransportResponse — Webhook Configuration (in Responses)
TransportResponse — Webhook Configuration (in Responses)Returned as part of the Subscription object.
| Field | Type | Description |
|---|---|---|
type | string | Transport type. Currently WEBHOOK. |
method | string | HTTP method: POST or PUT. |
endpoint | string | Configured webhook URL. |
authToken | string | Configured auth token value (if set). |
authTokenHeader | string | Configured auth token header name (if set). Default: auth-token. |
PatchSubscription — Request Body
PatchSubscription — Request BodyUsed when partially updating a subscription (PATCH /subscriptions/{subscriptionId}). All fields are optional — only fields you include will be modified.
| Field | Type | Constraints | Description |
|---|---|---|---|
family | array of strings | 1–3 items; or ["ALL"] alone | Updated event families. |
events | array of strings | 1–25 items | Updated event types. |
transport | Transport object | See Transport above | Updated webhook configuration. |
RenewSubscription — Request Body
RenewSubscription — Request BodyUsed when renewing a subscription (POST /subscriptions/{subscriptionId}:renew). The entire body is optional — omit it to renew with no changes.
| Field | Type | Constraints | Description |
|---|---|---|---|
transport.authToken | string | 0–256 chars | New auth token value to rotate at renewal time. |
SubscriptionPage — Paginated List Response
SubscriptionPage — Paginated List ResponseReturned by GET /subscriptions.
| Field | Type | Description |
|---|---|---|
pagination.pageNumber | integer | Current page number (1-indexed). |
pagination.pageSize | integer | Records per page (max 25). |
pagination.total | integer | Total number of subscriptions across all pages. |
pagination.totalPages | integer | Total pages available. |
subscriptions | array of Subscription | The subscriptions on this page. |
links.prev | string | URL of the previous page; blank if on the first page. |
links.next | string | URL of the next page; blank if on the last page. |
Problem — Error Response
Problem — Error ResponseAll 4xx and 5xx responses use the RFC 7807 Problem Detail format with Content-Type: application/problem+json.
| Field | Type | Required | Description |
|---|---|---|---|
type | string (URI) | Yes | URI identifying the problem type. |
title | string | Yes | Short human-readable summary. |
status | integer | Yes | HTTP status code. |
detail | string | No | Human-readable explanation of this specific occurrence. |
instance | string (URI) | No | URI identifying this specific occurrence. |
violations | array | No | List of field-level violations (used with 400 responses). |
violations[].field | string | — | Field name that caused the violation. |
violations[].message | string | — | Human-readable explanation of the violation. |
violations[].code | integer | — | Machine-readable violation code. Use this in error handling logic — messages may change between releases, codes do not. |
Limits & Constraints
| Limit | Value |
|---|---|
| Max families per subscription | 3 (or ALL alone) |
| Max event types per subscription | 25 (or ALL) |
Max endpoint URL length | 2048 characters |
Max authToken length | 256 characters |
Max authTokenHeader length | 256 characters |
subscriptionId format | 36-character UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) |
List pageSize default | 10 |
List pageSize maximum | 25 |
List pageNumber maximum | 1000 |
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
| Method | Endpoint | Operation |
|---|---|---|
POST | /subscriptions | Create a new subscription |
GET | /subscriptions | List all subscriptions (paginated) |
GET | /subscriptions/{subscriptionId} | Get a subscription by ID |
PATCH | /subscriptions/{subscriptionId} | Update a subscription |
POST | /subscriptions/{subscriptionId}:renew | Renew 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:
| Parameter | Type | Default | Maximum | Description |
|---|---|---|---|---|
pageNumber | integer | 1 | 1000 | Page to retrieve (1-indexed) |
pageSize | integer | 10 | 25 | Number 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.
| Behaviour | Detail |
|---|---|
| Delivery guarantee | At-least-once — the same event may be delivered more than once |
| Event ordering | Events may arrive out of order — use event timestamps to sort if sequence matters |
| Retry on failure | Yes — Avaya Infinity retries delivery when your endpoint returns a non-2xx response or does not respond in time |
| Acknowledgement | Return 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:
| Header | Description |
|---|---|
Content-Type | application/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 '', 200Best practice: Respond with
200immediately 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:
- Extract a unique identifier from the event payload (a combination of event type, timestamp, and entity ID works well).
- Check whether you have already processed an event with that identifier — for example using Redis
SET NXwith a short TTL, or a deduplication column in your database. - If already processed, discard the event and still return
200. Do not return an error — that would trigger further retries. - 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:
| Field | Type | Description |
|---|---|---|
eventType | string | The event type, e.g. Agent.LoggedIn, Interaction.Created. |
timestamp | string (ISO 8601) | When the event occurred. Use this to sort out-of-order deliveries. |
accountId | string | Your account/tenant identifier. |
payload | object | Event-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 Status | Title | Common Causes |
|---|---|---|
400 Bad Request | Constraint Violation | Missing required fields; ALL combined with other families; invalid family value; empty events array; authToken and authTokenHeader not provided together |
401 Unauthorized | Unauthorized | Expired, malformed, or missing Bearer token |
403 Forbidden | Forbidden | Token lacks EVENT_NOTIFICATION scope; account mismatch |
404 Not Found | Resource Not Found | Invalid or already-deleted subscriptionId |
500 Internal Server Error | Server Error | Platform 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— notviolations[].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 3000ngrok 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 3000Using 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.
- Open webhook.site and copy your unique URL.
- Create a subscription using that URL as the
endpoint. - Trigger activity in Avaya Infinity (agent login, create an interaction, etc.).
- 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
statusisACTIVEviaGET /subscriptions/{subscriptionId}. IfINACTIVE, the subscription has expired — call the Renew endpoint to reactivate it. - Verify that your
endpointURL is publicly accessible over HTTPS. Avaya Infinity cannot reachlocalhost, private network addresses, or plain HTTP in production. - Check that your server is returning a
2xxHTTP 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 afterBearer, 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_NOTIFICATIONscope. Contact Avaya Support if unsure. - Ensure the
customerIdsubdomain in your base URL matches the account your token was issued for.
My Create Subscription call returns 400
- Check the
violationsarray in the response body — it identifies the exact field and constraint that failed. - Common causes:
familyincludesALLalongside other values;eventsarray is empty;transport.endpointis not a valid URI;authTokenorauthTokenHeaderis provided without the other.
Security Guidelines
- Never expose your
client_secretor Bearer token in client-side applications, mobile apps, or public repositories. - Always configure
authTokenandauthTokenHeaderand 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
authTokenregularly using the Renew endpoint — for example monthly. See Pattern 4: Webhook Auth Token Rotation. - Respond quickly. Acknowledge the webhook with
200immediately 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.Createdevent 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.Completedevent 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
Updated about 2 months ago
