Token导航 LogoToken导航

Agent Skill · 开发

terra-webhooks(泰拉网络钩子)

terra-webhooks 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

terra-webhooks 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描有未通过项,按安装量在开发类的 21,746 个 Skill 里排第 17,811。

总安装 376周安装 16GitHub Stars 9下载 132核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:terra-webhooks(泰拉网络钩子)
来源仓库:https://github.com/adaptationio/skrillz
仓库路径:skills/terra-webhooks
安装命令:
npx skills add https://github.com/adaptationio/skrillz --skill terra-webhooks
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/adaptationio/skrillz --skill terra-webhooks

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态进行整理。可结合来源仓库和原始 README 核验具体用法。安装前建议确认权限范围、维护状态及是否触发联网或文件读写。

安装命令:npx skills add https://github.com/adaptationio/skrillz --skill terra-webhooks

来源仓库内路径:skills/terra-webhooks

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/adaptationio/skrillz --skill terra-webhooks;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

terra-webhooks 怎么安装?

在终端执行 npx skills add https://github.com/adaptationio/skrillz --skill terra-webhooks。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

terra-webhooks 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

terra-webhooks 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

terra-webhooks 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

terra-webhooks 用的人多吗?

公开数据是累计安装 376 次,近一周安装 16 次,下载 132 次,GitHub 9 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

terra-webhooks 安全吗?

第三方扫描有未通过项,风险较高。除非你确认过源码,否则不建议直接安装到常用环境。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。

先想清楚的地方

  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
terra-webhooks

泰拉网络钩子 · 本页

3769需要联网MIT未通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Terra Webhooks

Handle real-time health data delivery from Terra API.

Quick Start

from flask import Flask, request
import hmac
import hashlib

app = Flask(__name__)

TERRA_SIGNING_SECRET = "your_signing_secret_from_dashboard"

@app.route("/webhooks/terra", methods=["POST"])
def handle_terra_webhook():
    # 1. Verify signature
    signature = request.headers.get("terra-signature")
    if not verify_signature(signature, request.get_data()):
        return "Invalid signature", 401

    # 2. Parse payload
    payload = request.get_json()
    event_type = payload.get("type")

    # 3. Handle event
    if event_type == "activity":
        handle_activity(payload)
    elif event_type == "sleep":
        handle_sleep(payload)
    elif event_type == "auth":
        handle_user_connected(payload)

    # 4. Respond immediately
    return "OK", 200

def verify_signature(header: str, body: bytes) -> bool:
    """Verify Terra webhook signature."""
    parts = dict(p.split("=") for p in header.split(","))
    timestamp = parts["t"]
    signature = parts["v1"]

    message = f"{timestamp}.{body.decode()}"
    expected = hmac.new(
        TERRA_SIGNING_SECRET.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, signature)

Webhook Event Types

Authentication Events

EventDescription
authUser successfully connected
deauthUser disconnected
user_reauthUser re-authenticated
access_revokedProvider revoked access
connection_errorConnection failed

Data Events

EventDescription
activityNew workout/activity data
sleepNew sleep session data
bodyBody metrics update
dailyDaily summary update
nutritionNutrition/meal data
menstruationCycle tracking data
athleteUser profile update

Processing Events

EventDescription
processingData is being processed
large_request_processingLarge request in progress
large_request_sendingLarge request sending chunks

Event Payloads

auth - User Connected

{
  "type": "auth",
  "user": {
    "user_id": "terra_abc123",
    "provider": "FITBIT",
    "reference_id": "user_12345",
    "scopes": ["activity", "sleep", "body"]
  },
  "status": "authenticated"
}

activity - Workout Data

{
  "type": "activity",
  "user": {
    "user_id": "terra_abc123",
    "provider": "GARMIN",
    "reference_id": "user_12345"
  },
  "data": [{
    "metadata": {
      "start_time": "2025-12-05T07:00:00Z",
      "end_time": "2025-12-05T08:00:00Z",
      "type": "running"
    },
    "calories_data": {
      "total_burned_calories": 450
    },
    "heart_rate_data": {
      "summary": { "avg_hr_bpm": 145, "max_hr_bpm": 175 }
    },
    "distance_data": { "distance_meters": 8500 }
  }]
}

sleep - Sleep Data

{
  "type": "sleep",
  "user": {
    "user_id": "terra_abc123",
    "provider": "OURA"
  },
  "data": [{
    "metadata": {
      "start_time": "2025-12-04T22:30:00Z",
      "end_time": "2025-12-05T06:30:00Z"
    },
    "sleep_durations_data": {
      "sleep_efficiency": 0.92
    },
    "asleep": {
      "duration_deep_sleep_state_seconds": 5400,
      "duration_REM_sleep_state_seconds": 6600
    }
  }]
}

daily - Daily Summary

{
  "type": "daily",
  "user": {
    "user_id": "terra_abc123",
    "provider": "FITBIT"
  },
  "data": [{
    "metadata": {
      "start_time": "2025-12-05T00:00:00Z",
      "end_time": "2025-12-05T23:59:59Z"
    },
    "movement_data": { "steps_count": 10500 },
    "calories_data": { "total_burned_calories": 2400 }
  }]
}

