Documentation

API Reference

Quick Start

Get started with push capabilities through real-world examples: text messages, group messages, rich media, action buttons, and group webhook configuration. Enter your device token to switch platforms/languages and send test messages.

2Send Test Message

01

Plain Text Message

Basic message type with title and content

GET
curl "https://api.linu.aprilzz.com/v1/push/ios/YOUR_DEVICE_TOKEN?title=Welcome to Linu&text=This is your first test message"
POST
curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["YOUR_DEVICE_TOKEN"],
    "message": {
      "title": "Welcome to Linu",
      "text": "This is your first test message"
    }
  }'
02

Group Text Message

Text message sent to a group, automatically grouped

GET
curl "https://api.linu.aprilzz.com/v1/push/ios/YOUR_DEVICE_TOKEN?title=Service Notification&text=Service updated&group_id=service-001"
POST
curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{"ios": ["YOUR_DEVICE_TOKEN"], "message": {"title": "Service Notification", "text": "Service updated", "group_id": "service-001"}}'
03

Rich Media Message

Message with image. Add group_id to send to a group

GET
curl "https://api.linu.aprilzz.com/v1/push/ios/YOUR_DEVICE_TOKEN?title=Server Status&text=Current server status screenshot&media_url=https://example.com/screenshot.png&media_type=image"
POST
curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["YOUR_DEVICE_TOKEN"],
    "message": {
      "title": "Server Status",
      "text": "Current server status screenshot",
      "media": {"type": "image", "url": "https://example.com/screenshot.png"}
    }
  }'
04

Action Message

Message with interactive buttons, suitable for manual review

GET
# GET requests do not support actions, please use POST
POST
curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["YOUR_DEVICE_TOKEN"],
    "message": {
      "title": "AI Drawing Task Completed",
      "text": "Task ID: #8823, please review the generated result.",
      "actions": [
        {"label": "Approve", "callback": {"url": "https://your-server.com/webhook", "method": "POST"}, "payload": "{\"action\": \"approve\", \"task_id\": \"8823\"}"},
        {"label": "Regenerate", "callback": {"url": "https://your-server.com/webhook", "method": "POST"}, "payload": "{\"action\": \"regenerate\", \"task_id\": \"8823\"}"}
      ]
    }
  }'
05

Group & Webhook Config

Configure group name, icon, actions, and reply webhook

GET
// GET requests do not support complex config, please use POST
POST
curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["YOUR_DEVICE_TOKEN"],
    "message": {
      "title": "Code Review Request",
      "text": "Automated deployment confirmation from GitHub Actions.",
      "group_id": "devops-bot",
      "group_config": {
        "name": "DevOps Assistant",
        "icon_url": "https://api.dicebear.com/9.x/bottts/png?seed=devops",
        "reply_callback": {"url": "https://your-server.com/webhook/reply", "method": "POST"},
        "actions": [
          {"label": "View Details", "callback": {"url": "https://your-server.com/webhook/details", "method": "POST"}},
          {"label": "Ignore", "callback": {"url": "https://your-server.com/webhook/ignore", "method": "POST"}}
        ]
      }
    },
    "options": {"priority": "ringing"}
  }'

Advanced Usage

Deep dive into advanced push capabilities: silent push, continuous ringing, critical alerts, end-to-end encryption, batch push & rate limiting, Webhook callbacks, and more to meet complex business needs.

01

Silent Push

Updates without disturbing the user. Used for background sync, e.g., updating group name, icon, buttons, or reply_callback. Client updates local session display silently. Set options.priority to silent.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "group_id": "devops-monitor",
      "group_config": {
        "name": "DevOps Monitor",
        "icon_url": "https://example.com/devops-icon.png",
        "reply_callback": {"url": "https://api.example.com/devops/ack", "method": "POST"}
      }
    },
    "options": { "priority": "silent" }
  }'
02

Continuous Ringing

High priority push for emergencies or critical faults requiring prolonged attention. Supports long built-in or custom audio. Set options.priority to ringing, use with options.ttl.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "title": "Critical Error",
      "text": "Production DB connection timeout, immediate action required!",
      "sound": "alarm_loop"
    },
    "options": { "priority": "ringing", "ttl": 60 }
  }'
03

Critical Alert

