Skip to main content

DeBox Bot Go SDK Webhook Guide

Source repository:

This document covers DeBox bot Webhook integration for Go services. It is intended for backend developers who need a production-grade callback receiver, payload parser, and reply flow.

1. Webhook vs Long Polling (strictly mutually exclusive)

This guide implements Webhook mode. While a Webhook URL is configured in BotMother, Webhook is the only effective receiving path and Long Polling cannot receive messages. To return to Long Polling, clear the Webhook settings first.

For the complete mode-selection flow, see DeBox Bot Development Overview. For Long Polling implementation, see DeBox Bot Go SDK.

2. When to use Webhook

Use Webhook when:

  • Your service is publicly reachable over HTTP/HTTPS.
  • You need lower latency than polling.
  • You want a push-based event flow.
  • You need server-side handling for media messages or group join events.

3. BotMother Webhook configuration

In the BotMother Webhook settings, configure Webhook URL and Webhook Key.

Example Webhook URL:

  • https://your-domain.com/bot/webhook

Also ensure:

  • Webhook URL is stable, publicly reachable, and uses HTTPS.
  • Your backend validates that X-API-KEY on each callback matches the configured Webhook Key.
  • If you change Webhook Key, update the value used by your backend validator at the same time.

4. Delivery contract

4.1 HTTP request

DeBox sends:

  • Method: POST
  • Content-Type: application/json
  • Header: X-API-KEY: <webhook-key>

Recommendations:

  • Reject requests with missing or invalid X-API-KEY.
  • Return 200 OK only after your application has accepted the event.
  • Implement idempotency using your own event deduplication strategy if needed.

4.2 Top-level payload fields

Webhook callbacks use a flattened JSON payload. The most important fields are:

FieldTypeRequiredDescription
from_user_idstringYesThe event sender's DeBox user_id value. It is the same as the sender's uid and invite_code. In private chat, this is the other user. In group chat, this is the speaking user. For current join-group events, this is the joined member.
to_user_idstringYesThe target bot account's DeBox user_id value; the same value as that account's uid and invite_code.
namestringNoDisplay name of from_user_id.
picstringNoAvatar URL of from_user_id.
addressstringNoWallet address of from_user_id.
languagestringNoUser language, if available.
group_idstringNoDeBox group ID or chatroom ID. Empty for private chat.
parse_modestringYesType of the current payload, such as text, image, video, file, link, event:joinGroup.
messagestringYesNormalized message body. Meaning depends on parse_mode.
message_rawstringYesOriginal content after DeBox parsing. Meaning depends on parse_mode.
mention_usersarray<object>NoMentioned users in group text messages. Present when the source message contains mentions.

mention_users item structure:

FieldTypeDescription
user_idstringThe mentioned user's DeBox user_id value; the same value as that user's uid and invite_code
namestringDisplay name
picstringAvatar URL
addressstringWallet address

4.3 parse_mode values

parse_modeMeaningmessage / message_raw
textPlain text or command-like textMessage text. Mentions are removed from message and preserved in message_raw.
imageImage messageImage URL
videoVideo messageVideo URL
fileFile messageFile URL
linkShared link / dapp shareShared link URL
event:joinGroupGroup join eventCurrent joined member's user_id value

Important behavior notes:

  • For image, video, and file, the bot receives a URL string, not the binary file body.
  • For text, message is normalized text, while message_raw preserves the original text.
  • For event:joinGroup, current payload semantics are event-oriented rather than conversational:
    • group_id is the target group.
    • from_user_id is the joined member exposed by the current callback implementation.
    • message contains the same identifier value as the joined member's user_id.

5. Payload examples by message type

5.1 Text message

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"pic": "https://cdn.example.com/alice.png",
"address": "0x1234",
"language": "en",
"group_id": "cc0onr82",
"parse_mode": "text",
"message": "hello bot",
"message_raw": "@MyBot hello bot",
"mention_users": [
{
"user_id": "u_bot",
"name": "MyBot",
"pic": "https://cdn.example.com/bot.png",
"address": ""
}
]
}

5.2 Image message

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"language": "en",
"group_id": "cc0onr82",
"parse_mode": "image",
"message": "https://cdn.example.com/image.png",
"message_raw": "https://cdn.example.com/image.png"
}

5.3 Video message

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"language": "en",
"group_id": "cc0onr82",
"parse_mode": "video",
"message": "https://cdn.example.com/video.mp4",
"message_raw": "https://cdn.example.com/video.mp4"
}

5.4 File message

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"language": "en",
"group_id": "cc0onr82",
"parse_mode": "file",
"message": "https://cdn.example.com/report.pdf",
"message_raw": "https://cdn.example.com/report.pdf"
}

5.5 Group join event

{
"from_user_id": "u_new_member",
"to_user_id": "u_bot",
"name": "New Member",
"pic": "https://cdn.example.com/new-member.png",
"address": "0xabcd",
"language": "en",
"group_id": "cc0onr82",
"parse_mode": "event:joinGroup",
"message": "u_new_member",
"message_raw": "u_new_member"
}