deauth - User Disconnected

{
  "type": "deauth",
  "user": {
    "user_id": "terra_abc123",
    "provider": "FITBIT"
  },
  "status": "deauthenticated"
}

Operations

setup-webhook-endpoint

Create a production-ready webhook handler.

from flask import Flask, request
from celery import Celery
import hmac
import hashlib
import logging

app = Flask(__name__)
celery = Celery()
logger = logging.getLogger(__name__)

TERRA_SIGNING_SECRET = "your_signing_secret"

# Terra webhook source IPs (for additional security)
TERRA_IPS = [
    "18.133.218.210", "18.169.82.189", "18.132.162.19",
    "18.130.218.186", "13.43.183.154", "3.11.208.36",
    "35.214.201.105", "35.214.230.71", "35.214.252.53", "35.214.229.114"
]

@app.route("/webhooks/terra", methods=["POST"])
def terra_webhook():
    # Optional: IP whitelist check
    client_ip = request.remote_addr
    if client_ip not in TERRA_IPS:
        logger.warning(f"Webhook from unknown IP: {client_ip}")
        # Consider: return "Forbidden", 403

    # Verify signature
    signature = request.headers.get("terra-signature")
    raw_body = request.get_data()

    if not signature or not verify_signature(signature, raw_body):
        logger.error("Invalid webhook signature")
        return "Invalid signature", 401

    # Parse and queue for async processing
    payload = request.get_json()
    process_webhook.delay(payload)

    # Respond immediately (within 5 seconds)
    return "OK", 200

def verify_signature(header: str, body: bytes) -> bool:
    """HMAC-SHA256 signature verification."""
    try:
        parts = dict(p.split("=") for p in header.split(","))
        timestamp = parts["t"]
        signature = parts["v1"]

        message = f"{timestamp}.{body.decode()}"
        expected = hmac.new(
            TERRA_SIGNING_SECRET.encode(),
            message.encode(),
            hashlib.sha256
        ).hexdigest()

        # Constant-time comparison to prevent timing attacks
        return hmac.compare_digest(expected, signature)
    except Exception as e:
        logger.error(f"Signature verification error: {e}")
        return False

@celery.task
def process_webhook(payload: dict):
    """Async webhook processing."""
    event_type = payload.get("type")
    user = payload.get("user", {})

    logger.info(f"Processing {event_type} for user {user.get('user_id')}")

    handlers = {
        "auth": handle_auth,
        "deauth": handle_deauth,
        "activity": handle_activity,
        "sleep": handle_sleep,
        "body": handle_body,
        "daily": handle_daily,
        "nutrition": handle_nutrition,
    }

    handler = handlers.get(event_type)
    if handler:
        handler(payload)
    else:
        logger.warning(f"Unknown event type: {event_type}")

handle-data-events

Process incoming health data.

def handle_activity(payload: dict):
    """Handle activity/workout data."""
    user_id = payload["user"]["user_id"]

    for activity in payload.get("data", []):
        metadata = activity["metadata"]
        unique_key = f"{user_id}:{metadata['start_time']}:{metadata['end_time']}"

        # Insert if not exists (activities are unique sessions)
        db.activities.update_one(
            {"_id": unique_key},
            {"$setOnInsert": activity},
            upsert=True
        )

        logger.info(f"Processed activity: {metadata['type']}")

def handle_daily(payload: dict):
    """Handle daily summary data."""
    user_id = payload["user"]["user_id"]

    for daily in payload.get("data", []):
        date = daily["metadata"]["start_time"][:10]  # YYYY-MM-DD

        # UPSERT - daily data updates multiple times per day
        db.daily.update_one(
            {"user_id": user_id, "date": date},
            {"$set": daily},
            upsert=True
        )

        logger.info(f"Updated daily for {date}")

def handle_sleep(payload: dict):
    """Handle sleep data."""
    user_id = payload["user"]["user_id"]

    for sleep in payload.get("data", []):
        metadata = sleep["metadata"]
        unique_key = f"{user_id}:{metadata['start_time']}:{metadata['end_time']}"

        db.sleep.update_one(
            {"_id": unique_key},
            {"$setOnInsert": sleep},
            upsert=True
        )

def handle_body(payload: dict):
    """Handle body metrics data."""
    user_id = payload["user"]["user_id"]

    for body in payload.get("data", []):
        date = body["metadata"]["start_time"][:10]

        # UPSERT - body data updates multiple times per day
        db.body.update_one(
            {"user_id": user_id, "date": date},
            {"$set": body},
            upsert=True
        )

handle-auth-events

Process connection lifecycle events.