iOS permission pending. On Android, behaves like ringing. Suitable for safety, medical, or emergency alerts that must be noticed immediately.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "android": ["fcm-token"],
    "message": {
      "title": "Security Alert",
      "text": "Anomaly detected, please check immediately"
    },
    "options": { "priority": "critical", "ttl": 60 }
  }'
04

Deep Links & Navigation

Use message.detail.url to specify a link to open when the notification is clicked. The client will open the link in the system browser, enabling direct navigation from notification to specific pages.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "title": "Build Failed",
      "text": "Frontend project build-1024 failed",
      "detail": { "url": "https://ci.example.com/build/1024" }
    }
  }'
05

Custom Sound

Use message.sound to specify notification tone. Supports built-in or user-uploaded custom audio. Can be combined with continuous ringing for long alerts.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "title": "Reminder",
      "text": "Meeting starting soon",
      "sound": "meeting_reminder"
    }
  }'
06

Rich Media (Image & Video)

message.media supports type image or video. url is required; thumbnail_url is optional. Displayed in notification or detail view. URL must be publicly accessible.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "title": "New Update",
      "text": "You have a new video",
      "media": {
        "type": "video",
        "url": "https://example.com/video.mp4",
        "thumbnail_url": "https://example.com/thumb.jpg"
      }
    }
  }'
07

TTL & Expiration

options.ttl in seconds. Messages not delivered within TTL may be dropped by APNS/FCM. Suitable for time-sensitive alerts (e.g., calls, flash sales).

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": { "title": "Limited Time Offer", "text": "Ending soon" },
    "options": { "priority": "normal", "ttl": 300 }
  }'
08

Groups & Webhook: Making Linu Great

Linu's proudest design. Simple push only tells you 'what happened', but with Groups and Webhook, Linu lets you 'solve the problem'. Imagine: Server alert -> auto-grouped to 'Ops Monitor' -> click 'Restart Service' button -> Linu Webhook triggers your restart script. No laptop needed, no app switching. Information under control.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "group_id": "ci-pipeline-main",
      "title": "Deployment Successful",
      "text": "Production deployment completed in 3m 20s",
      "group_config": {
        "name": "CI/CD Pipeline",
        "icon_url": "https://example.com/ci-icon.png",
        "reply_callback": {"url": "https://api.example.com/ci/trigger", "method": "POST"},
        "actions": [
          {"label": "View Logs", "callback": {"url": "https://ci.example.com/build/12345/logs", "method": "GET"}},
          {"label": "Rollback", "callback": {"url": "https://api.example.com/ci/rollback", "method": "POST"}, "payload": "build-12345"}
        ]
      }
    }
  }'
09

Group Config Fields

group_config supports: name, icon_url, reply_callback, actions (menu buttons). Max 4 level-1 actions; unlimited level-2. No level-3. Note: level-1 callback ignored if it has children. 4KB total size limit.

{
  "group_config": {
    "name": "K8s Alerts",
    "icon_url": "https://example.com/k8s-icon.png",
    "reply_callback": {
      "url": "https://api.example.com/ops/ack",
      "method": "POST"
    },
    "actions": [
      {
        "label": "Pods Management",
        "children": [
          {
            "label": "Restart All Pods",
            "callback": "https://api.example.com/ops/restart",
            "method": "POST",
            "payload": "cluster-prod"
          },
          {
            "label": "View Node Status",
            "callback": "https://api.example.com/ops/nodes",
            "method": "GET"
          }
        ]
      },
      {
        "label": "Acknowledge",
        "callback": "https://api.example.com/ops/ack",
        "method": "POST",
        "payload": "alert-id-123"
      }
    ]
  }
}
10

Silent Group Update

To update group name, icon, reply URL, or buttons without a notification: send message with only group_id and group_config, set options.priority to silent.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "group_id": "k8s-cluster-prod",
      "group_config": {
        "name": "K8s Prod Cluster (Handling)",
        "icon_url": "https://example.com/k8s-handling.png",
        "reply_callback": {"url": "https://api.example.com/ops/reply", "method": "POST"},
        "actions": [{"label": "Check Progress", "callback": {"url": "https://ops.example.com/tickets/123", "method": "GET"}}]
      }
    },
    "options": { "priority": "silent" }
  }'
11

