> 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/eng/api-guide/open-api.md).

# Open API

***

## **➊ API Authentication**

### **1-1. Enable OPEN API**&#x20;

1. Go to **\[Service Management] → \[Authentication]**.
2. Enable **Open API**.
3. When Open API is enabled, a **Service API Key** is automatically generated.
   * The API Key is used to authenticate API requests and encrypt transmitted data.
   * You can regenerate the API Key by clicking **Change API Key**.

***

### **1-2. Authorization Header**

{% hint style="danger" %}
Please make sure to set the following values in **every request header**.
{% endhint %}

{% hint style="info" %}

* If your service uses **Security Service features**, you can enable IP-based spam policies from\
  \&#xNAN;**\[Service Management] → \[Security Management] → \[Spam Management]**.

* When creating tickets via the **Open API**, if you include the **OC-Client-IP** value in the request header, spam detection will be performed based on the request’s IP address.
  {% endhint %}

* Authorization : Authentication string generated using the **Security Key**

* X-TC-Timestamp : Current UTC timestamp {new Date().getTime()}

* OUCODE : User Code (If not set, the default value is **Owner**)

***

### **1-3. Authorization Generation**

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

{% stepper %}
{% step %}

#### **Generate the string according to the rules below.**

1. **Organization ID**
   * You can find this value on the \[Global Management] → \[Contract Service Status] → \[Organization Information] screen.
   * It is composed in a format similar to the following: `AbcdE1fghIj23K4x`
2. **API URI**
   * Enter the URI of the API you want to call. The URI must include the Service ID.
     * Example: `/yourService/openapi/v1/ticket.json`&#x20;
3. **Query Parameters**
   * If the API you are calling includes query parameters, sort the parameters by key name in alphabetical order first. Then concatenate only the parameter values using the `&` character. If there is only one parameter, do not include the `&` character. If there are no query parameters, this step can be omitted.
     * Example: Query parameter `1234`
       1. Sorted by key (alphabetical): `language=ko&page=1&pageSize=10`
       2. Concatenated values: `ko&1&10`
4. **Timestamp**
   * Apply the timestamp of the time when the API request is made. This value must be identical to the **X-TC-Timestamp** value sent in the request header.
     * Example: `1764031689401`
       {% endstep %}

{% step %}

#### **String Encryption**

* Encrypt the generated string using the **API Key** created in **1-1. Enable Open API** to generate the authentication key.
* The encryption method used is **HmacSHA256**.
  {% endstep %}

{% step %}

#### **Sending the Authentication Key**

* Set the generated authentication key as the value of the **Authorization** request header and send the request to Contiple.
* Contiple encrypts the received header and body values using the same API Key and compares the resulting Hash value.
* If the values match, the API request is processed successfully.
* If they do not match, an error code **400** is returned.
  {% endstep %}
  {% endstepper %}

***

### **1-4. JAVA Example**

#### **(1) Standard Request (GET)**

```java
// 유저 티켓 리스트
String URL = "http://nhn-cs.oc.nhncloud.com/APISimple/openapi/v1/ticket/enduser/usercode/list.json?categoryId=1&language=ko";
String organizationId = "WopqM8euoYw89B7i"; // 조직ID
String securityKey = "431402c0eaaf46d889f243db9e7492e2"; // 서비스 Key
String uri = "/APISimple/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"); // 매개변수 이름의 알파벳 순서에 따라(categoryId=1&language=ko)& 기호로 매개변수 값을 연결해 주세요 (1&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) Standard Request (POST)**

```java
// 티켓 생성
String URL = "http://nhn-cs.oc.nhncloud.com/APISimple/openapi/v1/ticket.json?language=ko";
String organizationId = "WopqM8euoYw89B7i"; // 조직ID
String securityKey = "431402c0eaaf46d889f243db9e7492e2"; // 서비스 Key
String uri = "/APISimple/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) File Upload**

