EzEzLife Developers
English
Bot API
EZLIFE / BOT PLATFORM

Bot API documentation

From bot creation to your first integration

A complete guide to BotMother, messaging, verification-code copying and reply keyboards. Examples use placeholders.

Updated HTTPS · JSON · UTF-8

Overview and capabilities

#

The public methods are getMe, sendMessage and getUpdates. Send private/group text messages and receive events that may include non-text content.

Use copy_text to show a code-copy icon, and reply_markup to display text buttons below the private-chat composer.

This domain hosts documentation. The API remains at im.ez7.life under /bot-api/. Recipients can copy their ID in Settings → Account details → ID. Reply using an incoming message.chat.id; for group events it is the group ID.

API base URL
https://im.ez7.life/bot-api/<BOT_TOKEN>/<METHOD>

Quick start

#
  1. Find and add @BotMother, then open bot management at the top of the conversation.
  2. Create a bot, copy its Token and store it in the backend environment variable EZLIFE_BOT_TOKEN.
  3. Call getMe to verify identity, then call sendMessage with a recipient user ID.
  4. Start polling getUpdates; persist the next offset only after processing succeeds.
bash
python3 bot.py

BotMother management

#

Use a 1–64 character display name. In the creation form, enter a 3–28 character username prefix beginning with a letter, using letters, digits and underscores. The form adds _bot; check availability before creating.

Management lets you search your bots, view Bot ID, status, creation time and a **** plus six-character suffix, and reissue or revoke Tokens. The bot quota is configured by the administrator.

CommandPurpose
/startShow management instructions
/newbotStart bot creation
/mybotsList your bots
/token @usernameReissue a Token; invalidate the old one
/revoke @usernameRevoke the current Token
/cancelCancel the creation flow
/helpShow help

Tokens and security

#

The full Token appears only when created or reissued. It is the bot password; keep it in backend configuration, outside websites, app packages, public repositories and logs.

Reissuing immediately invalidates the previous Token; revoking blocks external calls. A masked suffix cannot recover a full Token. Reissue and update your program if it is lost.

Legacy suffixes are recorded on the next valid Bot API call. General account API Tokens from account details serve account endpoints; this Bot API uses the bot Token issued by BotMother.

getMe · Bot identity

#
POST/bot-api/<BOT_TOKEN>/getMe

No business parameters are required. Returns the Bot ID, display name, username and group-capability flags. Start here to verify the Token in your environment.

Request
curl --fail-with-body --request POST \
  "https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/getMe" \
  --header 'Content-Type: application/json' --data '{}'
Response
{
  "ok": true,
  "result": {
    "id": "1000000000",
    "is_bot": true,
    "display_name": "Example Bot",
    "username": "example_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": true
  }
}

sendMessage · Send text

#
POST/bot-api/<BOT_TOKEN>/sendMessage

Use POST application/json. Always send chat_id as a string: a recipient user ID for private chats, or a group ID with chat_type: "group".

Text must contain 1–5000 characters; surrounding whitespace is trimmed. Record the returned message_id for your business. Sending has no idempotency key; after a timeout, check the outcome before resending.

ParameterTypeRequiredDescription
chat_idstringYesRecipient user ID, or group ID for group chats. Always a string.
textstringYesMessage text, 1–5000 characters.
chat_typestringNoDefaults to private; use group for groups. supergroup is also accepted.
copy_textstringNoA complete 4–12 digit string occurring in text.
reply_markupobject / JSON stringNoOptional reply keyboard object, or a JSON-encoded string.
Request
curl --fail-with-body --request POST \
  "https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/sendMessage" \
  --header 'Content-Type: application/json' --data-binary @- <<'JSON'
{
  "chat_id": "USER_ID",
  "text": "Your order has been processed."
}
JSON
sendMessage · group
{
  "chat_id": "GROUP_ID",
  "chat_type": "group",
  "text": "Your order has been processed."
}
Response
{
  "ok": true,
  "result": {
    "message_id": "EXAMPLE_MESSAGE_ID",
    "date": 1794297600,
    "chat": {
      "id": "USER_ID",
      "type": "private"
    },
    "text": "Your order has been processed."
  }
}

Successful sends return ok: true and result, with copy_text for codes, and reply_markup plus reply_keyboard_revision for menus. Receiving returns a result array; no updates means result: [].

Copy verification codes

#

Keep the system name, purpose and expiry in the message, then add copy_text. It must be a complete 4–12 digit ASCII string appearing in text. Strings preserve leading zeros.

The icon copies only the code. Your application generates codes and enforces expiry and verification; the API field only controls display and copying. Older clients can still read the message.