E2E Encryption: Principle

Linu uses AES-256-GCM. 1. App generates 32-byte key & exports it. 2. Sender encrypts JSON body (nonce + ciphertext + tag). 3. Send Base64 ciphertext as message field. 4. App decrypts. Gateway cannot decrypt, ensuring privacy.

+---------------+        +--------------+        +--------------+        +---------------+
|     Client    |        |  Linu Server |        |   APNs/FCM   |        |    Linu App   |
+---------------+        +--------------+        +--------------+        +---------------+
       |                        |                        |                        |
 1. Encrypt Payload             |                        |                        |
    (AES-256-GCM)               |                        |                        |
       |                        |                        |                        |
 2. Encrypted Push ------------>|                        |                        |
       |                        |                        |                        |
       |                        |  3. Encrypted Forward  |                        |
       |                        |----------------------->|                        |
       |                        |                        |                        |
       |                        |                        |  4. Encrypted Forward  |
       |                        |                        |----------------------->|
       |                        |                        |                        |
       |                        |                        |                        | 5. Decrypt & Show
       |                        |                        |                        |    (Using Local Key)
12

Encryption Script Example

Full demo: Encrypt message using Python or Node.js with AES-256-GCM, and send the final payload via API.

const crypto = require('crypto');

// 1. Prepare message content
const data = JSON.stringify({
  group_id: "security-audit",
  title: "Sensitive Operation Alert",
  text: "Prod DB export detected, user: admin",
  detail: {
    url: "https://vault.example.com/audit/logs/9981"
  },
  group_config: {
    name: "Security Audit",
    icon_url: "https://example.com/security-icon.png",
    reply_callback: {
      url: "https://api.example.com/security/ack",
      method: "POST"
    },
    actions: [
      { label: "Block Operation", callback: { url: "https://api.example.com/security/block", method: "POST" }, payload: "op-9981" },
      { label: "Mark as False Positive", callback: { url: "https://api.example.com/security/dismiss", method: "POST" }, payload: "op-9981" }
    ]
  }
});

// 2. Key (32 bytes)
// Replace with real key in production
// const key = Buffer.from("YOUR_BASE64_KEY", "base64");
const key = crypto.randomBytes(32); // Random key for demo

// 3. Nonce (12 bytes)
const nonce = crypto.randomBytes(12);

// 4. Encrypt (AES-256-GCM)
const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);

// Node.js createCipheriv separates ciphertext and tag
// cipher.final() returns remaining ciphertext
const ciphertext = Buffer.concat([
  cipher.update(data, 'utf8'),
  cipher.final()
]);
const tag = cipher.getAuthTag(); // 16 bytes

// 5. Concatenate: nonce + ciphertext + tag
const finalPayload = Buffer.concat([nonce, ciphertext, tag]);

// 6. Base64 Output
const messageStr = finalPayload.toString('base64');
console.log("Encrypted payload:", messageStr);

// 7. Send encrypted message
// Note: Encrypted message field is Base64 string
const payload = {
  ios: ["device-token"],
  // android: ["fcm-token"], // Optional
  message: messageStr, // Encrypted Base64 string
  options: { priority: "ringing", ttl: 300 }
};

fetch('https://api.linu.aprilzz.com/v1/push', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    // 'Authorization': 'Bearer YOUR_API_TOKEN' // If auth required
  },
  body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => console.log('Response:', data))
.catch(err => console.error('Error:', err));
13

Batch Push & Rate Limit

POST /push supports ios/android arrays. Max 500 devices/batch recommended. Server configures security.rate_limit_per_second (e.g., 50/s/IP) and body_limit_bytes (e.g., 10KB). Returns 429 or 400 on limit.

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["token1", "token2"],
    "android": ["fcm1", "fcm2"],
    "message": {
      "title": "Announcement",
      "text": "System maintenance tonight at 22:00"
    },
    "options": { "priority": "normal", "ttl": 86400 }
  }'
14

Webhook Callback & Payload

Linu App requests callback/reply_callback on user action.

Params:

- type: action (button), menu (group menu), reply (user reply).

- device_token: Device ID.

- group_id: Group ID.

- message_id: Message ID (action only).

- payload: Custom payload string.

- text: User input (reply only).

Note:

1. Supports GET and POST.

