TwexAPI で XChat Group に DM を送信する方法
DM workflow は 1:1 だけではありません。support team が既存 customer group に update を送る、creator program が campaign group と素材を確認する、operations team が既存 XChat conversation に承認済み update を送る、といった場面があります。
TwexAPI の Send DM (XChat v3) endpoint は、recipient に既存 group ID を渡すことでこの用途に対応します。group ID は g... で始まる必要があります。この endpoint は既存 group に送信しますが、新しい group を作成したり member を追加したりはしません。
Group DM は 1:1 より risk が高いです。1 つの mistake が複数人に届くため、group 確認、copy approval、1 回だけ送信、返却 message object の保存という流れが安全です。
Endpoint and Group Recipient
POST /v3/twitter/send-dm を使い、recipient に既存 XChat group ID を指定します。
| Field | Type | Required | Notes |
|---|---|---|---|
recipient | string | Yes | g... で始まる既存 group ID。 |
text | string | Yes | group message body。group purpose に合う内容にします。 |
cookie | string | Yes | 送信 account の X authentication cookie または auth_token。 |
media_urls | string array or null | No | 画像 1 枚の public URL。最大 1 件。video_url と同時指定不可。 |
video_url | string or null | No | 動画 1 本の public URL。media_urls と同時指定不可。 |
pin | string or null | No | Identity PIN。API default は 1234。 |
proxy | string or null | No | 送信 session 用の optional HTTP proxy。 |
Docs では $0.0025 per call とされています。重要なのは cost よりも、group message が intentional で audit 可能であることです。
Basic Group Message Request
curl --request POST \
--url https://api.twexapi.io/v3/twitter/send-dm \
--header "Authorization: Bearer $TWEXAPI_BEARER_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"recipient": "g1234567890",
"text": "Launch group update: export fix is live. Please rerun the latest report before sharing screenshots.",
"cookie": "auth_token=...; ct0=...; twid=...",
"pin": "1234"
}'成功すると data に message object が返ります。
1{
2 "code": 200,
3 "msg": "success",
4 "data": {
5 "id": "1234567890123456789",
6 "text": "Launch group update: export fix is live.",
7 "time": "2024-01-01T12:00:00Z",
8 "attachment": null,
9 "attachments": null,
10 "sequence_id": "2076661179334979585",
11 "sender_id": "1234567890123456789",
12 "recipient_id": "g1234567890"
13 }
14}返却された id、sequence_id、sender_id、recipient_id、time、raw response を保存します。group workflow では、g... ID がどの group を指すか分かる internal group label も保存します。
Python Group Sender
下の script は group mapping と approval state を必須にしています。form から arbitrary group ID を受け取り、そのまま送信する設計にはしていません。
1import os
2from datetime import datetime, timezone
3from typing import Any
4
5import requests
6
7API_URL = "https://api.twexapi.io/v3/twitter/send-dm"
8BEARER_TOKEN = os.environ["TWEXAPI_BEARER_TOKEN"]
9X_COOKIE = os.environ["TWEXAPI_X_COOKIE"]
10XCHAT_PIN = os.environ.get("TWEXAPI_XCHAT_PIN", "1234")
11PROXY = os.environ.get("TWEXAPI_PROXY")
12
13GROUPS = {
14 "launch_ops": "g1234567890",
15 "creator_review": "g9876543210",
16}
17
18def resolve_group_id(group_key: str) -> str:
19 group_id = GROUPS.get(group_key)
20 if not group_id:
21 raise ValueError(f"Unknown group key: {group_key}")
22 if not group_id.startswith("g"):
23 raise ValueError("XChat group recipient must start with 'g'")
24 return group_id
25
26def validate_group_message(text: str) -> str:
27 cleaned = text.strip()
28 if not cleaned:
29 raise ValueError("text is required")
30 return cleaned
31
32def send_group_dm(
33 group_key: str,
34 text: str,
35 *,
36 media_urls: list[str] | None = None,
37 video_url: str | None = None,
38) -> dict[str, Any]:
39 if media_urls and video_url:
40 raise ValueError("media_urls and video_url are mutually exclusive")
41 if media_urls and len(media_urls) > 1:
42 raise ValueError("media_urls supports at most one image")
43
44 payload: dict[str, Any] = {
45 "recipient": resolve_group_id(group_key),
46 "text": validate_group_message(text),
47 "cookie": X_COOKIE,
48 "pin": XCHAT_PIN,
49 }
50
51 if media_urls:
52 payload["media_urls"] = media_urls
53 if video_url:
54 payload["video_url"] = video_url
55 if PROXY:
56 payload["proxy"] = PROXY
57
58 response = requests.post(
59 API_URL,
60 headers={
61 "Authorization": f"Bearer {BEARER_TOKEN}",
62 "Content-Type": "application/json",
63 "Accept": "application/json",
64 },
65 json=payload,
66 timeout=60,
67 )
68 response.raise_for_status()
69 data = response.json()
70
71 if data.get("code") != 200:
72 raise RuntimeError(data.get("msg", "TwexAPI returned an error"))
73
74 return data
75
76def normalize_group_send(
77 draft: dict[str, Any],
78 response: dict[str, Any],
79) -> dict[str, Any]:
80 message = response.get("data") or {}
81 return {
82 "draft_id": draft["draft_id"],
83 "group_key": draft["group_key"],
84 "group_id": resolve_group_id(draft["group_key"]),
85 "message_id": message.get("id"),
86 "sequence_id": message.get("sequence_id"),
87 "sender_id": message.get("sender_id"),
88 "recipient_id": message.get("recipient_id"),
89 "sent_at": message.get("time") or datetime.now(timezone.utc).isoformat(),
90 "api_code": response.get("code"),
91 "api_msg": response.get("msg"),
92 "raw_response": response,
93 }
94
95if __name__ == "__main__":
96 draft = {
97 "draft_id": "launch-group-update-2026-07-16",
98 "group_key": "launch_ops",
99 "text": "Launch group update: export fix is live. Please rerun the latest report before sharing screenshots.",
100 "status": "approved",
101 }
102
103 if draft["status"] != "approved":
104 raise ValueError("Group DM draft must be approved before sending")
105
106 result = send_group_dm(draft["group_key"], draft["text"])
107 print(normalize_group_send(draft, result))trusted server environment で実行します。
TWEXAPI_BEARER_TOKEN="your_twexapi_bearer_token" \
TWEXAPI_X_COOKIE="auth_token=...; ct0=...; twid=..." \
TWEXAPI_XCHAT_PIN="1234" \
python send_xchat_group_dm.pyGROUPS map は明示的にしておきます。production では database から読み込んでもよいですが、approved internal group record だけを許可します。
Attach One Image or One Video
Group DM でも media rules は同じです。media_urls で画像 1 枚、または video_url で動画 1 本を送ります。両方は同時に使えません。
image_result = send_group_dm(
"launch_ops",
"Final launch checklist screenshotです。",
media_urls=["https://example.com/checklist.jpg"],
)video_result = send_group_dm(
"creator_review",
"Revised brief の short walkthrough です。",
video_url="https://example.com/brief-walkthrough.mp4",
)stable public URL を使い、full signed URL を log に残さないでください。media が sensitive な場合、security model が明確に許可していない限り public link 経由にしない方が安全です。
Group DM Workflow
Group message は 1:1 message より強い review path を持たせます。
- Group registry: approved group ID ごとに internal label、owner、purpose、allowed sender accounts を保存します。
- Draft: raw
g...value ではなく group key に対して message を作成します。 - Review: group purpose、message text、media choice、sender account を確認します。
- Send once: approved draft に対して
POST /v3/twitter/send-dmを 1 回だけ呼びます。 - Record: returned message object、group key、group ID、sender account、raw response を保存します。
- Follow up: message を project、ticket、campaign、customer record に紐づけます。
これにより、最も多い group DM accident、つまり正しい message を間違った group に送る問題を防ぎやすくなります。
Store Group Send State
| Field | Why it matters |
|---|---|
draft_id | sent message を approved draft に戻せます。 |
group_key | 人が読める internal group label。 |
group_id | XChat の g... recipient value。 |
sender_account_id | どの X account が送信したかを識別します。 |
message_text | approved text を保存します。 |
attachment_kind | no media、image、video を追跡します。 |
message_id | 返却された XChat message ID。 |
sequence_id | 後続の conversation pagination に使える場合があります。 |
sent_at | audit と timeline review に使います。 |
status | approved、sent、failed、needs_review を追跡します。 |
raw_response | original API evidence を保存します。 |
この table に full account cookie を保存しないでください。credential reference を保存します。
Common Mistakes
POST /v3/twitter/send-dmを group creation endpoint として扱う。これは既存 group への送信専用です。- unreviewed form から raw
g...ID を受け取り即送信する。 - group purpose に合わない generic broadcast message を送る。
media_urlsが画像 1 枚だけであることを忘れる。media_urlsとvideo_urlを同時に渡す。- group がすでに message を受け取ったか確認せず unknown timeout を retry する。
unknown failure では、retry 前に group conversation または message history を確認します。2 回目の送信は duplicate update になる可能性があります。
Wrap-up
TwexAPI で XChat group に DM を送るには、POST /v3/twitter/send-dm を呼び、recipient を既存の g... group ID にします。強い production setup は、group registry、message approval、approved draft ごとの 1 回送信、returned message object の durable storage です。
これにより、group messaging を useful に保ちながら uncontrolled broadcasting を避けられます。