ClawOps Docs

Python SDK

ClawOps Voice API의 공식 Python SDK 가이드. 동기 및 비동기 클라이언트, 타입 힌트, 자동 페이지네이션을 지원합니다.

Python SDK

ClawOps Voice API의 공식 Python SDK입니다. 동기 및 비동기 클라이언트를 모두 지원하며, 타입 힌트와 자동 페이지네이션을 제공합니다.

설치

pip install clawops

Python 3.10 이상이 필요합니다. SDK는 모든 메서드에 타입 힌트와 docstring을 제공하므로, IDE 자동완성으로 파라미터와 타입을 확인할 수 있습니다.

클라이언트 초기화

동기 클라이언트

from clawops import ClawOps

client = ClawOps(
    api_key="sk_...",
    account_id="AC1a2b3c4d",
)

비동기 클라이언트

from clawops import AsyncClawOps

async with AsyncClawOps(api_key="sk_...", account_id="AC1a2b3c4d") as client:
    call = await client.calls.create(
        to="01012345678",
        from_="07052358010",
        url="https://my-app.com/twiml",
    )

환경변수

SDK는 다음 환경변수를 자동으로 읽습니다. 클라이언트 생성 시 직접 지정한 값이 환경변수보다 우선합니다.

환경변수설명기본값
CLAWOPS_API_KEYAPI 키(필수)
CLAWOPS_ACCOUNT_ID계정 ID(필수)
CLAWOPS_BASE_URLAPI 기본 URLhttps://api.claw-ops.com
export CLAWOPS_API_KEY="sk_live_..."
export CLAWOPS_ACCOUNT_ID="AC1a2b3c4d"
# 환경변수가 설정되어 있으면 인자 생략 가능
from clawops import ClawOps
client = ClawOps()

Calls (통화)

아웃바운드 전화 발신, 통화 목록 조회, 단건 조회, 통화 종료를 지원합니다.

calls.create()

call = client.calls.create(
    to="01012345678",
    from_="07052358010",
    url="https://my-app.com/twiml",
    status_callback="https://my-app.com/status",
    status_callback_event="initiated ringing answered completed",
)
print(call.call_id)  # "CA..."

calls.list()

page = client.calls.list(status="completed", page=0, page_size=50)
for call in page.data:
    print(call.call_id, call.status)

calls.get()

call = client.calls.get("CA1a2b3c4d5e6f7890")
print(call.status, call.direction)

calls.update()

진행 중인 통화를 종료합니다. 현재 status="completed"만 지원합니다.

result = client.calls.update("CA1a2b3c4d5e6f7890", status="completed")

calls.get_transcript()

통화 전사 상태를 조회합니다. status 필드로 completed / pending / failed / not_requested 를 구분하고, completed 일 때는 segments 배열까지 inline 으로 반환합니다.

state = client.calls.get_transcript("CA1a2b3c4d5e6f7890")

if state.status == "completed":
    for seg in state.segments or []:
        print(f"[{seg.speaker}] {seg.start:.2f}s  {seg.text}")
elif state.status == "pending":
    print(f"진행 중 (시작: {state.started_at})")
elif state.status == "failed":
    print(f"실패 ({state.stage}): {state.error}")

전사 완료/실패 시점의 알림은 Transcript Webhook 으로 받습니다.

calls.request_transcript()

조직 설정 "통화 받아쓰기" 가 꺼져 있어도 특정 통화 1 건에 대해 전사를 명시 요청합니다. 재실행 금지 — 이미 요청된 통화는 ConflictError 가 발생합니다. 전사된 오디오 길이만큼 사용량 기반으로 과금됩니다.

from clawops import ConflictError

try:
    result = client.calls.request_transcript("CA1a2b3c4d5e6f7890")
    print(result.status, result.call_id)  # "pending", "CA..."
except ConflictError:
    # 이미 요청됨 — get_transcript 로 현재 상태 확인
    state = client.calls.get_transcript("CA1a2b3c4d5e6f7890")