2. If 'Fake Device ID' enabled in App, device_token is randomized.

15

Errors & Retry

400: NO_TARGETS, BATCH_TOO_LARGE (>500), EMPTY_MESSAGE, MISSING_GROUP_ID, INVALID_GROUP_ID (>32 chars), INVALID_TOKEN. 401: Unauthorized. 429: Rate limited (exponential backoff). 500: Server error (retryable).

400 NO_TARGETS       - No devices provided (ios/android)
400 BATCH_TOO_LARGE  - Batch size exceeds 500
400 EMPTY_MESSAGE    - Missing title/text for non-silent message
400 MISSING_GROUP_ID - group_config requires group_id
400 INVALID_GROUP_ID - group_id too long (>32 chars)
401 UNAUTHORIZED     - Missing or invalid Bearer Token

API Reference

Complete parameter documentation for server-side APIs.

POST /push (Batch Push)

POST/v1/push

Send messages to multiple iOS and Android devices in a single request. The request body must be in JSON format.

Request Body

ParameterTypeRequiredDescription
iosstring[]NoList of iOS device tokens. At least one of 'ios' or 'android' must be provided.
androidstring[]NoList of Android (FCM) tokens. At least one of 'ios' or 'android' must be provided.
messageobjectYesMessage content object containing title, text, media, etc. See **message** definition below.
optionsobjectNoPush options including priority, TTL, etc. See **options** definition below.

message

ParameterTypeRequiredDescription
titlestringNoMessage title. For non-silent messages, at least one of 'title' or 'text' is required.
textstringNoMessage body. For non-silent messages, at least one of 'title' or 'text' is required.
group_idstringNoGroup ID for notification grouping. Max length: 32 characters.
mediaobjectNoMultimedia attachment. See **media** definition below.
actionsarrayNoList of interactive buttons. See **action** definition below.
detailobjectNoDetail configuration, currently supports 'url' for click redirection. See **detail** definition below.
soundstringNoCustom sound filename (without extension).
group_configobjectNoGroup configuration. Must be used with 'group_id'. See **groupConfig** definition below.

options

ParameterTypeRequiredDescription
prioritystringNoPriority. Values: 'normal' (default), 'silent', 'ringing', 'critical'.
ttlnumberNoMessage Time-To-Live (seconds).

media

ParameterTypeRequiredDescription
typestringYesMedia type. Values: 'image' (default), 'video'.
urlstringYesURL of the media file.
thumbnail_urlstringNoVideo thumbnail URL (required for 'video' type).

action

ParameterTypeRequiredDescription
labelstringYesText displayed on the button.
callbackstring | objectYesCallback URL on click. Two formats: 1) String: URL only (GET); 2) Object: See **callback**.
payloadstringNoCustom payload data passed to the webhook on callback.
childrenarrayNoList of submenu items. See **action** definition.

callback

ParameterTypeRequiredDescription
urlstringYesWebhook URL.
methodstringNoHTTP method. Values: 'GET' (default), 'POST'.

detail

ParameterTypeRequiredDescription
urlstringNoClick redirection URL.

groupConfig

ParameterTypeRequiredDescription
namestringNoCustom group name (displayed in the conversation list).
icon_urlstringNoCustom group icon URL.
reply_callbackstring | objectNoWebhook configuration for reply messages. See **callback**.
actionsarrayNoGroup operation menu, supports up to 4 top-level buttons. See **action** definition.

GET /push (Simple Push)

GET/v1/push/:platform/:token

Send simple messages via URL parameters, suitable for scripts or quick testing.

Query Parameters

ParameterTypeRequiredDescription
titlestringNoMessage title.
textstringNoMessage body.
group_idstringNoGroup ID.
soundstringNoCustom sound.
media_urlstringNoURL of image or video.
media_typestringNoMedia type, 'image' or 'video'. Default: image.
thumbnailstringNoVideo thumbnail URL.
urlstringNoClick redirection URL.
prioritystringNoPriority (normal, silent, ringing, critical).
ttlnumberNoTime-To-Live (seconds).

Webhook Guide

Webhook is the core mechanism for message interaction. When a user clicks a button in a message or replies in a conversation, Linu App sends a request to your preset server address. This guide details how to configure and handle these callbacks.

Webhook Overview

