Virbe Documentation

Custom Endpoint

Integrate your own conversational engine with Virbe using the Custom Endpoint service.

The Custom Endpoint integration allows you to connect Virbe to any HTTP endpoint you control. This means you can use any AI/LLM backend – OpenAI, Anthropic, your own fine-tuned models, or entirely custom logic – as the conversational engine behind your Virbe assistant.

How It Works

  1. A user sends a message to your Virbe assistant
  2. Virbe Core forwards the message as a POST request to your configured endpoint
  3. Your endpoint processes the message and returns one or more response actions
  4. Virbe renders the response (text, UI components, animations, etc.)

Your endpoint can respond in one of two modes:

  • Stream – Server-Sent Events (SSE) for real-time, token-by-token or action-by-action delivery
  • Batch – A single JSON array containing all response actions at once

Setting Up in Virbe Dashboard

  1. Navigate to Configurations → Conversational Engines
  2. Select Custom Endpoint as the engine type
  3. Configure the following:
    • Endpoint URL – The base URL of your server (e.g. https://your-server.com/conversation/message)
    • Bearer Token (optional) – An authentication token sent in the Authorization header

Virbe automatically appends /stream or /batch to your base URL depending on the response mode configured for the profile.

Request Format

When a user sends a message, Virbe POSTs a JSON payload to your endpoint with the following structure:

{
  "id": "uuid",                    // Unique message ID
  "conversationId": "uuid",        // Conversation session ID
  "participantId": "uuid",         // ID of the message sender
  "participantType": "EndUser",    // Sender type
  "action": {
    "text": {
      "text": "User message here", // The user's message
      "language": "en"             // Message language
    }
  },
  "instant": "2025-01-01T00:00:00.000Z", // Timestamp
  "profile": {                     // Virbe profile metadata
    "id": "profile-id",
    "languages": ["en", "es"]
  },
  "conversation": {                // Conversation metadata
    "id": "uuid"
  },
  "language": "en"                 // Current conversation language
}

Response Formats

Stream Mode

Return responses with Content-Type: text/event-stream. Each action is sent as a separate SSE event:

Content-Type: text/event-stream

data: {"engineEvent":{"state":"ProcessingStarted","flowId":"flow-123","nodeId":"node-456"}}

data: {"text":{"text":"Hello! How can I help you?","language":"en"}}

data: {"engineEvent":{"state":"ProcessingCompleted","flowId":"flow-123","nodeId":"node-456","elapsedTime":1500}}

Each event must be prefixed with data: and followed by two newlines (\n\n).

Batch Mode

Return a JSON array of actions with Content-Type: application/json:

[
  {
    "engineEvent": {
      "state": "ProcessingStarted"
    }
  },
  {
    "text": {
      "text": "Hello! How can I help you?",
      "language": "en"
    }
  },
  {
    "engineEvent": {
      "state": "ProcessingCompleted",
      "elapsedTime": 100
    }
  }
]

Action Types

Each response action is a JSON object with one of the following keys. You can return multiple actions in a single response.

Text Action

The primary way to send a text response back to the user.

{
  "text": {
    "text": "The response text",
    "raw": "Optional raw/unformatted text",
    "html": "Optional <b>HTML</b> formatted text",
    "ssml": "<speak>Optional SSML for speech synthesis</speak>",
    "language": "en"
  }
}

Only text is required. Use ssml to control how text-to-speech pronounces the response.

Engine Event

Signals the processing state of your engine. Virbe uses these to show loading indicators and track performance.

{
  "engineEvent": {
    "state": "ProcessingStarted",
    "flowId": "flow-id",
    "nodeId": "node-id",
    "elapsedTime": 1000,
    "llmCallName": "optional-call-name",
    "metaData": {}
  }
}

Supported states:

  • ProcessingStarted – Engine began processing the message
  • NodeProcessing – A specific node is being processed
  • LlmCallStarted – An LLM API call has started
  • LlmCallEnded – An LLM API call has completed
  • LlmCallError – An LLM API call has failed
  • ProcessingCompleted – Engine finished processing

Your endpoint should always send ProcessingStarted at the beginning and ProcessingCompleted at the end of each response.

Tool Call

Reports tool/function calls made during processing, useful for transparency and debugging.

{
  "toolCall": {
    "id": "tool-call-1",
    "name": "get_weather",
    "toolId": "tool-identifier",
    "args": { "location": "San Francisco" },
    "result": { "temperature": 72, "condition": "sunny" },
    "error": null,
    "type": "function"
  }
}

Custom Action

Send arbitrary named actions with flexible payloads for application-specific behavior.

{
  "customAction": {
    "name": "show_notification",
    "value": { "message": "Processing complete!" }
  }
}

Signal

Emit named events that can be consumed by conversation flows or client applications.

{
  "signal": {
    "name": "signal-name",
    "value": "optional-value"
  }
}

Variable Store

Store key-value pairs in the conversation context for use in subsequent messages or flows.

{
  "variableStore": {
    "key": "user_preference",
    "value": "dark_mode"
  }
}

UI Action

Render interactive UI components in the client. Supports buttons, cards, input fields, and web views.

Buttons:

{
  "uiAction": {
    "name": "virbe-payload-v3",
    "value": {
      "type": "buttons",
      "timeoutMs": 10000,
      "buttons": [
        { "id": "btn-yes", "label": "Yes" },
        { "id": "btn-no", "label": "No" }
      ]
    }
  }
}

Cards:

{
  "uiAction": {
    "name": "virbe-payload-v3",
    "value": {
      "type": "cards",
      "timeoutMs": 15000,
      "cards": [
        {
          "id": "card-1",
          "title": "Product Name",
          "imageUrl": "https://example.com/image.jpg",
          "payloadType": "text",
          "payload": "I want this product"
        }
      ]
    }
  }
}

Input field:

{
  "uiAction": {
    "name": "virbe-payload-v3",
    "value": {
      "type": "input",
      "input": {
        "storeKey": "user_email",
        "inputLabel": "Enter your email",
        "inputType": "email",
        "submitButton": { "id": "submit", "label": "Submit" },
        "cancelButton": { "id": "cancel", "label": "Cancel" }
      }
    }
  }
}

Web view:

{
  "uiAction": {
    "name": "virbe-payload-v3",
    "value": {
      "type": "webView",
      "webView": {
        "url": "https://example.com/embed",
        "fullscreen": false,
        "transparent": false,
        "persistOnConversationStop": false
      }
    }
  }
}

Behavior Action

Trigger avatar animations including gestures and facial expressions.

{
  "behaviorAction": {
    "name": "virbe-payload-v3",
    "value": {
      "gestures": [
        { "name": "wave", "start": 0, "duration": 2000 }
      ],
      "emotions": [
        { "name": "happy", "start": 0, "duration": 3000 }
      ]
    }
  }
}

Flow Event

Signal conversation flow state transitions.

{
  "flowEvent": {
    "type": "start",
    "exitStatus": "success",
    "exitDetails": "optional details",
    "fromFlowId": "flow-a",
    "toFlowId": "flow-b"
  }
}

Reference Implementation

A complete working example is available at github.com/VirbeHQ/virbe-api-integration. It includes:

  • A Fastify server with both stream and batch endpoints
  • Zod schemas (@virbe/dtos) for type-safe request/response validation
  • Full TypeScript types for all action DTOs

Quick Start

# Clone the repository
git clone https://github.com/VirbeHQ/virbe-api-integration.git
cd virbe-api-integration

# Install dependencies
pnpm install

# Start the development server (API on port 3000, docs on port 4000)
turbo dev

Testing

# Test streaming (SSE) mode
tsx test-conversation-endpoint.ts sse

# Test batch (JSON) mode
tsx test-conversation-endpoint.ts json

Integration with Virbe CustomEndpointService

Under the hood, Virbe's CustomEndpointService handles communication with your endpoint:

const engine = {
  credentials: {
    url: 'https://your-server.com/conversation/message',
    token: 'optional-bearer-token',
  },
  configuration: {
    // Custom configuration passed to your endpoint
  },
};

The service will:

  1. Send a POST request with conversation data to your URL
  2. Automatically detect the response type based on the Content-Type header
  3. Parse and process actions from either SSE events or JSON arrays
  4. Render the actions in the Virbe client

Make sure your endpoint returns the correct Content-Type header: text/event-stream for streaming or application/json for batch responses. Virbe uses this header to determine how to parse the response.

On this page