CanalYar
EN
فارسیEnglish
Get a key

CanalYar API

With the same API the CanalYar WordPress plugin uses, you can publish, edit or delete posts in Telegram, Bale and Rubika channels from any site or software (Laravel, Django, Node, a mobile app …). Bots, proxies and messenger rate limits are our job.

Getting started and authentication

  1. Sign up in the CanalYar panel and get a plan.
  2. Copy the connection key from the panel. Treat it like a password: keep it on your server only, never in browser or app code.
  3. Add and verify your channel (in the panel, or with the channels API).

Base URL:

https://canalyarr.ir/bot-relay/v1

Send the key in the Authorization header:

Authorization: Bearer YOUR_KEY

The older method of putting the key in the URL (/v1/YOUR_KEY/publish) still works, but URLs end up in logs; the header is safer.

All requests are POST with a form body (multipart/form-data or application/x-www-form-urlencoded). Structured fields are JSON strings.

Responses and errors

Responses are always JSON with an ok key. Every failed response has these three keys:

{
  "ok": false,
  "error": "Error message (in Persian), ready to show to the user",
  "description": "The same message (for compatibility with the Bot API format)",
  "error_code": 402
}
StatusMeaning
400Invalid input: empty text, no channel selected, text too long, …
402Subscription expired (code: "expired") or no plan bought yet (code: "inactive").
403Invalid or disabled key.
404Unknown path or command.
429More than 120 requests per minute. The Retry-After header says how many seconds to wait.
502Sending failed for every channel; each channel’s reason is in results.
503The service is temporarily unavailable; try again shortly.

Account status

POST/api/account

Plan, expiry, monthly quota and the channel list. Channel IDs and the chat value you need for publishing come from here.

curl -X POST https://canalyarr.ir/bot-relay/v1/api/account \
  -H "Authorization: Bearer YOUR_KEY"
{
  "ok": true,
  "plan": { "name": "Pro", "max_channels": 5, "branding": false, … },
  "platforms": ["telegram", "bale", "rubika"],
  "expires": 1767225600,
  "active": true,
  "quota": 1000,
  "used": 42,
  "channels": [
    { "id": 7, "platform": "telegram", "chat": "@myshop", "title": "My shop", "status": "verified", "verified": true }
  ],
  …
}

A quota of zero means unlimited. expires is a Unix timestamp, and zero means it never expires.

Channels

You can only post in verified channels. Verifying proves the channel is yours: make the CanalYar bot (its name is in bots in the account response) an admin of the channel, add the channel so the bot posts a six-digit code in it, and send that code back.

Add

POST/api/channels/add

FieldNotes
platformtelegram, bale or rubika
chatChannel ID, e.g. @myshop
curl -X POST https://canalyarr.ir/bot-relay/v1/api/channels/add \
  -H "Authorization: Bearer YOUR_KEY" \
  -d platform=telegram -d chat=@myshop

The response returns the channel with its id.

Verify

POST/api/channels/verify

Fields: id (channel ID) and code (the six-digit code posted in the channel).

Test message and removal

POST/api/channels/test

POST/api/channels/remove

Both take an id field.

Publish a post

POST/publish

Publishes one post to one or more channels at once. Each successful channel uses one post from the monthly quota.