Linu supports two types of Webhooks:

  • Action Webhook Action Webhook — Triggered when a user clicks a message button
  • Reply Webhook Reply Webhook — Triggered when a user replies in a conversation

Workflow

+----------------+        +----------------+        +----------------+        +----------------+
|  Push Message  |        |      User      |        |    Linu App    |        |   Your Server  |
+----------------+        +----------------+        +----------------+        +----------------+
       |                         |                         |                         |
 1. Actions/Reply                |                         |                         |
    Configured                   |                         |                         |
       |                         |                         |                         |
       |------------------------>|                         |                         |
       |                         |                         |                         |
       |                         | 2. Click Button         |                         |
       |                         |    or Reply             |                         |
       |                         |------------------------>|                         |
       |                         |                         |                         |
       |                         |                         | 3. HTTP POST/GET        |
       |                         |                         |    (JSON Payload)       |
       |                         |                         |------------------------>|
       |                         |                         |                         |
       |                         |                         |                         | 4. Handle Logic
       |                         |                         |                         |    & Response

Action Webhook (Button Callback)

Action Webhook allows you to add interactive buttons to messages. There is no limit on the number of actions, but be aware of the 4K size limit for APNS and FCM. Suitable for approval confirmation, quick actions, status updates, etc.

Configure Buttons

Configure buttons via the **message.actions** array when pushing messages:

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "title": "AI Drawing Task Completed",
      "text": "Task ID: #8823, please review the generated result.",
      "group_id": "ai-task-8823",
      "actions": [
        {
          "label": "Approve",
          "callback": "https://api.example.com/webhook/approve",
          "method": "POST",
          "payload": "task-8823"
        },
        {
          "label": "Regenerate",
          "callback": "https://api.example.com/webhook/regenerate",
          "method": "POST",
          "payload": "task-8823"
        }
      ]
    }
  }'

Action Config Fields

ParameterTypeRequiredDescription
labelstringYesText displayed on the button, recommended 2-6 characters
callbackstring|objectYesURL address to callback when user clicks
methodstringNoHTTP method, supports GET or POST, default is GET
payloadstringNoCustom data, passed back to business server as is on click

Receive Callback Request

When a user clicks a button, the App sends a request to the configured **callback** URL:

// Action Webhook request body sent by App
{
  "type": "action",
  "payload": "task-8823",
  "message_id": "msg_xyz789",
  "group_id": "ai-task-8823",
  "device_token": "a1b2c3d4e5f6..."
}

Action Webhook Request Parameters

ParameterTypeRequiredDescription
typestringYesWebhook type, fixed as 'action'
payloadstringNoPayload set during push is returned as is, can be used for business ID, security verification, etc.
message_idstringYesMessage ID that triggered this Action
group_idstringNoGroup ID the message belongs to (if any)
device_tokenstringYesDevice token initiating the request (Note: App supports device token spoofing)

Reply Webhook (Reply Callback)

Reply Webhook allows users to reply directly to messages in a group/conversation. Suitable for DevOps alert confirmation, AI task instructions, ticket systems, and other bidirectional communication scenarios.

Configure Reply Webhook

Configure reply callback via **message.group_config.reply_callback** when pushing messages:

curl -X POST https://api.linu.aprilzz.com/v1/push \
  -H "Content-Type: application/json" \
  -d '{
    "ios": ["device-token"],
    "message": {
      "title": "CI/CD Deployment Confirmation",
      "text": "Production deployment request, please enter 'confirm' to execute.",
      "group_id": "deploy-prod-001",
      "group_config": {
        "name": "Deploy Bot",
        "icon_url": "https://example.com/bot-avatar.png",
        "reply_callback": {
          "url": "https://api.example.com/webhook/reply",
          "method": "POST"
        }
      }
    }
  }'

Webhook Config Fields

ParameterTypeRequiredDescription
urlstringYesWebhook callback address, must be a complete URL with HTTPS protocol
methodstringNoHTTP method, supports GET or POST, default is GET

Receive Reply Request

When a user sends a reply in a conversation, the App sends a request to the configured URL:

// Reply Webhook request body sent by App
{
  "type": "reply",
  "text": "confirm",
  "group_id": "deploy-prod-001",
  "device_token": "a1b2c3d4e5f6..."
}

