> For the complete documentation index, see [llms.txt](https://in7.gitbook.io/in7/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://in7.gitbook.io/in7/guide/in7/undefined-2/api.md).

# API 액세스

## IN7 API

IN7의 AI 기능을 여러분의 애플리케이션에서 직접 호출하세요. OpenAI SDK를 쓰고 있다면 `base_url`과 `api_key`만 바꿔서 그대로 붙일 수 있습니다.

```
Base URL   https://api.in7.day
인증        Authorization: Bearer sk_live_...
```

***

### 빠른 시작

#### 1. API 키 받기

API 키는 조직 관리자의 승인을 거쳐 발급됩니다. 직접 생성하는 경로는 없습니다.

1. `마이 페이지 > API 액세스`에서 용도를 적어 **발급 요청**
2. 조직 관리자가 **승인** (요청자에게 결과 메일 발송)
3. 승인 후 **7일 안에** 마이 페이지에서 **키 수령**

> ⚠️ **키는 수령 시 딱 한 번만 표시됩니다.** 서버에 저장되지 않아 다시 볼 수 없고, 메일로도 보내지 않습니다. 비밀 관리 도구나 환경 변수에 즉시 옮겨 두세요. 분실하면 기존 키를 폐기하고 새로 요청해야 합니다.

발급된 키는 `sk_live_`로 시작합니다.

#### 2. 키를 환경 변수에 넣기

```bash
export IN7_API_KEY="sk_live_xxxxxxxx"
```

#### 3. 첫 호출

**Python (OpenAI SDK)**

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["IN7_API_KEY"],
    base_url="https://api.in7.day/v1",
)

completion = client.chat.completions.create(
    model="orca-plus",
    messages=[{"role": "user", "content": "전기차 시장 동향을 3문단으로 요약해줘"}],
)

print(completion.choices[0].message.content)
```

**Node.js (OpenAI SDK)**

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.IN7_API_KEY,
  baseURL: "https://api.in7.day/v1",
});

const completion = await client.chat.completions.create({
  model: "orca-plus",
  messages: [{ role: "user", content: "전기차 시장 동향을 3문단으로 요약해줘" }],
});

console.log(completion.choices[0].message.content);
```

**cURL**

```bash
curl https://api.in7.day/v1/chat/completions \
  -H "Authorization: Bearer $IN7_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "orca-plus",
    "messages": [{"role": "user", "content": "전기차 시장 동향을 3문단으로 요약해줘"}]
  }'
```

***

### 인증

모든 요청에 API 키가 필요합니다. 두 가지 헤더를 지원하며 어느 쪽을 써도 동일합니다.

```bash
Authorization: Bearer sk_live_xxxxxxxx    # 권장 — 대부분의 SDK 기본값
X-API-Key: sk_live_xxxxxxxx
```

키는 코드에 하드코딩하지 말고 환경 변수나 비밀 관리 도구로 주입하세요. 브라우저 등 클라이언트 측 코드에 키를 노출하면 안 됩니다.

***

### 모델

`model`을 생략하면 IN7이 질문에 맞는 모델을 자동으로 선택합니다. 특정 모델을 쓰려면 이름을 지정하세요.

사용 가능한 모델은 API로 조회합니다. 문서에 적힌 목록보다 항상 최신입니다.

```bash
curl https://api.in7.day/v1/models \
  -H "Authorization: Bearer $IN7_API_KEY"
```

```json
{
  "object": "list",
  "data": [
    { "id": "orca-plus", "object": "model", "owned_by": "in7" }
  ]
}
```

여기서 받은 `id`를 `model`에 그대로 넣으면 됩니다. 조직에서 특정 모델을 제한해 둔 경우 그 모델을 지정하면 `403`이 반환됩니다.

#### 내장 도구

웹 검색, URL 읽기 같은 도구는 **서버에서 자동으로 동작**합니다. 모델을 지정하든 자동 선택에 맡기든 동일하며, 별도 설정 없이 질문만 하면 필요할 때 알아서 사용됩니다.

호출자가 직접 정의하는 OpenAI 방식의 function calling(`tools`, `tool_choice`)은 아직 지원하지 않습니다.

***

### 스트리밍

`stream: true`를 지정하면 응답을 토큰 단위로 받습니다.

```python
stream = client.chat.completions.create(
    model="orca-plus",
    messages=[{"role": "user", "content": "전기차 시장 동향을 설명해줘"}],
    stream=True,
    stream_options={"include_usage": True},
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print(f"\n총 토큰: {chunk.usage.total_tokens}")
```

응답은 `text/event-stream`으로 오고, `chat.completion.chunk` 프레임이 이어지다 `data: [DONE]`으로 끝납니다. `stream_options.include_usage`를 켜면 그 직전에 사용량만 담긴 프레임이 한 번 더 옵니다.

#### 스트리밍에서 달라지는 점