FieldNotes
textPost text, up to 4096 characters. *bold*, `code`, ~strikethrough~ and [text](https://link) (text link) are formatted on Telegram and turned into plain text on Bale and Rubika (links become “text: link”).
channelsJSON: [{"platform":"telegram","chat":"@myshop"}, …]
image0 … image9Optional; an image file (JPG, PNG, WEBP or GIF). Several images become an album on Telegram and Bale.
image_modecaption (default: text under the image), before (images, then text), after (text, then images) or none (no images). If the text is longer than the caption limit (1024 characters), it automatically becomes before.
fileOptional, instead of images: one file up to 30 MB. MP3/M4A go with a music player and MP4 with a video player; everything else as a document.
file_kindOptional, document: always send the file as a document (e.g. a video without a player).
rubika_extraOptional, 1: Rubika has no albums, so by default only the first image goes. With this field the rest follow, each in its own post.
curl -X POST https://canalyarr.ir/bot-relay/v1/publish \
  -H "Authorization: Bearer YOUR_KEY" \
  -F 'text=*Handmade leather bag*
Price: 1,200,000 Toman
https://myshop.ir/p/123' \
  -F 'channels=[{"platform":"telegram","chat":"@myshop"},{"platform":"bale","chat":"@myshop"}]' \
  -F image0=@bag-1.jpg \
  -F image1=@bag-2.jpg
{
  "ok": true,
  "results": [
    { "platform": "telegram", "chat": "@myshop", "ok": true, "ids": ["812", "813"], "text_id": "812", "kind": "caption", "error": "", "note": "" },
    { "platform": "bale", "chat": "@myshop", "ok": false, "ids": [], "error": "این کانال برای این سایت تأیید نشده است.", "code": "" }
  ],
  "usage": { "used": 43, "quota": 1000 }
}

If at least one channel succeeds, the response is 200; check each channel’s result in results. Keep ids, text_id and kind — you need them to edit or delete this post. A non-empty note says what was adjusted automatically (e.g. the text sent separately from the image).

Edit and delete posts

POST/edit

Uses no quota. The items field is a JSON array of up to 30 items per request:

[
  { "op": "edit",   "platform": "telegram", "chat": "@myshop", "id": "812", "kind": "caption", "text": "New text" },
  { "op": "delete", "platform": "bale",     "chat": "@myshop", "ids": ["55", "56"] }
]
curl -X POST https://canalyarr.ir/bot-relay/v1/edit \
  -H "Authorization: Bearer YOUR_KEY" \
  --data-urlencode 'items=[{"op":"edit","platform":"telegram","chat":"@myshop","id":"812","kind":"caption","text":"*Out of stock*"}]'

The response is like publishing: ok and one results entry per item.

Send reports

POST/api/reports

A 30-day summary (summary), daily counts (daily) and log rows, 50 per page. All fields are optional: page, days (1 to 90, default 14), platform, result (ok or failed), from and to (Unix time).

{ "ok": true, "summary": { "total": 120, "failed": 3, … }, "daily": [ … ], "total": 120, "page": 1,
  "rows": [ { "time": 1767225600, "platform": "managed", "method": "publish", "status": 200, "ok": true, "error": "" } ] }

Billing and invoices

POST/api/pay/start

Creates an order and returns the gateway URL. Fields: plan_id and months (from plans in the account response), return_url (after payment the user returns there with mkc_pay=ok|failed|canceled; it must be on your site’s address) and an optional coupon. The server calculates the amount. Response: url and amount.

POST/api/orders

All orders and payments for this site, newest first: id, number, plan, amount, discount, tax, paid and invoiced_at.

POST/api/invoice

POST/api/invoice/pay

POST/api/invoice/issue

Account, key and referrals

POST/api/profile

POST/api/profile/update

Account holder details: first_name, last_name, email, business, site_url and the same tax fields. update changes only the fields you send. The mobile number is the login ID and cannot be changed through the API.

POST/api/key/rotate

Creates a new key and returns it in key. The current key stops working immediately; store the response right away. This month’s usage carries over to the new key.

POST/api/sites

POST/api/sites/add

Each site has its own key, channels and subscription. sites returns all sites on the account with plan, expiry and current (the site of this key), but not the other sites’ keys. sites/add creates a new site with an optional site_url and returns its key in key this one time only. A new site is inactive until a plan is bought. Up to 10 sites.

POST/api/referral

Referral link (link), stats (stats: invited, purchased, bonus days), the bonus per purchase (days) and the newcomer discount percentage (discount).

Support (tickets)

CommandFields
/api/tickets— list of tickets, with categories (categories) and priorities (priorities)
/api/tickets/newsubject, category, body, priority (optional), file (optional; a JPG, PNG or WebP image)
/api/tickets/getid — the ticket with all its messages; new replies are marked as read
/api/tickets/replyid, body, file (optional). Replying to a closed ticket reopens it.
/api/tickets/closeid
/api/tickets/attachmentMessage id — returns the image itself, not JSON

This section and the account section only work for keys linked to a customer account in the panel.

Limits

Questions or problems? Ask through the tickets section of the panel.