Enneo

Events & Webhooks

Example: Processing Multiple AI Agents and/or Follow-Up Responses in Dark Mode

A procedure for the automatic execution of multiple AI agents and/or customer follow-up responses

Scenario

By default, Enneo only performs automatic execution (dark processing) of AI agents upon the first incoming email of a ticket and only when exactly one AI agent has been detected. There are two scenarios where standard dark processing does not apply:

  1. Multiple AI agents are detected: A customer raises several concerns in the same email, e.g., a termination and a revocation. Enneo does recognize both AI agents but does not automatically execute any of them due to safety reasons.
  2. Follow-up responses: A customer replies to an existing ticket. While the AI processing does run (tags, AI agent recognition, parameter extraction), the automatic execution is not triggered again.

In both cases, instead, the ticket is assigned to a human operator. This behavior can be adjusted with an event hook.

Info

This page applies to AI agents in the standard mode. With Smart Agent V2, one resolution plan covers all concerns of a ticket and is released as a whole by dark processing, follow-up responses included. The hook is not needed then: its whitelist would see the plan's AI agent next to the steps, and the call from the hook does not release a plan at level With approval.

Possible Solution

An event hook can react to incoming tickets or customer responses, check the detected AI agents, and trigger the automatic execution for all of them together. The customer receives one combined reply to all concerns.

Step 1: Set up event hook

Under Settings -> System Integration -> Events, create one or both of the following event hooks:

  • TicketCreated — is triggered after receiving a new ticket and completing the AI processing. Use this if you want multiple AI agents to be executed automatically in the initial email.
  • TicketResponse — is triggered when a response to an existing ticket is received from a customer, and the AI processing is completed. Use this if you also want AI agents to be automatically executed in follow-up responses.

The following code can be used for both events. It checks all recognized AI agents (intents) in ready status and runs them together in a single call:

POST /api/mind/ticket/:ticketId/autoexecute?allowMultipleIntents=true&allowWithReplies=true

Info

The parameter allowMultipleIntents=true allows the automatic execution even if multiple AI agents have been detected on the ticket. Without this parameter, execution is declined for safety reasons when multiple agents are detected.

The parameter allowWithReplies=true allows the automatic execution even if the ticket contains incoming customer responses. Without this parameter, execution is declined once there is a customer response on the ticket. This parameter is only relevant for the TicketResponse event.

Warning

Do not call the endpoint once per AI agent with executeAgentId. The first call already sends only that AI agent's reply and closes the ticket; every further call is declined and the remaining concerns stay unanswered. In addition, executeAgentId runs the AI agent even if its case is set to Off on the dark processing page.

Info

In this example code, error handling is deliberately omitted for better readability.

<?php

use EnneoSDK\ApiEnneo;

require(getenv()['SDK'] ?? 'sdk.php');

/** @var stdClass $in - contains all defined input parameters from the event */

// Load ticket data
$ticket = ApiEnneo::getTicket($in->ticketId);

// Only process open tickets
if ($ticket->status !== 'open') {
    echo json_encode(['autoExecute' => false, 'reason' => 'Ticket is not open']);
    return;
}

// Load intents (detected AI agents)
$intents = ApiEnneo::get(endpoint: '/api/mind/intent/byTicketId/' . $in->ticketId);

// Collect all intents in status "ready"
$readyIntents = array_filter($intents->intents, fn($i) => $i->status === 'ready');

if (empty($readyIntents)) {
    echo json_encode(['autoExecute' => false, 'reason' => 'No intents in status ready found']);
    return;
}

// Optional: Further checks, e.g. customer recognition or tags
// if (!$ticket->contractId) {
//     echo json_encode(['autoExecute' => false, 'reason' => 'No contract detected']);
//     return;
// }

// Optional: Only execute if every detected AI agent is allowed.
// The call always runs all of them, so leave the whole ticket to the operator if one is not allowed.
// $allowedAgentIds = [4, 5, 12, 34];
// foreach ($readyIntents as $intent) {
//     if (!in_array($intent->aiAgentId, $allowedAgentIds)) {
//         echo json_encode(['autoExecute' => false, 'reason' => 'AI agent ' . $intent->aiAgentId . ' is not allowed']);
//         return;
//     }
// }