|          | 일반 응답      | 스트리밍                            |
| -------- | ---------- | ------------------------------- |
| 실패 전달    | HTTP 상태 코드 | 첫 프레임 이후엔 `{"error": ...}` 프레임  |
| 종료 표시    | —          | `data: [DONE]` (실패 시 **오지 않음**) |
| 멱등 키 재사용 | 첫 결과 재생    | `409 stream_not_replayable`     |

**실패 처리** — 스트림이 시작되기 전 실패는 평소처럼 HTTP 오류로 옵니다. 시작된 뒤의 실패는 `{"error": ...}` 프레임으로 오고 `[DONE]`이 오지 않습니다. `[DONE]` **없이 끝난 스트림은 실패로 처리하세요.**

**재시도** — 스트림 응답은 저장해 두었다가 되돌려줄 수 없습니다. 같은 `Idempotency-Key`로 다시 보내면 `409 stream_not_replayable`입니다. 스트리밍을 재시도할 때는 새 멱등 키를 쓰거나 멱등 키 없이 보내세요.

**`finish_reason="length"`** — 답변이 끝까지 생성되지 못하고 잘린 경우에 옵니다.

***

### 대화 이어가기

대화 이력은 클라이언트가 관리합니다. OpenAI와 동일하게 이전 턴을 `messages`에 담아 매번 보내세요.

```python
messages = [{"role": "user", "content": "전기차 시장 동향을 요약해줘"}]
first = client.chat.completions.create(model="orca-plus", messages=messages)

messages.append({"role": "assistant", "content": first.choices[0].message.content})
messages.append({"role": "user", "content": "두 번째 항목을 더 자세히 설명해줘"})

second = client.chat.completions.create(model="orca-plus", messages=messages)
```

***

### 재시도와 중복 방지

타임아웃이나 네트워크 끊김으로 응답을 못 받으면, 요청이 서버에 도달했는지 알 수 없습니다. 그냥 다시 보내면 **두 번 실행되고 두 번 과금**될 수 있습니다.

`Idempotency-Key` 헤더를 붙이면 이 문제가 사라집니다. 서버가 키별로 첫 실행 결과를 기억해 두었다가, 같은 키가 다시 오면 **재실행 없이 그 결과를 그대로** 돌려줍니다.

#### 사용법

**키는 "하나의 논리적 요청"마다 하나씩** 만듭니다. 값은 여러분이 정하는 임의의 문자열이며, 요청끼리 겹치지 않기만 하면 됩니다 (UUID나 업무 식별자를 권장).

```python
import uuid

key = str(uuid.uuid4())      # 이 요청을 대표하는 키

completion = client.chat.completions.create(
    model="orca-plus",
    messages=[{"role": "user", "content": "..."}],
    extra_headers={"Idempotency-Key": key},
)
```

```bash
curl https://api.in7.day/v1/chat/completions \
  -H "Authorization: Bearer $IN7_API_KEY" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Content-Type: application/json" \
  -d '{ "model": "orca-plus", "messages": [...] }'
```

실패해서 다시 보낼 때는 **그 키를 그대로** 씁니다. 키를 새로 만들면 서버는 별개의 요청으로 보고 다시 실행합니다.

```python
for attempt in range(3):
    try:
        return client.chat.completions.create(
            ..., extra_headers={"Idempotency-Key": key},   # 같은 key 재사용
        )
    except (APITimeoutError, APIConnectionError):
        continue
```

#### 알아둘 점

| 상황               | 결과                               |
| ---------------- | -------------------------------- |
| 같은 키 + 같은 내용     | 첫 실행 결과를 그대로 반환 (재실행·재과금 없음)     |
| 같은 키 + **다른 내용** | `409` — 키를 재사용하려면 요청 내용도 같아야 합니다 |
| 새 키              | 새로운 실행                           |
| 헤더 생략            | 매 호출이 독립 실행 (중복 방지 없음)           |

기억되는 기간은 **24시간**입니다. 그 뒤 같은 키로 보내면 새 실행으로 처리됩니다.

**스트리밍에는 쓸 수 없습니다.** 스트림은 되돌려줄 결과가 남지 않아, 같은 키로 재시도하면 `409 stream_not_replayable`입니다. 스트리밍을 재시도할 때는 키를 새로 만들거나 헤더를 생략하세요.

***

### API 레퍼런스

#### `POST /v1/chat/completions`

OpenAI Chat Completions 호환. 스트리밍을 지원합니다.

| 필드                             | 타입      | 필수 | 설명                  |
| ------------------------------ | ------- | -- | ------------------- |
| `model`                        | string  | ✅  | 모델 id               |
| `messages`                     | array   | ✅  | 최대 200개             |
| `stream`                       | boolean |    | 기본 `false`          |
| `stream_options.include_usage` | boolean |    | 스트림 마지막에 사용량 프레임 추가 |

헤더로 `Idempotency-Key`를 받습니다.

**OpenAI와 다른 점**