Use this event when your bot needs to:

  • Send onboarding messages to new members.
  • Trigger group welcome workflows.
  • Record membership events in your own backend.

6. Go receiver design

Recommended server flow:

  1. Verify X-API-KEY.
  2. Parse JSON into a typed struct.
  3. Route logic by parse_mode.
  4. Infer chat context from group_id.
  5. Use Go SDK to send reply messages.

6.1 Suggested payload structs

type WebhookUser struct {
UserID string `json:"user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
}

type WebhookPayload struct {
FromUserID string `json:"from_user_id"`
ToUserID string `json:"to_user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
Language string `json:"language"`
GroupID string `json:"group_id"`
ParseMode string `json:"parse_mode"`
Message string `json:"message"`
MessageRaw string `json:"message_raw"`
MentionUsers []WebhookUser `json:"mention_users"`
}

6.2 Full sample (Gin + Go SDK)

package main

import (
"fmt"
"net/http"
"os"

"github.com/gin-gonic/gin"
boxbotapi "github.com/debox-pro/debox-chat-go-sdk/boxbotapi"
)

type WebhookUser struct {
UserID string `json:"user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
}

type WebhookPayload struct {
FromUserID string `json:"from_user_id"`
ToUserID string `json:"to_user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
Language string `json:"language"`
GroupID string `json:"group_id"`
ParseMode string `json:"parse_mode"`
Message string `json:"message"`
MessageRaw string `json:"message_raw"`
MentionUsers []WebhookUser `json:"mention_users"`
}

func main() {
webhookKey := os.Getenv("DEBOX_WEBHOOK_KEY")
apiKey := os.Getenv("DEBOX_BOT_API_KEY")
apiSecret := os.Getenv("DEBOX_BOT_API_SECRET")

if webhookKey == "" || apiKey == "" || apiSecret == "" {
panic("missing required env: DEBOX_WEBHOOK_KEY / DEBOX_BOT_API_KEY / DEBOX_BOT_API_SECRET")
}

bot, err := boxbotapi.NewBotAPI(apiKey, apiSecret)
if err != nil {
panic(err)
}

r := gin.Default()
r.POST("/bot/webhook", func(c *gin.Context) {
if c.GetHeader("X-API-KEY") != webhookKey {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid webhook key"})
return
}

var payload WebhookPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

chatType := "private"
chatID := payload.FromUserID
if payload.GroupID != "" {
chatType = "group"
chatID = payload.GroupID
}

switch payload.ParseMode {
case "text":
reply := boxbotapi.NewMessage(chatID, chatType, "Received: "+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "image":
reply := boxbotapi.NewMessage(chatID, chatType, "Image received: "+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "video":
reply := boxbotapi.NewMessage(chatID, chatType, "Video received: "+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "file":
reply := boxbotapi.NewMessage(chatID, chatType, "File received: "+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "event:joinGroup":
welcome := fmt.Sprintf("Welcome %s to the group.", payload.FromUserID)
reply := boxbotapi.NewMessage(chatID, chatType, welcome)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

default:
reply := boxbotapi.NewMessage(chatID, chatType, "Unsupported payload type: "+payload.ParseMode)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)
}

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"ok": true})
})

_ = r.Run(":8080")
}

7. Sending media replies with Go SDK

The complete outbound message contract is in the OpenAPI message fields. The following example only shows how to send media with the Go SDK.

image := boxbotapi.NewMessage("cc0onr82", "group", "https://cdn.example.com/welcome.png")
image.ParseMode = boxbotapi.ModeImage
_, _ = bot.Send(image)

video := boxbotapi.NewMessage("cc0onr82", "group", "https://cdn.example.com/intro.mp4")
video.ParseMode = boxbotapi.ModeVideo
_, _ = bot.Send(video)

file := boxbotapi.NewMessage("cc0onr82", "group", "https://cdn.example.com/guide.pdf")
file.ParseMode = boxbotapi.ModeFile
_, _ = bot.Send(file)

8. Production recommendations

  • Put webhook authentication and JSON parsing in middleware or a shared adapter.
  • Log parse_mode, from_user_id, to_user_id, and group_id for traceability.
  • Do not trust media URLs indefinitely; download or process them according to your retention policy.
  • For group bots, branch business logic by group_id instead of assuming one bot serves one group only.
  • Keep reply generation asynchronous if your downstream processing is slow.

9. Common pitfalls

  • Webhook configured but no updates received:
    • Check endpoint reachability, TLS, server logs, and X-API-KEY verification.
  • Webhook and Long Polling both enabled:
    • Only one receive mode is valid. With Webhook enabled, polling is not a valid receive path.
  • Treating media payloads as binary uploads:
    • Webhook delivers media URLs, not file streams.
  • Ignoring parse_mode:
    • Always branch on parse_mode, not only on the existence of message.
  • Local development without a public domain:
    • Use a tunnel to expose your local endpoint temporarily.