# Send Zulip to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "zulip",
    "name": "Zulip",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/suky57/zulip",
    "canonicalUrl": "https://clawhub.ai/suky57/zulip",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/zulip",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=zulip",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "SKILL.md",
      "references/api-quick-reference.md",
      "scripts/zulip_client.py"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "zulip",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-11T13:33:10.714Z",
      "expiresAt": "2026-05-18T13:33:10.714Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=zulip",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=zulip",
        "contentDisposition": "attachment; filename=\"zulip-1.0.2.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "zulip"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/zulip"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/zulip",
    "downloadUrl": "https://openagent3.xyz/downloads/zulip",
    "agentUrl": "https://openagent3.xyz/skills/zulip/agent",
    "manifestUrl": "https://openagent3.xyz/skills/zulip/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/zulip/agent.md"
  }
}
```
## Documentation

### Zulip Integration

Interact with Zulip chat platform for team communication.

### 1. Install Python Client

pip install zulip

### 2. Create Configuration File

Create ~/.config/zulip/zuliprc:

[api]
email=bot@example.zulipchat.com
key=YOUR_API_KEY_HERE
site=https://example.zulipchat.com

Get credentials from Zulip admin panel (Settings → Bots).

### 3. Verify Connection

python scripts/zulip_client.py streams

### Using the Helper Script

The scripts/zulip_client.py provides common operations:

List streams:

python scripts/zulip_client.py streams
python scripts/zulip_client.py streams --json

Read messages:

# Recent stream messages (by name)
python scripts/zulip_client.py messages --stream "General" --num 10

# By stream ID (more reliable, use 'streams' to find IDs)
python scripts/zulip_client.py messages --stream-id 42 --num 10

# Specific topic
python scripts/zulip_client.py messages --stream "General" --topic "Updates"

# Private messages
python scripts/zulip_client.py messages --type private --num 5

# Mentions
python scripts/zulip_client.py messages --type mentioned

Note: Stream names may have descriptions that look like part of the name. Use --stream-id for unambiguous identification.

Send messages:

# To stream
python scripts/zulip_client.py send --type stream --to "General" --topic "Updates" --content "Hello!"

# Private message (user_id)
python scripts/zulip_client.py send --type private --to 123 --content "Hi there"

List users:

python scripts/zulip_client.py users
python scripts/zulip_client.py users --json

### Using Python Client Directly

import zulip

client = zulip.Client(config_file="~/.config/zulip/zuliprc")

# Read messages
result = client.get_messages({
    "anchor": "newest",
    "num_before": 10,
    "num_after": 0,
    "narrow": [{"operator": "stream", "operand": "General"}]
})

# Send to stream
client.send_message({
    "type": "stream",
    "to": "General",
    "topic": "Updates",
    "content": "Message text"
})

# Send DM
client.send_message({
    "type": "private",
    "to": [user_id],
    "content": "Private message"
})

### Using curl

# List streams
curl -u "bot@example.com:KEY" https://example.zulipchat.com/api/v1/streams

# Get messages
curl -u "bot@example.com:KEY" -G \\
  "https://example.zulipchat.com/api/v1/messages" \\
  --data-urlencode 'anchor=newest' \\
  --data-urlencode 'num_before=20' \\
  --data-urlencode 'num_after=0' \\
  --data-urlencode 'narrow=[{"operator":"stream","operand":"General"}]'

# Send message
curl -X POST "https://example.zulipchat.com/api/v1/messages" \\
  -u "bot@example.com:KEY" \\
  --data-urlencode 'type=stream' \\
  --data-urlencode 'to=General' \\
  --data-urlencode 'topic=Updates' \\
  --data-urlencode 'content=Hello!'

### Monitor Stream for New Messages

def get_latest_messages(client, stream_name, last_seen_id=None):
    narrow = [{"operator": "stream", "operand": stream_name}]
    
    if last_seen_id:
        # Get only messages after last seen
        request = {
            "anchor": last_seen_id,
            "num_before": 0,
            "num_after": 100,
            "narrow": narrow
        }
    else:
        # Get recent messages
        request = {
            "anchor": "newest",
            "num_before": 20,
            "num_after": 0,
            "narrow": narrow
        }
    
    result = client.get_messages(request)
    return result["messages"]

### Reply to Topic

def reply_to_message(client, original_message, reply_text):
    """Reply in the same stream/topic as original message."""
    client.send_message({
        "type": "stream",
        "to": original_message["display_recipient"],
        "topic": original_message["subject"],
        "content": reply_text
    })

### Search Messages

def search_messages(client, keyword, stream=None):
    narrow = [{"operator": "search", "operand": keyword}]
    
    if stream:
        narrow.append({"operator": "stream", "operand": stream})
    
    result = client.get_messages({
        "anchor": "newest",
        "num_before": 50,
        "num_after": 0,
        "narrow": narrow
    })
    
    return result["messages"]

### Get User ID by Email

def get_user_id(client, email):
    """Find user_id by email address."""
    result = client.get_members()
    
    for user in result["members"]:
        if user["email"] == email:
            return user["user_id"]
    
    return None

### Message Formatting

Zulip uses Markdown:

Bold: **text**
Italic: *text*
Code: \`code\`
Code block: \`\`\`language\\ncode\\n\`\`\`
Quote: > quoted text
Mention user: @**Full Name**
Link stream: #**stream-name**
Link: [text](url)

### Upload and Share Files

with open("file.pdf", "rb") as f:
    result = client.upload_file(f)
    file_url = result["uri"]

# Share in message
client.send_message({
    "type": "stream",
    "to": "General",
    "topic": "Files",
    "content": f"Check out [this file]({file_url})"
})

### React to Messages

# Add reaction
client.add_reaction({
    "message_id": 123,
    "emoji_name": "thumbs_up"
})

# Remove reaction
client.remove_reaction({
    "message_id": 123,
    "emoji_name": "thumbs_up"
})

### Reference

See references/api-quick-reference.md for complete API documentation, endpoints, and examples.

### Troubleshooting

Config file not found:

Ensure ~/.config/zulip/zuliprc exists with correct format
Check file permissions (should be readable)

Authentication failed:

Verify API key is correct
Check bot is active in Zulip admin panel
Ensure site URL matches organization URL

Empty messages array:

Bot might not be subscribed to the stream
Use client.get_subscriptions() to check subscriptions
Admin may need to add bot to private streams

Rate limit errors:

Standard limit: 200 requests/minute
Message limit: ~20-30/minute
Add delays between bulk operations
Check Retry-After header on 429 responses
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: suky57
- Version: 1.0.2
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-05-11T13:33:10.714Z
- Expires at: 2026-05-18T13:33:10.714Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/zulip)
- [Send to Agent page](https://openagent3.xyz/skills/zulip/agent)
- [JSON manifest](https://openagent3.xyz/skills/zulip/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/zulip/agent.md)
- [Download page](https://openagent3.xyz/downloads/zulip)