The following sections reference a few values from your VIA AI tenant account that are accessible from the VIA AI Console:
Project ID: The ID of the project is available from the Projects viewTenant API Key: The tenant-specific API key will be made available to you by your VIA AI contactWebhook Secret: The webhook secret is available from the project settings, webhooks configuration view
Use Bearer token authentication using your tenant API key:
{
"Content-Type": "application/json",
"Authorization": "Bearer <YOUR TENANT API KEY>"
}
To post a message to a VIA concierge conversation use:
POST /api/message
With the following body payload (example):
{
"project_id": 770,
"from_id": "user@wearevia.ai",
"message_text": "What events take place in San Diego in Oct 17-19, 2025?",
"new_conversation": true,
"params": {
"param1": "param1 value",
"param2": "param2 value"
}
}project_id: Your concierge project ID (viewable from the VIA AI Console)from_id: A unique user identifiermessage_text: Your message textnew_conversation: Use true to start a new conversation. If false, and a conversation already exists for the specified from_id the conversation will continue. Otherwise a new conversation will be created.language: optional language identifierparams: Optional parameters. Parameters are accessible via instruction variables
The response:
{
"success": true,
"conversation_id": "r7z2vGJGukw610UQwonSl50y"
}Use the conversation_id returned value to correlate the response over webhook with the appropriate message that was sent. Messages are always delivered in the order they were received.
To receive VIA platform responses you will need to install a webhook via the VIA Console, subscribe to the message_reply event (see below) and verify the webhook successfully against your client.
VIA will call your webhook with the following payload:
{
"timstamp": "<TIMESTAMP IN ISO FORMAT>",
"project_id": "<PROJECT ID>",
"event_id": "<EVENT_ID>",
"event_payload": "<EVENT PAYLOAD OBJECT>"
}This event will be generated as a response to a /api/message call. The event payload object varies and depends on which schema is used by the AI to render the result. For example:
{
"via_api_reply": {
"response_type": "<RESPONSE TYPE (presentation schema name)>",
"response": "<AI GENERATED PRESENTATION OBJECT>"
},
"followon": "How can I help you?",
"options": [
{
"label": "Ask me another question",
"emoji": "β",
"llm_value": "<AI GENERATED VALUE TO BE SENT BACK TO LLM>"
},
{
"label": "Continue",
"emoji": "π",
"llm_value": "<AI GENERATED VALUE TO BE SENT BACK TO LLM>"
}
]
}Presentation Schemas can be used to control the returned reply to a message in order to utilize different rendering methods (referring specifically to the via_api_reply/response property in the returned message_reply payload). The via_api_reply/response type will contain schema ID used by the AI to format the output.
Presentation schemas can be defined from the VIA Console, under the Instructions tab. You need to provide a unique ID for your schema and for API calls, the platform needs to be API. You may use explicit JSON or use node.js Zod module to define it using code.
When sending a message simply request the result to be returned using the presentation schema ID. For example: "What events take place in San Diego in Oct 17-19, 2025? Return your answer using the my_event_response schema"
Note If a schema cannot be found, the returned payload will be formatted using the default schema which is essentially one block of markdown formatted text.
This event will be generated whenever a new itinerary is created, usually at the end of a successful conversation. The event payload will contain the itinerary object (refer to the examples in the repo)
This event will be generated whenever a user profile has been created or updated. The event payload will contain the aggregated user profile (AUP, refer to the examples in the repo).
This event will be generated whenever a new user conversation profile is created (UCP, refer to the examples in the repo). The conversation profile contains an AI-generated summary (typically using a reasoning model) of the conversation where the prompt is defined at the tenant level (i.e. is identical for all tenant projects) by a VIA admin. User profiles are created and updated based on one or more conversation profiles.
This event will be generated whenever the VIA platform detects a conversation with negative sentiment. The detection time depends on how often summarization is applied to the conversation (typically every 1h). The event payload will cointain the conversation summary object generated by an AI prompt. The summary object has an arbitrary format that depends on the type of summary being conducted.
From the VIA Console, define a new webhook to receive reply messages. You must enable and verify the webhook in order to activate it. VIA will attempt to call your webhook up to 5 tries using the following strategy:
(Next Attempt Timestamp) = (Initial Delay) * 2 ^ (Retry Attempt)
Initial delay is normally set to 1 second but may change slightly depending on system load
VIA implements a security mechanism for webhook authentication by employing a digital signature. This signature is crafted using a secret key provided during the webhook setup alongside the request body. The resulting data is encapsulated in the signature header property. The process to calculate the signature is as follows:
HMAC-SHA256 = {Base64(HmacSHA256(body_bytes, YOUR_WEBHOOK_SECRET))}
To verify the request came from VIA, compute an HMAC digest using your secret key and the body and compare it to the signature portion (after the space) contained in the header. If they match, you can be sure the webhook was sent from VIA. Otherwise, ensure your code returns an unspecific error immediately without invoking additional logic.
This signature header property contains the SHA algorithm used to generate the signature e.g. sha256, a '=' separator, and the signature itself.
signature: sha256 HMAC-SHA256
e.g. signature: sha256=37d2725109df92747ffcee59833a1d1262d74b9703fa1234c789407218b4a4ef
You must verify your webhook from the VIA Console so that it will be called
Use the VIA Console to obtain your unique webhook secret
var express = require('express')
const crypto = require('crypto');
var app = express()
// Set your runtime variables
let secret = '<YOUR WEBHOOK SECRET>';
let port = 8080;
// Signature verification
const verifyHmac = (
receivedHmacHeader,
receivedBody,
secret
) => {
const hmacParts = receivedHmacHeader.split('=')
const receivedHmac = hmacParts[1]
const hash = crypto
.createHmac(hmacParts[0], secret)
.update(receivedBody)
.digest('hex')
return receivedHmac == hash
}
app.use(express.raw({ type: "*/*" }))
// Main webhook entry point
app.post('/webhook', function (req, res) {
// Some initial basic verification
if (!req.headers.signature) {
console.log('Signature doesn\'t exist');
return res.sendStatus(500);
}
else {
console.log('Signature: ' + req.headers.signature);
}
// Verify source from signature
if (!verifyHmac(req.headers.signature, req.body, secret)) {
console.log('Signature verification failed');
return res.sendStatus(500);
}
console.log("Signature verified");
// Extract payload
let payload = JSON.parse(req.body);
console.log(JSON.stringify(payload, null, 4));
// Parse the actual payload here
// TODO
res.send('Ok')
})
console.log(`Listening for events on ` + port)
app.listen(port);from flask import Flask
from flask import request
import hashlib, hmac, json
app = Flask(__name__)
# Set your runtime variables
secret = '<YOUR WEBHOOK SECRET>'
port = 8080
# Signature verification
def verifyHmac(received_hmac_header, received_body, secret):
hmac_parts = received_hmac_header.split('=')
received_hmac = hmac_parts[1]
hash = hmac.new(bytes(secret, 'UTF-8'), received_body, hashlib.sha256).hexdigest()
return received_hmac == hash
# Main webhook entry point
@app.route('/webhook', methods=['POST'])
def webhook():
# Some initial basic verification
if 'signature' not in request.headers:
print("Signature doesn't exist")
return '', 500
else:
print('Signature:', request.headers['signature'])
# Verify source from signature
if not verifyHmac(request.headers['signature'], request.data, secret):
print('Signature verification failed')
return '', 500
print('Signature verified')
# Extract payload
payload = request.get_json()
print(json.dumps(payload, indent=4))
# Parse the actual payload here
# TODO
return 'Ok'
if __name__ == '__main__':
print('Listening for events on ' + str(port))
app.run(port=port)This repo contains the following VIA payload samples:
- Flights: When searching for flights only, the payload describes a list of categorized flight search results and their respective attributes
- Hotels: When searching for hotels only, the payload describes a list of categorized hotels search results and their respective attributes
- Packages: Dynamic packages are bundles between flight and hotel products that are offered together. Packages can be dynamic (e.g. any hotel offered with any flight) or static (flights offered with specific hotels) as indicated the relationship vacation_id
- Itinerary: At the end of a successful user conversation, the VIA concierge generates an itinerary object representing all the selection the user has made for the travel plan.
- User Conversation Profile (UCP): Represents all the information the VIA has learned about a user in a single conversation.
- Aggregated User Profile (AUP): The aggregated user profile represents all the information that VIA has learned about a particular user up to the current point of time. This is based on multiple conversations conducted with the user.
Note: Payload currently supports only flights and hotels