Telegram Send

Send voice, photo, and document files via Telegram bot — with a Bot-API-first delivery policy, automatic chat ID resolution, and a local endpoint fallback for text notices and retries.

Default Active Communication

Active by default: telegram-send is injected into every session automatically. No manual loading required. When you say "send via Telegram" or "텔레그램으로 보내줌", the skill activates and the agent follows the delivery policy described on this page.

Quick Reference

Skill nametelegram-send
CategoryCommunication
Default activeYes — injected at session start
SKILL.md path~/.cli-jaw/skills/telegram-send/SKILL.md
System toolscurl, jq
Config source~/.cli-jaw/settings.json (telegram.token, telegram.allowedChatIds)
Primary APITelegram Bot API (https://api.telegram.org/bot<TOKEN>/...)
Fallback APILocal endpoint (http://localhost:3457/api/telegram/send)
Supported typesphoto, voice, document, text
Related skillsemail-draft-polish, whatsapp, xurl
Related guideTelegram Guide

Delivery Policy (Bot-First)

The skill follows a strict two-tier delivery strategy. The Telegram Bot API is always tried first. The local CLI-JAW endpoint serves as a convenience layer for text and as a fallback when the Bot API fails.

Message TypePrimary MethodFallback
photoTelegram Bot API (sendPhoto)Local endpoint (one retry)
voiceTelegram Bot API (sendVoice)Local endpoint (one retry)
documentTelegram Bot API (sendDocument)Local endpoint (one retry)
textLocal endpoint (convenience)Telegram Bot API (sendMessage)
Key rule: For photo, voice, and document types, the agent always attempts the direct Bot API first. The local endpoint is only used as a single-retry fallback if the Bot API call fails. For text status notices, the local endpoint is used first for convenience.

Required Inputs

InputSourceRequired For
Bot token~/.cli-jaw/settings.jsontelegram.tokenAll Bot API calls
Chat ID~/.cli-jaw/settings.jsontelegram.allowedChatIds[-1]All Bot API calls
File pathProvided by user or generated by previous taskphoto, voice, document
Caption / textProvided by user (optional for media)text; optional caption for media

Step-by-Step Workflow

Step 1: Read Token and Chat ID

The agent reads credentials from the CLI-JAW settings file. Tokens are kept in shell variables only — never printed to stdout or logs.

TOKEN=$(jq -r '.telegram.token' ~/.cli-jaw/settings.json)
CHAT_ID=$(jq -r '.telegram.allowedChatIds[-1]' ~/.cli-jaw/settings.json)

If CHAT_ID resolves to null (no previous Telegram message on record), the agent recovers by pinging the local endpoint:

CHAT_ID=$(curl -sS -X POST http://localhost:3457/api/telegram/send \
  -H "Content-Type: application/json" \
  -d '{"type":"text","text":"chat_id check"}' | jq -r '.chat_id')
Security: The bot token is never printed, echoed, or included in agent text responses. It exists only in shell variables for the duration of the command. This prevents credential leakage to logs and chat history.

Step 2: Send via Bot API (Primary)

The agent constructs a curl multipart form POST to the appropriate Telegram Bot API endpoint based on the message type.

Send a photo

curl -sS -X POST "https://api.telegram.org/bot${TOKEN}/sendPhoto" \
  -F "chat_id=${CHAT_ID}" \
  -F "photo=@/tmp/chart.png" \
  -F "caption=Analysis chart"

Send a voice message

curl -sS -X POST "https://api.telegram.org/bot${TOKEN}/sendVoice" \
  -F "chat_id=${CHAT_ID}" \
  -F "voice=@/tmp/reply.ogg" \
  -F "caption=Voice response"

Send a document

curl -sS -X POST "https://api.telegram.org/bot${TOKEN}/sendDocument" \
  -F "chat_id=${CHAT_ID}" \
  -F "document=@/tmp/report.pdf" \
  -F "caption=Weekly report"

Step 3: Local Endpoint (Secondary / Fallback)

The local CLI-JAW server exposes a Telegram send endpoint at:

POST http://localhost:3457/api/telegram/send

Content-Type: application/json

Accepts: type, text, file_path, caption

Text message (primary use of local endpoint)

curl -sS -X POST http://localhost:3457/api/telegram/send \
  -H "Content-Type: application/json" \
  -d '{"type":"text","text":"Deployment complete!"}'

Media fallback via local endpoint

curl -sS -X POST http://localhost:3457/api/telegram/send \
  -H "Content-Type: application/json" \
  -d '{"type":"photo","file_path":"/tmp/chart.png","caption":"Analysis chart"}'

Step 4: Verify Delivery

A successful send returns a JSON response with "ok": true:

{"ok":true,"chat_id":8231528245,"type":"text"}

The agent checks this response before reporting success to the user. If the response indicates failure, and the primary method was Bot API, one retry is attempted via the local endpoint.

Supported Message Types

TypeBot API MethodRequired FieldsOptional Fields
photosendPhotochat_id, photo (file)caption, parse_mode
voicesendVoicechat_id, voice (file, OGG format)caption, duration
documentsendDocumentchat_id, document (file)caption, parse_mode
textsendMessage / local endpointchat_id, textparse_mode, disable_notification

Configuration

The skill reads all configuration from ~/.cli-jaw/settings.json. No separate config file is needed.

// ~/.cli-jaw/settings.json (relevant keys)
{
  "telegram": {
    "token": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
    "allowedChatIds": [8231528245]
  }
}
KeyTypeDescription
telegram.tokenstringTelegram Bot API token from @BotFather
telegram.allowedChatIdsnumber[]Array of allowed chat IDs. The skill uses the last entry by default.
Getting your Chat ID: If you have not sent a message to the bot yet, allowedChatIds may be empty. The skill will auto-discover your chat ID by sending a probe message via the local endpoint and reading the chat_id field from the response.

Dependencies

This skill requires only standard Unix tools that are pre-installed on macOS and most Linux distributions.

curl Pre-installed on macOS and Linux
HTTP client used for all Bot API and local endpoint requests
jq brew install jq
JSON processor used to read settings and parse API responses

Verify dependencies

# Check curl
curl --version

# Check jq
jq --version

# Install jq if missing (macOS)
brew install jq

# Install jq if missing (Ubuntu/Debian)
sudo apt-get install -y jq

Safety Rules

The skill enforces strict security practices around token handling:

RuleDetail
No token printingThe bot token is never echoed, printed, or included in agent text output. It exists only in shell variables.
Shell-only storageTokens are read into $TOKEN and used inline. They are not written to temp files or exported to the environment.
No log leakageCommands use -sS flags to suppress progress bars while still showing errors, avoiding accidental token exposure in verbose logs.
Single retry limitIf Bot API fails, only one fallback attempt via local endpoint is permitted. No infinite retry loops.
Allowed chat IDsThe skill only sends to chat IDs listed in telegram.allowedChatIds, preventing accidental messages to unauthorized chats.

Quick Verification

To test that the Telegram integration is working, run this ping command:

curl -sS -X POST http://localhost:3457/api/telegram/send \
  -H "Content-Type: application/json" \
  -d '{"type":"text","text":"ping"}'

Expected response:

{"ok":true,"chat_id":8231528245,"type":"text"}

If you receive an error, check that:

  1. The CLI-JAW server is running (jaw server status)
  2. The bot token is configured in ~/.cli-jaw/settings.json
  3. You have sent at least one message to the bot on Telegram (to establish the chat)

"~해줌" Usage Examples

Real-world examples showing how to trigger the telegram-send skill in natural language. Korean and English both work.

"이 차트 텔레그램으로 보내줌"
Sends the most recently generated chart image (PNG) as a photo via the Bot API sendPhoto endpoint. The agent locates the chart file from the current task context and attaches it automatically.
"분석 결과 음성 메시지로 텔레그램에 보내줌"
Converts the analysis summary to a voice message (OGG format) and sends it via sendVoice. If TTS generation is available, the agent produces the audio first, then delivers it.
"주간 보고서 PDF 텔레그램으로 보내줌"
Sends the weekly report PDF as a document via sendDocument. The caption is set to a brief description of the file. The agent checks for the file at output/pdf/ or the path specified by the user.
"배포 완료되면 텔레그램으로 알려줌"
After a deployment task finishes, sends a text notification like "Deployment complete" to Telegram via the local endpoint. This is a status notice, so the local endpoint is used as primary.
"스크린샷 찍어서 텔레그램으로 바로 보내줌"
Captures a screenshot of the current screen or browser, saves it as a PNG, and immediately sends it as a photo via the Bot API. Combines the screen-capture skill output with telegram-send.

Integration with Other Skills

The telegram-send skill is often the final step in a multi-skill workflow. It pairs naturally with any skill that produces output files or status updates.

WorkflowSkills InvolvedExample
Report generation + deliverypdf + telegram-send"보고서 PDF 만들어서 텔레그램으로 보내줌"
Chart + notificationdev-data + telegram-send"매출 차트 만들어서 텔레그램에 공유해줌"
Screenshot capture + sendscreen-capture + telegram-send"화면 찍어서 텔레그램으로 바로 보내줌"
Voice replyvideo (TTS) + telegram-send"이 내용 음성으로 변환해서 텔레그램에 보내줌"
Multi-channel broadcasttelegram-send + whatsapp + xurl"이 공지사항 텔레그램, 왔츠앱, X 전부 보내줌"
Deploy notificationcloudflare + telegram-send"Workers 배포하고 결과 텔레그램으로 알려줌"

API Reference

Bot API Endpoints Used

EndpointMethodPurpose
/bot<TOKEN>/sendPhotoPOST (multipart/form-data)Send an image file
/bot<TOKEN>/sendVoicePOST (multipart/form-data)Send an OGG voice message
/bot<TOKEN>/sendDocumentPOST (multipart/form-data)Send any file as a document
/bot<TOKEN>/sendMessagePOST (application/json)Send a text message (fallback for text type)

Local Endpoint

EndpointMethodBody
http://localhost:3457/api/telegram/sendPOST{"type": "photo|voice|document|text", "text": "...", "file_path": "...", "caption": "..."}

Response Format

// Success
{"ok": true, "chat_id": 8231528245, "type": "text"}

// Failure (example)
{"ok": false, "error": "Bot token not configured"}

Troubleshooting

Chat ID is null

If jq -r '.telegram.allowedChatIds[-1]' returns null, the bot has not received any messages from you yet. Send any message to the bot on Telegram first, then the chat ID will be recorded in settings. Alternatively, the agent will auto-recover by probing the local endpoint.

Bot API returns 401 Unauthorized

The bot token is invalid or expired. Generate a new token from @BotFather and update ~/.cli-jaw/settings.json.

Bot API returns 400 Bad Request

Common causes:

Local endpoint connection refused

The CLI-JAW server is not running. Start it with:

jaw server start

# Or check status
jaw server status

jq not found

Install jq to parse the settings file:

# macOS
brew install jq

# Ubuntu/Debian
sudo apt-get install -y jq

# Verify
jq --version

Comparison with Telegram Guide

CLI-JAW documentation covers Telegram in two places. They serve different purposes:

This page (Skill Reference)Telegram Guide
FocusTechnical details of the telegram-send skill — API commands, delivery policy, safety rulesEnd-to-end setup guide for connecting Telegram to CLI-JAW
AudienceUsers who want to understand exactly what the agent does when sending to TelegramNew users setting up Telegram integration for the first time
CoversSKILL.md internals, Bot API calls, fallback logic, troubleshootingBot creation, token setup, initial message pairing, dashboard config