> For the complete documentation index, see [llms.txt](https://docs.contiple.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.contiple.com/api/open-api.md).

# Open API

***

## **➊ API 인증 방법**

### **1-1. OPEN API 활성화**

* \[서비스 관리] → \[인증]에서 OPEN API를 활성화해주세요.
* 활성화 시, 서비스 API Key가 자동 생성됩니다.
  * API를 호출하고 전송되는 데이터를 암호화하는 데에 사용하는 인증키입니다.
  * **API Key 변경** 버튼을 클릭하여 변경할 수 있습니다.

***

### **1-2. 인증 Header**

> 각 Request Header에 아래의 값들을 반드시 설정해주세요.

{% hint style="info" %}

* 보안 서비스 기능을 사용하는 서비스의 경우, \[서비스 관리] → \[보안 서비스] → \[스팸 관리] 메뉴에서 고객의 IP를 통한 스팸 정책을 활성화할 수 있습니다.

* &#x20;Open API를 통해 티켓 생성 시, OC-Client-IP 값을 Request Header에 설정하시면 해당 IP를 기준으로 스팸 여부를 판단합니다.
  {% endhint %}

* **Authorization** : 암호화 규칙에 따라 생성된 인증 문자열(아래 1-3 참고)

* **X-TC-Timestamp** : 현재 UTC 시간 값 {new Date().getTime()}

* **Content-Type** : application/json; charset=utf-8

***

### **1-3. 인증키(Authorization) 생성 방법**

<figure><img src="/files/Ceo85SJaUPbKymfTFDJu" alt=""><figcaption></figcaption></figure>

#### **① 아래 규칙에 따라 문자열 생성**

* **조직ID(Organization ID)**
  * NHN Cloud 조직 ID로, \[전체관리 > 계약 서비스 현황 > 조직정보] 화면에서 확인 가능합니다.
  * `AbcdE1fghIj23K4x`와 같은 형태로 구성되어 있습니다.
* **API URI**
  * 호출하려는 API의 URI를 입력합니다. URI에는 서비스ID가 포함되어야 합니다.
  * 예시 : `/yourService/openapi/v1/ticket.json`<br>
* **쿼리 파라미터**
  * 호출 API에 쿼리 파라미터가 존재할 경우, 키(Key) 알파벳 순서로 정렬한 후 값(Value)을 & 기호로 연결합니다. 파라미터가 1개 일 경우 & 기호는 제외하며, 파라미터가 없으면 생략합니다.
  * 예시
    * 쿼리 파라미터가 `page=1&pageSize=10&language=ko` 일 경우,
    * 키(Key)의 알파벳 순으로 정렬 :  `language=ko&page=1&pageSize=10`&#x20;
    * 값(Value)을 `&`기호로 연결 : `ko&1&10`&#x20;
* **타임 스탬프**
  * API를 요청하는 시간의 Timestamp를 적용합니다. 요청 Header로 전송되는 **X-TC-Timestamp** 값과 동일해야 합니다.
  * 예시 : `1764031689401`

#### **② 문자열 암호화**

* 생성한 문자열을 [1-1.Open API 활성화](#id-1-1.-open-api) 에서 생성된 API Key로 암호화하여 인증키를 생성합니다.
* 암호화 방식은 **HmacSHA256** 방식을 사용합니다.

#### ③ 인증키 전송

* 인증키를 요청 Header의 Authorization에 적용하여 Contiple로 전송합니다.
* Contiple은 전달받은 Header와 Body 변수 값을 동일한 API Key로 암호화하여 Hash 값을 비교합니다.
* 동일할 경우 API가 정상적으로 처리되며, 일치하지 않을 경우 오류코드 400을 반환합니다.

***

### **1-4. JAVA 예제**

#### **(1) 일반 요청(GET)**

```
// 유저 티켓 리스트
String URL = "http://yourOrg.oc.nhncloud.com/yourService/openapi/v1/ticket/enduser/usercode/list.json?categoryId=1&language=ko";
String organizationId = "AbcdE1fghIj23K4x"; // OrganizationID
String securityKey = "123456a0bcde12a789b123bc4d1234a1"; // Service API Key
String uri = "/yourService/openapi/v1/ticket/enduser/usercode/list.json"; // Request URI
long timestamp = new Date().getTime();
StringBuilder sb = new StringBuilder();
sb.append(organizationId);
sb.append(uri);
sb.append("1").append("&").append("ko"); // 매개변수 이름의 알파벳 순서에 따라 &기호로 값 연결, 없으면 생략
sb.append(timestamp);// X-TC-Timestamp값과 동일

SecretKeySpec signingKey = new SecretKeySpec(securityKey.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance(signingKey.getAlgorithm());
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(sb.toString().getBytes("UTF-8"));
String authorization = new String(Base64.encodeBase64(rawHmac));

Request request = new Request.Builder().url(URL).get()
.header("Content-Type", "application/json")
.header("Authorization", authorization)
.header("X-TC-Timestamp", Long.toString(timestamp))
.build();
Call call = client.newCall(request);
Response response = call.execute();
```

#### **(2) 일반 요청(POST)**

```
// 티켓 생성
String URL = "http://yourOrg.oc.nhncloud.com/yourService/openapi/v1/ticket.json?language=ko";
String organizationId = "AbcdE1fghIj23K4x"; // OrganizationID
String securityKey = "123456a0bcde12a789b123bc4d1234a1"; // Service API Key
String uri = "/yourService/openapi/v1/ticket.json"; // Request URI
long timestamp = new Date().getTime();
StringBuilder sb = new StringBuilder();

String body = mapper.writeValueAsString(bodyContentObject);

sb.append(organizationId);
sb.append(uri);
sb.append("ko").append("&"); // 매개변수 이름의 알파벳 순서에 따라 &기호로 값 연결, 없으면 생략
sb.append(body);// 매개변수 뒤에 body의 문자열 내용 추가
sb.append(timestamp);// X-TC-Timestamp값과 동일

SecretKeySpec signingKey = new SecretKeySpec(securityKey.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance(signingKey.getAlgorithm());
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(sb.toString().getBytes("UTF-8"));
String authorization = new String(Base64.encodeBase64(rawHmac));

RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body);

Request request = new Request.Builder().url(URL).post(body)
.header("Content-Type", "application/json")
.header("Authorization", authorization)
.header("X-TC-Timestamp", Long.toString(timestamp))
.header("OC-Client-IP", ip)
.build();

Call call = client.newCall(request);
Response response = call.execute();
```

#### **(3) 파일 업로드**

```
String URL = "http://yourOrg.oc.nhncloud.com/yourService/openapi/v1/ticket/attachments/upload.json";
String organizationId = "AbcdE1fghIj23K4x"; // OrganizationID
String securityKey = "123456a0bcde12a789b123bc4d1234a1"; // Service API Key
String uri = "/yourService/openapi/v1/ticket/attachments/upload.json"; // Request URI
StringBuilder sb = new StringBuilder();
sb.append(organizationId);
sb.append(uri);
DigestUtils.appendMd5DigestAsHex(file.getInputStream(), sb);// 파일 첨부 시 파일의 MD5는 파라미터 값으로 인증 문자열에 추가
sb.append(new Date().getTime());// X-TC-Timestamp값과 동일
SecretKeySpec signingKey = new SecretKeySpec(securityKey.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance(signingKey.getAlgorithm());
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(sb.toString().getBytes("UTF-8"));
String authorization = new String(Base64.encodeBase64(rawHmac));
```

#### **(3) 콘티플 측 인증 방법**

```
// Generate authorization string sample with request
StringBuilder sb = new StringBuilder();
sb.append(org.getId()); // organization Id
sb.append(request.getRequestURI()); // request URI
// upload file
if (request instanceof MultipartHttpServletRequest) {
    MultipartHttpServletRequest mpRequest = (MultipartHttpServletRequest) request;
    MultipartFile file = mpRequest.getFile("file");
    DigestUtils.appendMd5DigestAsHex(file.getInputStream(), sb);
// other
} else {
    Map<String, String[]> params = request.getParameterMap();
    if (!params.isEmpty()) {
        // Sort parameter
        Map<String, String[]> treeMap = new TreeMap<>(params);
        for (Map.Entry<String, String[]> entry : treeMap.entrySet()) {
            sb.append(entry.getValue()[0]).append("&");
        }
        sb.deleteCharAt(sb.length() - 1); // Delete '&' character
    }   
    if (request instanceof BodyReaderHttpServletRequestWrapper) {
        BodyReaderHttpServletRequestWrapper requestWrapper = (BodyReaderHttpServletRequestWrapper) request;
        if (requestWrapper.hasBody()) {
            String body = new String(requestWrapper.getBody(), StandardCharsets.UTF_8);
            if (!params.isEmpty()) {
                // params is not empty, add '&' character
                sb.append("&");
            }
            sb.append(body);
        }
    }   
}
String time = request.getHeader("X-TC-Timestamp");
// No '&' character
sb.append(time);

return sb.toString();
```

* **콘티플 인증 실패 시, 결과는 아래와 같이 반환됩니다.**\
  (Authorization 문자열의 암호화 방식이 올바른지 확인해주세요.)

```
{
    "header": {
        "resultCode": 400,
        "resultMessage": "무효한 요청입니다.",
        "isSuccessful": false
    },
    "result": null
}
```

* **인증 실패 상황**

> **400**
>
> 1. Authorization is blank
>
> 2. X-TC-Timestamp is not numeric
>
> 3. X-TC-Timestamp is expired (5분 내 유효)
>
> 4. Multipart request but file is null
>
> 5. Authorization is incorrect
>
> **403**
>
> 1. securityKey is null
> 2. clientIp is not allowed

***

## **➋ 공통 리턴 결과**

### **2-1. 리턴 결과 예제**

```
//성공: 상세
{
    "header": {
        "resultCode": 200,
        "resultMessage": "",
        "isSuccessful": true
    },
    "result": {
        "content": {
        }
    }
}

//성공: 리스트
{
    "header": {
        "resultCode": 200,
        "resultMessage": "",
        "isSuccessful": true
    },
    "result": {
        "contents": [{
        }]
    }
}

//실패
{
    "header": {
        "resultCode": 403,
        "resultMessage": "Access Denied",
        "isSuccessful": false
    },
    "result": null
}
```

***

### **2-2. 리턴 결과 설명**

<table><thead><tr><th width="132">명칭</th><th width="173">변수</th><th width="119">데이터 타입</th><th>설명</th></tr></thead><tbody><tr><td><strong>Header</strong></td><td>resultCode</td><td>Integer</td><td>리턴 결과 Code, 정상은 200</td></tr><tr><td></td><td>resultMessage</td><td>String</td><td>리턴 오류 메시지</td></tr><tr><td></td><td>isSuccessful</td><td>Boolean</td><td>실행 결과(성공: true, 실패: false)</td></tr><tr><td><strong>Result</strong></td><td>contents</td><td>JSON</td><td>목록 결과 내용</td></tr><tr><td></td><td>content</td><td>JSON</td><td>상세 결과 내용</td></tr></tbody></table>

***

### **2-3. 리턴 코드**

> * 200 : SUCCESS
> * 400 : Bad Request
> * 403 : Access Denied(Forbidden)
> * 404 : Not Data Found
> * 500 : Server Error
> * 9007 : 관련된 데이터가 이미 존재
> * 9005 : 관련된 데이터가 없음
> * 1001 : 문의 횟수가 상한을 초과했습니다. 잠시 후 문의해주세요.
>   * \[스팸 관리] → \[반복 문의 차단] 기능 사용 시 작동
>   * 동일 IP로 1분 내에 3번 이상 문의 생성 시도 시, 24시간 동안 티켓 생성 차단
> * 1002 : 문의 횟수가 상한을 초과했습니다. 잠시 후 문의해주세요.
>   * \[스팸 관리] → \[반복 문의 차단] 기능 사용 시 작동
>   * 동일 IP로 24시간 내에 10번 이상 문의 생성 시도 시, 24시간 동안 티켓 생성 차단

***

### **2-4. 리턴 코드(실패) 상세**

> **400**
>
> 1. Authorization is blank
>
> 2. X-TC-Timestamp is not numeric
>
> 3. X-TC-Timestamp is expired (5분 내 유효)
>
> 4. Multipart request but file is null
>
> 5. Authorization is incorrect
>
> **403**
>
> 1. securityKey is null
> 2. clientIp is not allowed

***

## **➌ API 목록**

### **3-1. 개발 환경 URL**

<table><thead><tr><th width="159">환경</th><th>BaseUrl</th></tr></thead><tbody><tr><td>알파</td><td>https://{domain}.oc.alpha-nhncloud.com</td></tr><tr><td>베타</td><td>https://{domain}.oc.beta-nhncloud.com</td></tr><tr><td>리얼</td><td>https://{domain}.oc.nhncloud.com</td></tr></tbody></table>

***

### **3-2. Security Key URL**

<table><thead><tr><th width="276">Security Key</th><th>URL</th></tr></thead><tbody><tr><td>서비스의 Security Key</td><td>/{serviceId}/openapi/v1/*</td></tr><tr><td>인증 없이 직접 사용 가능</td><td>/{serviceId}/api/v2/*</td></tr></tbody></table>

***

### **3-3. API 목록**

<table><thead><tr><th width="122">그룹</th><th width="177">명칭</th><th width="77">유형</th><th>URL</th><th>설명</th></tr></thead><tbody><tr><td><strong>서비스</strong></td><td>서비스 상세</td><td>GET</td><td>/{serviceId}/api/v2/service.json</td><td>서비스 ID를 통해 서비스 정보 조회</td></tr><tr><td><strong>공지사항</strong></td><td>말머리 리스트</td><td>GET</td><td>/{serviceId}/api/v2/notice/categories.json</td><td>공지사항 말머리 리스트 취득</td></tr><tr><td></td><td>태그 리스트</td><td>GET</td><td>/{serviceId}/api/v2/notice/tags.json</td><td>공지사항 태그 리스트 취득</td></tr><tr><td></td><td>공지사항 리스트</td><td>GET</td><td>/{serviceId}/api/v2/notice/list.json</td><td>헬프센터 공지사항 리스트</td></tr><tr><td></td><td>공지사항 상세</td><td>GET</td><td>/{serviceId}/api/v2/notice/detail/{id}.json</td><td>공지사항 ID를 통해 공지사항 내용 취득</td></tr><tr><td></td><td>공지사항 첨부파일 열기 및 다운로드</td><td>GET</td><td>/{serviceId}/api/v2/notice/attachments/{id}</td><td>공지사항 첨부파일 열기/다운로드</td></tr><tr><td><strong>FAQ</strong></td><td>카테고리 리스트</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/categories.json</td><td>FAQ 카테고리 리스트 취득</td></tr><tr><td></td><td>FAQ 리스트</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/list.json</td><td>헬프센터 FAQ 리스트</td></tr><tr><td></td><td>FAQ 상세</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/detail/{id}.json</td><td>FAQ ID를 통해 FAQ 내용 취득</td></tr><tr><td></td><td>FAQ 첨부파일 열기 및 다운로드</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/attachments/{id}</td><td>FAQ 첨부파일 열기 및 다운로드</td></tr><tr><td><strong>문의 접수</strong></td><td>접수유형 리스트</td><td>GET</td><td>/{serviceId}/api/v2/ticket/categories.json</td><td>서비스 내 접수유형 리스트 조회</td></tr><tr><td></td><td>접수유형 필드 리스트</td><td>GET</td><td>/{serviceId}/api/v2/ticket/field/user/{categoryId}.json</td><td>접수유형을 통하여 대응되는 필드 리스트 확인</td></tr><tr><td></td><td>티켓 첨부파일 업로드</td><td>POST</td><td>/{serviceId}/openapi/v1/ticket/attachments/upload.json</td><td>서버에 파일 업로드</td></tr><tr><td></td><td>티켓 생성</td><td>POST</td><td>/{serviceId}/openapi/v1/ticket.json</td><td>신규 티켓 생성</td></tr><tr><td><strong>문의 내역</strong></td><td>고객 티켓 리스트</td><td>GET</td><td>/{serviceId}/openapi/v1/ticket/enduser/{usercode}/list.json</td><td>검색 조건을 통해 조건에 맞는 고객의 티켓 리스트 노출</td></tr><tr><td></td><td>티켓 상세</td><td>GET</td><td>/{serviceId}/openapi/v1/ticket/enduser/{usercode}/{ticketId}/detail.json</td><td>고객이 접수한 티켓 상세 조회</td></tr><tr><td></td><td>티켓 첨부파일 열기 및 다운로드</td><td>GET</td><td>/{serviceId}/api/v2/ticket/attachments/{id}</td><td>티켓 첨부파일 열기/다운로드</td></tr><tr><td></td><td>고객 재문의</td><td>POST</td><td>/{serviceId}/openapi/v1/ticket/enduser/{usercode}/{ticketId}/comment.json</td><td>티켓 ID 기준으로 고객 재문의</td></tr></tbody></table>