```java
String URL = "http://nhn-cs.oc.nhncloud.com/APISimple/openapi/v1/ticket/attachments/upload.json";
String organizationId = "WopqM8euoYw89B7i"; // NHN Cloud 조직ID
String securityKey = "431402c0eaaf46d889f243db9e7492e2"; // 서비스 Key
String uri = "/APISimple/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) Contiple-side authentication method**

```java
// 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();
```

* **If Contiple authentication fails, the result is returned as shown below.**
* **(Please verify that the Authorization string is encrypted using the correct method.)**

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

* **Authentication failure condition:**

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

***

## **➋ Common Return Results**

### **2-1. Return Result Example**

```json
//성공: 상세
{
    "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. Return Result Description**

<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>Return result code, <code>200</code> indicates a successful request.</td></tr><tr><td></td><td>resultMessage</td><td>String</td><td>Return error message</td></tr><tr><td></td><td>isSuccessful</td><td>Boolean</td><td>Execution result (Sucess: <code>true</code>, Failure: <code>false</code>) </td></tr><tr><td><strong>Result</strong></td><td>contents</td><td>JSON</td><td>List result content</td></tr><tr><td></td><td>content</td><td>JSON</td><td>Detail result content</td></tr></tbody></table>

***

### **2-3. Return Code**

> * 200 : SUCCESS
> * 400 : Bad Request
> * 403 : Access Denied(Forbidden)
> * 404 : Not Data Found
> * 500 : Server Error
> * 9007 : The related data already exists.
> * 9005 : No related data exists.
> * 1001 : The number of inquiries has exceeded the limit. Please contact us again after a while.
>   * The following cases apply when **\[Spam Management] → \[Spam Inquiry Blocking]** is enabled:
>   * If inquiry creation is attempted 3 or more times within 1 minute from the same IP, ticket creation is blocked for 24 hours.
> * 1002 : The number of inquiries has exceeded the limit. Please contact us again after a while.
>   * The following cases apply when **\[Spam Management] → \[Spam Inquiry Blocking]** is enabled:
>   * If inquiry creation is attempted 10 or more times within 24 hours from the same IP, ticket creation is blocked for 24 hours.

***

### **2-4. Return Code Details (Failure)**

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

***

## **➌ API List**

### **3-1. Development Environment URL**

<table><thead><tr><th width="159">환경</th><th>BaseUrl</th></tr></thead><tbody><tr><td>Alpha</td><td>https://{domain}.oc.alpha-nhncloud.com</td></tr><tr><td>Beta</td><td>https://{domain}.oc.beta-nhncloud.com</td></tr><tr><td>Real</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 of Service</td><td>/{serviceId}/openapi/v1/*</td></tr><tr><td>Available for direct use without authentication</td><td>/{serviceId}/api/v2/*</td></tr></tbody></table>

***

### **3-3. API List**

<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>Service</strong></td><td>Service Details</td><td>GET</td><td>/{serviceId}/api/v2/service.json</td><td>Retrieve service information using the Service ID</td></tr><tr><td><strong>Notice</strong></td><td>Heading List</td><td>GET</td><td>/{serviceId}/api/v2/notice/categories.json</td><td>Retrieve the list of notice headings</td></tr><tr><td></td><td>Tag List</td><td>GET</td><td>/{serviceId}/api/v2/notice/tags.json</td><td>Retrieve the list of notice tags</td></tr><tr><td></td><td>Notice List</td><td>GET</td><td>/{serviceId}/api/v2/notice/list.json</td><td>Retrieve the Help Center notice list</td></tr><tr><td></td><td>Notice Details</td><td>GET</td><td>/{serviceId}/api/v2/notice/detail/{id}.json</td><td>Retrieve notice content using the Notice ID</td></tr><tr><td></td><td>Open &#x26; Download Notice Attachments</td><td>GET</td><td>/{serviceId}/api/v2/notice/attachments/{id}</td><td>Open or download notice attachments</td></tr><tr><td><strong>FAQ</strong></td><td>Category List</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/categories.json</td><td>Retrieve the FAQ category list</td></tr><tr><td></td><td>FAQ List</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/list.json</td><td>Retrieve the Help Center FAQ list</td></tr><tr><td></td><td>FAQ Detail</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/detail/{id}.json</td><td>Retrieve FAQ content using the FAQ ID</td></tr><tr><td></td><td>Open &#x26; Download FAQ Attachments</td><td>GET</td><td>/{serviceId}/api/v2/helpdoc/attachments/{id}</td><td>Open or download FAQ attachments</td></tr><tr><td><strong>Submit Inquiry</strong></td><td>Inquiry Type List</td><td>GET</td><td>/{serviceId}/api/v2/ticket/categories.json</td><td>Retrieve the list of inquiry types within the service</td></tr><tr><td></td><td>Inquiry Type Field List</td><td>GET</td><td>/{serviceId}/api/v2/ticket/field/user/{categoryId}.json</td><td>Retrieve the list of fields associated with a specific inquiry type</td></tr><tr><td></td><td>Ticket Attachment Upload</td><td>POST</td><td>/{serviceId}/openapi/v1/ticket/attachments/upload.json</td><td>Upload files to the server</td></tr><tr><td></td><td>Create Ticket</td><td>POST</td><td>/{serviceId}/openapi/v1/ticket.json</td><td>Create a new ticket</td></tr><tr><td><strong>Inquiry History</strong></td><td>Customer Ticket List</td><td>GET</td><td>/{serviceId}/openapi/v1/ticket/enduser/{usercode}/list.json</td><td>Display a customer’s ticket list based on search conditions</td></tr><tr><td></td><td>Ticket Details</td><td>GET</td><td>/{serviceId}/openapi/v1/ticket/enduser/{usercode}/{ticketId}/detail.json</td><td>Retrieve details of a ticket submitted by the customer</td></tr><tr><td></td><td>Open &#x26; Download Ticket Attachments</td><td>GET</td><td>/{serviceId}/api/v2/ticket/attachments/{id}</td><td>Open or download ticket attachments</td></tr><tr><td></td><td>Customer Follow-up Inquiry</td><td>POST</td><td>/{serviceId}/openapi/v1/ticket/enduser/{usercode}/{ticketId}/comment.json</td><td>Submit a follow-up inquiry based on the Ticket ID</td></tr></tbody></table>
