API連携
API サーバー実装ガイド
AI ボットと相性の良い API を作るためのルールとサンプルコードです。既存の API をそのまま接続しても構いませんが、ボット専用エンドポイントを分けるとセキュリティと応答の質が上がります。
#応答の規約
- リクエスト・応答は JSON(
Content-Type: application/json)。 - HTTP 2xx で、本文に
ok: falseがなければ成功とみなします。 - 失敗は
{ "ok": false, "error": "コード", "message": "お客様に説明する文" }。AI はmessageを根拠にお客様へ状況を説明します。 - 応答は AI が答えるのに必要なフィールドだけ。ステータスコードの横に人が読むラベル(
status_label)を付けると、回答が正確になります。 - 日付は ISO 8601 + タイムゾーン(
2026-09-20T10:12:00+09:00)。
{
"ok": true,
"order": {
"order_no": "DM-10001",
"status": "shipped",
"status_label": "配送中",
"items": [{ "name": "タンブラー 473ml", "qty": 1 }],
"total": 24900,
"shipping": {
"carrier": "CJ大韓通運",
"tracking_no": "689123456789",
"tracking_url": "https://trace.cjlogistics.com/…"
}
}
}#サーバーセキュリティのチェックリスト
- キーの検証 — すべてのリクエストでヘッダーのキーを確認し、一致しなければ 401。
- 所有権の再確認 —
/customers/{id}/orders/{no}で、その注文が本当にそのお客様のものかをサーバーで確認。処理(dry_run:false)のときも再度確認。 - 最小データ — 住所・電話番号は全体ではなく一部をマスク(ソウル市江南区 ***)。
- 冪等性 — キャンセルのように 2 回呼ばれ得る処理は、2 回目も同じ結果を返すか、「すでに処理済み」を返してください。
- IP 制限 — 可能なら Cliptalk サーバーの IP だけを許可。
- ログ — 誰(顧客 ID)が何をしたかを残し、キーはログに残さないでください。
#例: Node.js(Express)
本人確認・注文照会・キャンセル(dry_run)の 3 エンドポイントを持つ最小例です。右のコードをコピーし、DB の部分だけ差し替えてください。
import express from 'express';
const app = express();
app.use(express.json());
// 1) キー検証
app.use('/chatbot', (req, res, next) => {
if (req.get('x-api-key') !== process.env.CHATBOT_API_KEY) {
return res.status(401).json({ ok: false, error: 'unauthorized', message: '認証に失敗しました' });
}
next();
});
// 2) 本人確認
app.post('/chatbot/verify', async (req, res) => {
const { order_no, phone_last4 } = req.body;
const order = await db.orders.findOne({ order_no });
if (!order || order.phone.slice(-4) !== phone_last4) {
return res.json({ ok: false, error: 'not_found',
message: '注文番号と連絡先が一致する注文がありません。' });
}
const c = await db.customers.findById(order.customer_id);
res.json({ ok: true, customer: { id: c.id, name: c.name } });
});
// 3) 注文照会(所有権の確認)
app.get('/chatbot/customers/:cid/orders/:no', async (req, res) => {
const o = await db.orders.findOne({ order_no: req.params.no, customer_id: req.params.cid });
if (!o) return res.status(404).json({ ok: false, error: 'order_not_found', message: '注文が見つかりません。' });
res.json({ ok: true, order: toChatbotView(o) });
});
// 4) キャンセル(dry_run 対応)
app.post('/chatbot/customers/:cid/orders/:no/cancel', async (req, res) => {
const o = await db.orders.findOne({ order_no: req.params.no, customer_id: req.params.cid });
if (!o) return res.status(404).json({ ok: false, error: 'order_not_found', message: '注文が見つかりません。' });
if (!['paid', 'preparing'].includes(o.status)) {
return res.json({ ok: false, error: 'not_cancellable', message: 'すでに発送済みのためキャンセルできません。' });
}
const summary = [`注文番号: ${o.order_no}`, `返金予定: ${o.total.toLocaleString()}ウォン`];
if (req.body.dry_run) return res.json({ ok: true, dry_run: true, summary });
await cancelOrder(o);
res.json({ ok: true, status: 'cancelled', message: `注文 ${o.order_no} をキャンセルしました。` });
});
app.listen(3000);#Cliptalk が付けるエラーコード
| error | 意味 |
|---|---|
http_<状態> | API が 2xx 以外の状態を返した(例: http_404) |
bad_json | 応答が JSON ではない |
timeout | 制限時間を超過 |
connector_unreachable | DNS・接続・SSL エラー |
blocked_address | 内部網 / ローカルアドレスへ解決されるドメイン |
missing_params | 必須パラメータの値がない |
not_verified | 本人確認の前に、顧客情報が必要なオペレーションを呼んだ |
secret_missing | 参照したシークレットが保存されていない |
secret_origin_mismatch | シークレットが別の API アドレス用に保存されている(アドレス変更後は再入力) |