×

Share to WeChat

Scan this QR code with WeChat

Inside WeChat, use the ··· menu at the top right: Send to Chat / Share to Moments

XTUIS.CN

Phone notifications in one line of code

A WeChat push API for scripts, servers and AI agents. Follow our WeChat Official Account to get a token, send one HTTP request, and the message is on your phone.

  • Free
  • Real-time
  • Cross-platform
  • Private
Xtuis: an HTTP request from a laptop arrives as a notification on a phone

Use cases

Quant trading

Server ops

Web scraping

Game & automation scripts

AI agents

For AI agents, the Agent Xtuis CLI lets Claude Code, Codex and others push with one plain-language request, without your token ever appearing in the chat.

How to use

  1. Follow: scan the QR code below in WeChat and follow our Official Account.
    QR code of the Xtuis WeChat Official Account: follow it to get your token
  2. Copy: your token arrives automatically once you follow; copy it and keep it safe.
  3. Push: send an HTTP request and the message arrives in WeChat.
    Copy this URL
    https://wx.xtuis.cn/ABCDEFG.send?text=Gold is up&desp=Gold rose 100 today

    Paste it into your browser's address bar, replace ABCDEFG with your token and press Enter. The notification arrives in WeChat.

    Or send it from the language you use:
    GET example

    Every example below makes the same GET request, with the parameters URL-encoded onto the URL. Replace YOUR_TOKEN with your own token. Non-ASCII text such as Chinese must be URL-encoded; MetaTrader (MQL5) WebRequest rejects a URL with raw Chinese characters with HTTP 400.

    curl -G 'https://wx.xtuis.cn/YOUR_TOKEN.send' \
      --data-urlencode 'text=Gold is up' \
      --data-urlencode 'desp=Gold rose 100 today'
    import requests
    
    params = {
        "text": "Gold is up",
        "desp": "Gold rose 100 today",
    }
    
    response = requests.get(
        "https://wx.xtuis.cn/YOUR_TOKEN.send",
        params=params,
        timeout=10,
    )
    
    print(response.text)
    const params = new URLSearchParams({
      text: "Gold is up",
      desp: "Gold rose 100 today",
    });
    
    fetch(`https://wx.xtuis.cn/YOUR_TOKEN.send?${params.toString()}`)
      .then((res) => res.text())
      .then((text) => console.log(text));
    const axios = require("axios");
    
    axios
      .get("https://wx.xtuis.cn/YOUR_TOKEN.send", {
        params: {
          text: "Gold is up",
          desp: "Gold rose 100 today",
        },
        timeout: 10000,
      })
      .then(({ data }) => console.log(data))
      .catch((err) => console.error(err.message));
    package main
    
    import (
        "fmt"
        "io"
        "net/http"
        "net/url"
    )
    
    func main() {
        params := url.Values{}
        params.Set("text", "Gold is up")
        params.Set("desp", "Gold rose 100 today")
    
        endpoint := "https://wx.xtuis.cn/YOUR_TOKEN.send?" + params.Encode()
        resp, err := http.Get(endpoint)
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()
    
        body, _ := io.ReadAll(resp.Body)
        fmt.Println(string(body))
    }
    <?php
    $query = http_build_query([
        'text' => 'Gold is up',
        'desp' => 'Gold rose 100 today',
    ]);
    
    $url = 'https://wx.xtuis.cn/YOUR_TOKEN.send?' . $query;
    $result = file_get_contents($url);
    
    echo $result;
    import java.net.URI;
    import java.net.URLEncoder;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.charset.StandardCharsets;
    
    HttpClient client = HttpClient.newHttpClient();
    String query = "text=" + URLEncoder.encode("Gold is up", StandardCharsets.UTF_8)
        + "&desp=" + URLEncoder.encode("Gold rose 100 today", StandardCharsets.UTF_8);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://wx.xtuis.cn/YOUR_TOKEN.send?" + query))
        .GET()
        .build();
    
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    
    using var client = new HttpClient();
    
    var query = $"text={Uri.EscapeDataString("Gold is up")}&desp={Uri.EscapeDataString("Gold rose 100 today")}";
    var url = $"https://wx.xtuis.cn/YOUR_TOKEN.send?{query}";
    
    var result = await client.GetStringAsync(url);
    Console.WriteLine(result);
    require "uri"
    require "net/http"
    
    uri = URI("https://wx.xtuis.cn/YOUR_TOKEN.send")
    uri.query = URI.encode_www_form(
      text: "Gold is up",
      desp: "Gold rose 100 today"
    )
    
    response = Net::HTTP.get_response(uri)
    puts response.body
    use reqwest::blocking::Client;
    use reqwest::Url;
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let mut url = Url::parse("https://wx.xtuis.cn/YOUR_TOKEN.send")?;
        url.query_pairs_mut()
            .append_pair("text", "Gold is up")
            .append_pair("desp", "Gold rose 100 today");
    
        let body = Client::new().get(url).send()?.text()?;
        println!("{body}");
        Ok(())
    }
    import java.net.URI
    import java.net.URLEncoder
    import java.net.http.HttpClient
    import java.net.http.HttpRequest
    import java.net.http.HttpResponse
    import java.nio.charset.StandardCharsets
    
    fun main() {
        val query = "text=" + URLEncoder.encode("Gold is up", StandardCharsets.UTF_8) +
            "&desp=" + URLEncoder.encode("Gold rose 100 today", StandardCharsets.UTF_8)
    
        val request = HttpRequest.newBuilder()
            .uri(URI.create("https://wx.xtuis.cn/YOUR_TOKEN.send?$query"))
            .GET()
            .build()
    
        val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
        println(response.body())
    }
    $text = [uri]::EscapeDataString("Gold is up")
    $desp = [uri]::EscapeDataString("Gold rose 100 today")
    
    $url = "https://wx.xtuis.cn/YOUR_TOKEN.send?text=$text&desp=$desp"
    $result = Invoke-RestMethod -Method Get -Uri $url
    
    $result
    // MetaTrader 5 / MQL5 Script
    // 1) Tools → Options → Expert Advisors
    // 2) Allow WebRequest for listed URL, add: https://wx.xtuis.cn
    #property script_show_inputs
    
    input string InpToken = "YOUR_TOKEN";
    input string InpText  = "Gold is up";
    input string InpDesp  = "Gold rose 100 today";
    
    string UrlEncodeUtf8(const string s)
    {
       uchar bytes[];
       int n = StringToCharArray(s, bytes, 0, WHOLE_ARRAY, CP_UTF8);
       string out = "";
       for(int i = 0; i < n - 1; i++)
       {
          int c = bytes[i];
          if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
             || c == '-' || c == '_' || c == '.' || c == '~')
             out += CharToString((uchar)c);
          else
             out += StringFormat("%%%02X", c);
       }
       return out;
    }
    
    void OnStart()
    {
       string url = StringFormat(
          "https://wx.xtuis.cn/%s.send?text=%s&desp=%s",
          InpToken,
          UrlEncodeUtf8(InpText),
          UrlEncodeUtf8(InpDesp)
       );
    
       char   post[];
       char   result[];
       string result_headers;
       ResetLastError();
    
       int res = WebRequest("GET", url, "", 5000, post, result, result_headers);
       if(res == -1)
       {
          Print("WebRequest failed, LastError=", GetLastError(),
                " (check that https://wx.xtuis.cn is allowed)");
          return;
       }
    
       Print("HTTP ", res, " body=", CharArrayToString(result));
    }
    POST works too, with up to 64 KB of text. POST examples by language:
    POST example

    The POST examples send application/x-www-form-urlencoded with the fields text and desp.

    curl 'https://wx.xtuis.cn/YOUR_TOKEN.send' \
      --data-urlencode 'text=Your title' \
      --data-urlencode 'desp=Up to 64 KB of text'
    import requests
    
    payload = {
        "text": "Your title",
        "desp": "Up to 64 KB of text",
    }
    
    response = requests.post(
        "https://wx.xtuis.cn/YOUR_TOKEN.send",
        data=payload,
        timeout=10,
    )
    
    print(response.text)
    const body = new URLSearchParams({
      text: "Your title",
      desp: "Up to 64 KB of text",
    });
    
    fetch("https://wx.xtuis.cn/YOUR_TOKEN.send", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
      },
      body,
    })
      .then((res) => res.text())
      .then((text) => console.log(text));
    const axios = require("axios");
    
    const form = new URLSearchParams({
      text: "Your title",
      desp: "Up to 64 KB of text",
    });
    
    axios
      .post("https://wx.xtuis.cn/YOUR_TOKEN.send", form, {
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
        timeout: 10000,
      })
      .then(({ data }) => console.log(data))
      .catch((err) => console.error(err.message));
    package main
    
    import (
        "fmt"
        "io"
        "net/http"
        "net/url"
    )
    
    func main() {
        form := url.Values{}
        form.Set("text", "Your title")
        form.Set("desp", "Up to 64 KB of text")
    
        resp, err := http.PostForm("https://wx.xtuis.cn/YOUR_TOKEN.send", form)
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()
    
        body, _ := io.ReadAll(resp.Body)
        fmt.Println(string(body))
    }
    <?php
    $payload = http_build_query([
        'text' => 'Your title',
        'desp' => 'Up to 64 KB of text',
    ]);
    
    $opts = [
        'http' => [
            'method'  => 'POST',
            'header'  => "Content-Type: application/x-www-form-urlencoded\r\n",
            'content' => $payload,
        ],
    ];
    
    $context = stream_context_create($opts);
    $result = file_get_contents('https://wx.xtuis.cn/YOUR_TOKEN.send', false, $context);
    
    echo $result;
    import java.net.URI;
    import java.net.URLEncoder;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.charset.StandardCharsets;
    
    HttpClient client = HttpClient.newHttpClient();
    String form = "text=" + URLEncoder.encode("Your title", StandardCharsets.UTF_8)
        + "&desp=" + URLEncoder.encode("Up to 64 KB of text", StandardCharsets.UTF_8);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://wx.xtuis.cn/YOUR_TOKEN.send"))
        .header("Content-Type", "application/x-www-form-urlencoded")
        .POST(HttpRequest.BodyPublishers.ofString(form))
        .build();
    
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
    using System;
    using System.Collections.Generic;
    using System.Net.Http;
    using System.Threading.Tasks;
    
    using var client = new HttpClient();
    
    var form = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        ["text"] = "Your title",
        ["desp"] = "Up to 64 KB of text",
    });
    
    var response = await client.PostAsync("https://wx.xtuis.cn/YOUR_TOKEN.send", form);
    var result = await response.Content.ReadAsStringAsync();
    Console.WriteLine(result);
    require "uri"
    require "net/http"
    
    uri = URI("https://wx.xtuis.cn/YOUR_TOKEN.send")
    response = Net::HTTP.post_form(uri, {
      "text" => "Your title",
      "desp" => "Up to 64 KB of text"
    })
    
    puts response.body
    use reqwest::blocking::Client;
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let body = Client::new()
            .post("https://wx.xtuis.cn/YOUR_TOKEN.send")
            .form(&[
                ("text", "Your title"),
                ("desp", "Up to 64 KB of text"),
            ])
            .send()?
            .text()?;
    
        println!("{body}");
        Ok(())
    }
    import java.net.URI
    import java.net.URLEncoder
    import java.net.http.HttpClient
    import java.net.http.HttpRequest
    import java.net.http.HttpResponse
    import java.nio.charset.StandardCharsets
    
    fun main() {
        val form = "text=" + URLEncoder.encode("Your title", StandardCharsets.UTF_8) +
            "&desp=" + URLEncoder.encode("Up to 64 KB of text", StandardCharsets.UTF_8)
    
        val request = HttpRequest.newBuilder()
            .uri(URI.create("https://wx.xtuis.cn/YOUR_TOKEN.send"))
            .header("Content-Type", "application/x-www-form-urlencoded")
            .POST(HttpRequest.BodyPublishers.ofString(form))
            .build()
    
        val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
        println(response.body())
    }
    $body = @{
      text = "Your title"
      desp = "Up to 64 KB of text"
    }
    
    $result = Invoke-RestMethod -Method Post `
      -Uri "https://wx.xtuis.cn/YOUR_TOKEN.send" `
      -Body $body `
      -ContentType "application/x-www-form-urlencoded"
    
    $result
    // MetaTrader 5 / MQL5 — POST (non-ASCII text such as Chinese is safer in the body than in the URL)
    // Tools → Options → Expert Advisors → allow https://wx.xtuis.cn
    #property script_show_inputs
    
    input string InpToken = "YOUR_TOKEN";
    input string InpText  = "Your title";
    input string InpDesp  = "Up to 64 KB of text";
    
    string UrlEncodeUtf8(const string s)
    {
       uchar bytes[];
       int n = StringToCharArray(s, bytes, 0, WHOLE_ARRAY, CP_UTF8);
       string out = "";
       for(int i = 0; i < n - 1; i++)
       {
          int c = bytes[i];
          if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
             || c == '-' || c == '_' || c == '.' || c == '~')
             out += CharToString((uchar)c);
          else
             out += StringFormat("%%%02X", c);
       }
       return out;
    }
    
    void OnStart()
    {
       string url = "https://wx.xtuis.cn/" + InpToken + ".send";
       string body = "text=" + UrlEncodeUtf8(InpText) + "&desp=" + UrlEncodeUtf8(InpDesp);
    
       uchar post[];
       int n = StringToCharArray(body, post, 0, WHOLE_ARRAY, CP_UTF8);
       ArrayResize(post, n - 1);
    
       char   result[];
       string headers = "Content-Type: application/x-www-form-urlencoded\r\n";
       string result_headers;
       ResetLastError();
    
       int res = WebRequest("POST", url, headers, 5000, post, result, result_headers);
       if(res == -1)
       {
          Print("WebRequest failed, LastError=", GetLastError());
          return;
       }
       Print("HTTP ", res, " body=", CharArrayToString(result));
    }

Parameters

Steps

Just send an HTTP request.

HTTP request format

https://wx.xtuis.cn/YOUR_TOKEN.send

Parameters:

  • text: message title, required. The full title is shown on the message detail page / in the mini program.
  • desp: message body, optional. The full body is also on the detail page / in the mini program.

The WeChat notification card: every message gets a content-safety check before it is sent. If it passes, the card shows your real title after a "message n today" counter (the title itself is cut at about 17 characters). If it doesn't, both title and body are replaced with a safety notice that names the reason category.

Example:

https://wx.xtuis.cn/YOUR_TOKEN.send?text=Silver alert 2012&desp=Silver above 5000

If it passes, the card reads roughly [n] Silver alert 2012.

Steps

iPhone only. Steps:

  • Install the "Bark" app on your iPhone
  • Set Xtuis as Bark's private server
  • Send a Bark HTTP request

HTTP request format

Register: Bark server, private address

https://bark.xtuis.cn?xia=YOUR_TOKEN

Use: send the Bark HTTP request

https://bark.xtuis.cn/YOUR_TOKEN.send

Parameters:

  • text: message title, required.
  • desp: message body, optional.

Example:

https://bark.xtuis.cn/YOUR_TOKEN.send?text=Silver alert&desp=Silver 2012 above 5000
Limit Count When you go over
Messages per day 300 Further requests get HTTP 429. Wait as long as Retry-After / retry_after_seconds says (until the daily reset) and try again; your account is not locked for this. Only extreme bursts of abuse can lock it.
Messages per minute 30 At most 30 within 60 seconds, counted from the first request of the window. Beyond that you get HTTP 429; wait the number of seconds in the Retry-After header or the JSON field retry_after_seconds, then retry. Only sustained malicious flooding can lock an account.
HTTP requests per IP per day 5000 Requests from that IP may be refused for a while; switching to another outbound IP usually lets you keep sending (this is not an account lock).

Need a higher quota?

These limits apply to the free WeChat / Bark service. For higher limits, upgrade to Email alerts or WeCom alerts (open xtuis.cn/payment/ in WeChat).

Delivery status

Every message you send gets a unique msg_id back from Xtuis. Use it to look up the final delivery result: delivered to the phone, or failed, and why. Typical uses: retrying failures in your program, closing the loop on ops alerts, or confirming that a flash-sale alert really arrived.

A msg_id looks like this:

wx_20260623164956_8db1c691e77fe369

Lookup endpoint (GET):

https://msgstatus.xtuis.cn/status/YOUR_MSG_ID

Replace YOUR_MSG_ID with the ID you got back. For example:

https://msgstatus.xtuis.cn/status/wx_20260623164956_8db1c691e77fe369

The response gives the message's current state (success / failed / processing). Failures come with a reason, such as a wrong token, not following the Official Account, or a lock for exceeding limits, so your code can react precisely.

Rate limit: up to 5 lookups per minute from one IP; more are refused (HTTP 429). If you poll inside a retry/backoff loop, leave a gap between calls.

For more technical detail (how msg_id is generated, status code definitions, how the lookup works), see the project's design document on msg_id status tracking.

Agent Xtuis — built for AI agents

Agent Xtuis CLI is a command-line tool built for AI agents: the agent pushes a message to your phone with a single shell command (xtuis send). The CLI is not tied to any one agent; Claude Code, Codex, Cline and others can all call it. The agent never has to build HTTP requests, and your token never appears in the conversation: everything goes through a local config file.

Triggering a push with a plain-language request relies on Skill support, which currently covers Claude Code only (put a SKILL.md in ~/.claude/skills/ and Claude picks it up). Codex, Cline and other agents call xtuis send explicitly for now; Skill support for them is on the way.

One sentence, and the agent installs it for you:

Read https://xtuis.cn/doc/cli-setup.md and follow its steps to install and configure the Agent Xtuis CLI for me.

Send that sentence to your AI agent. It reads the official setup guide and works through CLI install → Skill registration → token setup → test push on its own. Your token never appears in the conversation (the agent has you type it into the terminal yourself); at the end, you just check your phone for the test notification.

Once it is installed, just tell your agent what you want:

  • "Send me this stack trace with Xtuis"
  • "Let me know when the deploy finishes"
  • "Push the current git status to my phone"
  • "Remind me about the 3 pm meeting" (the agent uses sleep or a scheduled job to delay the push)

Full setup guide and command list (in Chinese; agents read it fine): https://xtuis.cn/doc/cli-setup.md

Xtuis MT5 plugin — trading signals straight to your phone

An order fills, a stop-loss triggers, price breaks a key level, a trailing stop kicks in: you can't sit watching the chart for all of it. The Xtuis MT5 plugin lets MetaTrader 5 push those moments to WeChat on your phone with one line of code.

No server to run, no webhook to configure, any broker. Install the plugin, follow the Official Account to get a token, and add one line to your EA, indicator or script.

Three steps

  1. Install the plugin: download XTuis_MT5.7z, extract it with 7-Zip or WinRAR, run the .exe installer inside, enter the Xtuis address xtuis.cn when asked and click Install. Done.
  2. Get a token: scan the QR code in WeChat to follow the Xtuis Official Account, then copy and keep the token you receive.
  3. Run the test: download xtuis_test.mq5, drop it into MT5's MQL5/Scripts folder, refresh the Navigator panel and double-click it. The test message reaches your phone within seconds.

Once installed, call it from your EA or indicator like this:

#include <xtuis.mqh>

void OnTradeTransaction(...)
{
   XTuisSend(Token, "Order filled", "Gold 100g long @ 520.30", "wx");
}

The plugin supports the WeChat channel only for now (always pass "wx"). Declare the token once at the top of your script; there is no URL to build on each call.

Xtuis MT5 plugin install: extract, run the exe, enter the address, installed into MT5 (labels in Chinese)
Download the plugin (XTuis_MT5.7z) Download the test script (xtuis_test.mq5)

Windows x64 only (an MT5 limitation). If SmartScreen blocks the first install, click "More info → Run anyway".

Install at a glance: extract → run the exe → enter the Xtuis address → installed into MT5

FAQ

What is Xtuis?

Xtuis is a service that lets you push a notification to your phone with one line of code: a single HTTP request.

Why use it?

Free: free within the message limits (see Parameters).
Simple: no sign-up and no app to install, on any platform. Just follow our WeChat Official Account.
Real-time: pushed instantly, with millisecond-level latency.
Private:
  • Message content and detailed logs are never shared with advertisers, data brokers or other commercial third parties.
  • Message content is kept for about 3 days; then the server deletes it and the detail page no longer opens.

When does an account get locked?

Ordinary over-limit use (daily or per-minute quota) only returns HTTP 429 or pauses sending for the day; it does not lock your account. Only sustained malicious bursts can. A locked account unlocks automatically once you pay on the unlock page, opened in WeChat.

How do I unlock my account?

Open https://xtuis.cn/unlock/ in WeChat. It recognises your WeChat account and unlocks it as soon as payment completes. Still stuck? Email weixin@xtuis.cn.

Contact

Opened in WeChat. Fill your token into the examples below with one tap (masked by default).