sendMessage · copy_text
{
  "chat_id": "USER_ID",
  "text": "Example sign-in code: 003812. Valid for 5 minutes.",
  "copy_text": "003812"
}

Reply keyboards

#

This is a multirow text reply keyboard. Clicking sends the button label as a normal user message; handle it through message.text in getUpdates.

Use 1–8 rows, 1–4 buttons per row, at most 24 buttons. Labels contain 1–64 characters without blank values, line breaks or controls. A button is a string or an object containing only text.

Omitting reply_markup keeps the menu; a new keyboard replaces it. Use remove_keyboard: true alone to remove the menu and its show control.

Users can hide or show the menu. One-time keyboards hide only after a successful button send; a new menu version opens again. State is isolated per user and bot, checked about every five seconds in active bot chats.

Keyboards are private-chat only; group messages with reply_markup return 422. Message text is still required. Use JSON true/false, not strings. Unknown markup/button properties are rejected. JSON-string reply_markup is limited to 16 KiB.

ParameterTypeRequiredDescription
reply_markupobject / JSON stringNoOptional reply keyboard object, or a JSON-encoded string.
keyboardarray[]YesTwo-dimensional array: one inner array per row.
resize_keyboardbooleanNoDefault true for compact buttons; false uses taller buttons.
one_time_keyboardbooleanNoDefault false; true hides after a successful button send.
remove_keyboardbooleanNoTrue clears the menu; cannot combine with other keyboard options.
sendMessage · reply_markup
{
  "chat_id": "USER_ID",
  "text": "Choose a service:",
  "reply_markup": {
    "keyboard": [
      [
        {
          "text": "Language settings"
        },
        {
          "text": "Track parcel"
        }
      ],
      [
        {
          "text": "Notifications"
        },
        {
          "text": "Enter tracking number"
        }
      ]
    ],
    "resize_keyboard": true,
    "one_time_keyboard": false
  }
}
sendMessage · one_time_keyboard
{
  "chat_id": "USER_ID",
  "text": "Choose a service:",
  "reply_markup": {
    "keyboard": [
      [
        "Language settings",
        "Track parcel"
      ]
    ],
    "one_time_keyboard": true
  }
}
sendMessage · remove_keyboard
{
  "chat_id": "USER_ID",
  "text": "This operation has finished.",
  "reply_markup": {
    "remove_keyboard": true
  }
}

getUpdates · Receive messages

#
POST/bot-api/<BOT_TOKEN>/getUpdates

This is short polling: no messages returns an empty array immediately. Poll every 1–3 seconds and back off after failures. Public webhooks and long polling are not available.

After successful processing, set the next offset to the last update_id + 1. Earlier updates are acknowledged. On failure, keep the old offset and use update_id or a business key for deduplication.

Coordinate consumers of the same bot so separate processes do not advance each other’s offset. Inspect content_type and raw_content for non-text events.

ParameterTypeRequiredDescription
offsetintegerNoNonnegative next update ID; defaults to 0.
limitintegerNo1–100 updates per request; defaults to 100.
Request
curl --fail-with-body --request POST \
  "https://im.ez7.life/bot-api/${EZLIFE_BOT_TOKEN}/getUpdates" \
  --header 'Content-Type: application/json' \
  --data '{"offset": 0, "limit": 100}'
Response
{
  "ok": true,
  "result": [
    {
      "update_id": 120,
      "message": {
        "message_id": "EXAMPLE_MESSAGE_ID",
        "date": 1794297600,
        "chat": {
          "id": "1000000001",
          "type": "private"
        },
        "from": {
          "id": "1000000001",
          "display_name": "Example User"
        },
        "text": "Language settings",
        "content_type": 101,
        "raw_content": "{\n  \"content\": \"Language settings\"\n}"
      }
    }
  ]
}
ParameterDescription
update_idIncreasing update ID for consumption acknowledgements.
message_idPlatform message ID.
dateUnix timestamp in seconds.
chatSender user ID for private chats, or group ID, with private/group type.
fromThe actual sender’s ID and display name.
textText content; may be empty for non-text events.
content_typeContent type; 101 is text.
raw_contentOriginal content for received image, file and other events.

Complete Python example

#

The complete standard-library Python example handles /start, /ping and button labels, and persists its offset. Run it in a private working directory.

Replace the sample button handlers with business logic. Make orders, payments and similar actions idempotent before storing consumption progress.

bot.py
import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path

TOKEN = os.environ["EZLIFE_BOT_TOKEN"]
BASE = "https://im.ez7.life/bot-api/" + TOKEN
OFFSET_FILE = Path("bot-offset.json")
MENU = [
  [
    "Language settings",
    "Track parcel"
  ],
  [
    "Notifications",
    "Enter tracking number"
  ]
]