Reply Webhook Request Parameters

ParameterTypeRequiredDescription
typestringYesWebhook type, fixed as 'reply'
textstringYesReply text content entered by the user
group_idstringYesGroup/Session ID where the user replied
device_tokenstringYesDevice token initiating the reply

Server Implementation Examples

Below are examples of handling Webhooks using different languages/frameworks:

Node.js / Express

// Node.js / Express Example
const express = require('express');
const app = express();
app.use(express.json());

// Action Webhook Handler
app.post('/webhook/approve', (req, res) => {
  const { type, payload, device_token } = req.body;

  if (type === 'action') {
    console.log(`Received Action callback, payload: ${payload}`);
    // Business logic, e.g., mark task as approved
    // await markTaskApproved(payload);
  }

  // Return success response
  res.status(200).json({ success: true });
});

// Reply Webhook Handler
app.post('/webhook/reply', (req, res) => {
  const { type, text, group_id, device_token } = req.body;

  if (type === 'reply') {
    console.log(`Received user reply: ${text}`);
    console.log(`Session ID: ${group_id}`);

    // Handle reply logic, e.g., trigger deployment
    // if (text === 'confirm') await triggerDeploy(group_id);
  }

  // Return success response
  res.status(200).json({ message: 'Success' });
});

app.listen(3000);

Python / Flask

# Python / Flask Example
from flask import Flask, request, jsonify

app = Flask(__name__)

# Action Webhook Handler
@app.route('/webhook/confirm', methods=['POST'])
def handle_action():
    data = request.json
    msg_type = data.get('type')
    payload = data.get('payload')
    
    if msg_type == 'action':
        print(f'Received Action callback, payload: {payload}')
        # Business logic
        # update_order_status(payload, 'confirmed')

    return jsonify({'message': 'Success'}), 200

# Reply Webhook Handler
@app.route('/webhook/reply', methods=['POST'])
def handle_reply():
    data = request.json
    msg_type = data.get('type')
    text = data.get('text')
    group_id = data.get('group_id')

    if msg_type == 'reply':
        print(f'Received user reply: {text}')
        print(f'Session ID: {group_id}')
        # Handle reply logic
        # forward_to_support(group_id, text)

    return jsonify({'message': 'Success'}), 200

if __name__ == '__main__':
    app.run(port=3000)

Response Format & Error Handling

Your server needs to return a response within a reasonable time (recommended within 5 seconds). The App determines the result based on the response status code:

  • 2xxSuccess, App may show a success message
  • 4xxClient error, App may show an error message
  • 5xxServer error, App may prompt to retry later
  • No response for over 10 seconds, App shows timeout prompt

Response Example

// Success response
// HTTP Status Code: 200-299
{
  "message": "Operation completed successfully" // Optional: Will be shown as toast
}

// Error response
// HTTP Status Code: 400-599
{
  "message": "Failed to find order #12345" // Optional: Will be shown as toast
}

FAQ

Q: Will Webhook requests be retried?

The current App version does not automatically retry failed Webhooks. If your server is temporarily unavailable, the request will be lost. It is recommended to ensure server high availability.

Q: Can I send a new message in the Webhook response?

Webhook responses do not directly trigger new pushes. If you need to send a new message after processing a user action, please call the **POST /push** interface separately from the server.

Q: What is the difference between GET and POST methods?

The GET method appends parameters to the URL query string; the POST method puts parameters in the request body (JSON format). For scenarios involving sensitive information, POST is recommended.

Q: How to test Webhooks?

You can use online tools like webhook.site or RequestBin to generate temporary Webhook URLs for testing and viewing request content and format.

Deploy Your Own Server

Linu Server is a high-performance, stateless Rust binary. It does not depend on external databases or message queues, requiring only simple file system access (for WAL logs and certificate reading) to run.

Runtime Prerequisites

  • Apple Developer Program: Join to obtain APNS push certificates (.p8 or .p12).
  • Google Firebase Project: Create project to obtain FCM Service Account configuration (.json).
  • Server: A Linux/macOS server, recommended 1 core 2GB RAM minimum.

1. Get Source & Build

Linu uses standard Cargo workflow. Recommended to use --release flag for optimized build in production.
# 1. Clone repository
git clone https://github.com/daoye/linu.git
cd linu

