튜토리얼 카테고리 Skills 소개
ZH EN JA KO
고급 활용

OpenClaw REST API 개발 및 서드파티 통합 튜토리얼

· 35 분 소요

서문

OpenClaw Gateway는 채팅 채널에 서비스를 제공할 뿐만 아니라 완전한 REST API를 노출합니다. 이러한 API를 통해 AI 기능을 어떤 애플리케이션에든 통합할 수 있습니다. 자체 구축한 웹 프론트엔드, CRM 시스템, 고객 서비스 티켓 플랫폼 등 무엇이든 가능합니다. 본 글에서는 API 사용 방법과 통합 사례를 상세히 소개합니다.

1. API 개요

1.1 기본 정보

속성
기본 URL http://localhost:18789/api/v1
프로토콜 HTTP/HTTPS
데이터 형식 JSON
인증 방식 Bearer Token
속도 제한 기본 60 RPM

1.2 핵심 엔드포인트 목록

메서드 엔드포인트 기능
POST /api/v1/chat 메시지 전송 및 AI 응답 수신
POST /api/v1/chat/stream 스트리밍 메시지 전송
POST /api/v1/send 지정 채널로 메시지 전송
GET /api/v1/conversations 대화 목록 조회
GET /api/v1/conversations/:id 대화 상세 조회
DELETE /api/v1/conversations/:id 대화 삭제
GET /api/v1/skills 스킬 목록 조회
GET /api/v1/models 사용 가능한 모델 목록 조회
GET /health 헬스 체크
GET /health/detail 상세 상태

2. 인증 설정

2.1 API Token 생성

# API Token 생성
openclaw token create --name "my-app" --scope "chat,send,read"

# 출력:
# Token: oc_tok_a1b2c3d4e5f6g7h8i9j0...
# Scope: chat, send, read
# Created: 2026-04-09

# 모든 Token 확인
openclaw token list

# Token 취소
openclaw token revoke oc_tok_a1b2c3d4e5f6g7h8i9j0

2.2 설정 파일에서 설정

// ~/.config/openclaw/openclaw.json5
{
  "api": {
    "enabled": true,
    "port": 18789,
    "auth": {
      "enabled": true,
      "tokens": [
        {
          "name": "my-web-app",
          "token": "oc_tok_a1b2c3d4e5f6g7h8i9j0",
          "scope": ["chat", "send", "read", "admin"],
          "rateLimit": 120  // RPM
        },
        {
          "name": "crm-integration",
          "token": "oc_tok_x9y8z7w6v5u4t3s2r1q0",
          "scope": ["chat", "send"],
          "rateLimit": 30
        }
      ]
    }
  }
}

2.3 인증 요청 예시

모든 API 요청은 Header에 Token을 포함해야 합니다:

curl -H "Authorization: Bearer oc_tok_a1b2c3d4e5f6g7h8i9j0" \
  http://localhost:18789/api/v1/models

3. 핵심 API 사용

3.1 메시지 전송 및 응답 수신

curl 예시:

curl -X POST http://localhost:18789/api/v1/chat \
  -H "Authorization: Bearer $OPENCLAW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Python으로 퀵소트 알고리즘을 작성해 주세요",
    "conversationId": "conv_123",
    "model": "claude-sonnet-4-20250514",
    "systemPrompt": "당신은 전문 프로그래밍 어시스턴트입니다",
    "maxTokens": 2000,
    "temperature": 0.7
  }'

응답 예시:

{
  "id": "msg_abc123",
  "conversationId": "conv_123",
  "role": "assistant",
  "content": "다음은 Python으로 구현한 퀵소트 알고리즘입니다:\n\n```python\ndef quicksort(arr):\n    if len(arr) <= 1:\n        return arr\n    pivot = arr[len(arr) // 2]\n    left = [x for x in arr if x < pivot]\n    middle = [x for x in arr if x == pivot]\n    right = [x for x in arr if x > pivot]\n    return quicksort(left) + middle + quicksort(right)\n```",
  "model": "claude-sonnet-4-20250514",
  "usage": {
    "inputTokens": 45,
    "outputTokens": 156
  },
  "latency": 2341
}

3.2 스트리밍 출력

curl 예시 (SSE):

curl -X POST http://localhost:18789/api/v1/chat/stream \
  -H "Authorization: Bearer $OPENCLAW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "message": "인공지능에 관한 짧은 글을 작성해 주세요",
    "model": "claude-sonnet-4-20250514"
  }'