class ApiFailure(Exception):
    pass

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, *args):
        return None

HTTP = urllib.request.build_opener(NoRedirect())

def api(method, payload):
    req = urllib.request.Request(
        BASE + "/" + method,
        data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with HTTP.open(req, timeout=20) as response:
            body = json.load(response)
    except urllib.error.HTTPError as error:
        raise ApiFailure("HTTP " + str(error.code)) from None
    except (urllib.error.URLError, TimeoutError, OSError, ValueError):
        raise ApiFailure("Network or response failure") from None
    if not body.get("ok"):
        raise ApiFailure("API error " + str(body.get("error_code", "unknown")))
    return body["result"]

def handle(update):
    message = update.get("message", {})
    chat = message.get("chat", {})
    text = message.get("text", "")
    if not chat.get("id"):
        return
    payload = {"chat_id": str(chat["id"]),
               "chat_type": chat.get("type", "private")}
    if text == "/start" and payload["chat_type"] == "private":
        payload.update(text="Choose a service:", reply_markup={
            "keyboard": MENU, "resize_keyboard": True,
            "one_time_keyboard": False,
        })
    elif text == "/ping":
        payload["text"] = "pong"
    elif text in [label for row in MENU for label in row]:
        payload["text"] = "This operation has finished."
    else:
        return
    api("sendMessage", payload)

def save_offset(value):
    temp = OFFSET_FILE.with_suffix(".tmp")
    fd = os.open(temp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
    with os.fdopen(fd, "w", encoding="utf-8") as stream:
        json.dump(value, stream)
        stream.flush()
        os.fsync(stream.fileno())
    os.replace(temp, OFFSET_FILE)

def main():
    offset = json.loads(OFFSET_FILE.read_text()) if OFFSET_FILE.exists() else 0
    if isinstance(offset, bool) or not isinstance(offset, int) or offset < 0:
        raise ValueError("Invalid offset file")
    delay = 1
    while True:
        try:
            updates = api("getUpdates", {"offset": offset, "limit": 100})
            for update in updates:
                handle(update)
                next_offset = int(update["update_id"]) + 1
                save_offset(next_offset)
                offset = next_offset
            delay = 1
            time.sleep(2)
        except (ApiFailure, OSError, ValueError):
            print("Processing failed; offset retained. Check configuration.", flush=True)
            time.sleep(delay)
            delay = min(delay * 2, 30)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        pass

Groups and message scope

#

The bot must join the target group first. Owner approval is enabled by default. Review pending invitations in the bot conversation under More → Bot settings → Joined groups.

Group privacy mode is off by default: receiving-enabled groups do not need @mentions. A bot’s own messages are excluded from its own queue; group lifecycle notices are not ordinary messages.

The per-group receiving switch controls updates, forwarding and auto replies for that group. User-owned bots forward enabled group messages to their owner’s private chat with a group-name prefix. External access still controls API calls and update delivery.

Bot settings and auto replies

#

Only the owner manages join approval, external access, per-group receiving and keyword auto replies. Disabling external access retains the Token but rejects program calls.

Up to 50 rules: keywords 1–100 characters, replies 1–2000. Rules match case-insensitive containment in order; the first enabled match replies.

Auto replies work in private chats and receiving-enabled groups, with protection against bot reply chains. AI API integration is currently planned and has no public method.

Error handling

#

Early input errors, such as invalid JSON or an oversized body, use errorCode and message. Other API failures use error_code and description. Always check HTTP status and ok first.

Response
{
  "ok": false,
  "error_code": 422,
  "description": "chat_id 与 1–5000 字的 text 为必填"
}
StatusAction
400Invalid JSON. Check encoding and syntax.
401Token invalid, revoked, reissued, or bot suspended. Update credentials.
403The owner disabled external access. Enable it in bot settings.
404Check the /bot-api/ path, Token format and method name.
409A menu update for this bot/user is in progress. Retry shortly.
413The request body exceeds 1 MiB. Reduce its size.
422Correct parameters, code formatting or keyboard structure.
500Temporary failure. Keep receive offset and back off; check send outcomes before retrying.

Limits and client support

#

This guide covers the current public API. Media sending, inline callback buttons, contact/location request buttons and public webhooks have no public methods yet.

Reply keyboards require a current Web/H5 client. Clients bundling older H5 resources need an update to display new controls.

The complete request body is limited to 1 MiB. Adjust send and polling rates to actual load; this guide promises no fixed throughput. Group capability flags do not replace group membership, approval or receiving permissions.