# 2. Build Release version
cargo build --release

# 3. Binary located at target/release/linu-server
Build Dependencies: Rust 1.75+, OpenSSL (libssl-dev), pkg-config.

2. Configuration

Copy config.example.toml to config.toml. The table below shows a simplified high-performance production configuration example (defaults and comments removed). See table below for more advanced options.

# Recommended Production Config (Simplified/Optimized)
# See parameter table below for advanced options

[server]
port = 3000
host = "0.0.0.0"
worker_count = 8  # Recommended: CPU cores x 1~2

# Enable auth in production
# auth_token = "your-secure-token-here"

[push.apns]
# P8 Auth recommended (No annual renewal needed)
p8_key_path = "./certs/AuthKey_XXXXXXXXXX.p8"
key_id = "XXXXXXXXXX"
team_id = "XXXXXXXXXX"
sandbox = false   # Production environment

[push.fcm]
service_account_path = "./certs/firebase-service-account.json"

[wal]
path = "data/wal.log"
max_size_mb = 50  # Increase log rotation threshold appropriately

[engine]
queue_capacity = 5000 # Increase queue size for traffic bursts

[security]
rate_limit_per_second = 100
body_limit_bytes = 20480
SectionParameterDescription
[server]portListening port, default 3000.
hostBind address. 127.0.0.1 for local only, 0.0.0.0 for external access.
worker_countPush worker threads. Recommended CPU cores x 1-2 (default 4).
auth_tokenAPI Auth Token (Optional). If enabled, request header must carry Authorization: Bearer <token>. Leave empty or comment out to disable.
[push.apns]p8_key_pathP8 Private Key Path (Recommended). Compared to P12, never expires and supports multiple Apps.
key_idP8 Key ID (10 chars), from Apple Developer.
team_idTeam ID (10 chars), from Apple Developer Membership.
sandboxUse sandbox environment. true for dev, false for prod.
cert_pathP12 Certificate Path (Optional). Required if using P12 auth.
cert_passwordP12 Certificate Password (Optional).
pool_max_idle_per_hostHTTP/2 Max Idle Connections per Host. Default 2.
pool_idle_timeout_secsIdle connection timeout (seconds). Default 259200 (3 days).
connect_timeout_msTCP connection timeout (ms). Default 10000.
request_timeout_msSingle request timeout (ms). Default 30000.
http2_keepalive_interval_secsHTTP/2 Ping interval (seconds). Default 3600.
http2_keepalive_timeout_secsHTTP/2 Ping timeout (seconds). Default 20.
max_retriesMax retries (network/server errors only). Default 3.
initial_retry_delay_msInitial retry delay (ms). Default 1000.
max_retry_delay_msMax retry delay (ms). Default 30000.
retry_backoff_multiplierRetry backoff multiplier (exponential). Default 2.0.
[push.fcm]service_account_pathFirebase Service Account JSON file path.
(通用网络配置)Supports all pool_*, connect_*, request_*, http2_*, retry_* parameters above.
[wal]pathWrite-Ahead Log path. For crash recovery, ensuring no message loss.
max_size_mbMax size per log file (MB). Rotate when exceeded.
max_filesMax number of old log files to keep.
max_age_daysDays to keep old log files. Default 7 days.
cleanup_interval_secsInterval to clean up expired logs (seconds).
[engine]queue_capacityIn-memory queue capacity. New requests exceeding this limit return 503 Service Unavailable.<br/>Formula: capacity &ge; peak_QPS &times; avg_processing_time
[security]rate_limit_per_secondRate limit per IP per second. 0 to disable (not recommended).
body_limit_bytesRequest body size limit (bytes).

3. Start Service

Linu Server supports multiple config loading methods, priority: Command line -c > Env var CONFIG_PATH > Current directory config.toml.

# Method 1: Use default config file
# Automatically reads config.toml in current directory
./target/release/linu-server

# Method 2: Specify via command line argument
./target/release/linu-server -c /etc/linu/prod.toml

# Method 3: Specify via environment variable
CONFIG_PATH=/etc/linu/prod.toml ./target/release/linu-server

# View help
./target/release/linu-server --help

# Verify service health status
curl http://localhost:8080/health

Questions or suggestions? Submit an Issue or PR on GitHub.