# SSE 이벤트 스트림 반환:
# data: {"type":"start","conversationId":"conv_456"}
# data: {"type":"delta","content":"인공"}
# data: {"type":"delta","content":"지능"}
# data: {"type":"delta","content":"(AI)"}
# ...
# data: {"type":"done","usage":{"inputTokens":20,"outputTokens":350}}

3.3 채널로 메시지 전송

# Telegram 사용자에게 메시지 전송
curl -X POST http://localhost:18789/api/v1/send \
  -H "Authorization: Bearer $OPENCLAW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "telegram",
    "chatId": "123456789",
    "message": "API를 통해 전송된 메시지입니다"
  }'

# Discord 채널로 메시지 전송
curl -X POST http://localhost:18789/api/v1/send \
  -H "Authorization: Bearer $OPENCLAW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "discord",
    "chatId": "CHANNEL_ID",
    "message": "시스템 공지: 오늘 밤 22:00부터 서버 유지보수가 시작됩니다"
  }'

4. Webhook 수신

4.1 Webhook 콜백 설정

OpenClaw가 메시지를 수신하거나 처리를 완료하면 애플리케이션에 Webhook 알림을 보낼 수 있습니다:

{
  "api": {
    "webhooks": {
      "outgoing": [
        {
          "url": "https://your-app.com/api/openclaw-callback",
          "secret": "your-webhook-secret",
          "events": [
            "message.received",
            "message.sent",
            "conversation.created",
            "channel.connected",
            "channel.disconnected"
          ],
          "retry": {
            "maxAttempts": 3,
            "backoffMs": 5000
          }
        }
      ]
    }
  }
}

4.2 Webhook 이벤트 형식

{
  "event": "message.received",
  "timestamp": "2026-04-09T10:30:00Z",
  "data": {
    "messageId": "msg_xyz789",
    "conversationId": "conv_123",
    "channel": "telegram",
    "chatId": "123456789",
    "from": {
      "id": "user_456",
      "name": "Alice"
    },
    "content": "사용자가 보낸 메시지 내용",
    "timestamp": "2026-04-09T10:30:00Z"
  },
  "signature": "sha256=abcdef..."
}

4.3 Webhook 서명 검증

import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    expected = 'sha256=' + hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

5. Python 통합 예시

5.1 기본 래퍼

import requests
from typing import Optional, Generator

class OpenClawClient:
    def __init__(self, base_url: str = "http://localhost:18789",
                 token: str = ""):
        self.base_url = base_url.rstrip("/")
        self.headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }

    def chat(self, message: str, conversation_id: Optional[str] = None,
             model: str = "claude-sonnet-4-20250514",
             system_prompt: Optional[str] = None) -> dict:
        """메시지를 전송하고 AI 응답을 수신합니다"""
        payload = {
            "message": message,
            "model": model,
        }
        if conversation_id:
            payload["conversationId"] = conversation_id
        if system_prompt:
            payload["systemPrompt"] = system_prompt

        resp = requests.post(
            f"{self.base_url}/api/v1/chat",
            json=payload,
            headers=self.headers,
            timeout=120
        )
        resp.raise_for_status()
        return resp.json()

    def chat_stream(self, message: str,
                    model: str = "claude-sonnet-4-20250514") -> Generator:
        """스트리밍으로 메시지를 전송합니다"""
        payload = {"message": message, "model": model}
        headers = {**self.headers, "Accept": "text/event-stream"}

        resp = requests.post(
            f"{self.base_url}/api/v1/chat/stream",
            json=payload,
            headers=headers,
            stream=True,
            timeout=120
        )
        resp.raise_for_status()

        for line in resp.iter_lines():
            if line:
                line = line.decode("utf-8")
                if line.startswith("data: "):
                    yield line[6:]

    def send(self, channel: str, chat_id: str, message: str) -> dict:
        """지정 채널로 메시지를 전송합니다"""
        resp = requests.post(
            f"{self.base_url}/api/v1/send",
            json={
                "channel": channel,
                "chatId": chat_id,
                "message": message
            },
            headers=self.headers
        )
        resp.raise_for_status()
        return resp.json()

    def health(self) -> dict:
        """헬스 체크"""
        resp = requests.get(f"{self.base_url}/health")
        return resp.json()


# 사용 예시
client = OpenClawClient(token="oc_tok_a1b2c3d4e5f6g7h8i9j0")

# 일반 대화
result = client.chat("오늘 날씨가 어떤가요?")
print(result["content"])

