EN
Open CopyPost

Webhooks: Send Telegram Messages to Your CRM and Your Own Server

2 min readWebhooksIntegrations

How to receive every post from Telegram channels on your own HTTP service via webhooks: the JSON format, media as links, and signature verification in Python.

Contents
  1. Setup
  2. What you receive
  3. Verifying the signature
  4. Reliability
  5. Limits

Sometimes you need messages not in another channel but in your own system: a CRM, a knowledge base, analytics, your own bot. For this, pipelines have webhooks — HTTP requests to your URL for every delivered message.

Setup #

  1. In the Webhooks section, add your service’s URL, for example https://crm.example.com/telegram.
  2. In the pipeline editor, select this webhook.
  3. Copy the webhook’s secret — you’ll need it to verify the signature.

A pipeline can send messages only to a webhook, without any recipient channels.

What you receive #

For each message (an album comes as one request), you get a POST with JSON:

{
  "pipeline_id": 12,
  "account_id": 3,
  "chat_id": -1001234567890,
  "messages": [{
    "id": 4521,
    "date": 1791936000,
    "text": "Today's news",
    "entities": [{"type": "bold", "offset": 0, "length": 7}],
    "media": {"type": "photo", "url": "https://cpo.st/m/…", "mime_type": "image/jpeg"}
  }]
}

The text arrives after the pipeline’s filters and rewrites have been applied. Media is passed as links: the file is downloaded on first access and is available for 7 days.

Verifying the signature #

Every request is signed: the X-Copyposter-Signature header contains an HMAC-SHA256 of the string <X-Copyposter-Timestamp>.<request body> using the webhook secret. Verification in Python:

import hmac, hashlib, time

def verify(secret: str, timestamp: str, body: bytes, signature: str) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False  # old request — possibly a replay
    expected = "sha256=" + hmac.new(secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Reliability #

If your service is unavailable or responds with 429/5xx, CopyPost retries the request after 1 s, 5 s, 30 s, 2 minutes and 10 minutes. Respond with 2xx quickly and do heavy processing in the background.

The URL must be public: webhooks are not sent to local or internal addresses.

Limits #

1,000 requests per month for free, then packs of 10,000 requests. Your current usage is shown on the Plan page.