// Execute all detected AI agents together
$result = ApiEnneo::post(
    endpoint: '/api/mind/ticket/' . $in->ticketId . '/autoexecute?allowMultipleIntents=true&allowWithReplies=true',
    body: []
);

echo json_encode(['autoExecute' => $result->success, 'agentIds' => array_values(array_map(fn($i) => $i->aiAgentId, $readyIntents)), 'message' => $result->message]);
import importlib.util
import os
import json

file_path = os.getenv('SDK', 'sdk.py')
spec = importlib.util.spec_from_file_location('sdk', file_path)
sdk = importlib.util.module_from_spec(spec)
spec.loader.exec_module(sdk)

# Load input parameters from the event
in_data = sdk.load_input_data()

# Load ticket data
ticket = sdk.ApiEnneo.get_ticket(in_data['ticketId'])

# Only process open tickets
if ticket.get('status') != 'open':
    print(json.dumps({'autoExecute': False, 'reason': 'Ticket is not open'}))
    exit()

# Load intents (detected AI agents)
intents_response = sdk.ApiEnneo.get(endpoint=f'/api/mind/intent/byTicketId/{in_data["ticketId"]}')
intents = intents_response.get('intents', [])

# Collect all intents in status "ready"
ready_intents = [i for i in intents if i.get('status') == 'ready']

if not ready_intents:
    print(json.dumps({'autoExecute': False, 'reason': 'No intents in status ready found'}))
    exit()

# Optional: Further checks, e.g. customer recognition or tags
# if not ticket.get('contractId'):
#     print(json.dumps({'autoExecute': False, 'reason': 'No contract detected'}))
#     exit()

# Optional: Only execute if every detected AI agent is allowed.
# The call always runs all of them, so leave the whole ticket to the operator if one is not allowed.
# allowed_agent_ids = [4, 5, 12, 34]
# not_allowed = [i['aiAgentId'] for i in ready_intents if i['aiAgentId'] not in allowed_agent_ids]
# if not_allowed:
#     print(json.dumps({'autoExecute': False, 'reason': f'AI agents not allowed: {not_allowed}'}))
#     exit()

# Execute all detected AI agents together
try:
    result = sdk.ApiEnneo.post(
        endpoint=f'/api/mind/ticket/{in_data["ticketId"]}/autoexecute?allowMultipleIntents=true&allowWithReplies=true',
        body={}
    )
except Exception as e:
    # If enneo declines the execution, the SDK raises an exception
    print(json.dumps({'autoExecute': False, 'reason': str(e)}))
    exit()

print(json.dumps({'autoExecute': result.get('success') is True, 'agentIds': [i['aiAgentId'] for i in ready_intents], 'message': result.get('message')}))

Step 2: Automatic Processing by enneo

After the call to the Autoexecute endpoint, enneo first checks that for every detected AI agent the recommended case is set to With approval or Automatic on the dark processing page. If that is not the case for one AI agent, nothing is executed and the ticket stays with the operator. Otherwise, enneo automatically carries out the following steps:

  1. All AI agents are executed.
  2. Their responses are combined into one email and sent to the customer.
  3. The ticket is closed.
  4. The processing counts as fully automated (L5) in reporting.

If one of the AI agents fails, no email is sent and the ticket goes to the operator with a note. Actions of AI agents that already ran before stay executed.

Notes

  • The TicketCreated event is suitable for the initial email with multiple concerns. The TicketResponse event is ideal for follow-up responses. Both events can be configured with the same code simultaneously.
  • The allowWithReplies=true parameter is necessary because enneo standardly blocks the automatic execution as soon as an incoming customer response exists in the ticket. For the TicketCreated event, this parameter has no effect because there are no responses yet.
  • The allowMultipleIntents=true parameter is necessary because without it, execution is declined for safety reasons when multiple agents are detected.
  • The call from the hook waits neither for an approval nor for a configured delay: cases at level With approval are executed immediately as well. If they should still be approved, leave those AI agents out of the whitelist (allowedAgentIds).
  • The AI agents' responses are inserted one after another into the same email template. If the response texts contain a greeting or sign-off themselves, it appears more than once.