calls.get_summary()

통화 요약 상태를 조회합니다. status 필드로 completed / pending / failed / not_requested 를 구분하고, completed 일 때는 result_json (조직 스키마에 맞춘 JSON) 까지 inline 으로 반환합니다.

summary = client.calls.get_summary("CA1a2b3c4d5e6f7890")

if summary.status == "completed":
    data = summary.result_json or {}
    print(data.get("coreSummary"))
    for d in data.get("decisions", []):
        print(f"- 결정: {d}")
elif summary.status == "failed":
    print(f"실패: {summary.failed_reason}")

요약 완료/실패 시점의 알림은 Summary Webhook 으로 받습니다.

Call Transfer (통화 전환)

AI Agent에서 통화 중 다른 번호로 전환할 수 있습니다. BuiltinTool.TRANSFER_CALL을 활성화하면 AI가 자동으로 전환하거나, call.transfer()로 직접 호출할 수 있습니다.

from clawops.agent import ClawOpsAgent, OpenAIRealtime, BuiltinTool

agent = ClawOpsAgent(
    from_="07012341234",
    session=OpenAIRealtime(system_prompt="고객 문의 후 상담원에게 전환하세요."),
    builtin_tools=[BuiltinTool.HANG_UP, BuiltinTool.TRANSFER_CALL],
)

Blind Transfer

await call.transfer("01012345678")

Warm Transfer

await call.transfer("01012345678", mode="warm", whisper="VIP 고객입니다.")

전환 후 AI 복귀

await call.transfer("01012345678", after_transfer="return")

자세한 파라미터와 활용 예시는 Voice Agent 문서를 참고하세요.

Messages (메시지)

SMS, MMS, RCS, 카카오 메시지 발송 및 조회를 지원합니다.

messages.create()

SMS (단문, 200byte 이하)

msg = client.messages.create(
    to="01012345678",
    from_="07052358010",
    body="안녕하세요, ClawOps입니다.",
)
print(msg.message_id)

LMS (장문, 2000자 이하, 첨부 없음)

msg = client.messages.create(
    to="01012345678",
    from_="07052358010",
    body="긴 내용의 메시지입니다...",
    type="mms",
    subject="알림",
)

MMS (이미지 첨부, 최대 3개)

msg = client.messages.create(
    to="01012345678",
    from_="07052358010",
    body="사진을 보내드립니다.",
    type="mms",
    subject="제목",
    media_url=["https://example.com/photo.jpg"],
)
print(msg.num_media)   # 1
print(msg.media_url)   # ["https://example.com/photo.jpg"]

media_url에는 jpg, jpeg, png, bmp 이미지만 지원되며 장당 300KB 이하여야 합니다. type="mms"일 때만 사용할 수 있습니다.

messages.list()

page = client.messages.list(type="sms", status="sent", page=0)
for msg in page.data:
    print(msg.message_id, msg.status)

messages.get()

msg = client.messages.get("MG0123456789abcdef")
print(msg.status, msg.body)

Numbers (전화번호)

PSTN 번호 발급, 목록 조회, 설정 수정, 삭제를 지원합니다.

numbers.create()

number = client.numbers.create(webhook_url="https://my-app.com/voice")
print(number.phone_number)

numbers.list()

numbers = client.numbers.list()
for n in numbers:
    print(n.phone_number, n.webhook_url)

numbers.update()

updated = client.numbers.update(
    "07052358010",
    webhook_url="https://my-app.com/new-voice",
    webhook_method="POST",
)

numbers.delete()

client.numbers.delete("07052358010")

WebhookLogs (웹훅 로그)

page = client.webhook_logs.list("WH_abc123", page=0, page_size=20)
for log in page.data:
    print(log)

페이지네이션

calls.list(), messages.list(), webhook_logs.list()SyncPage (또는 AsyncPage) 객체를 반환합니다.

자동 페이지네이션

