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) 때도 다시 확인. - 최소 데이터 — 전체 주소·전화번호 대신 일부 마스킹(서울시 강남구 ***).
- 멱등성 — 취소처럼 두 번 호출될 수 있는 처리는 두 번째 호출에서 같은 결과를 주거나 "이미 처리됨" 을 돌려주세요.
- IP 제한 — 가능하면 클립톡 서버 IP 만 허용.
- 로그 — 누가(고객 ID) 무엇을 했는지 남기고, 키는 로그에 남기지 마세요.
#예제: Node.js (Express)
본인확인·주문 조회·취소(dry_run) 세 개의 엔드포인트를 가진 최소 예제입니다. 오른쪽 코드를 그대로 복사해 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);#클립톡이 붙이는 오류 코드
| 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 주소용으로 저장됨 (주소 변경 후 재입력 필요) |