* `temperature` · `top_p` · `max_tokens` · `stop` 등은 **받아들이지만 실행에 반영되지 않습니다.** 모델의 서버 기본값으로 실행되므로 값을 조절해도 결과가 달라지지 않습니다
* `tools`/`tool_choice` → `400 tools_not_supported`
* `n`은 1만 허용 → 그 외 `400 unsupported_value`
* `content`는 **문자열만** 지원합니다. 멀티모달 배열 형식은 받지 않습니다
* 역할은 하나의 프롬프트 문자열로 합쳐집니다. **별도의 system 채널이 없어** system 메시지도 텍스트 라벨로만 남습니다
* 합쳐진 전체 대화가 **100,000자를 넘으면** `400` 입니다. 서버가 잘라 주지 않으니 오래된 턴은 클라이언트가 정리하세요
* **마지막 메시지는 비어 있지 않은** `user` 여야 합니다 → 그 외 `400 invalid_last_message` (앞선 턴은 비어 있어도 됩니다)

#### `GET /v1/models`

호출 가능한 모델 목록. OpenAI 형식입니다.

***

### 오류

| 코드    | 의미                    | 대처                                                                             |
| ----- | --------------------- | ------------------------------------------------------------------------------ |
| `400` | 미지원 옵션·형식 오류          | `code` 확인 — `tools_not_supported`, `unsupported_value`, `invalid_last_message` |
| `401` | 인증 실패                 | 아래 참고                                                                          |
| `403` | 권한·정책·한도 문제           | 사용 한도 초과는 메시지로 구분됩니다                                                           |
| `404` | 접근 불가 리소스, 또는 API 미개방 | 조직에 API 액세스가 켜져 있는지 관리자에게 확인                                                   |
| `409` | 멱등 키 충돌               | 같은 키로 다른 내용을 보냈거나, 스트림 응답 재생 시도(`stream_not_replayable`)                       |
| `422` | 요청 본문 검증 실패           | 필드 길이·타입 확인                                                                    |
| `429` | 호출 빈도·동시 실행 상한 초과     | `Retry-After` 만큼 기다린 뒤 지수 백오프로 재시도                                             |
| `503` | 일시적 처리 불가             | 재시도                                                                            |

#### 401이 반환될 때

응답만으로는 원인을 구분할 수 없습니다. 실제 원인은 넷 중 하나입니다.

1. 키 문자열 오류 (복사 누락, 공백 포함)
2. 키가 폐기됨
3. 키가 만료됨
4. **키 소유자의 계정이 비활성화·삭제되었거나 조직을 떠남**

4번이 특히 놓치기 쉽습니다. 담당자가 퇴사하거나 계정이 비활성화되면 그 사람이 발급받은 키는 즉시 동작을 멈춥니다. 이 경우 소유자와 조직 관리자에게 폐기 알림 메일이 발송되므로 메일함을 확인하세요.

**같은 조직 안에서 팀만 옮긴 경우에는 키가 계속 동작합니다.**

***

### 제한

| 항목       | 값        |
| -------- | -------- |
| 프롬프트 길이  | 100,000자 |
| 대화 메시지 수 | 200개     |
| 멱등 키 유지  | 24시간     |

호출 빈도와 동시 실행 수에도 조직 단위 상한이 있습니다. 상한은 조직마다 다르게 설정되며 운영 중 조정될 수 있으니, 특정 수치를 전제로 설계하지 마세요. 상한에 걸리면 `429`가 반환되고 `Retry-After` 헤더에 재시도까지 기다릴 초가 담깁니다 — **이 값을 보고 지수 백오프로 재시도**하면 됩니다. 필요한 처리량이 정해져 있다면 조직 관리자에게 문의하세요.

사용량은 **호출자 개인의 사용량**으로 집계되며, 웹 채팅 사용량과 합산되어 조직·팀·개인 한도에 함께 반영됩니다.

***

### 키 관리

#### 교체(회전)

`조직 관리 > API 액세스`에서 관리자가 회전을 시작하면 새 키가 발급되고 **기존 키는 24시간 동안 계속 동작**합니다. 이 사이에 애플리케이션의 키를 교체하면 중단 없이 넘어갈 수 있습니다. 24시간이 지나면 구 키는 자동 폐기됩니다.

#### 폐기

키가 유출됐다면 즉시 폐기하세요. 폐기는 다른 어떤 설정과도 무관하게 항상 가능하며, 즉시 해당 키의 호출이 차단됩니다.

#### 만료

기본 90일, 최대 1년입니다. 만료 14일 전에 소유자와 조직 관리자에게 알림 메일이 발송됩니다.

#### 소유자 조건

키는 발급받은 사람에게 귀속됩니다. 소유자가 조직의 활성 구성원이 아니게 되면 키는 자동으로 폐기되고 알림이 발송됩니다.

시스템이 사용하는 키라도 **관리 주체가 되는 실제 계정**이 필요합니다. 담당자 변경이 예정되어 있다면 미리 새 담당자 이름으로 발급받아 두세요.

***

### FAQ

**응답이 오래 걸립니다.** 모델과 질문 복잡도에 따라 수십 초가 걸릴 수 있습니다. 클라이언트 타임아웃을 넉넉히 잡고, 재시도 시에는 같은 멱등 키를 사용하세요(스트리밍 제외).
