퍼블릭 API 및 웹훅
REST API를 사용하면 귀하의 CRM, 사이트 백엔드 또는 자체 봇이 cenaly.com의 리드와 대화를 패널과 동일하게 다룰 수 있습니다. 카드 조회, 속성 변경, 이벤트 전송, 대화 응답이 가능합니다. 반대로 웹훅은 새로운 리드, 이벤트, 메시지를 귀하의 서버에 직접 알려줍니다.
시작에 필요한 것#
| 항목 | 위치 |
|---|---|
| 역할 | 계정 소유자 — 키는 소유자 명의로 작동하며, 직원은 키를 볼 수 없습니다 |
| 섹션 | 활성화된 채팅(리드와 대화가 여기에 있습니다) |
| 키 | 통합 → API 및 웹훅 → 키 → 키 생성 |
| API 주소 | https://api.cenaly.com/public-api/v1 |
| 메서드 레퍼런스 | api.cenaly.com/public-api/v1/docs |
| OpenAPI 3.1 | api.cenaly.com/public-api/v1/openapi.json |
1단계. 키와 권한#
- 통합을 열고 페이지 상단에서 API 및 웹훅을 클릭합니다.
- 키 탭에서 키 생성을 클릭하고 이름을 입력합니다("CRM", "사이트 백엔드", "Telegram 봇").
- 권한 세트를 선택하거나 권한을 하나씩 체크합니다. 지점이 여러 개인 경우 키가 허용된 지점과 만료 기간을 지정합니다.
- 시크릿
cnk_…를 복사합니다. 한 번만 표시됩니다. 당사에는 지문(SHA-256)만 저장됩니다.
| 권한 | 제공 기능 |
|---|---|
contacts:read |
리드 목록, 검색, 카드, 속성 |
contacts:write |
속성 및 태그 변경, 병합, 정보주체 요청에 따른 삭제 |
events:read |
리드 이벤트 피드 |
events:write |
이벤트 전송(구매, 가입, 기타 자체 이벤트) |
conversations:read |
대화 및 메시지 |
conversations:write |
상담원 답변, 메모, 상태, 담당자, 라벨 |
messages:send |
리드의 마지막 대화로 메시지 전송 |
segments:read |
저장된 세그먼트 및 구성원 |
세트: CRM / 백엔드(리드, 속성, 이벤트, 대화 및 세그먼트 읽기), 자체 봇(리드 읽기, 대화 응답, 리드에 메시지 쓰기), 읽기 전용, 전체.
- 권한은 키의 권한과 지점을 즉시 변경합니다. 통합은 계속 작동합니다.
- 재발급은 새 시크릿을 발급합니다. 기존 시크릿은 즉시 작동을 멈추고 권한과 로그는 유지됩니다.
- 폐기는 즉시 영구적으로 적용되며, 다음 요청은
401을 받습니다.
키는 Authorization: Bearer cnk_… 헤더(또는 X-Api-Key: cnk_…)로 전달됩니다. 키는 서버에만 보관하세요. 브라우저나 모바일 앱에 두지 마세요.
2단계. 첫 요청#
KEY=cnk_ваш_ключ
API=https://api.cenaly.com/public-api/v1
# что умеет ключ: права, точки, лимит
curl -H "Authorization: Bearer $KEY" "$API/me"
# лиды, свежие сверху (20 на страницу)
curl -H "Authorization: Bearer $KEY" "$API/contacts?limit=20"
# поиск по имени, почте или телефону
curl -H "Authorization: Bearer $KEY" "$API/contacts?q=anna"
응답은 항상 봉투 형식입니다.
{
"meta": { "status": 200, "requestId": "…", "nextCursor": "WyIyMDI2LTEwLTA0…" },
"data": { "total": 128, "contacts": [ { "id": "…", "status": "lead", "name": "Анна", "updatedAt": "2026-10-04T09:12:00Z" } ] }
}
다음 페이지는 cursor=<meta.nextCursor>를 붙인 동일한 요청입니다. nextCursor가 없으면 더 이상 페이지가 없습니다. 오류는 {"meta": {...}, "error": {"code": "…"}}입니다.
지점. 키에 지점 하나만 허용된 경우 지정할 필요가 없습니다. 여러 개인 경우 location=<지점 id>를 추가하세요(목록은 GET /v1/locations). 그렇지 않으면 400 location_required 응답이 반환됩니다.
자체 user_id로 속성과 이벤트 처리. 당사의 리드 id를 저장할 필요가 없습니다. ?by=user_id를 추가하면 주소가 귀하의 사용자 식별자가 됩니다. 해당 user_id의 리드가 아직 없으면 당사가 생성합니다.
# свойства: операции update_or_create, set_once, add, delete, append, union, exclude
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"ops":[{"op":"update_or_create","key":"$email","value":"anna@example.com"},
{"op":"add","key":"orders","value":1},
{"op":"union","key":"interests","value":"yoga"}]}' \
"$API/contacts/u-1001/props?by=user_id"
# события пачкой (до 100), повтор с тем же id засчитывается один раз
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"events":[{"id":"ord-5531","userId":"u-1001","name":"order_paid","props":{"sum":45,"currency":"GEL"}}]}' \
"$API/events"
# ответ в диалоге и смена статуса
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"text":"Здравствуйте! Заказ передан курьеру."}' "$API/conversations/<id>/reply"
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"status":"resolved"}' "$API/conversations/<id>/status"
쓰기는 패널의 편집과 동일한 큐를 통해 처리됩니다. 변경 사항은 몇 초 내에 적용되며, user_id로 쓰기 요청에 대한 응답은 pending과 함께 202입니다. 리드 카드에서 편집 작성자는 "API: <키 이름>"으로 표시됩니다.
메서드#
| 메서드 | 기능 | 권한 |
|---|---|---|
GET /v1/me, GET /v1/locations |
키, 권한, 지점 | — |
GET /v1/contacts |
리드: status (anon, lead, user), q, limit, cursor |
contacts:read |
GET /v1/contacts/{id} |
카드: 속성, 태그, 이벤트 요약 | contacts:read |
POST /v1/contacts/{id}/props |
속성을 작업으로 변경 (최대 250건) 또는 {"props":{…}} |
contacts:write |
POST / DELETE /v1/contacts/{id}/tags |
리드 태그 | contacts:write |
POST /v1/contacts/merge |
두 카드 병합: 미리보기 후 "apply": true |
contacts:write |
POST /v1/contacts/{id}/erase |
정보주체 요청에 따른 삭제 ("confirm": true) |
contacts:write |
GET /v1/contacts/{id}/events |
리드 이벤트 피드 | events:read |
POST /v1/contacts/{id}/events, POST /v1/events |
이벤트 (한 번에 최대 100건) | events:write |
GET /v1/contacts/{id}/conversations |
리드 대화 | conversations:read |
POST /v1/contacts/{id}/messages |
리드의 마지막 대화에 메시지 보내기 | messages:send |
GET /v1/conversations, /{id}, /{id}/messages |
대화 및 메시지 | conversations:read |
POST /v1/conversations/{id}/reply |
상담원 답변 또는 메모 ("type": "note") |
conversations:write |
POST /v1/conversations/{id}/status, /assign, /tags |
상태, 담당자, 라벨 | conversations:write |
GET /v1/segments, GET /v1/segments/{id}/contacts |
세그먼트 및 구성원 | segments:read |
각 메서드의 필드, 예시, 오류 코드는 레퍼런스에서 확인할 수 있습니다.
3단계. 웹훅#
웹훅은 지점에서 설정합니다: 채팅 → 설정 → 웹훅 — https 주소, 이벤트, 서명 시크릿입니다. 대화 이벤트(새 대화, 새 메시지, 상태, 평가, 라벨)에 리드 이벤트가 추가되었습니다:
| 이벤트 | 발생 시점 |
|---|---|
lead.created |
새 리드가 생겼을 때 (채팅이나 양식에서 연락처를 남기거나 API로 생성한 경우) |
lead.props_changed |
리드 속성이 변경되었을 때 — 변경된 키 목록은 changedProps에 있습니다 |
lead.event_tracked |
리드에 이벤트가 발생했을 때; 기본값은 시스템 이벤트($…)를 제외한 모든 이벤트이며, 이름 목록은 "어떤 이벤트를 보낼지" 필드로 지정합니다 |
message.goal_reached |
리드가 메시지 목표(팝업, 발송)에 도달했을 때 |
{
"event": "lead.event_tracked",
"deliveryId": "WD…",
"at": "2026-10-04T09:12:03Z",
"webhookId": "…",
"locationId": "…",
"contact": { "id": "…", "status": "user", "userId": "u-1001", "props": { "orders": 3 }, "tags": ["vip"] },
"leadEvent": { "name": "order_paid", "at": "2026-10-04T09:12:00Z", "props": { "sum": 45 } }
}
리드의 이름, 이메일, 전화번호는 웹훅에서 "고객 연락처 전달" 체크박스가 켜져 있을 때만 본문에 포함됩니다. 서명 검증은 웹훅 시크릿으로 본문에 HMAC-SHA256을 적용합니다:
# заголовок X-Meni-Signature: sha256=<hex>
echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"
5초 안에 2xx로 응답하세요 — 그렇지 않으면 전송이 반복됩니다. 실패가 길게 이어지면 웹훅이 비활성화되며, 이는 설정에서 확인할 수 있습니다. 중복은 deliveryId로 걸러내세요.
제한#
- 키당 분당 60회 요청; 초과 시
Retry-After헤더와 함께429가 반환됩니다. 남은 횟수는X-RateLimit-*에 있습니다. - 페이지당 최대 200건(기본값 50), 한 묶음에 최대 100건의 이벤트, 요청당 최대 250건의 속성 작업, 한 번에 최대 30개의 태그.
- 계정당 최대 20개의 활성 키.
- 키 호출 로그는 30일간 보관됩니다: 시간, 메서드, 경로 템플릿(id와 본문 제외), 응답 코드, 소요 시간.
- API로 전송한 이벤트는 리드 카드(피드, 요약, 세그먼트)에 기록되지만 방문 분석 원본 로그에는 기록되지 않습니다.
- 리드에게는 기존 대화에만 메시지를 보낼 수 있습니다: 대화가 없으면
409 no_conversation응답이 반환됩니다.
문제 해결#
| 응답 | 의미 | 해결 방법 |
|---|---|---|
401 api_key_missing / api_key_invalid |
요청에 키가 없거나, 키가 잘못되었거나, 폐기되었거나, 만료됨 | 헤더를 확인하고 필요하면 키를 재발급하세요 |
403 scope_required |
키에 필요한 권한이 없음 | 키의 권한에서 권한을 추가하세요 |
400 location_required |
키로 여러 지점에 접근할 수 있음 | location=<id>를 추가하세요 |
404 location_not_found |
키의 지점 목록에 해당 지점이 없음 | GET /v1/locations로 확인하세요 |
404 not_found |
리드 또는 대화가 없음 | 자체 id를 사용하는 경우 ?by=user_id를 사용하세요 |
429 rate_limited |
분당 60회 요청 초과 | Retry-After초만큼 기다리고, 이벤트를 묶음으로 보내세요 |
502 upstream_error |
일시적 오류 | 나중에 요청을 다시 시도하세요 |
모든 요청은 호출 로그 탭에서 응답 코드와 키 이름과 함께 확인할 수 있습니다.
자주 묻는 질문#
브라우저에서 API를 호출할 수 있나요? 아니요: 키는 지점의 모든 리드에 대한 접근 권한을 부여합니다. 자체 서버에서 API를 호출하세요.
user_id는 리드 id와 어떻게 다른가요? 리드 id는 카드의 저희 식별자입니다. user_id는 사용자의 식별자(CRM의 번호, 사이트 계정의 id)이며, ?by=user_id를 사용하면 이 식별자로 카드를 지정할 수 있습니다.
이벤트를 다시 보내면 어떻게 되나요? 같은 id의 이벤트는 한 번만 집계됩니다 — 네트워크 오류 시 요청을 안심하고 반복할 수 있습니다.
지점마다 별도의 키가 필요한가요? 아니요: 하나의 키로 여러 지점을 다룰 수 있으며, 지점은 location 파라미터로 선택합니다.
관련 섹션#
- 고객과의 채팅 — 대화, 리드, 웹훅 설정
- 리드 — 단일 인바운드 흐름
- 자체 PBX의 API와 MCP — 별도의 전화 통신 API
- 통합