# 스트리밍 대화
for chunk in client.chat_stream("이야기 하나 해주세요"):
    print(chunk, end="", flush=True)

# Telegram으로 전송
client.send("telegram", "123456789", "Hello from API!")

6. Node.js 통합 예시

6.1 기본 래퍼

// openclaw-client.js
const axios = require('axios');

class OpenClawClient {
  constructor(baseUrl = 'http://localhost:18789', token = '') {
    this.baseUrl = baseUrl;
    this.headers = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };
  }

  async chat(message, options = {}) {
    const { conversationId, model = 'claude-sonnet-4-20250514', systemPrompt } = options;

    const payload = { message, model };
    if (conversationId) payload.conversationId = conversationId;
    if (systemPrompt) payload.systemPrompt = systemPrompt;

    const resp = await axios.post(
      `${this.baseUrl}/api/v1/chat`,
      payload,
      { headers: this.headers, timeout: 120000 }
    );
    return resp.data;
  }

  async send(channel, chatId, message) {
    const resp = await axios.post(
      `${this.baseUrl}/api/v1/send`,
      { channel, chatId, message },
      { headers: this.headers }
    );
    return resp.data;
  }

  async health() {
    const resp = await axios.get(`${this.baseUrl}/health`);
    return resp.data;
  }

  async getModels() {
    const resp = await axios.get(
      `${this.baseUrl}/api/v1/models`,
      { headers: this.headers }
    );
    return resp.data;
  }
}

module.exports = OpenClawClient;

// 사용 예시
async function main() {
  const client = new OpenClawClient(
    'http://localhost:18789',
    'oc_tok_a1b2c3d4e5f6g7h8i9j0'
  );

  // 일반 대화
  const result = await client.chat('JavaScript로 피보나치 수열을 구현해 주세요', {
    systemPrompt: '당신은 프로그래밍 튜터입니다. 설명은 명확하고 이해하기 쉽게 해주세요'
  });
  console.log(result.content);

  // Discord로 전송
  await client.send('discord', 'CHANNEL_ID', 'API에서 보낸 메시지');

  // 사용 가능한 모델 조회
  const models = await client.getModels();
  console.log('사용 가능한 모델:', models);
}

main().catch(console.error);

7. 서드파티 시스템 통합

7.1 CRM 시스템 통합

다음 예시는 OpenClaw를 CRM에 통합하여 고객 피드백을 자동 분석합니다:

# crm_integration.py
from openclaw_client import OpenClawClient

client = OpenClawClient(token="oc_tok_xxx")

def analyze_customer_feedback(feedback_text, customer_id):
    """고객 피드백을 분석하고 분류합니다"""
    result = client.chat(
        f"다음 고객 피드백을 분석하여 JSON 형식으로 결과를 반환해 주세요:\n\n{feedback_text}",
        options={
            "systemPrompt": """당신은 고객 피드백 분석 전문가입니다. 피드백을 분류하여 JSON으로 반환해 주세요:
{
  "sentiment": "positive/neutral/negative",
  "category": "제품 품질/고객 서비스/가격/배송/기타",
  "urgency": "high/medium/low",
  "summary": "한 줄 요약",
  "suggestedAction": "권장 처리 방법"
}"""
        }
    )
    return result["content"]

# 사용
analysis = analyze_customer_feedback(
    "제품 품질이 너무 안 좋아요. 구매한 지 이틀 만에 고장 났고, 고객 서비스 태도도 매우 나빴습니다!",
    "CUST_001"
)
print(analysis)

7.2 티켓 시스템 통합

# helpdesk_integration.py
def auto_reply_ticket(ticket):
    """자동으로 티켓 초기 응답을 생성합니다"""
    result = client.chat(
        f"티켓 제목: {ticket['title']}\n"
        f"티켓 설명: {ticket['description']}\n"
        f"우선순위: {ticket['priority']}\n\n"
        f"전문적인 초기 응답을 작성해 주세요.",
        options={
            "systemPrompt": "당신은 기술 지원 전문가입니다. 티켓 내용을 바탕으로 초기 답변과 처리 권장사항을 제공하세요. 응답은 전문적이고 친절해야 합니다."
        }
    )

    return {
        "reply": result["content"],
        "auto_generated": True,
        "model": result.get("model"),
        "needs_review": ticket["priority"] == "high"
    }

7.3 자체 구축 웹 프론트엔드