for call in client.calls.list().auto_paging_iter():
    print(call.call_id)

수동 페이지네이션

page = client.calls.list(page=0, page_size=10)
print(f"전체 {page.meta.total}건 중 {len(page.data)}건 조회")

while page.has_next_page():
    page = page.next_page()
    for call in page.data:
        print(call.call_id)

비동기 페이지네이션

page = await client.calls.list()
async for call in page.auto_paging_iter():
    print(call.call_id)

에러 처리

SDK는 계층적 예외 구조를 제공합니다. 모든 예외는 ClawOpsError를 상속합니다.

ClawOpsError
  APIError
    APIStatusError
      BadRequestError          (400)
      AuthenticationError      (401)
      PermissionDeniedError    (403)
      NotFoundError            (404)
      ConflictError            (409)
      RateLimitError           (429)
      UnprocessableEntityError (422)
      InternalServerError      (500)
      ServiceUnavailableError  (503)
    APIConnectionError
      APITimeoutError
    APIResponseValidationError
from clawops import ClawOps, AuthenticationError, NotFoundError, APIStatusError

client = ClawOps()

try:
    call = client.calls.get("CA_invalid_id")
except NotFoundError as e:
    print(f"통화를 찾을 수 없습니다: {e.message}")
except AuthenticationError:
    print("API 키가 유효하지 않습니다.")
except APIStatusError as e:
    print(f"API 에러: HTTP {e.status_code} - {e.message}")

동시 통화 한도 초과 (429)

구독 플랜의 동시 통화 한도를 초과하면 RateLimitError가 발생합니다. 자세한 내용은 동시 통화 한도 문서를 참고하세요.

from clawops import ClawOps, RateLimitError

client = ClawOps()

try:
    call = client.calls.create(
        to="01012345678",
        from_="07052358010",
        url="https://my-app.com/twiml",
    )
except RateLimitError as e:
    print(f"동시 통화 한도 초과: {e.message}")
    # 진행 중인 통화가 종료된 후 재시도하세요

SDK의 자동 재시도는 429 응답에도 동작하지만, 동시 통화 한도 초과는 진행 중인 통화가 종료되어야 해소되므로 RateLimitError를 명시적으로 처리하는 것을 권장합니다.

자동 재시도

SDK는 일시적 오류(408, 409, 429, 5xx)에 대해 지수 백오프로 자동 재시도합니다. 기본 최대 재시도 횟수는 2회입니다.

client = ClawOps(max_retries=5)   # 재시도 횟수 변경
client = ClawOps(max_retries=0)   # 재시도 비활성화

Webhook 서명 검증

from clawops import ClawOps, WebhookVerificationError

client = ClawOps()

@app.route("/webhook", methods=["POST"])
def webhook():
    try:
        client.webhooks.verify(
            url="https://my-app.com/webhook",
            params=request.form.to_dict(),
            signature=request.headers["X-Signature"],
            signing_key="your_signing_key",
        )
    except WebhookVerificationError:
        return "Unauthorized", 401

    call_id = request.form["CallId"]
    ...

멀티 계정 접근

client = ClawOps(api_key="sk_...", account_id="AC_main")

# 기본 계정의 리소스
client.calls.list()

# 다른 계정의 리소스
other = client.accounts("AC_other")
other.calls.list()
other.numbers.create()
other.messages.create(to="01012345678", from_="07052358010", body="Hello")

타임아웃 설정

import httpx
from clawops import ClawOps

# 단순 타임아웃 (초)
client = ClawOps(timeout=30.0)

# 세밀한 타임아웃 설정
client = ClawOps(timeout=httpx.Timeout(timeout=60.0, connect=5.0))

# 개별 요청에서 오버라이드
call = client.calls.create(
    to="01012345678",
    from_="07052358010",
    url="https://my-app.com/twiml",
    timeout=10.0,
)

모든 리소스 메서드는 extra_headers, extra_query, timeout 파라미터를 공통으로 지원합니다.