def handle_auth(payload: dict):
    """Handle new user connection."""
    user = payload["user"]

    # Store Terra user mapping
    db.terra_users.insert_one({
        "terra_user_id": user["user_id"],
        "provider": user["provider"],
        "reference_id": user["reference_id"],
        "scopes": user.get("scopes", []),
        "connected_at": datetime.now(),
        "status": "active"
    })

    # Trigger historical data backfill
    trigger_backfill.delay(user["user_id"])

    logger.info(f"User connected: {user['user_id']} via {user['provider']}")

def handle_deauth(payload: dict):
    """Handle user disconnection."""
    user = payload["user"]

    # Mark as disconnected
    db.terra_users.update_one(
        {"terra_user_id": user["user_id"]},
        {"$set": {"status": "disconnected", "disconnected_at": datetime.now()}}
    )

    logger.info(f"User disconnected: {user['user_id']}")

verify-signature

Signature verification utility.

import hmac
import hashlib

def verify_terra_signature(
    signature_header: str,
    raw_body: bytes,
    signing_secret: str
) -> bool:
    """
    Verify Terra webhook signature.

    Header format: terra-signature: t=1234567890,v1=abc123...

    Args:
        signature_header: The terra-signature header value
        raw_body: Raw request body (bytes)
        signing_secret: Your signing secret from Terra dashboard

    Returns:
        bool: True if signature is valid
    """
    try:
        # Parse header
        parts = {}
        for part in signature_header.split(","):
            key, value = part.split("=", 1)
            parts[key] = value

        timestamp = parts["t"]
        signature = parts["v1"]

        # Compute expected signature
        message = f"{timestamp}.{raw_body.decode()}"
        expected = hmac.new(
            signing_secret.encode(),
            message.encode(),
            hashlib.sha256
        ).hexdigest()

        # Constant-time comparison
        return hmac.compare_digest(expected, signature)

    except Exception:
        return False

Retry Logic

Terra retries failed webhooks:

AttemptDelay
1Immediate
2~30 seconds
3~2 minutes
4~10 minutes
5~30 minutes
6~2 hours
7~8 hours
8~24 hours

Total: ~8 retries over 24+ hours

Failure conditions:

  • Non-2XX response
  • Timeout (>5 seconds recommended)
  • Connection error

Idempotency

Handle duplicate webhooks safely:

def handle_webhook_idempotent(payload: dict):
    """Process webhook with idempotency."""

    # Generate idempotency key
    user_id = payload["user"]["user_id"]
    event_type = payload["type"]

    if event_type in ["activity", "sleep"]:
        # Session-based: use start+end time
        data = payload["data"][0]
        key = f"{user_id}:{data['metadata']['start_time']}:{data['metadata']['end_time']}"
    elif event_type in ["daily", "body"]:
        # Date-based: use date
        data = payload["data"][0]
        key = f"{user_id}:{data['metadata']['start_time'][:10]}"
    else:
        # Auth events: use user_id + type + timestamp
        key = f"{user_id}:{event_type}:{datetime.now().isoformat()}"

    # Check if already processed
    if db.processed_webhooks.find_one({"_id": key}):
        logger.info(f"Duplicate webhook skipped: {key}")
        return

    # Process and mark as done
    process_event(payload)
    db.processed_webhooks.insert_one({"_id": key, "processed_at": datetime.now()})

Testing Webhooks

Local Development with ngrok

# Install ngrok
npm install -g ngrok

# Start your server
python app.py  # Running on localhost:5000

# Expose with ngrok
ngrok http 5000

# Use ngrok URL in Terra dashboard
# https://abc123.ngrok.io/webhooks/terra

Testing with curl

# Simulate webhook (without signature)
curl -X POST http://localhost:5000/webhooks/terra \
  -H "Content-Type: application/json" \
  -d '{
    "type": "activity",
    "user": {"user_id": "test123", "provider": "FITBIT"},
    "data": [{"metadata": {"type": "running"}}]
  }'

Webhook.site Testing

  1. Go to https://webhook.site
  2. Copy your unique URL
  3. Add to Terra dashboard as webhook destination
  4. Connect a test user and observe payloads

IP Whitelisting

Terra webhooks come from these IPs:

TERRA_IPS = [
    "18.133.218.210",
    "18.169.82.189",
    "18.132.162.19",
    "18.130.218.186",
    "13.43.183.154",
    "3.11.208.36",
    "35.214.201.105",
    "35.214.230.71",
    "35.214.252.53",
    "35.214.229.114"
]

Dashboard Configuration

  1. Go to Terra Dashboard → Destinations → Webhooks
  2. Add your webhook URL (must be HTTPS in production)
  3. Copy the signing secret for signature verification
  4. Select which events to receive

Related Skills

  • terra-auth: Get signing secret
  • terra-connections: Handle auth/deauth events
  • terra-data: Data schema reference
  • terra-troubleshooting: Debug webhook issues

平台分布

github-copilot

25.61%
按下载量换算34

Claude Code

22.92%
按下载量换算30

mcpjam

17.44%
按下载量换算23

moltbot

12.57%
按下载量换算17

windsurf

7.74%
按下载量换算10

zencoder

2.91%
按下载量换算4

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。

继续浏览同类 Skills