Login

Bot API instructions

InterChat’s Bot API allows you to build powerful, automated integrations for your users without needing to understand or manage the underlying cryptography. Because InterChat relies on a zero-knowledge End-to-End Encrypted (E2EE) architecture, the InterChat primary node acts as a secure cryptographic proxy for your bot.

When your server sends a standard plaintext JSON reply, the InterChat node automatically encrypts it using your bot's dedicated Master Key before routing it to the customer.

1. Registration & Configuration

Before writing code, you need to register your bot inside the InterChat client application:

  1. Open InterChat and navigate to Settings > My Bots.
  2. Click Create Bot and fill in your Bot's Name, Username, and Description.
  3. In the Webhook URL field, enter the HTTPS endpoint of your external server (e.g., https://scripts.yourdomain.com/webhook).
  4. Click Generate New Token. Copy this API Token; you will use it as a Bearer token to authenticate your outbound messages.
  5. Save your bot.

2. Receiving Messages (The Webhook)

Whenever a user sends a message to your bot, InterChat will immediately dispatch an HTTP POST request to your configured Webhook URL.

Incoming JSON Payload:

{
  "userId": "019f3c79-763a-7e60-a6ed-535febd4d0bd",
  "text": "Show me info for my order",
  "command": "order", 
  "timestamp": 1732049182394
}

Note: The command field is only populated if the user's message starts with a forward slash (e.g., /order). Otherwise, it is omitted.

Your server must process this payload and return an HTTP 200 OK status immediately to acknowledge receipt.

3. Sending Replies (The API)

To send a message back to the user, make an HTTP POST request to InterChat's Bot API endpoint.

Outgoing JSON Payload:

{
  "targetUserId": "019f3c79-763a-7e60-a6ed-535febd4d0bd",
  "text": "Your order is out for delivery!",
  "inlineKeyboard": "" 
}

Note: inlineKeyboard accepts a stringified JSON array if you wish to attach interactive buttons to your message.

4. Example Server:

The provided main.go file is a zero-dependency, standalone Go web server that implements a complete mock logistics bot. It manages conversation state in-memory and demonstrates how to parse webhooks and send authenticated replies.

To run the server:

# Set your environment variables
export INTERCHAT_API_URL="https://gobackend.interchat.co.za/bot/send-message"
export BOT_API_TOKEN="your_generated_token_here"
export PORT="8080"

# Start the bot
go run main.go

./main.go

package main

import (
    "bytes"
    "encoding/json"
    "io"
    "log"
    "net/http"
    "os"
    "strings"
    "sync"
    "time"
)

// InterChatWebhookPayload represents the incoming HTTP POST data from InterChat.
type InterChatWebhookPayload struct {
    UserID    string `json:"userId"`
    Text      string `json:"text"`
    Command   string `json:"command,omitempty"`
    Timestamp int64  `json:"timestamp"`
}

// DevMessageRequest represents the required JSON structure to send a message back.
type DevMessageRequest struct {
    TargetUserID   string `json:"targetUserId"`
    Text           string `json:"text"`
    InlineKeyboard string `json:"inlineKeyboard"`
}

var (
    apiToken     string
    interchatURL string

    // Thread-safe in-memory store to track user conversation flows
    stateMutex sync.RWMutex
    userStates = make(map[string]string)
)

func main() {
    // 1. Load Configuration
    apiToken = os.Getenv("BOT_API_TOKEN")
    if apiToken == "" {
        log.Fatal("CRITICAL: BOT_API_TOKEN environment variable is not set.")
    }

    interchatURL = os.Getenv("INTERCHAT_API_URL")
    if interchatURL == "" {
        // Fallback to standard production URL if not explicitly set
        interchatURL = "https://gobackend.interchat.co.za/bot/send-message"
    }

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    // 2. Setup Routes
    mux := http.NewServeMux()
    mux.HandleFunc("POST /webhook", handleWebhook)

    // Basic health check endpoint
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Bot Sandbox is Online"))
    })

    // 3. Start Server
    log.Printf("🚀 Starting InterChat Bot Sandbox on port %s", port)
    log.Printf("Targeting API: %s", interchatURL)

    server := &http.Server{
        Addr:         ":" + port,
        Handler:      mux,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
    }

    log.Fatal(server.ListenAndServe())
}

// handleWebhook intercepts the JSON payload from InterChat's DispatchIfBot routing.
func handleWebhook(w http.ResponseWriter, r *http.Request) {
    bodyBytes, err := io.ReadAll(r.Body)
    if err != nil {
        log.Printf("Error reading webhook body: %v", err)
        http.Error(w, "Failed to read body", http.StatusBadRequest)
        return
    }
    defer r.Body.Close()

    var payload InterChatWebhookPayload
    if err := json.Unmarshal(bodyBytes, &payload); err != nil {
        log.Printf("Error unmarshaling JSON payload: %v", err)
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    log.Printf("Received message from User [%s]. Command: [%s], Text: [%s]", payload.UserID, payload.Command, payload.Text)

    // Acknowledge receipt immediately so InterChat doesn't hold the connection open
    w.WriteHeader(http.StatusOK)

    // Process the business logic asynchronously
    go processConversationFlow(payload)
}

// processConversationFlow evaluates the user's input and determines the appropriate reply.
func processConversationFlow(payload InterChatWebhookPayload) {
    userText := strings.ToLower(strings.TrimSpace(payload.Text))
    var replyText string

    stateMutex.RLock()
    currentState := userStates[payload.UserID]
    stateMutex.RUnlock()

    // Business Logic: Order Tracking Flow
    if currentState == "waiting_for_order_id" {
        // Mock database lookup
        replyText = "📦 Order ID " + userText + " found.\nStatus: In transit and out for delivery today!"
        log.Printf("Flow complete for user %s, clearing state.", payload.UserID)

        stateMutex.Lock()
        delete(userStates, payload.UserID)
        stateMutex.Unlock()

    } else {
        // Evaluate starting commands
        if payload.Command == "order" {
            replyText = "Sure! Please enter your 5-digit Order ID:"
            log.Printf("Starting order flow for user %s.", payload.UserID)

            stateMutex.Lock()
            userStates[payload.UserID] = "waiting_for_order_id"
            stateMutex.Unlock()

        } else if payload.Command == "start" {
            replyText = "Welcome to Logistics Corp! 🚚\n\nSend /order to check your tracking status."
        } else {
            replyText = "I didn't quite catch that. Send /order to track a shipment."
        }
    }

    // Dispatch the reply via InterChat API
    sendReply(payload.UserID, replyText)
}

// sendReply constructs the HTTP POST to InterChat using the bot's API Token.
func sendReply(targetUserID, text string) {
    reqBody := DevMessageRequest{
        TargetUserID:   targetUserID,
        Text:           text,
        InlineKeyboard: "", // Stringified JSON array if interactive buttons are needed
    }

    jsonPayload, err := json.Marshal(reqBody)
    if err != nil {
        log.Printf("Error marshaling outgoing message: %v", err)
        return
    }

    req, err := http.NewRequest("POST", interchatURL, bytes.NewBuffer(jsonPayload))
    if err != nil {
        log.Printf("Error creating outbound HTTP request: %v", err)
        return
    }

    // Securely append headers required by SendMessageAsBot
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", "Bearer "+apiToken)

    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        log.Printf("Network error sending reply to InterChat: %v", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        respBytes, _ := io.ReadAll(resp.Body)
        log.Printf("API Error: InterChat rejected message (Status %d): %s", resp.StatusCode, string(respBytes))
    } else {
        log.Printf("Successfully pushed reply to user %s", targetUserID)
    }
}