<!-- 간단한 웹 채팅 프론트엔드 -->
<!DOCTYPE html>
<html>
<head>
  <title>AI 어시스턴트</title>
  <style>
    #chat { max-width: 600px; margin: 0 auto; padding: 20px; }
    .message { margin: 10px 0; padding: 10px; border-radius: 8px; }
    .user { background: #e3f2fd; text-align: right; }
    .assistant { background: #f5f5f5; }
    #input-area { display: flex; gap: 10px; margin-top: 20px; }
    #message-input { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
    button { padding: 10px 20px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; }
  </style>
</head>
<body>
  <div id="chat">
    <h2>AI 어시스턴트</h2>
    <div id="messages"></div>
    <div id="input-area">
      <input id="message-input" placeholder="메시지를 입력하세요..." />
      <button onclick="sendMessage()">전송</button>
    </div>
  </div>

  <script>
    const API_URL = 'http://localhost:18789/api/v1/chat';
    const TOKEN = 'oc_tok_a1b2c3d4e5f6g7h8i9j0';
    let conversationId = null;

    async function sendMessage() {
      const input = document.getElementById('message-input');
      const message = input.value.trim();
      if (!message) return;

      appendMessage('user', message);
      input.value = '';

      try {
        const resp = await fetch(API_URL, {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${TOKEN}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            message,
            conversationId,
            model: 'claude-sonnet-4-20250514'
          })
        });

        const data = await resp.json();
        conversationId = data.conversationId;
        appendMessage('assistant', data.content);
      } catch (error) {
        appendMessage('assistant', '오류가 발생했습니다: ' + error.message);
      }
    }

    function appendMessage(role, content) {
      const div = document.createElement('div');
      div.className = `message ${role}`;
      div.textContent = content;
      document.getElementById('messages').appendChild(div);
      div.scrollIntoView({ behavior: 'smooth' });
    }

    document.getElementById('message-input')
      .addEventListener('keydown', e => { if (e.key === 'Enter') sendMessage(); });
  </script>
</body>
</html>

8. API 속도 제한

8.1 속도 제한 설정

{
  "api": {
    "rateLimit": {
      // 전역 제한
      "global": {
        "windowMs": 60000,    // 1분 윈도우
        "maxRequests": 120    // 최대 120회
      },
      // Token별 제한
      "perToken": {
        "windowMs": 60000,
        "maxRequests": 60
      }
    }
  }
}

8.2 속도 제한 오류 처리

속도 제한이 발동되면 API가 429 Too Many Requests를 반환합니다:

{
  "error": "rate_limit_exceeded",
  "message": "요청이 너무 빈번합니다. 잠시 후 다시 시도해 주세요",
  "retryAfter": 30
}

클라이언트에서 재시도 로직을 구현합니다:

import time

def chat_with_retry(client, message, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.chat(message)
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:
                retry_after = int(e.response.headers.get('Retry-After', 30))
                print(f"속도 제한 발동, {retry_after}초 후 재시도...")
                time.sleep(retry_after)
            else:
                raise
    raise Exception("최대 재시도 횟수 초과")

9. 보안 모범 사례

조치 설명
HTTPS 사용 프로덕션 환경에서는 반드시 Nginx 리버스 프록시를 통해 HTTPS 활성화
Token 권한 최소화 필요한 scope만 부여
IP 화이트리스트 API 접근 출처 제한
입력 검증 사용자 입력 정리, 프롬프트 인젝션 방지
로그 감사 모든 API 호출 기록
정기 Token 교체 90일마다 교체
{
  "api": {
    "security": {
      // IP 화이트리스트
      "allowedIPs": ["10.0.0.0/8", "192.168.1.0/24"],
      // CORS 설정
      "cors": {
        "enabled": true,
        "origins": ["https://your-app.com"],
        "methods": ["GET", "POST", "DELETE"]
      },
      // 요청 본문 크기 제한
      "maxBodySize": "1MB"
    }
  }
}

10. 디버깅 및 문제 해결

# API 요청 로그 확인
openclaw logs | grep -i "api\|request\|response"

# verbose curl로 디버깅
curl -v -X POST http://localhost:18789/api/v1/chat \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message":"test"}'

# API 서비스 상태 확인
curl -s http://localhost:18789/health | jq .

위의 API 통합 방안을 통해 OpenClaw의 AI 기능을 기존 시스템에 원활하게 통합하여 강력한 지능형 애플리케이션을 구축할 수 있습니다.

OpenClaw는 무료 오픈소스 개인 AI 어시스턴트로, WhatsApp, Telegram, Discord 등 다양한 플랫폼을 지원합니다