API 문서

이 API 스택이 실제 서비스에서 어떻게 동작하는지 궁금하다면 — 같은 엔진으로 구동되는 AI 사주 앱 인타(INTA)에서 사주 분석·AI 대화를 직접 체험해 보세요. 인타가 이 API의 살아있는 레퍼런스입니다. 인타의 AI 음성 응답(TTS)도 같은 스택으로 구동됩니다 — 음성 API는 추후 공개 예정입니다.

Quickstart

HRUM(흐름)은 기억하는 AI, 사주 분석, LLM 리셀링을 제공합니다. 다음 3단계를 따르세요:

기본 주소는 https://api.hrum.ai(API)·https://console.hrum.ai(콘솔)입니다. 기존 주소 api.float.do·console.float.do·api.inta.do 도 그대로 동작하니 이미 연동했다면 바꾸지 않아도 됩니다.

  1. API 키 발급 - 콘솔에 가입한 뒤 API 키 화면에서 발급받습니다(키는 발급 직후 한 번만 보입니다). 가입부터 에러 처리까지의 순서는 개발자 시작 가이드에 정리되어 있습니다.
  2. 요청 보내기 - 아래의 예제를 따라 POST 요청을 보냅니다.
  3. 응답 처리 - JSON 응답에서 사주 정보를 추출합니다.
curl -X POST https://api.hrum.ai/api/v1/partners/v1/manseryeok \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "year": 1990,
    "month": 1,
    "day": 15,
    "hour": 14,
    "minute": 30,
    "timezone": "Asia/Seoul",
    "gender": "male"
  }'

User-Agent 주의: Python 표준 라이브러리 urllib의 기본 User-Agent(Python-urllib/3.x)는 보안 규칙에 걸려 403 (error 1010)이 반환됩니다. requests, OpenAI SDK, Node, curl은 정상 동작합니다. urllib이나 자체 HTTP 클라이언트를 쓴다면 User-Agent: <앱이름>/<버전> 헤더를 직접 지정하세요. (실측: python-requests 통과 · OpenAI/Python 통과 · node 통과 · Python-urllib 403)

API Reference

HRUM은 기억하는 AI, 사주 분석, LLM 리셀링을 제공합니다.

RIDM Memory API

엔드유저별 대화 기억을 호스팅하면서 ridm/* 모델에 메모리 레이어를 추가합니다. 메모리는 ridm/* 전용이며, OpenAI/Google 모델은 리셀(원본 그대로) — 같은 모델, 다른 상품입니다. 유튜브 자막 자동 주입 기능도 지원합니다.

세션이 없습니다 — 무한 세션. 같은 user로 호출하면 대화는 끝없이 이어집니다. 매 호출마다 서버가 historyCount개의 최근 대화 턴과 recallCount개의 장기 기억(요약·프로필·기억한 사실·진행 중 고민)을 자동으로 붙여 주므로, 파트너는 새 메시지 하나만 보내면 됩니다. 얼마나 멀리까지 참조할지는 두 숫자로 조절하고, 엔드유저별 저장 메시지 수 상한은 memoryCap으로 정합니다. 히스토리를 직접 관리하고 싶으면 historyCount를 생략하고 OpenAI 방식대로 messages에 이전 대화를 실어 보내면 됩니다.

긴 대화도 비용이 선형으로 늘지 않습니다. 오래된 어시스턴트 턴은 자동으로 압축됩니다 — 최신 2턴은 원문 그대로, 그 이전은 500자 → 200자로 단계적으로 줄여 입력 토큰을 아낍니다(사용자 턴은 그대로). 그리고 "저번에 ~라고 했잖아"처럼 과거 발언을 묻는 질문에는 서버가 저장된 실제 대화를 검색해 날짜·화자와 함께 인용합니다(recalledMemories에 포함) — 기록이 없으면 지어내지 않고 없다고 답합니다.

LLM API (리셀) vs RIDM Memory API — 무엇이 다른가

같은 모델, 다른 상품 — 접두사만 다르고 나머지는 동일합니다. 응답에 recalledMemories·memorySaved가 붙으면 RIDM 경로입니다.

기능 LLM API (리셀) RIDM Memory API
모델 선택 openai/*, google/*, anthropic/* 등 ridm/* (접두사 붙임)
응답 형식 원본 모델 응답 그대로 (OpenAI 호환) 원본 모델 + 메모리 필드
user 파라미터 무시됨 필수 (endUserId, 기억 유지)
대화 기억·요약·프로필 회상 ✗ ✓ (recallCount·memoryCap)
문서 처리 fileIds 명시 주입만 fileIds 명시 + 자동 회상
유튜브 링크 처리 youtube:true 지원 youtube:true 지원
감정 분석 (emotion) 무시됨 (단, 콘솔에서 감정 기능이 꺼진 계정이 emotion:true를 보내면 모델과 무관하게 403 FEATURE_DISABLED) ✓ (요약 사이클 포함)
모더레이션 user 메시지만 system + user 메시지
systemPrompt 제한 무시됨 — 리셀 모델에서는 systemPrompt가 무시되므로 messages에 {"role":"system"}으로 넣으세요 8,000자 검증 (ridm 전용)
응답 추가 필드 costUsd, costWon (비용) costWon, recalledMemories, memorySaved, memoryCapReached, filesRecalled, emotion, customProfile
가격 원가 × 환율 × 1.10 리셀가 × 1.30 (기억 유지·회상·감정·파일 자동회상 전부 포함, 기능별 추가 과금 없음)
예외 — ridm/saju-report: 메모리 없음 → 리셀 마진만, chat 불가 (리포트 전용)

코드 예시 비교

// LLM API (리셀) — 메모리 없음
{
  "model": "openai/gpt-5.6-terra",
  "messages": [{ "role": "user", "content": "..." }]
}

// RIDM Memory API — 기억 엔진 포함
{
  "model": "ridm/gpt-5.6-terra",  // 접두사만 다름
  "user": "user_123",              // 필수 (기억 유지)
  "messages": [{ "role": "user", "content": "..." }]
}

// 응답에서 차이
// 리셀: { "id": "...", "choices": [...], "usage": {...}, "costWon": 1, "costUsd": 0.00045 }
// RIDM: { "id": "...", "choices": [...], "usage": {...}, "costWon": 2, "recalledMemories": [...], "memorySaved": { "id": "turn-...", "keywords": [] } }

POST /api/v1/partners/v1/chat/completions

메모리를 지닌 대화형 LLM 호출. 같은 user/endUserId로 호출하면 과거 맥락이 유지됩니다.

요청 파라미터

파라미터 타입 필수 설명
model string O 모델명. RIDM: ridm/gpt-5.6-terra 등 / 리셀: openai/gpt-5.2, google/gemini-3.5-flash 등
user string O 파트너사가 발급한 엔드유저 ID (endUserId — 같은 값이면 기억 유지)
messages array O OpenAI 형식 메시지 배열 [{ "role": "user", "content": "..." }, ...]
systemPrompt string 시스템 지시문 (선택, 최대 8,000자 — ridm/* 전용). 리셀 모델에서는 systemPrompt가 무시되므로 messages에 {"role":"system"}으로 넣으세요.
youtube boolean true이면 마지막 메시지의 유튜브 링크 자막 자동 주입
stream boolean 스트리밍 응답 여부 (기본값: false). 형식은 아래 스트리밍(SSE) 형식 참고 — OpenAI chunk 형식이 아닙니다.
fileIds string[] 주입할 업로드 문서 ID (UUID, 최대 5개). 문서 처리 API 참고
emotion boolean 감정 상태 분석 (ridm/* 전용, 기본 false). 감정 상태 분석 참고
summaryEvery integer 요약·감정 갱신 주기 (턴 단위, 1~20, 기본 5, ridm/* 전용)
temperature number 0~2. 추론 전용 모델(추론을 끌 수 없는 모델)에는 전달되지 않습니다
top_p number 0~1
presence_penalty / frequency_penalty number 각 -2~2
seed / stop number / string|string[] OpenAI와 동일 의미로 상류에 전달
response_format object OpenAI 형식 (json_object 사용 시 messages 또는 systemPrompt에 "JSON" 단어 필요). Claude 모델은 무시
tools / tool_choice / parallel_tool_calls array / string|object / boolean OpenAI 형식 함수 호출. OpenAI·Google 모델(ridm/ 동명 포함) 지원, Claude 모델은 400 TOOLS_UNSUPPORTED_FOR_MODEL
n / stream_options — SDK 호환을 위해 받기만 하고 무시합니다 (항상 1개 응답)
max_tokens number 최대 출력 토큰 (1~8,192, 초과 시 400). gpt-5.1 이상·5.4·5.5·5.6 계열은 서버가 추론을 끄고(reasoning_effort none) 호출해 요청한 만큼 본문이 나옵니다. 추론을 끌 수 없는 모델(gpt-5·gpt-5-mini·gpt-5-nano·gpt-6-astra·o3·o3-mini·o4-mini)은 서버가 추론 예산 1,500토큰을 더해 호출하며, 이 추론 토큰도 출력으로 과금됩니다.
historyCount number 서버가 자동으로 붙여 줄 최근 대화 턴 수 (1~50). 지정하면 messages에는 새 메시지만 보내면 됩니다. 생략 시 서버 히스토리를 붙이지 않고 messages의 마지막 10개만 사용합니다.
recallCount number 질문과 관련된 장기 기억(과거 요약)을 몇 개까지 회상할지 (0~20, 기본 3). 프로필·기억한 사실·진행 중 고민은 항상 포함됩니다.
memoryCap number 엔드유저별 저장 메시지 수 기준 상한 (10~1000, 기본 100). 요청마다 적용되며 고정되지 않습니다. 저장된 메시지 수가 상한을 넘으면 이번 턴도 저장은 되지만 응답에 memoryCapReached: true, memorySaved: null이 옵니다.
candor string 말결 — 답변의 직설 정도. t / f(기본). ridm/* 전용, 리셀 모델에서는 무시. 말결 (가칭) 참고

말결 (가칭) — candor

같은 사주, 같은 질문이라도 듣고 싶은 말의 결은 사람마다 다릅니다. candor는 답변이 얼마나 직설적일지 정합니다. RIDM Memory API(ridm/*)와 사주 리딩 3종(리딩·리딩 메모리·리포트)에서 같은 방식으로 동작합니다.

  • f (F형) — 기본값. 아무것도 바꾸지 않습니다. 이 필드를 보내지 않은 것과 같습니다.
  • t (T형) — 직설가. 첫 문단에서 솔직한 판단을 분명히 말하고, 끝에 지금 할 수 있는 행동을 짚습니다. 사람을 비난하지 않고, 운명론적인 단정도 하지 않습니다.

예전 값도 계속 받습니다: blunt → t, balanced·gentle → f로 정규화됩니다(gentle은 더 이상 별도 말결이 아닙니다).

tone(존댓말·반말·격식)과는 다른 축이라 함께 쓸 수 있습니다. 예: tone: "formal" + candor: "t" = 정중하지만 직설적인 답변.

systemPrompt·persona가 우선합니다. 캐릭터 설정의 말투·금지사항·형식과 충돌하면 그쪽을 따릅니다.

사용자 메시지에서 위기 신호가 감지되면(안전 확인이 실패한 경우 포함) 말결은 자동으로 f로 돌아갑니다. candor를 t(또는 blunt)로 보낸 요청에만 응답에 candorApplied(실제 적용된 값, t | f)가 붙고, 이렇게 돌아간 경우 "f"로 표시됩니다. 예전 값으로 보내도 candorApplied는 t/f로 돌려줍니다.

말결 지시에 쓰인 토큰은 과금되지 않습니다. 대화 기록(RIDM Memory)에는 사용자가 보낸 원문만 저장됩니다. 리셀 모델(openai/* 등)에서는 무시됩니다. 허용되지 않은 값(예: "off", "harsh")을 보내면 400이 반환됩니다.

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/chat/completions \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ridm/gpt-5.6-terra",
    "user": "member-1024",
    "messages": [
      { "role": "user", "content": "지난번에 말한 이직 고민, 이어서 얘기해도 될까?" }
    ]
  }'

응답 예시

{
  "id": "chatcmpl-123",
  "object": "chat.completion",
  "created": 1629292800,
  "model": "ridm/gpt-5.6-terra",
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "안녕하세요! 이전 대화를 기억하고 있습니다..."
      },
      "finish_reason": "stop",
      "index": 0
    }
  ],
  "usage": {
    "prompt_tokens": 1250,
    "completion_tokens": 320,
    "prompt_tokens_details": { "cached_tokens": 0 }
  },
  "costWon": 45,
  "recalledMemories": [...],
  "memorySaved": { "id": "turn-1756200000000", "keywords": [] },
  "youtubeUsed": ["abc123def45"]
}

usage에는 total_tokens가 없습니다 — prompt_tokens, completion_tokens, prompt_tokens_details.cached_tokens, 그리고 추론 모델이면 completion_tokens_details.reasoning_tokens(이미 completion_tokens에 포함)가 옵니다. memorySaved는 저장된 턴 정보 객체 또는 null(상한 초과·도구 호출 턴), youtubeUsed는 자막을 주입한 영상 ID 배열입니다(URL 아님).

스트리밍(SSE) 형식

stream: true이면 OpenAI chunk 형식이 아니라 이름 있는 SSE 이벤트로 응답합니다. 따라서 OpenAI SDK의 스트림 파서로는 읽을 수 없습니다 — SSE를 직접 파싱하세요 (비스트리밍 응답은 OpenAI 호환).

이벤트 data
event: chunk RIDM: {"delta":"…"} (도구 호출 시 tool_calls 조각 포함) / 리셀: 상류 chunk 객체 그대로
event: message_done RIDM: {finish_reason, usage, costWon, recalledMemories, memorySaved, memoryCapReached, emotion?, customProfile?, budgetExceeded?, tool_calls?} / 리셀: {finish_reason, usage, costUsd, costWon, budgetExceeded?}. id·choices·model은 없습니다
event: error {message, code, status} — 헤더(200)가 이미 나간 뒤라 실제 오류 상태는 status로 확인
data: [DONE] 정상 종료 표시 (마지막 줄)
event: chunk
data: {"delta":"안녕하세요"}

event: message_done
data: {"finish_reason":"stop","usage":{"prompt_tokens":1250,"completion_tokens":320,"prompt_tokens_details":{"cached_tokens":0}},"costWon":45,"recalledMemories":[],"memorySaved":{"id":"turn-1756200000000","keywords":[]}}

data: [DONE]

에러 코드

공통 에러 외에 이 API에서 나오는 코드입니다. stream: true에서 응답이 시작된 뒤 난 오류는 event: error({message, code, status})로 옵니다. 리셀 모델도 같은 표를 따릅니다.

코드 HTTP 설명
MODERATION_FLAGGED400모더레이션 차단 (ridm: system+user, 리셀: user 메시지)
INSUFFICIENT_BALANCE402잔액 부족 (요청한 출력 전체를 미리 예약해 검사)
MODEL_NOT_FOUND400설정되지 않은 모델
INVALID_TOOLS / TOOLS_UNSUPPORTED_FOR_MODEL400tools 관련 필드 불일치 / Claude 모델에 도구 호출 요청
FILE_NOT_FOUND400fileIds에 없는 파일(또는 다른 파트너 파일) 포함
FEATURE_DISABLED403youtube·files·emotion 기능이 꺼진 계정에서 해당 옵션 사용
RATE_LIMITEDHTTP 400, 본문 status 429상류 속도 제한, 또는 리셀 일일 지출 상한 초과 (파트너당 기본 ₩100,000/일)
UPSTREAM_TIMEOUTHTTP 400, 본문 status 504상류 응답 시간 초과 (60초)
UPSTREAM_INVALID_REQUEST400상류 모델이 요청을 거절 (상류 400·422)
INVALID_RESPONSE_FORMAT400response_format: json_object인데 메시지에 "json"이라는 말이 없음 (OpenAI 계열)
REPORT_ONLY_MODEL400ridm/saju-report 지정 — 사주 리포트 API 전용
(없음)400ridm/* 모델에 user 누락 · systemPrompt 8,000자 초과 · 지원하지 않는 모델 접두사
provider_not_configured503Claude 모델이 지금 연결되어 있지 않음

주의사항

커스텀 프로필 (Custom Profile)

ridm/* 모델 전용. 엔드유저별 임의의 필드명-값을 저장하고 관리합니다. 자동으로 추출한 필드 값들을 저장하거나, 파트너가 직접 값을 지정하거나, 이번 턴 프롬프트에 특정 필드를 포함시킬 수 있습니다. 값은 누적되며, 스키마가 바뀌어도 기존 값은 보존됩니다(soft-update).

요청 파라미터 (POST /api/v1/partners/v1/chat/completions)

파라미터 타입 설명
extractProfileFields string[] 대화에서 자동으로 추출할 필드명 배열 (최대 100개). 요약 사이클에서 LLM이 사용자의 특성·선호·상황을 이 필드명으로 분류해 저장합니다. 추출값이 비어 있거나 null이면 건너뛰고 기존 값을 유지합니다 (누적 기반, 수동 지정 값은 추출값으로 덮어쓰지 않음). 추가 과금 없음.
injectProfileFields string[] 이번 턴 프롬프트에 포함할 필드명 배열. 지정한 필드만 프롬프트에 주입되며, 자동 주입은 없습니다. 주입된 필드는 매 턴 입력 토큰으로 과금됩니다. 필요한 필드만 선택하세요.
endUserProfile object 필드명-값 객체. 예: { "name": "김철수", "age": "35", "interest": "육아" }. 지정한 값은 추출된 값을 덮어씁니다. 최대 100개 필드, 값당 150자 기본(최대 500자).
profileValueMaxChars number 값의 최대 글자수 (기본 150, 최대 500 — 초과값은 500으로 맞춤). 그 요청에서 저장되는 값(endUserProfile·이번 요약 사이클 추출값)에만 적용되며, 저장되지 않으므로 매 요청 다시 보내야 합니다.

요청 예시 (RIDM chat)

{
  "model": "ridm/gpt-5.6-terra",
  "user": "enduser_123",
  "messages": [
    { "role": "user", "content": "내일 아이 생일 파티 계획 도와줘" }
  ],
  "extractProfileFields": ["childAge", "interests", "budget", "constraints"],
  "injectProfileFields": ["name", "childAge", "interests"],
  "endUserProfile": {
    "name": "김철수",
    "childAge": "7",
    "interests": "과학·미술"
  },
  "profileValueMaxChars": 200
}

응답에서의 프로필

프로필 파라미터를 보낸 요청의 응답(스트리밍은 message_done)에는 customProfile 필드가 붙습니다.

"customProfile": {
  "injected": ["name", "childAge", "interests"],  // 이번 턴 프롬프트에 주입된 필드
  "stored": 4,                                     // 현재 저장된 필드 수
  "extractPending": ["budget"]                     // 추출 대상이지만 아직 값이 없는 필드
}

삭제 API

특정 필드를 삭제하려면 DELETE 요청을 보냅니다:

DELETE /api/v1/partners/v1/profile/{endUserId}/field/{key}
X-Partner-Api-Key: your-api-key

응답: { "success": true, "data": { "deleted": true } } — 없는 키나 처음 보는 endUserId여도 200에 "deleted": false입니다 (엔드유저를 새로 만들지 않습니다). 이 API만의 에러 코드는 없습니다 — 공통 에러만 나옵니다.

상한 및 제약

LLM API (리셀)

openai/*, google/* 등 원본 LLM 모델을 리셀(메모리 없이 원본 응답 그대로). 리셀 모델 요청의 user, systemPrompt, recallCount, memoryCap, summaryEvery, emotion, candor는 무시되며(시스템 지시는 messages에 {"role":"system"}으로), 응답에 메모리 필드(recalledMemories 등)가 없습니다. 가격 = 원가 × 환율 × 1.10.

제공 모델 (GET /api/v1/partners/v1/models와 같은 목록)

curl https://api.hrum.ai/api/v1/partners/v1/models \
  -H "X-Partner-Api-Key: your-api-key"

GET /api/v1/partners/v1/models 응답 — OpenAI models.list 형식이 아닙니다. 가격은 1M 토큰당 원(₩)이며 아래 숫자는 예시입니다. Claude 모델은 서버에 설정된 경우에만 목록에 나옵니다.

{
  "success": true,
  "data": {
    "models": [
      { "modelId": "openai/gpt-5.6-terra", "upstream": "gpt-5.6-terra", "provider": "openai", "input_cost_krw": 1234, "output_cost_krw": 9876, "category": "base" },
      { "modelId": "ridm/gpt-5.6-terra", "upstream": "gpt-5.6-terra", "provider": "openai", "input_cost_krw": 1604.2, "output_cost_krw": 12838.8, "category": "ridm" }
    ]
  }
}

이 API만의 에러 코드는 없습니다 — 공통 에러만 나옵니다. 무료입니다.

Claude 모델 (anthropic/*, ridm/claude-*)

  • 모델 (13종): anthropic/claude-fable-5-1 · anthropic/claude-opus-5-5 · anthropic/claude-sonnet-5-5 · anthropic/claude-haiku-5-5 · anthropic/claude-fable-5 · anthropic/claude-opus-5 · anthropic/claude-sonnet-5 · anthropic/claude-opus-4-8 · anthropic/claude-opus-4-7 · anthropic/claude-opus-4-6 · anthropic/claude-opus-4-5 · anthropic/claude-sonnet-4-6 · anthropic/claude-haiku-4-5 (리셀) / 같은 이름에 ridm/ 접두사를 붙이면 RIDM Memory API (리셀가 × 1.3).
  • 요청 형식은 그대로: OpenAI 형식으로 보내면 서버가 Claude 형식으로 바꿔 호출합니다. system 메시지가 여러 개여도 하나로 합쳐 전달됩니다.
  • temperature·top_p 무시: Claude 5.x는 샘플링 값을 받지 않아 보내도 반영되지 않습니다(오류 없음). 응답 톤은 system 프롬프트로 조절하세요.
  • max_tokens: 생략하면 8,192. Claude Fable 5·5.1, Opus 5·5.5는 내부 추론이 항상 켜져 있어 추론 토큰도 출력 토큰으로 과금되며, 답변이 잘리지 않도록 요청한 max_tokens에 1,500토큰 여유를 더해 호출합니다.
  • Haiku 5.5 장문 구간: 한 요청의 입력이 10만 토큰을 넘으면 Anthropic 공식 가격이 ×5 구간이라 그 요청은 입력·출력 토큰이 ×5로 과금됩니다.
  • 지원 범위: 텍스트 대화(스트리밍 포함). Claude 모델은 도구 호출(tools·tool_choice·parallel_tool_calls)을 지원하지 않아 400 TOOLS_UNSUPPORTED_FOR_MODEL을 반환합니다 — OpenAI·Google 리셀 모델은 도구 호출을 지원합니다. 이미지 입력은 지원하지 않습니다.

사고(추론) 모델과 max_tokens: 숨은 사고(thinking/reasoning)를 쓰는 모델은 사고 토큰이 출력 상한에 포함됩니다. 서버가 여유분을 더해 호출하지만 max_tokens가 매우 작으면(예: 16) 본문이 비어 있고 finish_reason: "length"로 응답할 수 있으며, 상류에서 소모된 토큰은 과금됩니다. 권장 최소값은 64 이상입니다. 해당 모델: openai/gpt-5 · openai/gpt-5-mini · openai/gpt-5-nano · openai/gpt-6-astra · openai/o3 · openai/o3-mini · openai/o4-mini · anthropic/claude-opus-5 · anthropic/claude-opus-5-5 · anthropic/claude-fable-5 · anthropic/claude-fable-5-1 · google/gemini-3.1-pro (ridm/ 접두사 동명 모델 포함).

사고용 여유분과 청구 토큰: 사고를 끌 수 없는 모델(openai/gpt-5 · openai/gpt-5-mini · openai/gpt-5-nano · openai/gpt-6-astra · openai/o3 · openai/o3-mini · openai/o4-mini · google/gemini-3.1-pro)과 thinking이 항상 켜진 모델(anthropic/claude-opus-5 · anthropic/claude-opus-5-5 · anthropic/claude-fable-5 · anthropic/claude-fable-5-1)은 (ridm/ 접두사 동명 모델 포함) 답변을 보장하기 위해 요청한 max_tokens 위에 서버가 사고용 여유분(최대 1,500토큰)을 더해 호출합니다. 사고 토큰도 출력 단가로 청구되므로 실제 청구되는 출력 토큰이 max_tokens보다 클 수 있습니다(예: max_tokens 200 요청에 사고 747 + 본문 81 = 828토큰 청구). 비용 상한을 엄격히 묶어야 하면 google/gemini-3.5-flash처럼 사고가 꺼지는 모델을 쓰세요.

POST /api/v1/partners/v1/chat/completions

요청 파라미터 (메모리 레이어 없음)

파라미터 타입 필수 설명
model string O ridm/gpt-5.6-terra(기억) · 리셀은 openai/gpt-5.2, google/gemini-3.5-flash 등 — 전체 목록은 요금
messages array O OpenAI 형식 메시지
temperature number 0.0~2.0 (생략 시 모델 기본값). top_p(0~1), presence_penalty·frequency_penalty(-2~2), seed, stop, response_format, tools·tool_choice·parallel_tool_calls도 RIDM과 같은 범위로 받습니다 (Claude는 도구 호출 불가)
systemPrompt string 무시됨 — 리셀 모델에서는 systemPrompt가 무시되므로 messages에 {"role":"system"}으로 넣으세요
fileIds string[] 주입할 문서 ID (UUID, 최대 5개, 명시 주입만)
max_tokens number 최대 출력 토큰 (1~8,192, 초과 시 400). 사고(추론) 모델은 사고 토큰이 이 상한에 포함되므로 너무 작으면(예: 16) 본문이 비고 finish_reason: "length"가 올 수 있으며, 상류가 소모한 토큰은 과금됩니다. 권장 최소 64 이상.
emotion boolean 무시됨 (감정 기능이 꺼진 계정은 emotion:true 시 403 FEATURE_DISABLED)
summaryEvery number 무시됨

사용 예시 (OpenAI SDK 호환)

const client = new OpenAI({
  apiKey: 'your-float-api-key',
  baseURL: 'https://api.hrum.ai/api/v1/partners/v1',
  defaultHeaders: { 'X-Partner-Api-Key': 'your-float-api-key' }
});

const response = await client.chat.completions.create({
  model: 'openai/gpt-5.6-terra',
  messages: [{ role: 'user', content: 'Hello' }]
});
// RIDM 모델 예: model: 'ridm/gpt-5.6-terra'

응답

비스트리밍 응답은 OpenAI 호환 스키마에 costUsd·costWon·requestId가 더해집니다. stream: true는 OpenAI chunk 형식이 아니므로 OpenAI SDK의 스트림 파서로 읽을 수 없습니다 — 스트리밍(SSE) 형식을 참고해 직접 파싱하세요.

{
  "id": "chatcmpl-1791452814000",
  "object": "chat.completion",
  "created": 1791452814,
  "model": "openai/gpt-5.6-terra",
  "usage": { "prompt_tokens": 9, "completion_tokens": 12, "prompt_tokens_details": { "cached_tokens": 0 } },
  "choices": [
    { "index": 0, "message": { "role": "assistant", "content": "Hello! How can I help?" }, "finish_reason": "stop" }
  ],
  "costUsd": 0.000162,
  "costWon": 1,
  "requestId": "resale-..."
}

에러 코드는 RIDM Memory API의 에러 표와 같습니다. 리셀 모델에는 일일 지출 상한(RATE_LIMITED)이 함께 걸립니다.

AI 사주 리딩 API

생성형 AI가 사주를 분석해 맞춤형 운세 해석과 조언을 제공합니다. 토큰 기반 과금이며, 해석 결과만 반환합니다.

POST /api/v1/partners/v1/reading

사주를 기반으로 AI 사주 리딩을 생성합니다. 내부 프롬프트와 이론은 비공개입니다.

요청 파라미터

파라미터 타입 필수 설명
speed string fast(기본, 빠른 응답) | value(응답 느림, 요금 20% 할인)
birth object birth 또는 saju 중 하나 {year, month, day, hour?, minute?, gender, calendar?}. calendar: solar(기본) | lunar — 음력은 양력으로 변환해 계산
saju object birth 또는 saju 중 하나 4기둥 직접 입력 {"year":{"stem":"경","branch":"오"},"month":{…},"day":{…},"hour"?:{…}}. birth와 함께 보내면 교차 검증 후 saju 우선(sajuMismatch)
year / month / day / gender number / string 평면 필드(레거시). 네 값이 모두 있을 때만 birth(양력)로 인정됩니다. birth·saju·평면 필드 어느 것도 없으면 400 MISSING_SUBJECT_INPUT
hour / minute number 출생시간 0-23 / 분 0-59 (선택). hour 생략 시 시주를 빼고 년·월·일 3주 기준으로 해석합니다
question string O 운세 풀이에 대한 질문 (예: "내 인생 경로는?")
tone string 응답 톤 ("formal", "casual", "warm", 기본값: "warm")
candor string 말결 — 답변의 직설 정도. t / f(기본). 과금 없음. 말결 (가칭) 참고
persona string 앱이 고른 캐릭터(이름·말투·태도, 최대 2,000자). 말투에만 적용되고 사주 해석·안전 경계·길이 지시보다 우선하지 않음. 질문처럼 입력으로 과금(글자수 × 0.25). 여러 캐릭터를 두면 서버 허용목록에서 골라 보낼 것 — 브라우저가 보낸 문장을 그대로 넣지 말 것
maxLength number 최대 응답 길이 (100~2,000, 기본값: 800)
tools object 추가 기능 플래그. 예: {"astrology": true, "youtube": true}. 모르는 키는 400 UNKNOWN_TOOL
dream string 엔드유저가 말한 꿈 내용 (1~1,000자). 보내면 꿈해몽이 풀이에 자동 포함됩니다. 포함 항목 참고
exclude string[] 포함 항목에서 뺄 것. ziwei(자미두수) · astro(점성술) · dream(꿈해몽) · tarot(타로). 뺀 항목은 근거도 요금도 빠집니다. 예: ["ziwei", "astro"]. 예전 값 today는 받아 주지만 아무 효과가 없습니다(오늘의 운세는 더 이상 포함 항목이 아님)
tarot object 보내면 이 풀이에 쓸 타로 카드를 뽑아 함께 읽습니다. 타로 API와 같은 옵션: count(1~10) 또는 spread 중 하나, reversed, seed. 카드 언어는 질문 언어를 따릅니다. 뽑힌 카드(이미지 주소 포함)는 응답 tarot에 옵니다
only string 사주 AI 안의 AI 하나만 따로 부릅니다. saju · ziwei · astro · tarot · dream. exclude와 함께 보내면 400. AI 하나만 따로 참고

요청 파라미터 — tools 옵션

옵션 타입 설명
astrology boolean true 시 서양 점성술 네이탈 차트를 리딩에 반영. 별도 과금 없음
youtube boolean true 시 question 안의 유튜브 링크 자막을 주입 (자막 글자수 × 0.25를 입력으로 과금). 최상위 youtube 필드는 쓰지 않습니다 — 반드시 tools 안에
fileIds string[] 주입할 문서 ID (UUID, 최대 5개)

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/reading \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "올해 이직해도 괜찮을까요?",
    "birth": { "year": 1990, "month": 5, "day": 15, "hour": 14, "minute": 30, "gender": "female" },
    "maxLength": 600,
    "tone": "warm"
  }'

응답 예시

{
  "success": true,
  "data": {
    "interpretation": "당신의 사주에서 보이는 특징은...",
    "sajuSource": "computed",
    "usage": {
      "inputTokens": 450,
      "outputTokens": 280
    },
    "costPoints": 18
  }
}

응답 필드

필드 설명
interpretation해석 텍스트
sajuSourcecomputed(birth로 계산) | provided(saju 입력 사용)
sajuMismatchbirth와 saju를 함께 보냈는데 기둥이 다를 때의 경고 ({pillars, computed, note}), 그 외 null/생략
toolsUsed실제 실행된 도구 (예: ["youtube"])
toolsSkipped요청했지만 건너뛴 도구 [{tool, reason}], reason: unavailable | cap_exceeded | error | auto_rejected
filesUsed주입된 문서 ID (tools.fileIds 사용 시)
usage참고용 토큰 수 (inputTokens는 사주·시스템 프롬프트 포함 실제값 — 과금 기준 아님)
costPoints실제 차감액(₩)
modulesUsed실제 포함된 항목 (예: ["saju","ziwei","astro"], saju가 맨 앞). 포함 항목 기능이 켜져 있을 때만 붙습니다
moduleFees포함된 항목별 고정 요금(₩) (예: {"ziwei":50,"astro":20}). 이미 costPoints에 들어 있습니다. 포함 항목 기능이 켜져 있을 때만 붙습니다
tarot타로가 포함됐을 때 뽑힌 카드 — 타로 API 응답과 같은 모양(cards[]의 imageUrl 포함, question·costPoints 제외)
only요청의 only 값을 그대로 돌려줍니다(보냈을 때만). only가 saju가 아니면 사주를 쓰지 않으므로 sajuSource가 없습니다
support위기 상담 안내 {message, contacts:[{name, number, hours}]}. dream 내용이 위기 표현이면 AI를 부르지 않고 interpretation에 도움 안내를 담아 costPoints: 0으로 돌려줍니다. 안전 확인을 하지 못한 경우에도 붙습니다

에러 코드

공통 에러 외에 이 API에서 나오는 코드:

HTTP 코드
400MISSING_SUBJECT_INPUT · INVALID_SAJU_PILLAR · INVALID_LUNAR_DATE · UNKNOWN_TOOL · INVALID_FILE_IDS · FILE_IDS_TOO_MANY · INVALID_FILE_ID_TYPE · FILE_NOT_FOUND · INVALID_TOOL_MODE · MEMORY_REQUIRES_MEMORY_ENDPOINT · EMOTION_REQUIRES_MEMORY · MISSING_ENDUSER_ID(메모리) · ONLY_WITH_EXCLUDE · ONLY_REQUIRES_INPUT · ONLY_DOMAIN_UNAVAILABLE · ONLY_INPUT_INVALID(AI 하나만 따로)
402INSUFFICIENT_BALANCE — 호출 전 maxLength × 2 토큰(value 등급 또는 한국어가 아닌 질문은 +4,000)의 출력 비용을 미리 예약하므로, 최종 청구액이 더 작아도 거절될 수 있습니다
403FEATURE_DISABLED — 계정에서 꺼진 기능(youtube·files·emotion) 사용

포함 항목 — 자미두수 · 점성술 · 꿈해몽 · 타로

사주 AI 안에 사주·자미두수·점성술·타로·꿈해몽 AI가 다 들어 있습니다. 오늘 일진은 따로 부르지 않아도 모든 풀이에 기본으로 반영됩니다(추가 요금 없음). 통합으로 한 번 부르면 포함된 항목의 계산 결과를 근거로 함께 풀이하고, 빼고 싶은 항목은 exclude로 뺍니다. 필요한 AI 하나만 부르려면 only를 씁니다. 계산 결과만 필요하면 같은 항목을 계산형 엔드포인트(/ziwei-chart · /astrology(점성술 토탈) · /dream · /tarot)로 하나씩 따로 불러 직접 앱을 만들 수도 있습니다. /reading/memory에서도 같은 필드로 씁니다.

항목언제 포함되나근거로 넣는 것요금 (= 단독 엔드포인트 1회 가격)
자미두수 ziwei생년월일 + 출생 시각(hour)이 있으면 기본명반 요약 — 명궁·신궁·삼방사정, 생년사화, 현재 대한·유년/ziwei-chart
점성술 astro생년월일이 있으면 기본 (시각 없어도 됨)네이탈 차트 요약 — 태양·달·수성~토성의 별자리와 도수, 상승궁(가능할 때)₩20 (점성술 단독 상품은 /astrology)
꿈해몽 dreamdream에 꿈 내용을 보내면꿈 내용 + 꿈에서 찾은 상징 낱말/dream (후보가 없으면 0원)
타로 tarottarot을 보내면이 요청에서 뽑힌 카드와 정·역방향 뜻 (응답 tarot에 이미지 주소와 함께)/tarot

요청 예시 (POST /api/v1/partners/v1/reading)

{
  "birth": { "year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "female" },
  "question": "어젯밤 꿈이 계속 마음에 걸려요. 요즘 저한테 무슨 뜻일까요?",
  "dream": "큰 뱀이 집 안으로 들어와 내 발 옆에 똬리를 틀었어요",
  "tarot": { "spread": "past-present-future" }
}

응답 예시

{
  "success": true,
  "data": {
    "interpretation": "뱀이 집 안으로 들어와 곁에 머무는 꿈은...",
    "sajuSource": "computed",
    "modulesUsed": ["saju", "dream", "ziwei", "astro", "tarot"],
    "moduleFees": { "dream": 30, "ziwei": 50, "astro": 20, "tarot": 20 },
    "tarot": {
      "lang": "ko",
      "count": 3,
      "spread": { "type": "past-present-future", "name": "과거·현재·미래", "description": "시간 흐름을 따라가는 질문", "positions": ["과거", "현재", "미래"] },
      "reversedAllowed": true,
      "seed": null,
      "cards": [
        { "position": 1, "positionLabel": "과거", "id": "wands_10", "name": "완드의 10", "nameEn": "Ten of Wands", "arcana": "wands", "reversed": true, "keywords": ["내려놓기", "위임", "해방"], "meaning": "무거운 짐을 마침내 내려놓거나 도움을 받기 시작하는 시기.", "imageUrl": "https://console.hrum.ai/tarot/cards/v2/wands_10.webp" }
      ],
      "cardBackUrl": "https://console.hrum.ai/tarot/cards/v2/back.webp"
    },
    "usage": {
      "inputTokens": 3140,
      "outputTokens": 760
    },
    "costPoints": 162
  }
}

사주만 (다른 항목 모두 빼기) (POST /api/v1/partners/v1/reading)

{
  "birth": { "year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "female" },
  "question": "올해 이직해도 괜찮을까요?",
  "exclude": ["ziwei", "astro"]
}

AI 하나만 따로 — only

사주 AI 안에 사주·자미두수·점성술·타로·꿈해몽 AI가 다 들어 있습니다. 위처럼 통합으로 한 번에 부르는 대신, 필요한 AI 하나만 only로 따로 부를 수 있습니다. 그 AI는 자기 근거(명반·차트·뽑힌 카드·해몽 후보)만 보고 답하며, 다른 항목은 넣지 않습니다.

only무엇을 부르나필요한 입력요금
saju사주 AI (다른 항목 없이 사주만)birth 또는 saju토큰 가격만 (고정 요금 없음)
ziwei자미두수 AIbirth + 출생 시각(hour)토큰 가격 + /ziwei-chart 1회 가격
astro점성술 AIbirth (시각 없어도 됨)토큰 가격 + ₩20 (점성술 항목 고정 요금)
tarot타로 AItarot 옵션 — birth 불필요토큰 가격 + /tarot 1회 가격
dream꿈해몽 AIdream 꿈 내용 — birth 불필요토큰 가격 + /dream 1회 가격

요청 예시 — 타로 AI만 (POST /api/v1/partners/v1/reading)

{
  "question": "지금 준비 중인 이직, 이대로 밀고 나가도 될까요?",
  "only": "tarot",
  "tarot": { "spread": "past-present-future" }
}

응답 예시

{
  "success": true,
  "data": {
    "interpretation": "과거 자리의 완드의 10 역방향은 오래 짊어진 짐을...",
    "only": "tarot",
    "modulesUsed": ["tarot"],
    "moduleFees": { "tarot": 20 },
    "tarot": {
      "lang": "ko",
      "count": 3,
      "spread": { "type": "past-present-future", "name": "과거·현재·미래", "description": "시간 흐름을 따라가는 질문", "positions": ["과거", "현재", "미래"] },
      "reversedAllowed": true,
      "seed": null,
      "cards": [
        { "position": 1, "positionLabel": "과거", "id": "wands_10", "name": "완드의 10", "nameEn": "Ten of Wands", "arcana": "wands", "reversed": true, "keywords": ["내려놓기", "위임", "해방"], "meaning": "무거운 짐을 마침내 내려놓거나 도움을 받기 시작하는 시기.", "imageUrl": "https://console.hrum.ai/tarot/cards/v2/wands_10.webp" }
      ],
      "cardBackUrl": "https://console.hrum.ai/tarot/cards/v2/back.webp"
    },
    "usage": {
      "inputTokens": 1180,
      "outputTokens": 720
    },
    "costPoints": 37
  }
}

주의사항

AI 사주 리딩 · 메모리 API

엔드유저별 대화 기억을 호스팅하고, 재방문 시 과거 맥락을 이어서 풀이합니다. 개인화된 지속적 상담 경험을 제공합니다. 가격은 AI 사주 리딩 토큰 단가의 1.3배입니다 (1M 토큰당 입력 3,754원 · 출력 30,030원 — 리딩은 입력 2,888원 · 출력 23,100원, 부가세 별도).

POST /api/v1/partners/v1/reading/memory

엔드유저별 기억을 바탕으로 AI 사주 리딩을 생성합니다.

요청 파라미터

파라미터 타입 필수 설명
endUserId string O 파트너사가 발급한 엔드유저 ID (고유값 필수)
speed string fast(기본, 빠른 응답) | value(응답 느림, 요금 20% 할인)
birth object birth 또는 saju 중 하나 {year, month, day, hour?, minute?, gender, calendar?}. calendar: solar(기본) | lunar — 음력은 양력으로 변환해 계산
saju object birth 또는 saju 중 하나 4기둥 직접 입력 {"year":{"stem":"경","branch":"오"},"month":{…},"day":{…},"hour"?:{…}}. birth와 함께 보내면 교차 검증 후 saju 우선(sajuMismatch)
year / month / day / gender number / string 평면 필드(레거시). 네 값이 모두 있을 때만 birth(양력)로 인정됩니다. birth·saju·평면 필드 어느 것도 없으면 400 MISSING_SUBJECT_INPUT
hour / minute number 출생시간 0-23 / 분 0-59 (선택). hour 생략 시 시주를 빼고 년·월·일 3주 기준으로 해석합니다
question string O 운세 풀이에 대한 질문
tone string 응답 톤 ("formal", "casual", "warm", 기본값: "warm")
candor string 말결 — 답변의 직설 정도. t / f(기본). 과금 없음. 말결 (가칭) 참고
persona string 앱이 고른 캐릭터(이름·말투·태도, 최대 2,000자). 말투에만 적용되고 사주 해석·안전 경계·길이 지시보다 우선하지 않음. 질문처럼 입력으로 과금(글자수 × 0.25). 여러 캐릭터를 두면 서버 허용목록에서 골라 보낼 것 — 브라우저가 보낸 문장을 그대로 넣지 말 것
tagFilter string[] 검색할 메모리 태그 필터 (선택사항)
maxLength number 최대 응답 길이 (100~2,000, 기본값: 800)
recallCount number 회상할 과거 기억 수 (0~20, 기본 3)
tools object 추가 기능 플래그. 예: {"astrology": true}. 모르는 키는 400 UNKNOWN_TOOL

요청 파라미터 — tools 옵션

옵션 타입 설명
astrology boolean true 시 서양 점성술 네이탈 차트를 리딩에 반영. 별도 과금 없음
fileIds string[] 주입할 문서 ID (UUID, 최대 5개). 생략 시 질문 태그와 겹치는 분석 문서를 자동 회상(filesRecalled)
emotion / summaryEvery boolean / integer 감정 상태 분석 (응답 emotion) / 요약·감정 갱신 주기 1~20 (기본 5)
youtube boolean 유튜브 주입은 /reading에서만 지원(/reading/memory는 아직 미지원) — 보내도 자막이 주입되지 않습니다

응답 예시

{
  "success": true,
  "data": {
    "interpretation": "당신의 사주에서 보이는 특징은...",
    "sajuSource": "computed",
    "memory": {
      "used": true,
      "summaryInjected": false,
      "notesInjected": 2,
      "tags": ["직장"],
      "overheadTokens": 120
    },
    "usage": {
      "inputTokens": 750,
      "outputTokens": 380
    },
    "costPoints": 36,
    "toolsUsed": ["memory"]
  }
}

그 밖의 응답 필드(sajuMismatch, toolsSkipped, filesUsed)는 AI 사주 리딩 API와 같고, 자동 회상된 문서는 filesRecalled, tools.emotion: true이면 emotion이 붙습니다. 과금 기준(유튜브 제외)도 리딩 API와 같습니다.

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/reading/memory \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "endUserId": "member-1024",
    "question": "지난번에 얘기한 이직 건, 다음 달에 움직여도 될까요?",
    "birth": { "year": 1990, "month": 5, "day": 15, "hour": 14, "minute": 30, "gender": "female" }
  }'

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400MISSING_ENDUSER_IDendUserId가 비었거나 공백뿐
400MISSING_SUBJECT_INPUT · INVALID_SAJU_PILLAR · INVALID_LUNAR_DATE출생 정보·사주 입력 오류 (리딩 API와 같음)
400UNKNOWN_TOOL · INVALID_TOOL_MODE · INVALID_FILE_IDS · FILE_IDS_TOO_MANY · INVALID_FILE_ID_TYPE · FILE_NOT_FOUNDtools·fileIds 오류. 이 경로는 tools.emotion·tools.summaryEvery도 받습니다
400ONLY_WITH_EXCLUDE · ONLY_REQUIRES_INPUT · ONLY_DOMAIN_UNAVAILABLE · ONLY_INPUT_INVALIDAI 하나만 따로 관련 오류
402INSUFFICIENT_BALANCE출력 비용을 미리 예약하지 못함 (리딩 API와 같은 방식)
403FEATURE_DISABLED계정에서 꺼진 기능(youtube·files·emotion) 사용

메모리 관리 API

AI 사주 리딩 메모리형(POST /reading/memory)과 RIDM 채팅(ridm/* 모델의 user)은 같은 엔드유저 기억 저장소를 씁니다. 아래 경로의 {endUserId} 는 그때 보낸 값 그대로입니다(예: 채팅을 세션 단위로 나눴다면 회원id:대화id 같은 키 전체).

엔드포인트 메서드 설명
/api/v1/partners/v1/memory/{endUserId} GET 엔드유저의 저장된 메모리 조회 → {endUserId, summary, metaSummary, tags, profile, concerns, facts} (profile·facts는 현재 항상 null / [])
/api/v1/partners/v1/memory/{endUserId}/messages GET 엔드유저의 메시지 목록 조회. 쿼리 limit(기본 50, 최대 200)·offset → {endUserId, messages:[{id, role, content, tags, createdAt}], offset, limit, total}
/api/v1/partners/v1/memory/search?endUserId={endUserId}&tags=… GET 메모리 태그 검색 (tags는 쉼표 구분, RIDM 메모리 요금에 포함, 별도 과금 없음) → {endUserId, query:{tags}, results} (results 최대 20개)
/api/v1/partners/v1/memory/{endUserId} DELETE 엔드유저의 모든 메모리와 그 엔드유저로 올린 파일(/files)까지 삭제 → 204 (본문 없음)

요청·응답 예시

# 저장된 메모리 조회
curl https://api.hrum.ai/api/v1/partners/v1/memory/member-1024 \
  -H "X-Partner-Api-Key: your-api-key"

{
  "success": true,
  "data": {
    "endUserId": "member-1024",
    "summary": "이직을 고민 중이며 다음 달 면접을 앞두고 있다...",
    "metaSummary": "...",
    "tags": ["이직", "면접"],
    "profile": null,
    "concerns": ["..."],
    "facts": []
  }
}
# 메시지 목록 (오래된 순)
curl "https://api.hrum.ai/api/v1/partners/v1/memory/member-1024/messages?limit=2&offset=0" \
  -H "X-Partner-Api-Key: your-api-key"

{
  "success": true,
  "data": {
    "endUserId": "member-1024",
    "messages": [
      { "id": "...", "role": "user", "content": "이직 고민이 있어요", "tags": ["이직"], "createdAt": "2026-10-11T09:00:00.000Z" },
      { "id": "...", "role": "assistant", "content": "...", "tags": [], "createdAt": "2026-10-11T09:00:04.000Z" }
    ],
    "offset": 0,
    "limit": 2,
    "total": 18
  }
}
# 태그 검색
curl "https://api.hrum.ai/api/v1/partners/v1/memory/search?endUserId=member-1024&tags=이직,면접" \
  -H "X-Partner-Api-Key: your-api-key"

{
  "success": true,
  "data": {
    "endUserId": "member-1024",
    "query": { "tags": ["이직", "면접"] },
    "results": [
      { "id": "...", "summary": "다음 달 면접 일정을 이야기함", "level": 0, "tags": ["면접"], "createdAt": "2026-10-11T09:00:00.000Z" }
    ]
  }
}
# 전부 삭제 (메모리 + 업로드 파일) — 204, 본문 없음
curl -X DELETE https://api.hrum.ai/api/v1/partners/v1/memory/member-1024 \
  -H "X-Partner-Api-Key: your-api-key"

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404END_USER_NOT_FOUND처음 보는 endUserId (네 경로 공통)
400MISSING_PARAM검색 — endUserId 누락
400MISSING_TAGS검색 — tags가 비어 있음

주의사항

사주 리포트 API

질문 없이 버튼 한 번으로 총평·재물·연애·대운까지 모든 사주 해석이 한 번에 나옵니다. 대화형(reading)과 별개의 단품 결제형 상품. 전용 모델 ridm/saju-report(RIDM 자체 리포트 특화 모델 — 원샷 상품 특성상 메모리 미장착)가 기본입니다. 리딩 API의 응답 상한은 2,000자입니다 — 그 이상의 장문 해설(최대 12섹션, 약 6,000자)은 이 리포트 API를 사용하세요.

POST /api/v1/partners/v1/reading/report

생년월일(또는 사주)을 넣으면 섹션별 마크다운 사주 리포트를 반환합니다.

요청 파라미터

파라미터 타입 필수 설명
birthobjectbirth 또는 saju 중 하나{year, month, day, hour?, minute?, gender, calendar?} — calendar: "lunar"는 양력으로 변환해 계산
sajuobjectbirth 또는 saju 중 하나4기둥 직접 입력 {"year":{"stem":"경","branch":"오"},"month":{…},"day":{…},"hour"?:{…}}
year / month / day / gendernumber / string선택평면 필드(레거시, 양력). 네 값이 모두 있을 때만 birth로 인정. birth·saju 모두 없으면 400 MISSING_SUBJECT_INPUT
hour / minutenumber선택hour 생략 시 시주를 빼고 년·월·일 3주 기준으로 해석합니다. minute은 hour 없이 보내면 무시
tonestring선택warm(기본) | casual | formal
candorstring선택말결 — t | f(기본). 좋은 점과 어려운 점의 배치·강도만 바뀌고 섹션 구성·의료·법률·투자 확답 금지는 그대로. 과금 없음. 말결 (가칭) 참고
personastring선택말투·캐릭터 지시 (최대 2,000자 — 이 길이만 입력 과금)
sectionsstring[]선택섹션 제목 목록 (1~12개, 제목당 최대 30자). 기본 8개: 총평 · 타고난 기질 · 재물운 · 연애·인연 · 직업·적성 · 올해와 내년 · 10년 대운 흐름 · 마지막 한 줄 처방

리포트는 항상 한국어로 작성됩니다. speed·tools·lang·question 등 위 표에 없는 필드를 보내면 400으로 거절됩니다.

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/reading/report \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "birth": { "year": 1990, "month": 5, "day": 15, "hour": 14, "minute": 30, "gender": "female" },
    "sections": ["총평", "재물운", "연애·인연"]
  }'

응답

{
  "success": true,
  "data": {
    "report": "## 총평\n...",        // 섹션별 마크다운 전문
    "sections": ["총평", "..."],
    "sajuSource": "computed",
    "usage": { "inputTokens": 5756, "outputTokens": 2019 },
    "costPoints": 77,
    "model": "ridm/saju-report"
    // "candorApplied": "t"  — candor를 t로 보낸 경우에만 (t | f)
  }
}

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400MISSING_SUBJECT_INPUTbirth·saju가 모두 없음
400INVALID_SAJU_PILLARsaju의 기둥이 60갑자가 아님
400INVALID_LUNAR_DATE없는 음력 날짜
402INSUFFICIENT_BALANCE리포트 최대 길이(6,000토큰)의 출력 비용을 미리 예약하지 못함

유튜브 요약 API

유튜브 동영상의 자막을 추출하여 요약 및 Q&A 분석을 제공합니다. 자막이 없는 경우 400 에러(무과금).

POST /api/v1/partners/v1/youtube/summary

요청 파라미터

파라미터 타입 필수 설명
url string O 유튜브 URL (예: https://www.youtube.com/watch?v=dQw4w9WgXcQ) 또는 11자리 영상 ID
question string 특정 질문 (선택사항 — 있으면 Q&A 모드)
maxLength number 요약 길이 목표 (100~4,000, 기본 1,000). 출력 토큰 예산으로 쓰이며 정확한 글자수를 보장하지 않습니다
tone string 톤 선택: warm | casual | formal (기본: casual). question(Q&A) 모드에서는 무시됩니다
lang string 자막 조회 언어 (기본: ko). 요약 결과는 언어와 무관하게 한국어로 작성됩니다

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/youtube/summary \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "maxLength": 1000 }'

응답 예시

{
  "success": true,
  "data": {
    "summary": "## 0:00~1:30 오프닝\n영상 제목 설명...\n\n## 1:30~5:00 본론\n...",
    "video": {
      "videoId": "dQw4w9WgXcQ",
      "title": "Video Title",
      "channel": "Channel Name"
    },
    "transcriptSource": "supadata",
    "usage": {
      "promptTokens": 7059,
      "completionTokens": 1384
    },
    "costPoints": 78,
    "model": "ridm/gpt-5.6-terra"
  }
}

주의사항

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400INVALID_YOUTUBE_URLURL에서 영상 ID를 찾지 못함
400TRANSCRIPT_UNAVAILABLE자막이 없는 영상 (과금 없음)
400LLM_GENERATION_FAILED요약 생성 실패 또는 빈 결과
402INSUFFICIENT_BALANCE요약 비용을 미리 예약하지 못함
403FEATURE_DISABLED계정에서 유튜브 기능이 꺼져 있음

기능 켜고 끄기

유튜브 요약 API는 파트너 계정 및 요청 단위로 옵트인할 수 있습니다. 콘솔의 "기능 설정"에서 전역 활성화 상태를 관리하며, chat은 최상위 youtube:true, reading은 tools: { "youtube": true }를 명시해 요청별로 활성화합니다.

문서 처리 API

PDF, Word(.docx), Excel(.xlsx), PowerPoint(.pptx), CSV, 텍스트, Markdown, 한글(.hwpx) 문서를 업로드하고, AI로 분석하여 맥락 기반 지식베이스에 적재할 수 있습니다. 문서는 자동 추출되며, "AI 분석" 옵션을 활성화하면 문서를 맥락 단위로 분할하고 각 구간의 요약과 검색 태그를 생성하여 저장합니다.

지원 형식 및 제한

항목 사양
지원 형식 .pdf, .docx, .xlsx, .pptx, .csv, .txt, .md, .hwpx (한글 구버전 .hwp는 미지원)
최대 파일 크기 10 MB
최대 추출 텍스트 200,000자 (초과 시 charsCapped: true 표시)
요청당 최대 파일 5개 (fileIds)
chat 주입 크기 파일당 최대 40,000자 / 전체 80,000자
분석 청크 범위 3~8구간 (장문서 40,000자 이상 시 2회 패스)
태그 범위 구간당 최대 10개 태그, 전체 합산 최대 10개

엔드포인트

메서드 경로 설명
POST /api/v1/partners/v1/files 파일 업로드 (multipart)
GET /api/v1/partners/v1/files 파일 목록 조회 (파트너 스코프). 쿼리 endUserId, limit(기본 20, 최대 100), offset → {files, total, limit, offset}
GET /api/v1/partners/v1/files/:id 파일 상세 조회 (추출 텍스트 포함)
DELETE /api/v1/partners/v1/files/:id 파일 삭제

파일 업로드

POST /api/v1/partners/v1/files

Multipart form 형식으로 파일을 업로드합니다.

요청 필드

필드 타입 필수 설명
file binary O 업로드 파일
endUserId string 엔드유저 ID (분석할 때 필수)
analyze 'true'|'false' AI 분석 활성화 (기본: 'false')

cURL 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/files \
  -H "X-Partner-Api-Key: YOUR_API_KEY" \
  -F "[email protected]" \
  -F "endUserId=user_123" \
  -F "analyze=true"

응답 예시

{
  "success": true,
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "sample.pdf",
    "originalName": "sample.pdf",
    "mime": "application/pdf",
    "size": 125000,
    "textLength": 8942,
    "charsCapped": false,
    "textPreview": "1장. 개요 ...",
    "summary": "문서 전체 요약...",
    "context": "문서 전체 요약...",
    "status": "analyzed",
    "analysisStatus": "analyzed",
    "analyzeRequested": true,
    "endUserId": "user_123",
    "usage": { "inputTokens": 3100, "outputTokens": 420, "costWon": 1 },
    "totalChunks": 3,
    "createdAt": "2026-08-26T12:34:56Z"
  }
}

목록·상세·삭제

# 목록 — 최신순, limit 1~100 (기본 20)
curl "https://api.hrum.ai/api/v1/partners/v1/files?endUserId=user_123&limit=20&offset=0" \
  -H "X-Partner-Api-Key: YOUR_API_KEY"

{
  "success": true,
  "data": {
    "files": [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "name": "sample.pdf", "status": "analyzed", ... } ],
    "total": 1,
    "limit": 20,
    "offset": 0
  }
}
# 상세 — 업로드 응답 필드 + extractedText + chunks
curl https://api.hrum.ai/api/v1/partners/v1/files/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "X-Partner-Api-Key: YOUR_API_KEY"

{
  "success": true,
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "sample.pdf",
    ...,
    "extractedText": "1장. 개요 ...",
    "chunks": [
      { "id": "...", "chunkIndex": 0, "context": "1장 요약...", "tags": ["개요"], "textLength": 2980 }
    ]
  }
}
# 삭제
curl -X DELETE https://api.hrum.ai/api/v1/partners/v1/files/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "X-Partner-Api-Key: YOUR_API_KEY"

{ "success": true, "data": { "deleted": true } }

Chat 및 리딩 연동

명시적 첨부: Chat completions 또는 AI 리딩 요청 시 fileIds 파라미터로 최대 5개의 문서를 주입할 수 있습니다. 분석된 파일은 문서 요약(최대 500자)과 원문 텍스트를, 미분석 파일은 원문 텍스트를 주입합니다(원문은 파일당 40,000자 / 전체 80,000자 제한, 자식 청크는 주입하지 않음). 주입된 문서는 마지막 사용자 메시지에 추가되어 입력 토큰에 포함됩니다. 리셀 모델은 fileIds 명시 주입만 지원합니다.

자동 회상 (ridm/* 전용): ridm/* chat 및 reading-memory 모델에서는 사용자 질문의 태그와 겹치는 분석된 파일을 자동으로 검색해 주입합니다(명시적 fileIds 미지정 시에만). 응답에 filesRecalled 배열로 회상된 파일 ID를 표시합니다.

Chat 요청 예시

{
  "model": "ridm/gpt-5.6-terra",
  "messages": [
    { "role": "user", "content": "이 문서에서 주요 내용을 요약해줄 수 있나요?" }
  ],
  "fileIds": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
  "user": "user_123"
}

응답 예시

{
  "id": "chatcmpl-abc123",
  "choices": [{
    "message": { "content": "문서의 주요 내용은..." },
    "finish_reason": "stop"
  }],
  "usage": { "prompt_tokens": 2500, "completion_tokens": 150 },
  "filesUsed": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"]
}

filesRecalled는 자동 회상된 파일이 있을 때만 붙습니다(비어 있으면 생략).

요금 및 과금

기능 켜고 끄기

문서 처리 기능은 파트너 계정 수준에서 활성화/비활성화할 수 있습니다. 콘솔의 "기능 설정"에서 관리하며, 비활성 시 모든 파일 요청에 대해 403 응답이 반환됩니다.

분석 상태 및 청크

GET /api/v1/partners/v1/files/:id 응답에는 analysisStatus, extractedText, chunks가 포함됩니다. chunks는 분석 완료이고 totalChunks가 2 이상일 때만 배열(각 청크: chunkIndex, context(요약), tags 최대 10개)이며, 그 외에는 null입니다. 업로드 응답에는 chunks가 없습니다.

상태 설명
ready 추출 완료·분석 없음 (analyze=false 또는 미지정 — analyzeRequested=false)
analyzing 분석 진행 중 (30,000자 초과 시 비동기)
analyzed 분석 완료 (context·tags·chunks[] 포함)
analyze_failed 분석 실패 (실패 사유는 응답에 포함되지 않음 — 다시 업로드하세요)

주의사항

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400FILE_NOT_PROVIDED업로드 — file 파트 없음
400INVALID_FILE_TYPE업로드 — 지원하지 않는 형식
400FILE_TOO_LARGE업로드 — 10MB 초과 (경우에 따라 코드 없는 413)
400END_USER_REQUIRED업로드 — analyze=true인데 endUserId 없음
400(없음)상세·삭제 — :id가 UUID가 아님
402INSUFFICIENT_BALANCE업로드 — 분석 비용을 미리 예약하지 못함
403FEATURE_DISABLED계정에서 문서 처리 기능이 꺼져 있음 (네 경로 공통)
404FILE_NOT_FOUND상세·삭제 — 없는 파일 또는 다른 계정의 파일
422EXTRACTION_FAILED업로드 — 문서에서 텍스트를 뽑지 못함

감정 상태 분석 (Emotion)

Chat API에서 요약 사이클에 편승하여 사용자와 AI의 감정 상태를 분석합니다. 별도의 LLM 호출이 없으며, 요약 프롬프트에만 감정 분석 블록을 추가하므로 추가 과금이 없습니다. ridm/* 모델에서만 지원됩니다.

개요

활성화 조건:

  1. Global: partner_emotion_enabled ✓
  2. Partner: 콘솔 기능 설정에서 "감정 상태 분석" 활성화
  3. Request: emotion: true 명시
  4. Model: ridm/* 계열만 지원

요약 주기 (summaryEvery)

감정 분석은 요약 사이클마다 자동으로 실행됩니다. summaryEvery 파라미터로 사이클 주기를 조절할 수 있습니다 (기본값: 5, 범위: 1–20).

요청 파라미터

파라미터 타입 필수 설명
emotion boolean 감정 상태 분석 활성화 (ridm/* 모델에서만, 기본값: false)
summaryEvery number 정수 1–20 (기본 5). 요약·감정 갱신 주기 (턴 단위)

요청 예시

{
  "model": "ridm/gpt-5.6-terra",
  "user": "user_123",
  "messages": [
    { "role": "user", "content": "오늘 하루가 힘들어..." }
  ],
  "emotion": true,
  "summaryEvery": 3
}

응답 필드

필드 경로 타입 설명
emotion object|null 감정 상태 (첫 사이클 전 또는 emotion=false 시 null/생략)
emotion.user object 사용자 감정
emotion.user.type10 string|null 10-class 감정 판정 (joyful|calm|curious|concerned|excited|thoughtful|neutral|sad|angry|surprised|null)
emotion.user.tone string|null 톤 (자유 문자열 — 고정 목록 아님, 예: "warm") 또는 null
emotion.user.distribution6 object 6-class 분포 {joy, sadness, anger, fear, disgust, surprise}, 합 = 1000 또는 0
emotion.assistant object AI 감정
emotion.assistant.distribution6 object AI의 6-class 분포
emotion.trend string|null 감정 추세 (improving|stable|declining|null)
emotion.updatedAt ISO 8601 마지막 감정 업데이트 시각
emotion.turnsSinceUpdate number 마지막 업데이트 이후 경과 턴 수

응답 예시

{
  "id": "chatcmpl-xyz",
  "choices": [{
    "message": { "content": "오늘 하루가 힘들었군요..." },
    "finish_reason": "stop"
  }],
  "emotion": {
    "user": {
      "type10": "sad",
      "tone": "warm",
      "distribution6": {"joy": 100, "sadness": 600, "anger": 50, "fear": 100, "disgust": 50, "surprise": 0}
    },
    "assistant": {
      "distribution6": {"joy": 200, "sadness": 100, "anger": 0, "fear": 0, "disgust": 0, "surprise": 0}
    },
    "trend": "stable",
    "updatedAt": "2026-08-26T12:34:56Z",
    "turnsSinceUpdate": 0
  },
  "usage": {...}
}

분포 필드 설명 (distribution6)

감정 값 설명
joy 0–1000 기쁨 강도
sadness 0–1000 슬픔 강도
anger 0–1000 분노 강도
fear 0–1000 두려움 강도
disgust 0–1000 혐오 강도
surprise 0–1000 놀람 강도

추세 판정 (trend)

스트리밍 응답 예시 (SSE)

스트리밍 모드(stream: true)에서는 감정 상태가 event: message_done SSE 이벤트의 데이터에 포함됩니다.

event: message_done
data: {"finish_reason":"stop","usage":{...},"costWon":2,"recalledMemories":[],"memorySaved":{"id":"turn-1756200000000","keywords":[]},"emotion":{"user":{"type10":"concerned","tone":"warm","distribution6":{...}},"assistant":{...},"trend":"stable","updatedAt":"2026-08-26T12:34:56Z","turnsSinceUpdate":0}}

data: [DONE]

비용 안내

주의사항

사주 이미지 API

사주의 오행과 에너지를 표현하는 AI 아트를 생성합니다. PNG 포맷으로 base64 인코딩되어 반환되며, 고정 ₩200 과금이 적용됩니다.

POST /api/v1/partners/v1/image

생년월일을 기반으로 사주 오행 이미지를 생성합니다.

요청 파라미터

파라미터 타입 필수 설명
year number O 출생년 (예: 1990)
month number O 출생월 (1-12)
day number O 출생일 (1-31)
hour number 출생시간 (0-23, 선택사항)
minute number 출생분 (0-59, 선택사항)
gender string O 성별 ("male" 또는 "female")
style string 스타일 ("background", "icon", "portrait", 기본값: "background") — 분위기만 바뀌고 크기는 동일

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/image \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "year": 1990, "month": 5, "day": 15, "hour": 14, "minute": 30, "gender": "female", "style": "portrait" }'

응답 예시

{
  "success": true,
  "data": {
    "imageDataUri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
    "costPoints": 200
  }
}

주의사항

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
402INSUFFICIENT_BALANCE잔액이 이미지 1장 가격보다 적음
504IMAGE_UPSTREAM_TIMEOUT80초 안에 이미지를 받지 못함 (과금 없음)

이미지 생성 API (모델 리셀)

POST /api/v1/partners/v1/images/generations

OpenAI Images API와 같은 형식으로 GPT-Image·Nano Banana 모델을 하나의 키로 호출합니다. 모델·해상도·품질마다 장당 가격이 정해져 있고, 표의 금액이 그대로 차감됩니다(생성된 장수 × 장당 가격). 프롬프트 입력 토큰 비용도 장당 가격에 포함되어 있어 별도로 붙지 않습니다.

요청 파라미터

파라미터타입필수설명
modelstringOopenai/gpt-image-2, openai/gpt-image-1-mini, google/nano-banana-pro, google/nano-banana, fal/nano-banana-pro, fal/nano-banana
promptstringO생성할 이미지 설명 (최대 1,000자). 초과 시 422 PROMPT_TOO_LONG(잔액 예약 뒤 검사하므로 잔액이 부족하면 402가 먼저 옴), 빈 문자열은 400 EMPTY_PROMPT
sizestringGPT-Image: 1024x1024(기본) · 1024x1536(세로) · 1536x1024(가로) · auto(모델이 선택 — 해당 품질의 가장 비싼 크기 가격으로 과금). 이전 값 256x256·512x512는 1024x1024, 1792x1024는 1536x1024, 1024x1792는 1024x1536으로 생성·과금됩니다. Nano Banana에서는 비율로만 쓰입니다 — aspect_ratio를 권장합니다.
qualitystringGPT-Image 전용: low · medium(기본) · high. 이전 값 standard=medium, hd=high로 그대로 동작합니다. Nano Banana에 low/medium/high를 보내면 400입니다.
aspect_ratiostringNano Banana 전용: 21:9 · 16:9 · 3:2 · 4:3 · 5:4 · 1:1(기본) · 4:5 · 3:4 · 2:3 · 9:16. 비율은 가격에 영향이 없습니다. size보다 우선합니다.
resolutionstringNano Banana 전용: Nano Banana는 1K만, Nano Banana Pro는 1K(기본) · 2K · 4K. 대문자 K만 허용합니다.
nnumber생성 장수 1~4 (기본 1). Google 직접 모델은 호출당 1장만 생성되며 1장만 과금됩니다.

GPT-Image에 aspect_ratio·resolution을 보내거나, 모델이 지원하지 않는 해상도를 요청하면 400 INVALID_PARAMETER로 거절되며 과금되지 않습니다.

그 밖의 에러: MODEL_NOT_FOUND·INVALID_MODEL(400, 없는 모델), INVALID_PROMPT(400, 상류가 프롬프트 거절), UPSTREAM_ERROR, RATE_LIMITED(현재 HTTP 400, 본문 status 429), INSUFFICIENT_BALANCE(402).

장당 가격 (₩, 부가세 별도)

가격표를 불러오는 중…

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/images/generations \
  -H "X-Partner-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/nano-banana-pro",
    "prompt": "A serene mountain landscape at sunset",
    "aspect_ratio": "16:9",
    "resolution": "2K"
  }'

응답 예시

{
  "created": 1791452814,
  "data": [ { "b64_json": "iVBORw0KGgo..." } ],
  "costWon": 225,
  "requestId": "..."
}

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400MODEL_NOT_FOUND가격표에 없는 모델
400INVALID_MODELopenai/·google/·fal/ 이외의 접두사
400INVALID_PARAMETER모델이 받지 않는 옵션 (GPT-Image에 aspect_ratio·resolution, Nano Banana에 quality, 지원하지 않는 해상도)
400EMPTY_PROMPT프롬프트가 공백뿐
400INVALID_PROMPT상류 모델이 프롬프트를 거절
400UPSTREAM_ERROR상류 오류 또는 이미지 0장 (과금 없음)
HTTP 400, 본문 status 429RATE_LIMITED상류 속도 제한 — 잠시 뒤 다시 시도
402INSUFFICIENT_BALANCE장당 가격 × n이 잔액보다 큼
422PROMPT_TOO_LONG프롬프트 1,000자 초과

오류 응답은 다른 API와 같은 {"success": false, ...} 형식입니다(성공 응답만 감싸지 않습니다).

만세력 API

POST /api/v1/partners/v1/manseryeok

사주 정체성 포함 — 응답 끝의 identity 필드에 삼재·신살·공망·통근·계절 강약·대운/세운 타임라인·사주 MBTI가 함께 들어갑니다(추가 요금 없음, 계산 실패 시 null).

원국 4주(년·월·일·시)의 천간·지지·한자·오행·십신·지장간·납음·12운성·신살을 계산합니다. 절기 기반 정확한 계산으로 원국 분석(용신·억부·조후용신·신강약·정탁·통관·병약·오행변수) → 대운 10구간(현재 대운 포함·간지합충형해) → 세운 5개년(연도별 등급·관계·해석) → 격국·상세해석 → 천간 10개론·계절 상태·공망·허자·비합·암록 까지, 최상위 기본 30개 필드 + 옵션 필드(하위 필드 포함 200개 이상)로 완전한 사주 해석을 제공합니다. 출생시간 미상 시 시주(wonkuk.hour)와 실제 시각이 필요한 trueSolarTime만 null이며, 용신·격국·신강약 등 분석 필드는 년·월·일 3주(6글자) 기준으로 계산된 값으로 그대로 제공됩니다 (birthHour.basis: "three_pillars").

요청 파라미터

파라미터 타입 필수 설명
year number O 출생년 (예: 1990)
month number O 출생월 (1-12)
day number O 출생일 (1-31)
hour number 출생시간 (0-23). 생략 시 시주만 응답에서 제외되고(wonkuk.hour: null), 용신·격국·신강약·세운 등 분석 필드는 년·월·일 3주(6글자) 기준으로 계산된 값으로 제공됩니다. minute은 hour 없이 보내면 무시됩니다.
minute number 출생분 (0-59, 기본값: 0)
timezone string O 타임존 (예: Asia/Seoul). 필수 — 생략하면 400입니다. 좌표를 보낼 때 표준 자오선을 정하는 데 쓰입니다
gender string O 성별 ("male" 또는 "female", 필수)
name string 성명 (선택사항)
astrology boolean 선택 true면 응답에 natalChart(태양·달·수성~토성 별자리) 동봉. 상승궁은 출생 시각과 위치(birthLatitude·birthLongitude 또는 country)를 함께 보냈을 때만 나오고, 위치 없이 보내면 별자리는 정오 기준으로 계산됩니다 — 상승궁 추정 범위 등 상세가 필요하면 네이탈 차트 API 사용
glossary boolean 선택 true면 이 차트에 나온 용어(간지 한자·십성·12운성·납음·신살·지장간·충형·지지관계·격국·공망·삼재)의 설명(title·short·body, lang 언어)을 응답 glossary에 동봉합니다. 추가 요금 없음. 요청 언어 설명이 없는 용어는 한국어로 오고 그 항목에 "fallbackLanguage": "ko"가 붙습니다. glossary의 키는 lang과 무관하게 한글/한자 코드입니다
lang string 해석 문자열 언어: ko(기본, 번역 안 함) · en · ja · th. INTA 앱 만세력 화면과 같은 번역표를 씁니다
calendar · isLeapMonth string · boolean "lunar"면 year·month·day를 음력으로 받아 양력으로 바꿔 계산합니다(기본 "solar"). isLeapMonth: true는 윤달 — 그해에 그 윤달이 없으면 평달로 계산하고 lunarInput.leapMonthApplied: false로 알려줍니다
birthLatitude · birthLongitude number 출생지 좌표(둘 다 보내야 함). 경도로 진태양시 보정을 합니다. 표준 자오선은 timezone의 표준시(서머타임 제외)로 정합니다
country string 출생 국가 2글자 코드. 좌표 없이 KR·JP·TH를 보내면 그 나라 대표 경도로 보정합니다(그 외 국가는 보정 없음)
zaSiRule string 자시(23시 이후) 일주: "traditional"(기본, 다음 날 일주) · "yajaSi"(야자시, 당일 일주)
birthTimePrecision string exact · approximate · estimated · unknown. 위치 보정은 exact일 때만 적용됩니다. 생략하고 좌표나 country를 보내면 exact로 봅니다

응답 필드 (기본 30개 + 옵션 필드)

필드 타입 설명
wonkuk object 원국 (4주 기둥). year, month, day, hour (hour는 출생시간 미상 시 null). 각 항목은 PillarFull 구조 참조
trueSolarTime object|null 진태양시(경도) 보정 결과. 좌표(birthLatitude·birthLongitude) 또는 country(KR·JP·TH)를 보내고 birthTimePrecision이 exact일 때(위치를 보내면 기본 exact)만 값이 있고, 그 밖에는 null. 필드: original·corrected(ISO 8601), originalLocal·correctedLocal(HH:MM), longitudeCorrection·equationOfTime·totalCorrectionMinutes(분), dayShift, dstApplied, basis('coords' 또는 'country'). 출생시간 미상 시 null
ohaengBalance object 오행 균형. { 목: int, 화: int, 토: int, 금: int, 수: int }
yongshin string 최종 용신 — 5법(전왕·조후·억부·통관·병약) 판정 결과로, 2026-10-08부터 yongshinDetail.primary와 같은 값입니다 (예: "수")
johuYongshin string|null 조후용신 ("억부용신"과 구분)
eokbuYongshin string 억부용신 (신강·신약을 저울질해 정한 용신). 최종 용신(yongshin)과 다를 수 있습니다
yongshinDetail object 완전판 용신 엔진 결과. 5법 우선순위(전왕 → 조후 → 억부 → 통관 → 병약)로 판정하며 method(채택한 법), primary(용신), secondary(보조 용신), reasoning(판정 근거 문장 배열), shin(용신·희신·기신·구신·한신 5등급), favorable_sipsin/unfavorable_sipsin(길·흉 십신), element_states(오행별 개수·과부족·길흉)를 포함합니다. 위의 yongshin은 이 엔진의 primary와 같은 값입니다(2026-10-08부터). 판정 근거까지 필요한 해석에는 이 필드를 쓰세요. 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값으로 제공됩니다.
ilganSummary string 일간 10개론. 예: "壬水 — 큰 바다의 기운. 지혜롭고 포용력이 넓습니다." (일간만 사용하므로 출생시간 미상과 무관하게 제공)
daeun array 대운 10구간. 각 항목은 시작나이, 천간·지지, 오행, 십신, 신살, isCurrent(현재 대운 여부). DaeunPeriod 구조 참조
seun array 세운 최근 5개년. 각 항목은 연도, 천간·지지, 오행, grade(good/caution/excellent/neutral), 관계(해/육합/형 등), 요약. SeunYear 구조 참조
branchRelations array 지지 관계. 년월일시 중 충·형·합이 있는 지지 쌍 목록. 각 항목은 type('沖(충)'/'刑(형)'/'半合(반합)' 등), pillars, description(예: "丑未沖 (축미충)")
gongmang array 공망 지지 (년주·일주 기준 2가지). 예: ["자", "해"]
cheonEulGwiIn array 천을귀인 지지 목록. 예: ["인", "술"]
wolryeong string 월령 (절입 기준 현재 달의 令 지지). 예: "축"
daeunNumber number 대운수 — 첫 대운이 시작되는 나이(daeun[0].startAge, 예: 3). 현재 대운의 순번이 아닙니다 — 현재 대운은 daeun[].isCurrent로 찾으세요
daeunDirection string 대운 방향. "순행" 또는 "역행"
gyeokguk object|null 격국 판정. name('정관격' 등)·summary('질서·명예·합리'). 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값
gyeokgukDetail object|null 격국 상세 해석. primary·source·trigger·interpretation(core/career/personality/challenge). 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값
cheonganDetail object|null 천간 10개론 상세. ilgan·hanja·archetype·characteristics·seasonal·relations. 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값
hapChungReport object|null 천간·지지 형충회합 통합 리포트. 원국 내 합/충 유지 상태, 대운/세운에서 새로 형성되는 관계. 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값
heojaReport object|null 허자 통합 리포트. hapHeo·jaHeo·bihap·bihapEmergence·heojaGyeok·amrok. 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값. 단 heojaGyeok(허자격)은 실제 시지가 있어야 성립하므로 비어 있습니다
gongmangDetail object|null 공망 상세 리포트. 년주·일주 기준 공망, 오행 공망 여부 등
ohaengByeonsuHits array 오행 변수 자동 추출. 왕·극·순화 공식 적용 예시. 각 항목은 key·subject(오행)·target·formula_idiom·description·category
strength object|null 신강약 점수·등급. dayMaster·score(-100~+100)·result('신강'/'신약')·level·breakdown(계절·부조·극설·지장간·통근). 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값
jeongtak object|null 정탁 판정. verdict('정'/'탁')·reasons·severity. 출생시간 미상 시에도 년·월·일 3주(6글자) 기준으로 계산된 값
tonggwan object|null 통관 여부. 복합 로직 결과
byeongyak object|null 병약 여부. 복합 로직 결과
natalChart object|null 서양 점성술 네이탈 차트 (옵션, astrology=true일 때만). 태양·달·수성~토성 7개 천체 별자리, 상승궁(위치를 보냈을 때)
countryCodeUsed string|null 이 응답을 계산할 때 쓴 국가코드(country를 보냈을 때), 없으면 null
identity object|null 항상 포함(2026-10-10~). 사주 정체성 — rooting_hints(통근)·season_strength_hint(계절 강약)·sinsal_list(신살)·gongmang(공망)·samjae(삼재)·timeline(대운/세운 타임라인)·mbti_inference(사주 MBTI)·coherence. 계산 실패 시 null. 사주 정체성 참조
ohaengDeficient array of string 항상 포함(2026-10-10~). ohaengBalance에서 개수가 0인 오행 목록(없으면 []). lang이 ko가 아니면 번역된 이름으로 옵니다(ohaengBalance의 키는 한글 그대로)
glossary object 옵션, glossary=true일 때만. 용어 키 → { title, short, body, fallbackLanguage? }
lunarInput object 옵션, calendar: "lunar"일 때만. 음력→양력 변환 내역
birthHour object 옵션, hour를 생략했을 때만. 출생시간 미상 메타 (아래 주의사항 참조)

PillarFull 구조 (각 주의 하위 필드)

응답의 wonkuk.year·wonkuk.month·wonkuk.day·wonkuk.hour 각각이 갖는 필드:

필드 타입 설명
stem string 천간. "갑", "을", "병", "정", "무", "기", "경", "신", "임", "계"
branch string 지지. "자", "축", "인", "묘", "진", "사", "오", "미", "신", "유", "술", "해"
stemHanja string 천간 한자. "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"
branchHanja string 지지 한자. "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"
ohaeng string 오행. "목", "화", "토", "금", "수" 중 하나
stemSipsin string 천간 십신 (일간 대비). "정관", "편관", "정재", "편재", "식신", "상관", "정인", "편인", "비견", "겁재" 중 하나
branchSipsin string 지지 십신 (일간 대비). 천간 십신과 동일한 10가지
jijanggan array of string 지장간 (지지 내 숨겨진 천간). 예: ["갑", "을", "목"] 또는 ["갑", "을"] 등
chunghyung array of string 충형 (다른 주와의 관계). "沖(충)"·"刑(형)" 등. 예: ["沖(충)", "刑(형)"]
hapPosition array of string 합 위치 (다른 주와의 관계). "半合(반합)"·"육합" 등. 예: ["半合(반합)"]
ungSung string 12운성. "장생", "목욕", "관대", "임관", "제왕", "쇠", "병", "사", "묘", "절", "태", "양" 중 하나
ungSungSub string (선택) 12운성 보조 정보 (선택 필드)
napEum string 납음 오행. 예: "성두토", "윤하수", "검봉금" 등
majorSinsal array of string 주요 신살. 예: ["장성살"], ["지살"] 등
minorSinsal array of string 보조 신살. 예: ["천을귀인", "원진살", "현침살", "육해살"] 등

DaeunPeriod 구조 (대운 각 구간)

필드 타입 설명
startAge number 시작 나이
endAge number 종료 나이
stem string 천간
branch string 지지
stemHanja string 천간 한자
branchHanja string 지지 한자
ohaeng string 오행
isCurrent boolean 현재 대운 여부 (true면 지금 진행 중)
stemSipsin string 천간 십신
branchSipsin string 지지 십신
sinsal array of string 신살 배열

SeunYear 구조 (세운 각 연도)

필드 타입 설명
year number 서력 연도
stem string 천간
branch string 지지
stemHanja string 천간 한자
branchHanja string 지지 한자
ohaeng string 오행
isCurrent boolean 현재 세운 여부
grade string 등급. "good" (순조로운), "excellent" (매우 좋은), "caution" (신중), "neutral" (변화 없음)
relations array 간지 관계. 각 항목은 type(해/육합/형 등)·target(년주/월주 등)·description
summary string 연도 해석 요약. 예: "전체적으로 순조로운 흐름이 예상됩니다."
sinsal array of string 신살 배열

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/manseryeok \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "year": 2000, "month": 1, "day": 15, "hour": 14, "minute": 30,
    "gender": "male", "calendar": "solar", "timezone": "Asia/Seoul"
  }'

응답 예시 (발췌: 2000-01-15 14:30 남성)

{
  "success": true,
  "data": {
    "wonkuk": {
      "year": {
        "stem": "기", "branch": "묘", "stemHanja": "己", "branchHanja": "卯",
        "ohaeng": "토", "stemSipsin": "정관", "branchSipsin": "상관",
        "jijanggan": ["갑", "을"], "chunghyung": [], "hapPosition": ["半合(반합)"],
        "ungSung": "사", "napEum": "성두토",
        "majorSinsal": ["장성살"],
        "minorSinsal": ["천을귀인", "원진살", "현침살", "육해살"]
      },
      "month": { ... },
      "day": { ... },
      "hour": { ... }
    },
    "trueSolarTime": null,
    "ohaengBalance": { "목": 1, "화": 2, "토": 3, "금": 1, "수": 1 },
    "yongshin": "수",
    "johuYongshin": "화",
    "yongshinDetail": {
      "method": "억부",
      "primary": "수",
      "secondary": ["금"],
      "reasoning": ["일간 임(수) 신약 (score=-66)", "신약 + root 있음(8) → 비겁(수)"],
      "shin": { "yongshin": "수", "huishin": "금", "gishin": "토", "gushin": "화", "hanshin": "목" },
      "favorable_sipsin": ["편인", "정인", "비견", "겁재"],
      "unfavorable_sipsin": ["편관", "정관", "편재", "정재", "식신", "상관"],
      "element_states": { "목": { "count": 1, "state": "lack", "effect": "neutral" }, "...": "5오행" }
    },
    "eokbuYongshin": "수",
    "ilganSummary": "壬水 — 큰 바다의 기운. 지혜롭고 포용력이 넓습니다.",
    "daeun": [
      {
        "startAge": 3, "endAge": 13,
        "stem": "병", "branch": "자", "stemHanja": "丙", "branchHanja": "子",
        "ohaeng": "화", "isCurrent": false,
        "stemSipsin": "편재", "branchSipsin": "겁재",
        "sinsal": ["년살", "장성살"]
      },
      {
        "startAge": 13, "endAge": 23,
        "stem": "을", "branch": "해", "stemHanja": "乙", "branchHanja": "亥",
        "ohaeng": "목", "isCurrent": false,
        "stemSipsin": "상관", "branchSipsin": "비견",
        "sinsal": ["지살", "망신살"]
      },
      {
        "startAge": 23, "endAge": 33,
        "stem": "갑", "branch": "술", "stemHanja": "甲", "branchHanja": "戌",
        "ohaeng": "목", "isCurrent": true,
        "stemSipsin": "식신", "branchSipsin": "편관",
        "sinsal": ["천살", "월살"]
      }
    ],
    "seun": [
      {
        "year": 2026, "stem": "병", "branch": "오", "stemHanja": "丙", "branchHanja": "午",
        "ohaeng": "화", "isCurrent": true, "grade": "good",
        "relations": [
          { "type": "해", "target": "월주", "description": "오-축 해" },
          { "type": "육합", "target": "시주", "description": "오-미 육합" }
        ],
        "summary": "전체적으로 순조로운 흐름이 예상됩니다.",
        "sinsal": ["육해살", "재살"]
      }
    ],
    "branchRelations": [
      { "type": "沖(충)", "pillars": ["축", "미"], "description": "丑未沖 (축미충)" },
      { "type": "刑(형)", "pillars": ["축", "미"], "description": "丑未刑 (축미형)" },
      { "type": "半合(반합)", "pillars": ["묘", "미"], "description": "卯未合 (묘미 반합)" }
    ],
    "gongmang": [],
    "cheonEulGwiIn": [],
    "wolryeong": "축",
    "daeunNumber": 3,
    "daeunDirection": "역행",
    "gyeokguk": {
      "name": "정관격",
      "summary": "질서·명예·합리"
    },
    "gyeokgukDetail": { ... },
    "cheonganDetail": { ... },
    "hapChungReport": { ... },
    "heojaReport": { ... },
    "gongmangDetail": { ... },
    "ohaengByeonsuHits": [
      {
        "key": "to_wang_deuk_mok",
        "subject": "土", "target": "木",
        "formula_idiom": "방능소통(方能疏通)",
        "description": "토가 왕성한데 목을 얻으면 소통이 된다",
        "category": "wang_geuk_sunwha"
      }
    ],
    "strength": {
      "dayMaster": "임",
      "score": -66,
      "result": "신약",
      "level": "중약",
      "breakdown": { "season": -30, "support": 8, "control": -38, "hidden": -9, "root": 8 }
    },
    "jeongtak": {
      "verdict": "탁",
      "reasons": ["파괴 십신 2개 감지", "용신 금 생 ✓", "일주 신약"],
      "severity": "극탁"
    },
    "tonggwan": { ... },
    "byeongyak": { ... },
    "countryCodeUsed": null,
    "identity": {
      "rooting_hints": { ... }, "season_strength_hint": { ... }, "sinsal_list": [ ... ],
      "gongmang": { ... }, "samjae": { ... }, "timeline": { ... },
      "mbti_inference": { ... }, "coherence": { ... }
    },
    "ohaengDeficient": []
  }
}

이 예시는 위치를 보내지 않은 요청이라 trueSolarTime이 null입니다. 같은 요청에 "country": "KR"(또는 좌표)를 더하면 trueSolarTime에 보정 결과가 들어가고("basis": "country"), countryCodeUsed는 "KR"이 됩니다. 보정 결과 형태는 아래 주의사항의 음력 입력 예시를 보세요.

주의사항

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400INVALID_LUNAR_DATE · INVALID_LOCATION없는 음력 날짜 / 위도·경도 중 하나만 보냄 (과금 없음)
402(없음)잔액이 호출 요금보다 적음 — 이 API는 호출 전 잔액 검사가 없어 코드 없는 402가 옵니다

평생 사주 API

POST /api/v1/partners/v1/lifetime

생년월일시로 원국을 계산하고, 차트에 해당하는 부품(일주·일간×월지·일간×신강약·일간×용신·오행 과다/부족 최대 3개·가장 센 십성 그룹)의 풀이 문단을 성격·재물·직업·연애·건강·인간관계·조언 7개 주제로 묶어 돌려줍니다. HRUM 사주이론으로 작성한 문단이며 같은 입력은 항상 같은 결과입니다. 원국 판정은 만세력 API와 동일합니다.

전체 7개 주제
주제 1개 (topic)
별자리·자미두수 층 (layers)

요청 파라미터

파라미터타입필수설명
year · month · daynumberO출생년(1900-2100)·월·일 (기본 양력, calendar: "lunar"면 음력)
hour · minutenumber출생시각. hour 생략 시 만세력 API와 같은 출생시간 미상 규칙(년·월·일 3주 기준)으로 계산합니다
timezonestringO타임존 (예: Asia/Seoul)
genderstringO"male" 또는 "female"
topicstring한 주제만 받을 때: personality, wealth, career, love, health, relationships, advice. 생략 시 7개 주제 전체
langstringko(기본) · en · ja · th
layersstring[]추가 풀이 층: "astro"(별자리 — 태양·달·상승궁) · "ziwei"(자미두수 — 명궁 주성 + 주제별 궁 주성). 각 주제의 사주 문단 뒤에 별자리 → 자미두수 순으로 붙습니다. 생략하거나 []면 응답이 지금과 똑같습니다
calendar · isLeapMonthstring · boolean"lunar"면 year·month·day를 음력으로 받아 양력으로 바꿔 계산합니다(기본 "solar"). isLeapMonth: true는 윤달이며, 그해에 그 윤달이 없으면 평달로 계산합니다. 음력으로 보내면 응답에 lunarInput(만세력 API와 같은 형식)이 붙습니다
birthLatitude · birthLongitudenumber출생지 좌표(둘 다 함께). 사주 문단과 layers가 같은 기준으로 진태양시 보정을 합니다. 표준 자오선은 timezone의 표준시(서머타임 제외)로 정합니다. 상승궁은 출생시각과 좌표가 모두 있어야 나옵니다
countrystring출생 국가 2글자 코드. 좌표 없이 KR·JP·TH를 보내면 그 나라 대표 경도로 보정합니다(그 외 국가는 보정 없음). layers도 이 국가를 씁니다(생략하면 lang으로 추정)
zaSiRulestring자시(23시 이후) 일주: "traditional"(기본, 다음 날 일주) · "yajaSi"(야자시, 당일 일주). 사주 문단에만 쓰입니다(자미두수는 23시부터 다음 날로 고정)
birthTimePrecisionstringexact · approximate · estimated · unknown. 위치 보정은 exact일 때만 적용됩니다. 생략하고 좌표나 country를 보내면 exact로 봅니다

요청 예시

{
  "year": 1990, "month": 9, "day": 4, "hour": 14, "minute": 30,
  "timezone": "Asia/Seoul", "gender": "male",
  "topic": "career", "lang": "ko"
}

응답 예시

{
  "success": true,
  "data": {
    "topics": [
      {
        "topic": "career",
        "paragraphs": [
          { "component": "ilju", "key": "임신", "text": "..." },
          { "component": "ilgan_wolji", "key": "임_신", "text": "..." },
          { "component": "ilgan_strength", "key": "임_신강", "text": "..." },
          { "component": "ilgan_yongshin", "key": "임_화", "text": "..." },
          { "component": "ohaeng_state", "key": "목_lack", "text": "..." },
          { "component": "ohaeng_state", "key": "토_lack", "text": "..." },
          { "component": "ohaeng_state", "key": "수_lack", "text": "..." },
          { "component": "top_sipsin", "key": "인성", "text": "..." }
        ]
      }
    ],
    "lang": "ko",
    "fallbackLanguage": null,
    "engine": { "version": 1, "styleVersion": "v1" }
  }
}
  • topics는 항상 위 7개 주제 순서, 주제 안 문단은 부품 순서(일주 → 일간×월지 → 신강약 → 용신 → 오행 상태 → 십성 그룹)입니다.
  • key는 한글 판정값입니다(언어와 무관). ohaeng_state는 차트에 따라 0~3개입니다.
  • 언어: 요청한 언어 문단이 아직 없으면 그 문단만 한국어로 내려가고 fallbackLanguage: "ko"가 표시됩니다.
  • 시각 보정: 좌표·country를 보내지 않으면 입력 시각 그대로 계산합니다(만세력 API와 동일). INTA 앱(한국어, 시각 직접 입력)과 같은 문단이 필요하면 country: "KR"을 보내세요. 해외 출생자는 timezone만으로는 보정되지 않으니 좌표를 함께 보내야 합니다.
  • 2026-10-11 변경: 좌표를 보내면 사주 문단도 진태양시 보정을 합니다(그전에는 layers에만 썼습니다). 한 응답 안에서 사주·별자리·자미두수가 같은 시각 기준을 쓰도록 맞춘 수정이라, 좌표를 보내던 호출은 사주 문단이 바뀔 수 있습니다. 새 필드와 좌표를 보내지 않는 호출의 응답은 그대로입니다.

별자리·자미두수 층 (layers)

INTA 앱의 평생 사주와 같은 구성입니다. 별자리는 태양·달·상승궁 별자리, 자미두수는 명궁(命宮) 주성 조합의 문단을 주제마다 하나씩 더하고, 이어서 그 주제를 맡은 궁의 주성 문단을 붙입니다 — 재물(wealth)=재백궁(財帛), 일(career)=관록궁(官祿), 연애(love)=부처궁(夫妻), 건강(health)=질액궁(疾厄). 성격·대인관계·조언 주제는 명궁 문단만 붙습니다. 층마다 첫 문단에 label(예: "별자리로 보면")이 붙어 화면에서 소제목으로 쓸 수 있습니다. topic을 지정하면 층도 그 주제 문단만 붙습니다.

{
  "year": 1990, "month": 9, "day": 4, "hour": 14, "minute": 30,
  "timezone": "Asia/Seoul", "gender": "male",
  "birthLatitude": 37.5665, "birthLongitude": 126.978,
  "topic": "career", "lang": "ko",
  "layers": ["astro", "ziwei"]
}
{
  "success": true,
  "data": {
    "topics": [
      {
        "topic": "career",
        "paragraphs": [
          { "component": "ilju", "key": "임신", "text": "..." },
          ...
          { "component": "top_sipsin", "key": "인성", "text": "..." },
          { "component": "astro_sun", "key": "vir", "text": "...", "label": "별자리로 보면" },
          { "component": "astro_moon", "key": "pis", "text": "..." },
          { "component": "astro_asc", "key": "sag", "text": "..." },
          { "component": "ziwei_ming", "key": "tt_jm", "text": "...", "label": "자미두수로 보면" },
          { "component": "ziwei_palace", "key": "cr_tj", "text": "..." }
        ]
      }
    ],
    "lang": "ko",
    "fallbackLanguage": null,
    "engine": { "version": 1, "styleVersion": "v1" },
    "layers": {
      "astro": { "included": true, "costWon": 20, "note": null },
      "ziwei": { "included": true, "costWon": 20, "note": null }
    }
  }
}
  • key: 별자리는 영문 세 글자 약어(vir=처녀자리 등), 자미두수는 명궁 주성 약어 조합(tt_jm=천동·거문, 주성이 없으면 empty)입니다.
  • ziwei_palace: 주제별 궁 문단입니다. key는 궁 약어_주성 약어(wl=재백 · cr=관록 · sp=부처 · hl=질액, 예: cr_tj=관록궁 천기)이고, 궁에 주성이 둘이면 문단도 둘, 주성이 없는 궁(공궁)이면 붙지 않습니다. 명궁 문단 바로 뒤에 오며 label은 없습니다(그 주제에 명궁 문단이 없을 때만 첫 궁 문단에 label이 붙습니다). 요금은 자미두수 층 +₩20 그대로입니다.
  • 출생시각이 없으면: 자미두수 층은 빠지고 layers.ziwei에 "included": false, "reason": "birth_time_required"와 안내 문구(note)가 내려갑니다. 별자리는 정오 기준으로 태양(달 별자리가 확실하면 달도)만 붙습니다.
  • 좌표가 없으면: 상승궁 문단이 빠지고 layers.astro.note에 이유가 적힙니다. country를 보내지 않으면 층의 출생 국가는 lang으로 추정합니다(ko→한국, ja→일본, th→태국).
  • reason은 birth_time_required · chart_unavailable · narrative_unavailable 중 하나이며, 빠진 층은 과금되지 않습니다.

요금

  • 전체(7개 주제) ₩100/호출, topic 지정 ₩20/호출. 성공한 호출만 과금되고, 같은 입력의 반복 호출도 각각 과금됩니다 (멱등성).
  • layers: 실제로 문단이 붙은 층마다 +₩20 (전체·주제 1개 동일). 예: 전체 + 별자리 + 자미두수 = ₩140. 잔액은 요청한 층까지 포함한 금액으로 미리 확인합니다.
  • 입력 오류(400)·잔액 부족(402)·문단 조회 실패(503 lifetime_narrative_unavailable)는 과금되지 않습니다. 사용량 로그 endpoint는 lifetime입니다.
  • 입력 옵션: 출생 입력은 만세력 API와 같습니다(calendar·isLeapMonth·좌표·country·zaSiRule·birthTimePrecision). 만세력 전용 옵션(astrology·glossary·name)은 받지 않으며, 보내면 400입니다(과금 없음).
  • 계정에 이 기능이 열려 있지 않으면 404 lifetime_not_enabled(과금 없음)입니다.

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404lifetime_not_enabled계정에 기능이 열려 있지 않음
400INVALID_LUNAR_DATE · INVALID_LOCATION없는 음력 날짜 / 위도·경도 중 하나만 보냄
402INSUFFICIENT_BALANCE요청한 층까지 포함한 금액을 미리 확인했을 때 잔액 부족
503lifetime_narrative_unavailable풀이 문단을 불러오지 못함 (과금 없음)

자미두수 명반 API

POST /api/v1/partners/v1/ziwei-chart

생년월일시로 자미두수(紫微斗數) 명반을 계산합니다. 12궁(궁 이름·천간·지지·대한 나이), 14주성과 보좌성(밝기·생년사화 포함), 오행국, 명궁·신궁 위치, 생년사화, 기준일의 대한·유년을 돌려줍니다. 같은 입력은 항상 같은 결과입니다. 라벨은 요청한 언어로 채워지고, key는 언어와 무관한 영문 고정값입니다.

계산 설정 (고정)

  • 윤달: 윤달 15일까지는 그 달, 16일부터는 다음 달로 봅니다.
  • 23시대 출생: 다음 날 자시로 계산합니다(notes.lateZiNotice에 안내 문구).
  • 해의 경계: 음력 정월 초하루(설). 입춘~설 사이 출생이면 사주(입춘 기준)와 해 간지가 다를 수 있고, 이때 chart.yearBasisNote에 안내 문구가 붙습니다.
  • 시각 보정: 사주와 같은 규칙입니다. 기본은 입력 시각 그대로(만세력 API와 동일)이고, birthTimePrecision: "exact"와 함께 좌표(birthLatitude·birthLongitude) 또는 countryCode(KR·JP·TH)를 보내면 진태양시로 보정합니다. 보정 결과는 clock에 나옵니다.
  • 사화: 삼합파 안성법 + 생년사화. 엔진은 iztro 2.6.1 고정입니다(chart.engine).

요청 파라미터

파라미터타입필수설명
year · month · daynumberO출생년(1900-2100)·월·일
hournumberO출생 시(0-23). 자미두수는 출생 시각 없이 명반을 세울 수 없습니다 — 생략하면 400 birth_time_required
minutenumber출생 분(0-59), 생략 시 0
genderstringO"male" 또는 "female" (대한 진행 방향을 정합니다)
calendarstringsolar(기본) 또는 lunar
isLeapMonthboolean음력 윤달이면 true. 양력 입력에서는 무시합니다. 그 해에 해당 윤달이 없으면 평달로 계산되고 chart.lunarDate.isLeapMonth가 false로 나옵니다
birthTimePrecisionstringexact(진태양시 보정) 또는 approximate(보정 없음 + notes.approximateTimeNote). 생략 시 보정 없음
birthLatitude · birthLongitudenumber출생지 좌표 (exact 보정용)
countryCodestring출생 국가 ISO 2자리, 대문자만(예: KR — kr은 400). 좌표가 없을 때 대표 경도로 보정
birthTimezoneOffsetnumber출생 시각의 UTC 오프셋(분, 540 = KST)
referenceDatestring대한·유년 기준일 YYYY-MM-DD (기본: 오늘, 한국 시간)
langstringko(기본) · en · ja · th
timezonestring만세력 API와의 호환용 (계산에 쓰이지 않음)
readingsbooleantrue면 응답에 readings(명궁 주성 조합·7개 주제·재백/관록/부처/질액궁 주성 풀이 문단, lang 언어)를 붙입니다. 추가 ₩20(합계 ₩70). 생략하면 응답은 그대로입니다

요청 예시

{
  "year": 1990, "month": 5, "day": 15, "hour": 14, "minute": 30,
  "gender": "female", "referenceDate": "2026-10-08", "lang": "ko"
}

응답 예시 (일부)

{
  "success": true,
  "data": {
    "engineVersion": 1,
    "lang": "ko",
    "referenceDate": "2026-10-08",
    "clock": {
      "inputSolarDate": "1990-05-15", "inputTime": "14:30",
      "correctedSolarDate": "1990-05-15", "correctedTime": "14:30",
      "correctionMinutes": 0, "dstApplied": false, "basis": null,
      "hourBranch": { "key": "wei", "label": "미" }
    },
    "chart": {
      "engine": { "name": "iztro", "version": "2.6.1", "config": { ... } },
      "solarDate": "1990-05-15",
      "lunarDate": { "year": 1990, "month": 4, "day": 21, "isLeapMonth": false },
      "yearGanzhi": "경오",
      "yearBasisNote": null,
      "fiveElementsClass": { "key": "earth5", "label": "토오국" },
      "soulPalaceBranch": { "key": "xu", "label": "술" },
      "bodyPalaceBranch": { "key": "zi", "label": "자" },
      "palaces": [
        ...,
        {
          "key": "ming", "label": "명궁",
          "branch": { "key": "xu", "label": "술" },
          "stem": { "key": "bing", "label": "병" },
          "isBodyPalace": false,
          "majorStars": [
            { "key": "ziwei", "label": "자미", "brightness": { "key": "de", "label": "득" }, "mutagen": null },
            { "key": "tianxiang", "label": "천상", "brightness": { "key": "de", "label": "득" }, "mutagen": null }
          ],
          "minorStars": [
            { "key": "lingxing", "label": "영성", "brightness": { "key": "miao", "label": "묘" }, "mutagen": null }
          ],
          "decadal": { "from": 5, "to": 14, "isCurrent": false }
        },
        ...
      ],
      "mutagens": [
        { "type": "lu", "label": "화록", "starKey": "taiyang", "starLabel": "태양", "palaceKey": "children", "palaceLabel": "자녀궁" },
        { "type": "quan", "label": "화권", "starKey": "wuqu", "starLabel": "무곡", "palaceKey": "wealth", "palaceLabel": "재백궁" },
        { "type": "ke", "label": "화과", "starKey": "taiyin", "starLabel": "태음", "palaceKey": "children", "palaceLabel": "자녀궁" },
        { "type": "ji", "label": "화기", "starKey": "tiantong", "starLabel": "천동", "palaceKey": "health", "palaceLabel": "질액궁" }
      ],
      "currentDecadal": { "palaceKey": "children", "from": 35, "to": 44 },
      "yearly": { "palaceKey": "wealth", "year": 2026 }
    },
    "features": [
      "bright:紫微=得", "co:紫微+天相@命", "cs:命=胎", "dir:逆", "hua:祿=太陽@子女",
      "lhua:忌=廉貞@流年", "limit:大限@子女", "limit:小限@辰", "ming:戌", "mingzhu:祿存",
      "star:天姚@辰", "star:紫微@命", "year:庚", "yy:陽女", ...
    ],
    "notes": { "lateZiNotice": null, "approximateTimeNote": null }
  }
}

같은 요청에 "lang": "ja"를 보내면 라벨만 바뀝니다 — 예: "yearGanzhi": "庚午", 명궁 "label": "命宮", 주성 "紫微"·"天相", 오행국 "土五局", 사화 "化禄".

  • palaces는 항상 12개이며 지지 순서 인 → 축(인묘진사오미신유술해자축)으로 정렬됩니다. 궁 종류는 key(ming, siblings, spouse, children, wealth, health, travel, friends, career, property, fortune, parents)로 구분하십시오.
  • mutagens는 생년사화(화록·화권·화과·화기)와 그 별이 앉은 궁입니다. 별 객체의 mutagen도 생년사화입니다.
  • features는 이 명반에서 참인 사실을 자미두수 규칙 키(<종류>:<몸통>, 고전 한자 토큰)로 적은 정렬된 문자열 배열입니다(명반당 약 1,200개, 약 27KB). 별 위치·밝기·생년사화·같은 궁·대궁·삼방사정·夾·궁간·오행국·명궁/신궁·납음·음양·대한 방향·운한 궁과 그 안의 별, 그리고 명반에는 따로 나오지 않는 잡성 위치·장생12·소한·대한/유년 사화·명주·신주·내인궁까지 들어 있어 규칙 조건을 문자열 비교로 맞춰 볼 수 있습니다. 크기 때문에 두 가지만 빠집니다: 세 별 co·meet(co:A+B+C = co:A+B이고 co:A+C, meet도 같음)와 잡성이 낀 관계 키(star:<잡성>@<지지>와 pal:·shen:·limit:로 대궁 +6, 삼방사정 0·±4·+6, 夾 ±1을 세면 됩니다).
  • 규칙 근거 카드의 원문 인용: 자미두수 규칙 카드(해석 DB, 허용된 파트너만)의 sources에서 공개 원문(퍼블릭 도메인 판본)에서 확인되지 않은 인용(match: "PARTIAL"이면서 원문 위치 foundAt이 없는 것)은 원문(quote)과 위치(location)를 빼고 "citation": "pending"(출처 확인 중)으로 내려갑니다. 규칙 문장과 sourceId는 그대로이고, 원문에서 확인된 인용(match: "EXACT"), 원문 위치(foundAt)가 확인된 PARTIAL 인용, 엔진 근거는 바뀌지 않습니다. 출처가 확인되지 않은 인용을 출처처럼 보이지 않게 하려는 처리입니다.
  • 전래본 인용(match: "QUOTED"): PD 앵커 없는 전래본 인용입니다. 원문(quote)과 위치(location)를 그대로 내려갑니다. 교정·교감 흔적이 있는 인용은 여기에 해당하지 않습니다.
  • currentDecadal은 첫 대한 시작 나이 전이면 null입니다. yearly.year는 유년 기준 음력 해(설 전이면 전년도)입니다.
  • readings: true면 readings = { ming, topics[], palaces[], lang, costWon }. ming은 명궁 주성 조합 문단(key·text·lang), topics는 주제별 문단(topic·text·lang), palaces는 궁·주성별 문단(palace·star·text·lang)입니다. 요청 언어 문장이 없는 문단은 한국어로 오고 그 문단의 lang이 ko입니다.

출생 시각이 없을 때 (400)

{
  "success": false,
  "statusCode": 400,
  "code": "birth_time_required",
  "message": "Zi Wei Dou Shu needs the birth hour (hour 0-23). ...",
  "partial": {
    "birthYearMutagens": [
      { "type": "lu", "label": "화록", "starKey": "taiyang", "starLabel": "태양" },
      ...
    ],
    "yearStemStars": [
      { "key": "lucun", "label": "녹존", "branch": { "key": "shen", "label": "신" } },
      ...
    ]
  }
}

시각 없이도 확정되는 생년사화의 별과 연간으로 정해지는 별(녹존·경양·타라·천괴·천월)의 지지만 partial로 돌려줍니다. 궁 배치는 시각이 있어야 정해지므로 지어내지 마십시오. 이 응답은 과금되지 않습니다.

요금

  • ₩50/호출. readings: true면 +₩20(합계 ₩70). 성공한 호출만 과금되고, 같은 입력의 반복 호출도 각각 과금됩니다 (멱등성).
  • 입력 오류(400 invalid_birth_date·birth_time_required 등)·잔액 부족(402)은 과금되지 않습니다. 사용량 로그 endpoint는 ziwei_chart입니다.
  • 과금 없는 그 밖의 오류: 404 ziwei_not_enabled(계정에 기능 미개방) · 404 ziwei_readings_not_enabled(풀이 문단 미개방) · 503 ziwei_readings_unavailable(풀이 문단을 불러오지 못함).

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404ziwei_not_enabled계정에 기능이 열려 있지 않음
404ziwei_readings_not_enabledreadings: true인데 풀이 문단이 열려 있지 않음
400invalid_birth_date없는 양력 날짜, 또는 음력 일·윤달 범위 밖
400birth_time_requiredhour 없음 — 위 예시처럼 partial이 함께 옵니다
400reference_date_before_birthreferenceDate가 출생일보다 앞섬
402INSUFFICIENT_BALANCE잔액 부족
503ziwei_readings_unavailable풀이 문단을 불러오지 못함

모두 과금되지 않습니다.

사주 정체성 (만세력에 포함)

2026-10-10부터 사주 정체성 내용은 만세력 API 응답의 identity 필드로 함께 돌려줍니다 — 삼재(samjae)·신살(sinsal_list)·공망(gongmang)·통근(rooting_hints)·계절 강약(season_strength_hint)·대운/세운 타임라인(timeline)·사주 MBTI(mbti_inference). 만세력 요금 외에 추가 요금은 없습니다. 기존 POST /api/v1/partners/v1/identity 는 이미 연동한 파트너를 위해 당분간 그대로 동작합니다. 새 연동은 만세력을 쓰세요.

  • 폐지 예고 헤더: /identity 응답에 Deprecation: true와 Link: </api/v1/partners/v1/manseryeok>; rel="successor-version"이 붙습니다. 종료일(Sunset)은 아직 정해지지 않았습니다.
  • 만세력의 identity와 다른 점: 만세력 응답에 이미 있는 키(saju·sipsin_map·strength·yongshin·gyeokguk·cheongan_detail·hap_chung_report·heoja_report·gongmang_detail·ohaeng_byeonsu_hits)는 identity에서 빠집니다. 같은 값은 만세력 최상위 필드(wonkuk·strength·yongshinDetail·gyeokgukDetail 등)에서 읽으세요.
  • currentYear·windowSize: 만세력은 이 두 파라미터를 받지 않습니다(보내면 400). 타임라인 기준 연도·범위를 바꿔야 하면 당분간 /identity를 쓰세요.
  • 출생시간 미상: /identity는 정오로 계산하고, 만세력의 identity는 만세력 본문과 같은 년·월·일 3주 기준으로 계산합니다.
  • 요금: 기존 /identity는 ₩10/호출(계정에 열려 있을 때만, 미개방 시 404 identity_not_enabled). 만세력의 identity는 추가 요금이 없습니다. 사용량 로그 endpoint는 identity입니다.

POST /api/v1/partners/v1/identity (구 엔드포인트)

요청 본문은 만세력 API와 같고, currentYear(1900~2200)·windowSize(1~10)를 더 받습니다.

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/identity \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "year": 1990, "month": 5, "day": 15, "hour": 12, "minute": 0, "timezone": "Asia/Seoul", "gender": "male" }'

응답 예시 (발췌, 실제 엔진 출력)

{
  "success": true,
  "data": {
    "saju": {
      "year":  { "cheongan": "경", "jiji": "오", "ohaeng": "금" },
      "month": { "cheongan": "신", "jiji": "사", "ohaeng": "금" },
      "day":   { "cheongan": "경", "jiji": "진", "ohaeng": "금" },
      "hour":  { ... }
    },
    "sipsin_map": { "year_cheongan": "비견", "year_jiji": "정관", "month_cheongan": "겁재", ... },
    "strength": { "dayMaster": "경", "score": -60, "result": "신약", "level": "중약", ... },
    "rooting_hints": { ... },
    "season_strength_hint": { "season": "여름", "strength_label": "최약(最弱)", ... },
    "sinsal_list": [ { "id": "shinsal_haksang", "name": "학당", "name_hanja": "學堂", "category": "길신", ... } ],
    "gongmang": { "sun": "갑술순", "void_branches": ["신", "유"], "hits": [] },
    "samjae": { "is_in_samjae": false, "current_year_branch": "오" },
    "yongshin": { "method": "조후", "primary": "수", ... },
    "timeline": { "current": { "daeun": { "startAge": 27, "endAge": 37, "stem": "갑", "branch": "신", ... }, ... } },
    "gyeokguk": { "primary": "비견격", ... },
    "mbti_inference": { "e_i": 48, "s_n": -8, "t_f": 16, "j_p": -18, "inferred": "ESFJ", ... },
    ...
  }
}

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404identity_not_enabled계정에 열려 있지 않음
400INVALID_LUNAR_DATE · INVALID_LOCATION없는 음력 날짜 / 위도·경도 중 하나만 보냄
402INSUFFICIENT_BALANCE잔액 부족

오늘의 사주 API

POST /api/v1/partners/v1/today

지정한 날(기본: 한국 시간 오늘)의 일진과 내 사주를 맞춰 하루 요약(일진·오늘의 십성·오늘 모드 MBTI·올해 흐름 점수·발현 신살·삼재)과 오늘의 운(일진×일간 관계, 행운 색·음식·키워드, 좋은 시간·피할 시간, 추천 활동, 한 줄 메시지)을 돌려줍니다. INTA 앱 홈의 "오늘" 카드와 같은 계산입니다. 여기에 활동 적합도(taskAffinity)와 오늘의 흐름 한 줄(summary.daily_flow)이 항상 붙고, 기온·날씨를 보내면 오늘의 옷차림(fashion), mbtiBehavior: true면 오늘 모드 MBTI 행동 패턴(mbtiBehavior)이 더해집니다.

요청 파라미터

만세력 API와 같은 출생 정보와 선택 옵션(lang·음력·좌표·zaSiRule 등)을 받습니다. 추가로:

파라미터타입필수설명
datestring볼 날짜 YYYY-MM-DD(1900-2100). 생략하면 한국 시간 기준 오늘
temperatureCnumber현재 기온(℃, −40~50). 보내면 응답에 fashion(아침·낮·저녁 옷차림)이 붙습니다. 생략하면 fashion 없음(추정하지 않음)
weatherstring날씨: clear · cloudy · rain · snow · storm
humiditynumber상대 습도(%, 0~100). temperatureC·weather·humidity 세 값을 모두 보내야 fashion.mood(시간대별 한 줄 날씨 코멘트)가 붙습니다
mbtiBehaviorbooleantrue면 summary.today_mbti의 행동 패턴(mbtiBehavior)을 붙입니다. 기본 false

요청 예시

{
  "year": 1990, "month": 5, "day": 15, "hour": 12, "minute": 0,
  "timezone": "Asia/Seoul", "gender": "male",
  "date": "2026-10-10",
  "temperatureC": 16, "weather": "cloudy", "humidity": 60,
  "mbtiBehavior": true
}

응답 예시 (실제 엔진 출력, 일부 생략)

{
  "success": true,
  "data": {
    "date": "2026-10-10",
    "summary": {
      "ilju": "경진",
      "day_master": "경",
      "gyeokguk": "비견격",
      "gyeokguk_reason": "월지 사 중기(경) 투간 [연간=경]",
      "inferred_mbti": "ESFJ",
      "today_mbti": "ESFJ",
      "current_mbti": "ESFJ",
      "strength_label": "신약",
      "yongshin": "수",
      "today_date": "2026-10-10",
      "today_ganji": "정사",
      "today_day_ohaeng": "화",
      "today_sipsin": "정관",
      "flow_score": 60,
      "flow_tier": "good",
      "top_favored_task_type": "routine",
      "top_caution_task_type": "movement",
      "active_sinsal_names": ["학당", "암록", "복성귀인", "화개살", "자형(오오)"],
      "in_samjae": false,
      "daily_flow": "오늘(정사일) 화 에너지가 흘러 소통·확장에 유리해."
    },
    "omen": {
      "relation": { "type": "GEUK", "label": "조율의 날" },
      "color": { "name": "주황", "hex": "#FFA45B", "ohaeng": "화" },
      "food": "고추장비빔밥",
      "keyword": "공개",
      "message": "표현하고 싶은 마음과 주변 반응이 엇갈릴 수 있는 날입니다. 즉흥적인 말보다 한 박자 늦춘 표현이 좋습니다.",
      "luckyHour": "오전 11시 ~ 오후 1시",
      "avoidHour": "밤 11시 이후",
      "activity": "칭찬 먼저 하기"
    },
    "taskAffinity": [
      { "taskType": "focus", "score": -0.25, "verdict": "neutral",
        "reason": "길 십신 2개 일치 · 흉 십신 2개 일치 · 일진 천간 정관(흉) · 일진 지지 편관(흉) · 천간합" },
      { "taskType": "creative", "score": -0.75, "verdict": "caution", "reason": "..." },
      ...,
      { "taskType": "routine", "score": 0.25, "verdict": "favored",
        "reason": "길 십신 4개 일치 · 일진 천간 정관(흉) · 일진 지지 편관(흉) · 천간합" }
    ],
    "fashion": {
      "temperatureC": 16, "tempBand": "T3",
      "main": "버건디 자켓", "accent": "가죽 벨트",
      "colorAttachLabel": "반지나 메탈 액세서리에",
      "byTime": {
        "morning": { "main": "버건디 자켓", "accent": "가죽 벨트", "tempBand": "T3" },
        "noon": { ... }, "evening": { ... }
      },
      "mood": {
        "feelsLikeC": 16, "tempBand": "jacket", "humidBand": "normal", "weatherKey": "cloudy",
        "morning": { "headline": "흐린 자켓 새벽", "body": "햇볕 없어 살짝 서늘. 자켓 한 벌.", "carry": [] },
        "noon": { ... }, "evening": { ... }
      }
    },
    "mbtiBehavior": {
      "mbti": "ESFJ",
      "headline": "협력·돌봄",
      "actions": ["약속·일정 챙기기 좋은 날", "주변에 도움 줄 수 있는 일 1개", "집·환경 정돈", "내 페이스도 지키기 — 다 떠안지 않기"],
      "closingLine": "주변과 결이 맞아 흐름이 부드러워요."
    }
  }
}
  • omen.relation.type은 내 일간과 그날 일진 천간의 관계 코드입니다: HAB(합) · SAENG(상생) · SAME(같은 오행) · GEUK(상극) · CHUNG(충). label은 사람이 읽는 이름입니다.
  • flow_score·flow_tier는 그 해(세운)의 흐름이고, today_mbti는 내 사주 MBTI에 그날 일진을 더한 "오늘 모드"입니다.
  • taskAffinity: 활동 7종(focus·creative·contact·finance·organize·movement·routine)의 오늘 적합도. 각 항목 { taskType, score, verdict, reason }, verdict는 favored · neutral · caution. 순서는 엔진 순서(점수순 아님)입니다.
  • summary.daily_flow: 오늘의 흐름 한 줄. summary.samjae_label: 삼재 해일 때 삼재 종류·단계 (아니면 없음).
  • fashion: temperatureC를 보냈을 때만. fashion.mood는 temperatureC·weather·humidity를 모두 보냈을 때만. mbtiBehavior: mbtiBehavior: true일 때만(그날 MBTI 패턴이 없으면 생략).
  • lang(en/ja/th)을 보내면 요약의 격국·흐름, 오늘의 운, taskAffinity·daily_flow·samjae_label·fashion·mbtiBehavior 문자열이 모두 번역됩니다. 생략·ko는 한국어입니다.
  • 같은 출생 정보·같은 date(와 같은 날씨 입력)면 결과는 항상 같습니다. 출생시간 미상은 정오로 계산하고 birthHour를 붙입니다. 음력 입력이면 lunarInput이 붙습니다.

요금

  • ₩10/호출. 성공한 호출만 과금되고, 같은 입력의 반복 호출도 각각 과금됩니다 (멱등성).
  • 입력 오류(400)·잔액 부족(402)·계산 실패는 과금되지 않습니다. 사용량 로그 endpoint는 today입니다.

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404today_not_enabled계정에 기능이 열려 있지 않음
400INVALID_LUNAR_DATE · INVALID_LOCATION없는 음력 날짜 / 위도·경도 중 하나만 보냄
402INSUFFICIENT_BALANCE잔액 부족

꿈해몽 API

POST /api/v1/partners/v1/dream

엔드유저가 말한 꿈 이야기에서 상징 낱말(예: 바다, 물고기)을 찾아 두드러진 순서로 돌려줍니다. 낱말은 HRUM이 직접 만든 꿈 상징 어휘집(일상 명사 1,000여 개와 그 동의어·활용형·입말)에서 찾습니다. 어휘집에는 낱말만 있고 해몽 뜻이나 길흉 판단은 없습니다. 돌려주는 낱말은 모두 꿈 내용에 그 낱말이나 동의어·활용형이 실제로 나온 것입니다.

요청 파라미터

파라미터타입필수설명
dreamstring✓꿈 이야기 (최대 1,000자)
feelingstring꿈에서 느낀 감정 (최대 300자, 예: "무섭고 답답했어요"). 꿈 내용과 합쳐 태그를 뽑습니다(합친 길이는 1,000자까지 사용)
langstring응답 언어. 현재 ko만 지원합니다(사전이 한국어 전용). 다른 값은 400
includeNegativeboolean준비 중 고전 근거에 흉한 판단(죽음·병·잃음 등)의 기록도 넣을지. 기본 false. 고전 근거가 열리기 전에는 무시됩니다

요청 예시

{
  "dream": "바다에서 커다란 물고기를 두 손으로 잡는 꿈을 꿨어요"
}

응답 예시

{
  "success": true,
  "data": {
    "lang": "ko",
    "mode": "glossary",
    "items": [
      { "keywords": ["바다"], "score": 1 },
      { "keywords": ["물고기"], "score": 0.979 },
      { "keywords": ["손"], "score": 0.749 }
    ]
  }
}
  • items는 최대 40개입니다. 항목 하나 = keywords(상징 어휘집의 기본 낱말 하나, 예: "치아"·"앞니"는 ["이빨"]) + score. 같은 낱말은 한 번만 나옵니다. 순서는 꿈에서 두드러진 순(먼저 나올수록, 여러 번 나올수록, 더 구체적인 낱말일수록 앞)이고, score는 0~1이며 목록 순서대로 같거나 작아집니다(1 = 이 꿈에서 가장 두드러진 상징).
  • mode: glossary(상징 어휘집에서 찾음) · none(찾은 상징 없음). feeling도 꿈 내용과 함께 찾습니다.
  • 같은 입력이면 같은 결과입니다. 외부 호출 없이 서버 안에서 바로 계산합니다.
  • 풀이 문장은 들어 있지 않습니다. 엔드유저에게 보여줄 풀이가 필요하면 꿈 내용과 이 낱말을 AI 사주 리딩(only: "dream")이나 LLM 입력으로 넘겨 쓰세요.
  • 2026-10-11 변경: 예전 응답의 title·meaning 필드는 없어지고 keywords로 바뀌었습니다. 같은 날 상징 낱말의 출처가 HRUM 자체 상징 어휘집으로 바뀌어, 항목마다 낱말이 하나이고 mode 값이 glossary · none 두 가지가 됐습니다(예전 값 tags · fallback_char는 더 나오지 않습니다).

요금

  • ₩30/호출. 찾은 항목이 하나 이상일 때만 과금되고, 결과가 비면(items: []) ₩0입니다. 같은 입력의 반복 호출도 각각 과금됩니다 (멱등성).
  • 입력 오류(400)·잔액 부족(402)은 과금되지 않습니다. 사용량 로그 endpoint는 dream입니다.

고전 근거 kbMatches 준비 중

꿈 내용에 나온 대상(하늘, 뱀, 시장 등)과 맞는 해몽 KB 규칙을 돌려줍니다. 근거는 공유저작물인 옛 해몽서 원문입니다 — 돈황본 『周公解夢書』(P.3908, 10세기)와 『古今圖書集成』 夢部. 같은 꿈 내용이면 언제나 같은 결과가 나옵니다(태그 서비스·LLM을 쓰지 않는 글자 규칙).

"kbMatches": [
  {
    "ruleId": "R-DREAM-DRAGON-002",
    "area": "DRAGON",
    "polarity": "positive",
    "status": "verified",
    "statement": "『주공해몽서』(돈황본 P.3908, 第十二 龍蛇章)는 뱀(虵=蛇)이 길에 있는 꿈(夢見虵當道者)에 대해 「大吉」라고 적는다(글자대로: 크게 길하다).",
    "quote": {
      "text": "夢見虵當道者大吉",
      "sourceId": "SRC-DREAM-P3908",
      "source": "『周公解夢書』 (敦煌 P.3908)",
      "location": "P.3908 第十二 龍蛇章 f09/左/行3下",
      "foundAt": "dream-pd/dunhuang-p3908-bnf/P3908-TRANSCRIPTION-merged.md:198"
    },
    "caveatRuleIds": [],
    "matchedTerms": ["구렁이", "길"]
  }
]
  • statement는 원문이 그렇게 적었다는 기록을 전하는 문장입니다. 꿈이 앞일을 알려 준다는 예언이 아닙니다. quote는 그 근거가 된 원문 한 줄과 위치입니다.
  • polarity: 원문 판단 부분 기준 positive · negative · neutral. negative(죽음·병·잃음 등의 기록)는 includeNegative: true일 때만 들어갑니다.
  • caveatRuleIds: 그 규칙에 걸린 주의 규칙(예: 죽음·질병 기록은 R-DREAM-CAVEAT-002).
  • 맞는 규칙이 없으면 빈 배열입니다. 상징 낱말(items)이 없어도 붙습니다.
  • 준비 중인 기능입니다. 열리기 전에는 응답에 kbMatches가 없고 응답·요금은 위와 같습니다. 요금은 열 때 안내합니다.

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404dream_not_enabled계정에 기능이 열려 있지 않음
402INSUFFICIENT_BALANCE잔액 부족

dream이 비었거나 1,000자를 넘으면 공통 400입니다. 찾은 상징이 없으면 에러가 아니라 200에 items: [](₩0)입니다.

관상·손금 측정 API

사진을 보내지 않습니다. 파트너 기기·브라우저에서 MediaPipe로 뽑은 좌표(얼굴 478점 / 손 21점, 손금 선 폴리라인)를 보내면, 서버가 같은 순수 함수로 측정 키(비율·각도·구역)를 계산하고 그 값에 맞는 관상·손금 KB 규칙(옛 원문 기록)을 돌려줍니다. LLM을 쓰지 않으며 같은 좌표면 언제나 같은 결과입니다. 좌표를 뽑는 참조 코드(브라우저 MediaPipe·Node·curl)는 참조 코드 페이지에 있습니다. 모델 이름·버전·정규화·좌표 개수가 아래 값과 하나라도 다르면 400입니다.

POST /api/v1/partners/v1/face-measure

파라미터타입필수설명
modelName, modelVersionstring✓좌표를 낸 모델. 허용 목록 밖이면 400 MEASURE_MODEL_UNSUPPORTED / MEASURE_MODEL_VERSION_MISMATCH. 현재: face_landmarker · float16/[email protected]
normalizationstring✓face-norm-1
landmarksnumber[][]✓정확히 478개 [x, y] 또는 [x, y, z] (이미지 폭·높이로 나눈 값, −0.02~1.02)
imageWidth, imageHeightnumber✓픽셀 크기
hairline[number, number]얼굴 가운데 발제선 한 점. 서버는 추정하지 않습니다 — 없으면 上停 관련 키가 null(사유 hairline_missing). 사용자가 이마 위 머리 시작점을 탭하는 입력을 권합니다(머리카락 분할 모델은 참고용)
qualityobjectyawDeg·pitchDeg·rollDeg·blendshapes(MediaPipe 계수 이름→점수). 보내야 품질 검사가 됩니다
includeNegativeboolean흉한 판단의 기록(polarity negative)도 넣을지. 기본 false (꿈해몽과 같은 이름·뜻)

POST /api/v1/partners/v1/palm-measure

파라미터타입필수설명
modelName, modelVersionstring✓현재: hand_landmarker · float16/[email protected]
normalizationstring✓palm-norm-1
landmarksnumber[][]✓정확히 21개
imageWidth, imageHeightnumber✓픽셀 크기
handednessstring✓left | right — 피사체의 실제 손(사용자가 고르게 하세요)
mirroredboolean좌우 반전 사진(셀카 미리보기)이면 true
creasesobject[]손금 선 폴리라인 (최대 64개, 선마다 점 2~512). 항목: points(✓), label(life·head·heart·fate·sun·mercury·simian·unknown), kind(segment | branch), autoLabel. 없으면 선 관련 키가 null(creases_missing)
autoLabelboolean이름 없는 선 전부에 서버 공용 휴리스틱으로 이름을 붙입니다. 선 하나에만 쓰려면 그 항목의 autoLabel. 직접 붙인 label이 항상 우선합니다
includeNegativeboolean얼굴과 같음. 기본 false
  • 선 조각은 보내기 전에 이어 주세요. 서버는 조각을 잇지 않고, 휴리스틱은 선마다 조각 하나에만 이름을 붙입니다. 다른 선과 교차하는 점에서 잘린 조각(예: 운명선과 만나는 감정선)은 참조 코드 stitch_creases.py처럼 이은 뒤 보내야 이름을 받습니다. 눈에 보이는 실제 끊김은 끊긴 채로 두세요(breaks 키가 셉니다).

요청 예시 (얼굴, 줄임)

{
  "modelName": "face_landmarker",
  "modelVersion": "float16/[email protected]",
  "normalization": "face-norm-1",
  "landmarks": [[0.5012, 0.3121, -0.031], … 478개 …],
  "imageWidth": 1080,
  "imageHeight": 1350,
  "hairline": [0.501, 0.182],
  "quality": { "yawDeg": 2.1, "pitchDeg": -3.4, "blendshapes": { "eyeBlinkLeft": 0.05, "jawOpen": 0.02 } }
}

응답 예시 (앞부분 발췌)

{
  "success": true,
  "data": {
    "normalization": "face-norm-1",
    "keys": { "face.thirds.upper_ratio": 0.3333, "face.brow.length_vs_eye": 1.625, "face.brow.outer_overhang": 0.1648, … },
    "reasons": {},
    "quality": { "rollDeg": 0, "iodPx": 158, "flags": [] },
    "kbMatches": [
      {
        "ruleId": "R-FACE-PALACE-031",
        "area": "PALACE",
        "kind": "interpretation",
        "polarity": "positive",
        "status": "verified",
        "statement": "『古今圖書集成』 相術部彙考 「十二宮訣」 兄弟는 눈썹이 눈보다 길게 지나가면(眉長過目) …라고 적는다 …",
        "quote": { "text": "眉長過目,三四兄弟", "sourceId": "SRC-FACE-HUIKAO", "source": "…", "location": "…", "foundAt": "…" },
        "caveatRuleIds": ["R-FACE-CAVEAT-001"],
        "matchedConditions": [{ "key": "face.brow.outer_overhang", "op": "gt", "value": 0, "actual": 0.1648 }]
      }
    ]
  }
}
  • keys: 측정 키 전부(키 정의·계산식·신뢰도는 키 표 문서). 값을 낼 수 없으면 null이고 reasons에 사유가 있습니다: hairline_missing · blink · smile · jaw_open · finger_bent · creases_missing · back_of_hand(손등 사진 — 선 관련 키만) 등.
  • kbMatches: 원문이 수치·비교를 직접 주는 규칙만 들어갑니다. 우리가 정한 임계값으로만 판정되는 규칙은 근거 실측(PoC) 전이라 넣지 않습니다. statement는 원문이 그렇게 적었다는 기록이지 예언·진단이 아닙니다. negative는 includeNegative: true일 때만.
  • 품질: 고개 돌림·숙임이 10°를 넘거나(MEASURE_QUALITY_POSE) 얼굴이 너무 작으면(MEASURE_QUALITY_FACE_SIZE) 요청 전체가 400입니다 — 다시 찍게 안내하세요. 눈 감음·웃음·입 벌림은 400이 아니라 해당 키만 null입니다.
  • 입력 오류는 400과 코드(MEASURE_LANDMARK_COUNT · MEASURE_COORD_RANGE · MEASURE_COORD_NAN · MEASURE_CREASE_LIMIT 등)로 돌려줍니다. 오류 메시지에는 점 번호만 있고 좌표 값은 없습니다.
  • 흉한 기록을 보여 줄 때(includeNegative: true): statement는 원문이 그렇게 적었다는 기록입니다. 이용자에게는 "이런 경향이 있다고 적혀 있다 + 이렇게 대처해 볼 수 있다"로 전하고, 사람을 깎아내리거나 성격을 낙인찍는 말로 바꾸지 마세요.

오류 코드

HTTPcode뜻
400MEASURE_MODEL_UNSUPPORTEDmodelName이 허용 목록에 없음
400MEASURE_MODEL_VERSION_MISMATCHmodelVersion이 허용 목록에 없음 (현재 float16/[email protected]만)
400MEASURE_NORMALIZATION_MISMATCHnormalization이 face-norm-1 / palm-norm-1이 아님
400MEASURE_LANDMARK_COUNT좌표 개수가 얼굴 478 / 손 21이 아님
400MEASURE_LANDMARK_SHAPE · MEASURE_COORD_NAN · MEASURE_COORD_RANGE · MEASURE_IMAGE_SIZE · MEASURE_DEGENERATE점 형식·숫자·범위(−0.02~1.02)·이미지 크기 오류
400MEASURE_HANDEDNESS · MEASURE_CREASE_LIMIT · MEASURE_CREASE_SHAPE · MEASURE_CREASE_LABEL손·손금 선 입력 오류
400MEASURE_QUALITY_POSE · MEASURE_QUALITY_FACE_SIZE고개가 10° 넘게 돌아감 · 얼굴이 너무 작음 — 다시 찍기
400(검증 오류)문서에 없는 필드를 보냄 — 허용된 필드만 보내세요
402INSUFFICIENT_BALANCE크레딧 잔액 부족
404face_measure_not_enabled · palm_measure_not_enabled아직 열리지 않은 기능

오류 응답은 모두 과금되지 않습니다.

개인정보

  • 사진을 받지 않습니다. 사진은 이용자 기기에서 좌표로 바뀌고, API에는 좌표만 옵니다.
  • 좌표를 저장하지 않습니다. 좌표는 얼굴·손 기하 정보라, 서버는 요청을 처리하는 동안 메모리에서만 쓰고 로그·DB·오류 메시지에 남기지 않습니다. 사용량 로그에는 endpoint·상태·금액만 남습니다.
  • 최종 이용자 동의는 파트너가 받아 주세요.
  • 참조 코드가 쓰는 MediaPipe는 Apache-2.0 라이선스이며 사용 통계를 보낼 수 있습니다. 제품에 넣을 때 라이선스 고지를 함께 배포하고 개인정보 처리방침에 반영해 주세요.

요금

  • 관상 측정 ₩30 /호출, 손금 측정 ₩30 /호출 (부가세 별도 · 부가세 포함 ₩33).
  • 측정 키가 하나 이상 나왔을 때만 과금됩니다. 측정 키가 하나도 없으면 ₩0, 입력·품질 오류(400)·잔액 부족(402)·미개방(404)도 과금되지 않습니다. 사용량 로그 endpoint는 face-measure / palm-measure입니다.

타로 API

POST /api/v1/partners/v1/tarot

장수(count, 1~10장)나 스프레드(spread)를 정하면 78장 덱(메이저 22 · 마이너 56)에서 겹치지 않게 카드를 뽑아, 카드마다 정·역방향, 키워드, 뜻, 카드 이미지 주소를 돌려줍니다. AI 해석은 없습니다 — 뜻은 카드에 정해진 문장 그대로입니다. 그래서 쓰는 방법이 셋입니다.

요청 파라미터

파라미터타입필수설명
countnumber△뽑을 장수 1~10 (정수). count와 spread 중 하나만 보냅니다. 둘 다 보내거나 둘 다 없으면 400
spreadstring△스프레드. 장수와 위치 이름이 정해져 있습니다: single(오늘의 한 장, 1장) · past-present-future(과거·현재·미래, 3장) · choice(선택: 현재 상황·장애물·결과, 3장) · celtic-condensed(켈틱 축약, 5장) · future-prediction(미래 예측, 5장)
reversedboolean역방향 허용 (기본값 true). false면 모두 정방향
seedstring재현용 문자열 (1~200자). 같은 seed + 같은 count/spread + 같은 reversed면 언제나 같은 카드가 나옵니다. 없으면 매번 무작위(암호학적 난수)
questionstring엔드유저 질문 (최대 500자). 해석에 쓰지 않고 응답에 그대로 돌려줍니다 — 다음 LLM 단계로 넘길 때 편하라고 둔 필드입니다
langstring카드 이름·키워드·뜻·스프레드 이름의 언어: ko(기본) · en · ja · th

요청 예시

{
  "spread": "past-present-future",
  "question": "올해 하반기에 이직해도 될까요?",
  "seed": "user-1042:2026-10-10"
}
curl -X POST https://api.hrum.ai/api/v1/partners/v1/tarot \
  -H "X-Partner-Api-Key: $FLOAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"count": 1, "lang": "en", "reversed": false}'

응답 예시 (카드 1장 발췌)

{
  "success": true,
  "data": {
    "lang": "ko",
    "count": 3,
    "spread": { "type": "past-present-future", "name": "과거·현재·미래", "description": "시간 흐름을 따라가는 질문", "positions": ["과거", "현재", "미래"] },
    "reversedAllowed": true,
    "seed": "hrum-example-1",
    "cards": [
      {
        "position": 1,
        "positionLabel": "과거",
        "id": "wands_10",
        "name": "완드의 10",
        "nameEn": "Ten of Wands",
        "arcana": "wands",
        "reversed": true,
        "keywords": ["내려놓기", "위임", "해방"],
        "meaning": "무거운 짐을 마침내 내려놓거나 도움을 받기 시작하는 시기.",
        "imageUrl": "https://console.hrum.ai/tarot/cards/v2/wands_10.webp"
      }
    ],
    "cardBackUrl": "https://console.hrum.ai/tarot/cards/v2/back.webp",
    "question": "올해 하반기에 이직해도 될까요?",
    "costPoints": 20
  }
}

요금

Waite 원전 근거 kb 준비 중

열리면 응답에 kb가 붙습니다. 뽑힌 카드마다 A. E. Waite, The Pictorial Key to the Tarot(1910, 퍼블릭 도메인) 원문에서 그 카드의 정·역방향 뜻을 규칙 ID(R-TAROT-*)와 원문 인용으로 돌려줍니다. 위의 meaning·keywords는 그대로 두고 kb만 더해지며, 요금은 같습니다.

{
  "method": "celtic-cross",
  "seed": "user-1042:2026-10-11"
}

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404tarot_not_enabled계정에 기능이 열려 있지 않음
404tarot_kb_not_enabledmethod를 보냈는데 원전 근거가 아직 열리지 않음
402INSUFFICIENT_BALANCE잔액 부족

count·spread·method 가운데 정확히 하나를 보내야 합니다. 둘 이상이거나 하나도 없으면 공통 400입니다.

네이탈 차트 API (서양 점성술) 판매 종료

판매 종료 — 점성술 토탈(/astrology)을 쓰세요. 같은 출생 입력으로 10개 천체·하우스·애스펙트에 미리 써 둔 풀이 문단까지 1회 ₩50에 한 번에 받습니다. 이미 붙여 둔 /natal-chart 호출은 그대로 동작하지만 새 연동에는 권하지 않습니다.

POST /api/v1/partners/v1/natal-chart

생년월일시로 서양 점성술 네이탈 차트(태양·달·수성·금성·화성·목성·토성 7개 천체 위치 + 상승궁)를 계산합니다. 천왕성·해왕성·명왕성은 포함하지 않습니다. 절기가 아닌 서양 점성술의 트로피컬 황도를 기준으로 합니다.

요청 파라미터

파라미터 타입 필수 설명
birthDate string O 출생일 YYYY-MM-DD (양력). 실제 달력 날짜만(2월30일 등 거부)
birthTime string 출생시각 HH:mm (24h). birthTimePrecision을 exact 또는 approximate로 함께 보내야 쓰입니다 — 정밀도를 생략하면 unknown으로 보고 시각을 무시해 정오 기준으로 계산하며, 상승궁은 null입니다
gender string 성별 ("male" | "female"). 호환용으로 받기만 하고 계산에는 쓰지 않습니다
birthLatitude number 위도 -90~90 (상승궁 계산에 필요, 경도와 함께)
birthLongitude number 경도 -180~180
birthTimezoneOffset number UTC 오프셋(분), 기본값: 540(KST)
birthTimePrecision string "exact" | "approximate"(시 단위) | "unknown". 생략 시 unknown(시각 무시, 정오 계산)
readings boolean true면 응답에 readings(행성 품위·애스펙트 풀이 문단) 동봉. 추가 ₩20. 계정에 열려 있지 않으면 404 natal_readings_not_enabled(과금 없음)
lang string readings 문단 언어 — ko(기본) · en · ja · th

응답 필드

필드 타입 설명
sun object 태양 위치 { body, sign(한글), signEn, element(한글: 불·흙·공기·물), degreeInSign, eclipticLongitude }
moon object 달 위치
mercury object 수성 위치
venus object 금성 위치
mars object 화성 위치
jupiter object 목성 위치
saturn object 토성 위치
ascendant object|null 상승궁. birthTimePrecision이 exact 또는 approximate이고 좌표가 있을 때 계산되며, 그 밖에는 null
ascendantReason string 상승궁이 null인 이유 (있을 때만, 예: 시각 정밀도가 unknown이거나 좌표 없음)
ascendantHourBasis boolean approximate(시 단위) 시각으로 계산한 상승궁이면 true — "시 단위 기준"으로 표시하세요
ascendantEstimate object|null approximate일 때 ±60분 범위의 상승궁 추정. window = { startLocal, endLocal, switchLocal, beforeSign, afterSign } (switchLocal은 범위 안에서 별자리가 바뀌는 시각, 안 바뀌면 null)
moonSignUncertain boolean 시각 미상(unknown)일 때만 포함 — 그날 안에 달 별자리가 바뀌면 true(정오 기준 달 별자리가 확실하지 않음)
coordsBasis string|null 상승궁 계산에 쓴 위치: "coords"(보낸 좌표) 또는 null(좌표 없음·시각 미상)
readings object readings: true 일 때만. dignities(본궁·고양·손상·추락에 놓인 행성 풀이) · aspects(가장 정확한 애스펙트 최대 2개, deviation = 정각에서 벗어난 도수) · lang · costWon(추가 요금)

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/natal-chart \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "birthDate": "1990-05-15",
    "birthTime": "12:00",
    "birthLatitude": 37.5665,
    "birthLongitude": 126.978,
    "birthTimezoneOffset": 540,
    "birthTimePrecision": "exact"
  }'

응답 예시

{
  "success": true,
  "data": {
    "sun": {
      "body": "Sun",
      "sign": "황소자리",
      "signEn": "Taurus",
      "element": "흙",
      "degreeInSign": 24.03,
      "eclipticLongitude": 54.0343
    },
    "moon": { "body": "Moon", "sign": "염소자리", "signEn": "Capricorn", "element": "흙", ... },
    "mercury": { "body": "Mercury", "sign": "황소자리", "signEn": "Taurus", ... },
    "venus": { "body": "Venus", "sign": "양자리", "signEn": "Aries", "element": "불", ... },
    "mars": { ... },
    "jupiter": { ... },
    "saturn": { ... },
    "ascendant": {
      "body": "Ascendant",
      "sign": "사자자리",
      "signEn": "Leo",
      "element": "불",
      "degreeInSign": 23.13,
      "eclipticLongitude": 143.1256
    },
    "coordsBasis": "coords"
  }
}

위 예시 요청: {"birthDate": "1990-05-15", "birthTime": "12:00", "birthLatitude": 37.5665, "birthLongitude": 126.978, "birthTimezoneOffset": 540, "birthTimePrecision": "exact"} (실제 엔진 출력 발췌).

풀이 문단 (readings: true)

행성 품위(본궁·고양·손상·추락)와 가장 정확한 애스펙트 2개에 대해 미리 써 둔 풀이 문단을 붙입니다. INTA 앱 만세력 화면과 같은 문단입니다. 아래는 실제 엔진 출력입니다 (1990-09-04 14:30, 서울 좌표, exact).

{
  "success": true,
  "data": {
    "sun": { ... }, "moon": { ... }, ...,
    "readings": {
      "dignities": [
        { "key": "Me_dom", "lang": "ko",
          "text": "수성이 본궁인 쌍둥이자리나 처녀자리에 있으면 정보를 모으고 분류해 말로 옮기는 기능이 막힘없이 또렷하게 드러나요. 생각이 많아질 때 하나를 골라 끝까지 파고들면 그 명료함이 실제 성과로 이어져요." },
        { "key": "Sa_dom", "lang": "ko",
          "text": "토성이 본궁인 염소자리나 물병자리에 있으면 책임을 지고 구조를 세워 반복 훈련으로 숙련을 쌓는 기능이 막힘없이 또렷하게 드러나요. 기준이 지나치게 굳어질 때 쉬어 갈 틈을 일정에 남겨 두면 그 인내가 성숙하게 쓰여요." }
      ],
      "aspects": [
        { "key": "Me_Sa_tr", "deviation": 0.04, "lang": "ko",
          "text": "수성과 토성의 트라인은 절제와 의지를 생각에 더해, 서두르지 않고 앞을 내다보며 구조를 짜는 데 힘이 실려요. 변화가 필요한 순간에 익숙한 방식만 고집할 수 있으니 새 방법도 한 번 시험해 보세요." },
        { "key": "Mo_Ma_sq", "deviation": 1.03, "lang": "ko",
          "text": "달과 화성이 스퀘어를 이루면 감정이 올라오는 순간 욱하거나 규칙에 반발하는 쪽으로 기울 수 있어요. 화가 난 뒤 반응을 한 템포 늦추는 연습을 하면 그 뜨거움이 지켜야 할 것을 지키는 힘으로 바뀌어요." }
      ],
      "lang": "ko",
      "costWon": 20
    }
  }
}
  • 출생 시간을 모르면(birthTimePrecision 미지정·unknown) 달은 위치가 넓게 흔들려 애스펙트 계산에서 빠집니다.
  • 요청 언어 문장이 없는 문단은 한국어로 오고, 그 문단의 lang 이 ko 입니다.
  • 풀이 문단을 불러오지 못하면 503 natal_readings_unavailable(과금 없음).

주의사항

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404natal_readings_not_enabledreadings: true인데 풀이 문단이 열려 있지 않음
503natal_readings_unavailable풀이 문단을 불러오지 못함
402(없음)잔액이 호출 요금보다 적음 — 이 API는 호출 전 잔액 검사가 없어 코드 없는 402가 옵니다

점성술 토탈 API

만세력처럼 한 번 호출로 점성술 계산 전부를 돌려줍니다. 기본은 전체 네이탈 차트(태양~명왕성 10개 천체 · 달 교점 · 4개 앵글 · 하우스(기본 플라시두스) · 애스펙트 · 품위)이고, 요청에 블록을 붙이면 트랜싯 + 2차 진행(timing)과 두 사람 시너스트리(partner)가 같은 응답에 함께 옵니다. 필요한 부분만 골라 쓰면 됩니다. LLM은 쓰지 않습니다. 숫자와 규칙 키(features)에 더해, HRUM이 미리 써 둔 풀이 문단(readings)이 추가 요금 없이 항상 함께 옵니다. INTA와 같은 공용 점성술 엔진이며, 천체 위치는 NASA/JPL Horizons·Swiss Ephemeris 기준값과 대조해 검증했습니다(행성 0.005° 이내). 해석 파라미터(오브·각 종류·품위표)는 모두 고전 원전 규칙에서 왔고, 원전끼리 갈리는 값(트랜싯·진행 오브)은 기본값을 두지 않고 직접 고르게 합니다(아래 "기본값과 직접 골라야 하는 값").

POST /api/v1/partners/v1/astrology

출생 입력은 네이탈 차트 API와 같습니다(birthDate · birthTime · birthTimePrecision · birthLatitude · birthLongitude · birthTimezoneOffset, 기본 540 = KST, 한국 서머타임·1954~61 UTC+8:30 자동 반영). birthTimePrecision을 생략하면 unknown(정오 기준)입니다.

파라미터타입필수설명
houseSystemsstring[]placidus · whole_sign · equal · porphyry 중 고른 것. 생략하면 ["placidus"]입니다. 극권 출생지(위도 약 66.56° 이상, angles.polar)에서는 플라시두스 대신 포르피리를 계산하고 그 결과에 "fallbackFrom": "placidus"를 붙입니다(플라시두스를 직접 지정해도 같음)
minorAspectsboolean세미섹스타일(30°)·세미스퀘어(45°)·세스퀴쿼드레이트(135°)·퀸컹스(150°) 포함(네이탈·시너스트리). 기본 false — 메이저 5각만
orbsstring네이탈·시너스트리 오브 표: leo1928(기본 — 합·충 해–달 12°, 해/달–행성 10°, 행성끼리 8°, 스퀘어·트라인 8°, 섹스타일 7°, 세미스퀘어·세스퀴 4°, 세미섹스타일·퀸컹스 2°) · heindel1919(메이저 단일 6°, 보수적)
nodestring규칙 키의 Nn(북교점)·Sn(남교점 = 북교점 + 180°)이 따를 교점: mean(기본, 평균 교점) · true(진 교점). features의 node_sign Nn_…·Sn_…와 교점 합 키(Mo_Nn_cj·Nn_Asc_cj 등)에 적용됩니다. MNn_…·TNn_… 키는 이 값과 상관없이 둘 다 나옵니다
langstringsignName(별자리 이름) 언어 — ko(기본) · en · ja · th
timingobject보내면 트랜싯 + 2차 진행을 계산해 응답 timing에 넣습니다. 아래 표
partnerobject상대의 출생 입력(위와 같은 필드, lang 제외). 보내면 두 사람 사이 각·별자리 관계를 응답 synastry에 넣습니다. 상대의 출생 시각과 좌표(birthLatitude·birthLongitude)가 있으면 상대 상승점(Asc)·중천(MC)도 계산해 교차 각에 넣습니다

timing 블록 안:

파라미터타입필수설명
orbDegreesnumberO트랜싯→출생, 진행→출생 각의 오브(도) 0.5~10. 모든 천체 쌍에 같은 값을 씁니다(메이저 5각만)
atstring보고 싶은 시점. YYYY-MM-DD(= 그날 00:00 UTC) 또는 오프셋을 붙인 일시(2026-10-11T09:00:00+09:00, ...Z). 오프셋 없는 일시는 400. 생략하면 지금
progressionsboolean2차 진행 계산 (기본 true)
yearLengthstring경과 시간을 "인생 몇 년"으로 바꿀 때 쓸 1년 길이: tropical(365.24219일, 기본) · julian(365.25일)

요청 예시 1 — 기본 (네이탈만)

{
  "birthDate": "1990-05-15",
  "birthTime": "14:30",
  "birthTimePrecision": "exact",
  "birthLatitude": 37.5665,
  "birthLongitude": 126.978,
  "houseSystems": ["placidus", "whole_sign"]
}
curl -X POST https://api.hrum.ai/api/v1/partners/v1/astrology \
  -H "X-Partner-Api-Key: $FLOAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"birthDate": "1990-05-15", "birthTime": "14:30", "birthTimePrecision": "exact", "birthLatitude": 37.5665, "birthLongitude": 126.978, "minorAspects": true, "lang": "en"}'

응답 예시 1 (발췌, 실제 엔진 출력)

{
  "success": true,
  "data": {
    "natal": {
      "utc": "1990-05-15T05:30:00.000Z",
      "timePrecision": "exact",
      "timeKnown": true,
      "zodiac": "tropical",
      "lang": "ko",
      "bodies": [
        { "id": "Sun", "longitude": 54.1347, "sign": "Taurus", "signKey": "tau", "signName": "황소자리", "degreeInSign": 24.1347, "speed": 0.9643, "retrograde": false, "dignities": [], "house": { "placidus": 9, "whole_sign": 9 } },
        { "id": "Moon", "longitude": 293.8244, "sign": "Capricorn", "signKey": "cap", "signName": "염소자리", "degreeInSign": 23.8244, "speed": 12.2943, "retrograde": false, "dignities": ["det"], "house": { "placidus": 4, "whole_sign": 5 } },
        ...
      ],
      "nodes": [
        { "id": "MeanNode", "longitude": 311.3535, "sign": "Aquarius", "signName": "물병자리", "speed": -0.0529,
          "south": { "longitude": 131.3535, "sign": "Leo", "signName": "사자자리", ... } },
        { "id": "TrueNode", ... }
      ],
      "angles": { "asc": 173.5996, "mc": 82.8067, "dsc": 353.5996, "ic": 262.8067,
        "ascSign": { "sign": "Virgo", "signName": "처녀자리", ... }, "mcSign": { ... }, "dscSign": { ... }, "icSign": { ... }, "polar": false },
      "anglesOmitted": null,
      "houseSystems": ["placidus", "whole_sign"],
      "houses": {
        "placidus": { "cusps": [173.5996, 199.6989, 229.8749, ...], "bodies": { "Sun": 9, "Moon": 4, "Jupiter": 10, ... } },
        "whole_sign": { "cusps": [150, 180, 210, ...], "bodies": { "Sun": 9, "Moon": 5, ... } }
      },
      "housesExcluded": [],
      "orbs": "leo1928",
      "minorAspects": false,
      "aspects": [
        { "a": "Jupiter", "b": "Uranus", "type": "opposition", "code": "op", "key": "Ju_Ur_op", "separation": 179.6986, "orb": 0.3014, "orbLimit": 8, "strength": "exact", "applying": false, "outOfSign": false },
        { "a": "Sun", "b": "Moon", "type": "trine", "code": "tr", "key": "Su_Mo_tr", "orb": 0.3104, "orbLimit": 8, "strength": "exact", "applying": true, "outOfSign": false },
        ...
      ],
      "dignities": { "Sun": [], "Moon": ["det"], "Mercury": [], "Venus": ["det"], "Mars": [], "Jupiter": ["exa"], "Saturn": ["dom"] },
      "moonSignUncertain": false,
      "features": [
        { "type": "planet_sign", "key": "Su_tau" },
        { "type": "dignity", "key": "Mo_det" },
        { "type": "planet_retrograde", "key": "Me_rx" },
        { "type": "planet_house", "key": "Su_h9", "system": "placidus" },
        { "type": "aspect", "key": "Su_Mo_tr", "strength": "exact", "applying": true, "orb": 0.31 },
        { "type": "meta", "key": "time_known" },
        ...
      ]
    },
    "readings": {
      "dignities": [
        { "key": "Mo_det", "text": "달이 염소자리에서 손상 자리에 있으면 감정을 바로 내보이기보다 책임과 일의 질서 속에서 조심스럽게 다루는 경향이 있어요. ...", "lang": "ko" },
        { "key": "Ve_det", ... }, { "key": "Ju_exa", ... }, { "key": "Sa_dom", ... }
      ],
      "aspects": [
        { "key": "Su_Mo_tr", "text": "태양과 달의 트라인은 의지와 정서가 같은 곳을 바라봐, 스스로 길을 내며 생활 전반을 고르게 꾸려 가는 힘이 실려요. ...", "lang": "ko", "deviation": 0.31 },
        { "key": "Su_Sa_tr", ... }, { "key": "Mo_Sa_cj", ... }, ...
      ],
      "lang": "ko"
    },
    "readingsUnavailable": false,
    "costPoints": 50
  }
}

timing·partner를 보내지 않으면 응답에 timing·synastry 키 자체가 없습니다.

요청 예시 2 — timing 포함 (트랜싯 + 2차 진행)

{
  "birthDate": "1990-05-15",
  "birthTime": "14:30",
  "birthTimePrecision": "exact",
  "birthLatitude": 37.5665,
  "birthLongitude": 126.978,
  "houseSystems": ["whole_sign"],
  "timing": { "at": "2026-10-11T09:00:00+09:00", "orbDegrees": 3, "yearLength": "tropical" }
}
curl -X POST https://api.hrum.ai/api/v1/partners/v1/astrology \
  -H "X-Partner-Api-Key: $FLOAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"birthDate": "1990-05-15", "timing": {"orbDegrees": 2, "progressions": false}}'

응답 예시 2 (발췌, natal은 예시 1과 같은 모양)

{
  "success": true,
  "data": {
    "natal": { ... },
    "timing": {
      "at": "2026-10-11T00:00:00.000Z",
      "orbDegrees": 3,
      "natalPoints": ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", "Asc", "MC"],
      "transits": {
        "bodies": [ { "id": "Sun", "longitude": 197.6966, "sign": "Libra", "signName": "천칭자리", "speed": 0.9889, "retrograde": false, ... }, ... ],
        "aspects": [
          { "a": "Mars", "b": "Mercury", "type": "square", "key": "tMa_nMe_sq", "orb": 0.6358, "orbLimit": 3, "strength": "exact", "applying": true, "outOfSign": false },
          ...
        ]
      },
      "progressions": {
        "yearLength": "tropical",
        "yearLengthDays": 365.24219,
        "ageYears": 36.4081,
        "instant": "1990-06-20T15:17:39.048Z",
        "bodies": [ { "id": "Sun", "longitude": 89.0352, "sign": "Gemini", "signName": "쌍둥이자리", ... }, ... ],
        "aspects": [
          { "a": "Uranus", "b": "Mercury", "type": "trine", "key": "pUr_nMe_tr", "orb": 0.0862, "orbLimit": 3, "strength": "exact", "applying": false, "outOfSign": false },
          ...
        ]
      },
      "features": [
        { "type": "transit", "key": "tMa_nMe_sq", "strength": "exact", "applying": true, "orb": 0.64 },
        { "type": "progressed", "key": "pUr_nMe_tr", "strength": "exact", "applying": false, "orb": 0.09 },
        ...
      ]
    },
    "readings": { ... }, "readingsUnavailable": false,
    "costPoints": 50
  }
}

요청 예시 3 — partner 포함 (시너스트리)

{
  "birthDate": "1990-05-15",
  "birthTime": "14:30",
  "birthTimePrecision": "exact",
  "birthLatitude": 37.5665,
  "birthLongitude": 126.978,
  "houseSystems": ["whole_sign"],
  "partner": { "birthDate": "1992-11-03", "birthTime": "08:10", "birthTimePrecision": "exact", "birthLatitude": 35.1796, "birthLongitude": 129.0756 }
}
curl -X POST https://api.hrum.ai/api/v1/partners/v1/astrology \
  -H "X-Partner-Api-Key: $FLOAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"birthDate": "1990-05-15", "birthTime": "14:30", "birthTimePrecision": "exact", "partner": {"birthDate": "1992-11-03"}}'

응답 예시 3 (발췌, natal은 예시 1과 같은 모양)

{
  "success": true,
  "data": {
    "natal": { ... },
    "synastry": {
      "partner": {
        "utc": "1992-11-02T23:10:00.000Z",
        "timePrecision": "exact",
        "timeKnown": true,
        "moonSignUncertain": false,
        "bodies": [ { "id": "Sun", "longitude": 220.7903, "sign": "Scorpio", "signName": "전갈자리", "speed": 1.0016, "retrograde": false, "dignities": [], ... }, ... ],
        "angles": { "asc": 252.31, "mc": 175.86, "ascSign": { "sign": "Sagittarius", "signName": "사수자리", ... }, "mcSign": { "sign": "Virgo", "signName": "처녀자리", ... } },
        "anglesOmitted": null
      },
      "orbs": "leo1928",
      "minorAspects": false,
      "aspects": [
        { "a": "Moon", "b": "Mars", "type": "opposition", "code": "op", "key": "aMo_bMa_op", "orb": 0.2993, "orbLimit": 10, "strength": "exact", "applying": null, "outOfSign": false },
        { "a": "Neptune", "b": "Uranus", "type": "conjunction", "code": "cj", "key": "aNe_bUr_cj", "orb": 0.3898, "orbLimit": 8, "strength": "exact", "applying": null, "outOfSign": false },
        ...
      ],
      "features": [
        { "type": "syn_aspect", "key": "aMo_bMa_op", "strength": "exact", "orb": 0.3 },
        { "type": "syn_aspect", "key": "aNe_bUr_cj", "strength": "exact", "orb": 0.39 },
        { "type": "astro_syn", "key": "Su_Mo_se" },
        { "type": "syn_sign", "key": "aAsc_bAsc_es" },
        ...
      ]
    },
    "readings": { ... }, "readingsUnavailable": false,
    "costPoints": 50
  }
}

기본값과 직접 골라야 하는 값

제공하지 않는 것

요금

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404astrology_not_enabled계정에 기능이 열려 있지 않음
400at_before_birthtiming.at이 출생 시각보다 앞섬 (진행 계산을 켠 경우)
402(없음)잔액이 호출 요금보다 적음 — 이 API는 호출 전 잔액 검사가 없어 코드 없는 402가 옵니다

운 흐름 API 준비 중

한 사람의 사주로 대운·세운·월운이 원국 위에 얹히는 흐름을 계산해 그래프용 숫자와 관계 목록으로 돌려줍니다. 한 번에 한 사람, LLM은 쓰지 않습니다. 세로축 값은 "운 글자를 얹은 일간 쪽 점수"입니다 — 원국 강약 총점에 운 글자마다 원국과 같은 글자당 점수표(지원·통제·지장간·뿌리)로 매긴 몫을 더하고 ±100으로 자른 값이며, 운세의 좋고 나쁨을 재지 않습니다(R-SAJU-UN-034). 신강·중화·신약 이름은 원국 것 그대로이고 운으로 다시 붙이지 않습니다.

요금: 1회 ₩50(만세력과 같음, 범위·월운을 늘려도 같은 가격). 400 등 실패 응답은 과금하지 않습니다.

숫자는 모두 INTA와 같은 공용 사주 엔진이 낸 값입니다. 출생 시각은 한국 표준시 이력 벽시계(1954~61 UTC+8:30, 서머타임 자동 반영)로 읽어 절입과 비교하므로 서버 시간대와 상관없이 같은 결과가 나옵니다.

POST /api/v1/partners/v1/saju-flow

파라미터타입필수설명
birthDatestringOYYYY-MM-DD (1900~2100). calendar 기준 날짜
birthTimestring | nullHH:mm. 없으면 시주 미상 — 여섯 글자 기준으로 계산하고 hourKnown: false
calendarstringOsolar · lunar (음력은 양력으로 바꿔 계산)
isLeapMonthboolean음력 윤달 (기본 false)
genderstring | nullmale · female. 없으면 대운 층이 없습니다(daeun: [], daeunIndex: null)
timezonestringAsia/Seoul만 받습니다
rangeobjectO{ "fromYear", "toYear" } 세운 범위(양력 연도, 최대 101년). 출생연도 ~ 출생연도+100을 한 번 받아 두고 화면에서 잘라 쓰는 방식을 권합니다
monthsOfYearinteger | null월운 12칸을 계산할 사주 연도(그해 입춘부터 다음 해 입춘 전까지). 생략하면 지금(한국 시각)이 속한 사주 연도 — 1월 1일~입춘 전에는 지난해. null이면 월운 층 없음(months: [])
langstringko · en · ja · th. 응답 값은 언어와 무관한 코드(한글 간지 읽기·오행·관계 이름)라 지금은 결과가 같습니다

요청 예시

{
  "birthDate": "1990-05-15",
  "birthTime": "14:30",
  "calendar": "solar",
  "gender": "male",
  "timezone": "Asia/Seoul",
  "range": { "fromYear": 1990, "toYear": 2090 },
  "monthsOfYear": 2026
}
curl -X POST https://api.hrum.ai/api/v1/partners/v1/saju-flow \
  -H "X-Partner-Api-Key: $FLOAT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: chart-123:flow:2026" \
  -d '{"birthDate": "1990-05-15", "birthTime": "14:30", "calendar": "solar", "gender": "male", "timezone": "Asia/Seoul", "range": {"fromYear": 1990, "toYear": 2090}, "monthsOfYear": 2026}'

응답 예시 (발췌, 실제 엔진 출력)

{
  "success": true,
  "data": {
    "engineVersion": "saju-flow-3",
    "kbVersion": "2026.10.11-2",
    "hourKnown": true,
    "natalAmbiguous": null,
    "natal": {
      "score": -37, "result": "중화", "level": "약약",
      "breakdown": { "season": -30, "support": 36, "control": -28, "hidden": -5, "root": 5 },
      "primaryYongshin": "토",
      "shin": { "yongshin": "토", "huishin": "화", "gishin": "목", "gushin": "수", "hanshin": "금" },
      "ruleIds": ["R-SAJU-STR-001", "R-SAJU-STR-018", "R-SAJU-STR-019", "R-SAJU-YONG-170"]
    },
    "daeun": [
      { "startYear": 1997, "endYear": 2006, "startAge": 7,
        "chars": [
          { "pos": "stem", "char": "임", "hanja": "壬", "ohaeng": "수", "sipsin": "식신", "shinRole": "gushin",
            "share": { "support": 0, "control": -8, "hidden": 0, "root": 0, "total": -8 } },
          { "pos": "branch", "char": "오", "hanja": "午", "ohaeng": "화", "sipsin": "정관", "shinRole": "huishin",
            "share": { "support": 0, "control": -10, "hidden": -5, "root": 0, "total": -15 } }
        ],
        "delta": -23, "line": { "value": -60, "clamped": false },
        "relations": [
          { "type": "자형", "from": "daeun", "with": "yearBranch", "a": "오", "b": "오", "ruleId": "R-SAJU-REL-044", "decisionRuleId": "R-SAJU-UN-036" },
          { "type": "육합", "from": "daeun", "with": "hourBranch", "a": "오", "b": "미", "ruleId": "R-SAJU-REL-015", "decisionRuleId": null }
        ],
        "triples": [], "hapPulim": [ ... ] },
      ...
    ],
    "years": [
      { "year": 2026, "daeunIndex": 2,
        "chars": [ { "pos": "stem", "char": "병", "hanja": "丙", ... "share": { ..., "total": -10 } },
                   { "pos": "branch", "char": "오", "hanja": "午", ... "share": { ..., "total": -15 } } ],
        "delta": -25, "line": { "value": -33, "clamped": false },
        "relations": [
          { "type": "천간합", "from": "seun", "with": "monthStem", "a": "병", "b": "신", "ruleId": "R-SAJU-REL-003", "decisionRuleId": null },
          ...
        ],
        "withDaeun": [], "triples": [],
        "hapPulim": [
          { "kind": "existing", "domain": "jiji", "pair": ["오", "미"], "status": "broken_by_dong",
            "reasonCode": "branch_hap_broken", "params": { "incoming": "오", "origin": "seun" },
            "triggeredBy": { "origin": "seun", "value": "오" },
            "reasonFallback": "동기 오(seun) 도래 → 합반 깨짐" },
          { "kind": "new", "domain": "cheongan", "pair": ["병", "신"], "chartValue": "신", "incomingValue": "병", "incomingOrigin": "seun",
            "reasonCode": "new_stem_hap", "params": { "incoming": "병", "origin": "seun", "partner": "신" },
            "reasonFallback": "병(seun) + 원국 신 → 새 천간 합 형성" },
          ...
        ],
        "ipchunNote": false },
      ...
    ],
    "months": [
      { "index": 1, "startsAt": "2026-02-04T04:55:00+09:00",
        "chars": [ { "pos": "stem", "char": "경", ... }, { "pos": "branch", "char": "인", ... } ],
        "delta": 4,
        "relations": [ { "type": "반합", "from": "wolun", "with": "yearBranch", "a": "인", "b": "오", "ruleId": "R-SAJU-REL-024", "decisionRuleId": "R-SAJU-UN-035" }, ... ] },
      ...
    ],
    "compatInputs": {
      "primaryYongshin": "토",
      "surfaceElementCounts": { "목": 0, "화": 2, "토": 2, "금": 3, "수": 1 },
      "surfaceAbsent": ["목"],
      "hourKnown": true,
      "ruleIds": ["R-SAJU-COMPAT-008", "R-SAJU-COMPAT-009", "R-SAJU-COMPAT-010"]
    },
    "caveats": ["R-SAJU-UN-028", "R-SAJU-PRAC-026", "R-SAJU-UN-034"],
    "birthBeforeIpchun": false,
    "costPoints": 0
  }
}

필드 읽는 법

요금

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
404saju_flow_not_enabled계정에 기능이 열려 있지 않음
400INVALID_DATEbirthDate 형식 오류 또는 없는 날짜
400INVALID_LUNAR_DATE없는 음력 날짜
400INVALID_RANGEtoYear가 fromYear보다 앞섬, 또는 101년 초과
400INVALID_TIMEbirthTime이 HH:mm 형식이 아님
402(없음)잔액이 호출 요금보다 적음 — 이 API는 호출 전 잔액 검사가 없어 코드 없는 402가 옵니다

궁합 API

두 사람의 사주로 궁합을 계산합니다. 사주 궁합(일주 60×60 궁합표 · 용도별 점수와 6축 · 천간/지지 관계 · 8글자 오행 보완 · 십신 · 교차 신살), 속궁합 기호표(참고 문헌(1995) 남녀 일주표의 기호·등급), 별자리 궁합(태양·달·상승궁 3층 + 용도별 행성, 별자리 궁합 문단 최대 2개), 띠 궁합(두 띠의 지지 관계와 등급)을 한 번에 돌려주고, 다섯 가지 점수를 용도별 가중치로 합친 종합 점수(headerBreakdown)도 함께 줍니다. INTA 앱 궁합 화면과 같은 엔진이고, 같은 입력은 항상 같은 결과입니다.

POST /api/v1/partners/v1/compat

두 사람의 사주를 기반으로 궁합을 분석합니다.

요청 파라미터

파라미터 타입 필수 설명
personABirthDate string O A의 출생일 (YYYY-MM-DD, 기본 양력. personACalendar: "lunar"면 음력)
personABirthTime string A의 출생시간 (HH:MM, 선택사항)
personAGender string A의 성별 ("male"/"female", "남"/"여"도 허용, 선택사항)
personBBirthDate string O B의 출생일 (YYYY-MM-DD, 기본 양력. personBCalendar: "lunar"면 음력)
personBBirthTime string B의 출생시간 (HH:MM, 선택사항)
personBGender string B의 성별 ("male"/"female", "남"/"여"도 허용, 선택사항)
purpose string 궁합 용도 — yeonae_attraction(연애, 기본) · marriage_stability(결혼) · friendship(우정) · family_bond(가족) · coworker_cooperation(동료) · business_partnership(사업). 점수·6축·관계 유형·관계 서술·별자리 우선 행성·종합 점수 가중치가 용도에 맞게 달라집니다. 그 밖의 값은 400 INVALID_PURPOSE(과금 없음)
lang string 문장 언어 — ko(기본) · en · ja · th. categoryNarrative · astroReadings · astroSynastry 문구 · classic1995 등급 · tti 가 이 언어로 옵니다. saju 블록 중 relationRatings의 key·meetingKeywords·authority·full8·conflict와 dayFlow.summary·yearFlow.years[].summary·dictionary도 이 언어를 따릅니다. 그 밖의 saju 기본 필드·mbti·yearFlow.years[].a/b는 현재 한국어만
personABirthTimePrecision / personBBirthTimePrecision string 시각 정확도 — exact · approximate(시 단위) · estimated · unknown. 별자리 궁합: 기본은 시간이 있으면 exact, 없으면 unknown. 사주: 진태양시 보정은 exact일 때만 적용되고, 생략한 채 좌표나 personXCountry를 보내면 exact로 봅니다(만세력 API와 같은 규칙)
personABirthLatitude / personABirthLongitude
personBBirthLatitude / personBBirthLongitude
number 출생지 좌표(둘 다 함께). 상승궁 계산과 사주 진태양시 보정에 같이 쓰입니다(2026-10-11부터 사주에도 적용). 없으면 상승궁은 personXCountry, 그것도 없으면 lang 의 나라 대표 좌표로 추정(ko→한국, ja→일본, th→태국 / en은 상승궁 생략)
personABirthTimezoneOffset / personBBirthTimezoneOffset number UTC 오프셋(분), 기본 540(KST). 좌표와 함께 보내면 사주 보정의 표준 자오선도 이 값으로 정합니다(생략하면 경도에서 가장 가까운 시간대)
personACalendar · personAIsLeapMonth
personBCalendar · personBIsLeapMonth
string · boolean "lunar"면 출생일을 음력으로 받아 양력으로 바꿔 계산합니다(기본 "solar"). IsLeapMonth: true는 윤달이며, 그해에 그 윤달이 없으면 평달로 계산합니다. 없는 음력 날짜는 400 INVALID_LUNAR_DATE(과금 없음). 음력으로 보낸 사람이 있으면 응답에 lunarInput({a, b}, 만세력 API와 같은 형식)이 붙습니다
personACountry / personBCountry string 출생 국가 2글자 코드. 좌표 없이 KR·JP·TH를 보내면 그 나라 대표 경도로 사주 시각을 보정합니다(그 외 국가는 보정 없음). 별자리 궁합의 상승궁 추정에도 lang 대신 이 국가를 씁니다
personAZaSiRule / personBZaSiRule string 자시(23시 이후) 일주: "traditional"(기본, 다음 날 일주) · "yajaSi"(야자시, 당일 일주)
details boolean 기본 false. true면 saju.full8(8글자 상세 분석) · saju.conflict({pattern, tip} 갈등 패턴과 조언) · saju.relationScores(천간·지지 관계 점수) · 최상위 dictionary({a, b} 두 사람 일주 사전 — 간지 기본값(오행·음양·지장간·12운성)·성향 키워드·한 줄 요약)를 붙입니다
flow boolean 기본 false. true면 yearFlow(기준 해 ±2년, 5개 해의 두 사람 흐름)와 dayFlow(기준일 ±7일, 15일의 두 사람 흐름)를 붙입니다
date string flow의 기준일 YYYY-MM-DD(1900-2100). 기본: 한국 시간 오늘
currentYear number yearFlow의 기준 해(1900~2100). 기본: date의 연도
personAMbti / personBMbti string MBTI 4글자(예: ENFP, 대소문자 무관). 둘 다 보내면 최상위 mbti(MBTI 궁합, 한국어)를 붙입니다

details·flow·MBTI 옵션은 추가 요금이 없습니다(호출당 ₩20 그대로).

응답 예시

아래는 실제 엔진 출력입니다 (A 남 1990-09-04 14:30, B 여 1992-02-20 10:15, 연애, 좌표 없음). 2026-10-10에 추가된 항상 포함 필드(saju.relationRatings·saju.meetingKeywords·saju.authority·tti.conflictFrame)는 이 예시에서 생략했습니다 — 아래 옵션 예시를 보세요.

{
  "success": true,
  "data": {
    "personABirthDate": "1990-09-04", "personABirthTime": "14:30", "personAGender": "male",
    "personBBirthDate": "1992-02-20", "personBBirthTime": "10:15", "personBGender": "female",
    "tti": {
      "userTti": "말", "partnerTti": "원숭이",
      "relation": { "type": null, "detail": null, "relations": [] },
      "tier": "흉"
    },
    "saju": {
      "purpose": "yeonae_attraction",
      "hourKnown": { "a": true, "b": true },
      "personA": { "ilju": "임신" },
      "personB": { "ilju": "병인" },
      "score": { "total": 59, "grade": "맞춰 가면 좋아요",
                 "breakdown": { "ilju": 53, "authority": 65, "eightChar": 65 } },
      "axes": { "attraction": 25, "stability": 59, "growth": 58,
                "communication": 24, "passion": 74, "longevity": 65 },
      "pattern": { "name": "폭발형",
                   "description": "에너지가 크게 부딪히는 뜨거운 관계예요. 감정이 커질 때 잠시 쉬어 가는 규칙이 도움이 돼요." },
      "relation": { "summary": "천간충 + 인신충",
                    "stem": { "type": "천간충", "detail": "병임충" },
                    "branches": [ { "type": "지지충", "detail": "인신충" } ] },
      "elements": { "basis": "eight", "aLacks": [], "bProvidesToA": [], "bLacks": ["토"], "aProvidesToB": ["토"] },
      "tenGods": { "aView": "편재", "bView": "편관(칠살)" },
      "bestFor": ["단기 끌림"],
      "weakFor": ["결혼·동거"],
      "highlights": [ "상대의 시지에 나의 천을귀인 (사) 발현 — 상대가 나의 해결사" ]
    },
    "categoryNarrative": {
      "category": "romantic", "purpose": "yeonae_attraction", "lang": "ko",
      "text": "상대 쪽의 즉각적인 열정과 나 쪽의 자유로운 흐름은 불과 물처럼 정면으로 맞부딪혀서, 처음 만났을 때의 팽팽한 긴장감이 곧 끌림이 되기 쉬워요. 짧고 뜨거운 설렘이 큰 관계라, 감정이 치솟는 날엔 하루쯤 식힐 틈을 두면 더 즐겁게 만날 수 있어요.",
      "styleVersion": "v2"
    },
    "costPoints": 20,
    "classic1995": {
      "symbol": "××", "grade": "대흉", "score": 50,
      "ttiRelation": { "type": null, "detail": null, "relations": [] },
      "conflictFrame": false
    },
    "astroSynastry": {
      "self": {
        "sun": { "signEn": "Virgo", "element": "earth", "modality": "mutable" },
        "moon": { "signEn": "Pisces", "element": "water", "modality": "mutable", "uncertain": false },
        "ascendant": { "signEn": "Sagittarius", "element": "fire", "modality": "mutable", "uncertain": false, "countryBasis": true },
        "venus": { "signEn": "Leo", ... }, "mars": { "signEn": "Gemini", ... },
        "mercury": { ... }, "jupiter": { ... }, "saturn": { ... }
      },
      "partner": { "sun": { "signEn": "Pisces", ... }, "moon": { "signEn": "Virgo", ... }, ... },
      "layers": [
        { "key": "sun", "relation": "opposite", "selfSign": "Virgo", "partnerSign": "Pisces",
          "textKey": "compat.astro.relation.opposite",
          "text": "충 — 정반대라 방향은 다르지만 서로를 끌어당길 수 있어요" },
        { "key": "moon", "relation": "opposite", "selfSign": "Pisces", "partnerSign": "Virgo",
          "textKey": "compat.astro.relation.opposite",
          "text": "충 — 정반대라 방향은 다르지만 서로를 끌어당길 수 있어요" },
        { "key": "ascendant", "relation": "neutral", "selfSign": "Sagittarius", "partnerSign": "Taurus",
          "textKey": "compat.astro.relation.neutral",
          "text": "뚜렷한 결 없이 각자의 스타일을 존중하면 되는 조합이에요" }
      ],
      "purposeBodies": [
        { "body": "venus", "relation": "opposite", "selfSign": "Leo", "partnerSign": "Aquarius",
          "textKey": "compat.astro.relation.opposite",
          "text": "충 — 정반대라 방향은 다르지만 서로를 끌어당길 수 있어요" },
        { "body": "mars", "relation": "same_element", "selfSign": "Gemini", "partnerSign": "Aquarius",
          "textKey": "compat.astro.relation.same_element",
          "text": "같은 원소 — 서로의 방식을 바로 알아볼 만큼 편안할 수 있어요" }
      ],
      "caveats": ["compat.astro.caveat.ascendantCountryBasis"],
      "caveatTexts": ["상승궁은 출생 도시가 없어 국가 대표 좌표로 추정했어요"],
      "score": 64
    },
    "astroReadings": [
      { "key": "Su_Mo_ss", "lang": "ko",
        "text": "한쪽의 태양과 다른 쪽의 달이 같은 별자리에 놓이면, 한 사람이 향하는 방향을 다른 사람의 마음이 낯설지 않게 알아보는 깊은 공명이 생기기 쉬워요. 닮은 만큼 같은 그림자도 함께 커질 수 있으니 서로의 빈틈을 웃으며 짚어 주면 좋아요." },
      { "key": "Ve_Ma_op", "lang": "ko",
        "text": "한쪽의 금성과 다른 쪽의 화성이 정반대 별자리에 놓이면, 서로에게 없는 매력을 알아보며 끌리는 보완의 흐름이 생기기 쉬워요. 원하는 속도와 표현 방식이 반대로 기울 수 있으니 서로의 선을 말로 정해 두면 그 끌림이 편안하게 이어져요." }
    ],
    "headerBreakdown": {
      "sixAxis": 55, "authority": 65, "full8": 65, "classic": 50, "natal": 64,
      "average": 61, "purpose": "yeonae_attraction",
      "weights": { "sixAxis": 0.2, "authority": 0.3, "full8": 0.2, "classic": 0.1, "natal": 0.2 }
    },
    "lang": "ko"
  }
}

옵션 예시 (details · flow · MBTI)

실제 엔진 출력 발췌입니다. 요청:

{
  "personABirthDate": "1990-05-15", "personABirthTime": "12:00", "personAGender": "male",
  "personBBirthDate": "1992-08-20", "personBBirthTime": "09:00", "personBGender": "female",
  "details": true, "flow": true, "date": "2026-10-10",
  "personAMbti": "ENFP", "personBMbti": "ISTJ"
}
{
  "success": true,
  "data": {
    "tti": { "userTti": "말", "partnerTti": "원숭이", ..., "tier": "흉", "conflictFrame": false },
    "saju": {
      "personA": { "ilju": "경진" }, "personB": { "ilju": "무진" },
      "score": { "total": 56, "grade": "맞춰 가면 좋아요", ... },
      ...,
      "relationRatings": [
        { "key": "연애", "stars": 3 }, { "key": "결혼·동거", "stars": 3 }, { "key": "친구", "stars": 4 },
        { "key": "사업 파트너", "stars": 3 }, { "key": "장기 관계", "stars": 3 }
      ],
      "meetingKeywords": ["괴강", "우직", "일반적 관계"],
      "authority": {
        "finalScore": 51, "grade": "주의", "gradeTier": "caution",
        "pattern": "자형 (자기 갈등 외부화)",
        "summary": "주의 · 자형 (자기 갈등 외부화) · 익숙한 결의 만남. 안락함 위에 자극을 어떻게 만들지가 핵심.",
        "levels": { "L1": { "raw": 0, "norm": 0 }, "L2": { "raw": 5, "norm": 14.3, "type": "동계절 (봄)" },
                    "L3": { "raw": 17, "norm": 56.7 }, "L4": { "raw": -17, "norm": -42.5 } },
        "factors": { "l1": [], "l2Tone": "...", "l3": [ ... ], "l4": ["자형(진진) — 자기 파괴 패턴", ...] },
        "flags": { "cheonHapJiHap": false, "dualChung": false },
        "climateBalance": "동계절 (봄)"
      },
      "conflict": {
        "pattern": "경은 무를 편인으로 인식. 일방향적 관계 성격.",
        "tip": "'괴강'과 '우직'은 서로 다른 결이에요. 같은 사건도 다르게 해석한다는 걸 인정하고, 서로의 관점을 한 번씩 빌려보세요."
      },
      "relationScores": { "stem": { "type": "편인", "category": "neutral", "score": 55 },
                          "branches": [ { "type": "자형", "score": -40 } ] },
      "full8": { "personA": { ... }, "personB": { ... }, "iljuEvaluation": { ... }, ... }
    },
    ...,
    "dictionary": { "a": { "id": "ilju_경진", "name_kr": "경진", "name_hanja": "庚辰", ... }, "b": { ... } },
    "yearFlow": {
      "baseYear": 2026,
      "years": [
        { "year": 2024, "score": 77, "tier": "great", "a": { ... }, "b": { ... },
          "summary": "두 분 모두 흐름이 좋아 같이 시작하기 좋은 해." },
        ...
      ]
    },
    "dayFlow": {
      "baseDate": "2026-10-10",
      "days": [
        { "date": "2026-10-03", "a": -89, "b": -40, "combined": -60, "tier": "hard", "isBaseDate": false },
        ...,
        { "date": "2026-10-10", "a": -9, "b": 27, "combined": 9, "tier": "neutral", "isBaseDate": true },
        ...
      ],
      "golden": [],
      "hard": [ { "date": "2026-10-03", "combined": -60 }, ... ],
      "summary": "3번 같이 가라앉는 날이 있어. 그날은 큰 결정·약속 미루자."
    },
    "mbti": { "id": "mbti_ENFP_ISTJ", "mbti_a": "ENFP", "mbti_b": "ISTJ",
              "socionics_relation": "Duality(이중관계)", "scores": { ..., "total": 64 }, ... }
  }
}

주의사항

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400INVALID_BIRTH_YEAR두 사람 중 출생 연도가 1900~2100 밖
400INVALID_LUNAR_DATE없는 음력 날짜
400COMPAT_UNAVAILABLE이 일주 조합의 궁합 자료가 없음
402INSUFFICIENT_BALANCE잔액 부족

요청 예시는 위 옵션 예시와 실행 예시를 보세요.

성격유형 API

MBTI 기반 성격 검사를 제공합니다. 무료 라이트 버전(24문항)과 프리미엄 버전(93문항)을 지원합니다.

GET /api/v1/partners/v1/personality-type/questions

성격 검사 문항을 조회합니다. 무료입니다.

curl "https://api.hrum.ai/api/v1/partners/v1/personality-type/questions?tier=free" \
  -H "X-Partner-Api-Key: your-api-key"

요청 파라미터

파라미터 타입 설명
tier string 검사 버전 ("free" 또는 "premium", 기본값: "free")

응답 예시

{
  "success": true,
  "data": {
    "tier": "free",
    "count": 24,
    "questions": [
      {
        "id": "q_001",
        "text": "갑자기 생긴 모임에도 일단 가본다",
        "choices": [
          { "value": 1, "label": "전혀 그렇지 않다" },
          { "value": 2, "label": "그렇지 않다" },
          { "value": 3, "label": "보통이다" },
          { "value": 4, "label": "그렇다" },
          { "value": 5, "label": "매우 그렇다" }
        ]
      }
    ]
  }
}

POST /api/v1/partners/v1/personality-type

성격 검사 답변을 제출하여 성격유형을 분석합니다.

curl -X POST https://api.hrum.ai/api/v1/partners/v1/personality-type \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "tier": "free",
    "answers": [
      { "questionId": "q_001", "answer": 4 },
      { "questionId": "q_002", "answer": 2 }
    ]
  }'

요청 파라미터

파라미터 타입 필수 설명
answers array O 답변 배열 [{ "questionId": "q_001", "answer": 4 }, ...]. questionId는 문항 조회 응답의 id(형식 q_001), answer는 정수 1~5
tier string O "free" 또는 "premium"

응답 예시

{
  "success": true,
  "data": {
    "personalityType": "ENTJ",
    "typeLabel": "지휘관",
    "scores": {
      "extraversion": 72,
      "sensing": 45,
      "thinking": 68,
      "judging": 80
    },
    "tier": "premium",
    "costPoints": 10
  }
}

가격

에러 코드

공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:

HTTP코드언제
400(없음)문항 조회 — tier가 free·premium이 아님 / 제출 — 없는 questionId
402INSUFFICIENT_BALANCE제출 — premium인데 잔액 부족

답이 1~5 밖이면 공통 400입니다. 모두 과금되지 않습니다.

해석 DB (KB) API

신청한 계정에서만 열리는 API입니다. 열리지 않은 계정은 세 경로 모두 404 kb_saju_explain_not_enabled를 받습니다(과금 없음). 사용하려면 지원으로 문의하세요.

사주 원국에서 성립하는 관계(합·충 등)와 구조(십신·지장간 등)를 규칙 카드와 해설 문장으로 풀어 줍니다. LLM을 쓰지 않아 같은 입력에는 항상 같은 결과가 나옵니다.

POST /api/v1/partners/v1/kb/saju/explain

요청 파라미터

파라미터타입필수설명
birthobjectO만세력 API 요청 본문과 같은 필드 (year·month·day·hour?·minute?·timezone·gender·calendar? …). birth.lang은 무시됩니다
sectionsstring[]relations · structure · all. 생략하면 전부(relations → structure 순)
levelstringL1(기본) · L3. L3 해설이 없는 항목은 L1로 채우고 fallbackLevel을 붙입니다
langstringko만 지원
channelstringstable(기본, 검수된 해설만) · preview(검수 전 해설 포함)

요청 예시

curl -X POST https://api.hrum.ai/api/v1/partners/v1/kb/saju/explain \
  -H "X-Partner-Api-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "birth": {
      "year": 1990, "month": 5, "day": 17, "hour": 14, "minute": 30,
      "timezone": "Asia/Seoul", "gender": "female"
    },
    "sections": ["relations"],
    "channel": "preview"
  }'

응답 예시 (발췌)

{
  "success": true,
  "data": {
    "kbVersion": "2026.10.09-3",
    "kbHash": "sha256:24b9a2d9...",
    "channel": "preview",
    "chart": {
      "pillars": {
        "year":  { "stem": "庚", "branch": "午" },
        "month": { "stem": "辛", "branch": "巳" },
        "day":   { "stem": "壬", "branch": "午" },
        "hour":  { "stem": "丁", "branch": "未" }
      },
      "timeUnknown": false
    },
    "sections": [
      {
        "id": "relations",
        "title": "...",
        "steps": [
          {
            "fact": { "type": "stem_pair", "a": "壬", "b": "丁", "posA": "day", "posB": "hour", "relation": "hap" },
            "factText": "일간 壬과 시간 丁은 천간합(天干合) 관계다.",
            "componentId": "saju:stem_pair:丁壬:hap",
            "rules": [
              { "id": "R-SAJU-REL-004", "kind": "definition", "status": "verified", "sourceCount": 2,
                "statement": "丁과 壬은 천간합 관계이고, 이 짝의 합화 오행은 목(木)이다." }
            ],
            "interp": {
              "meaning": { "text": "丁(정)과 壬(임)은 천간합으로 묶이는 짝이고, ...", "reviewed": false },
              "why": { ... },
              "pitfall": { ... }
            }
          }
        ],
        "absent": [
          { "type": "stem_pair", "a": "庚", "b": "辛", "posA": "year", "posB": "month",
            "note": "庚과 辛은 천간합·천간충 어느 것도 아니다." }
        ]
      }
    ],
    "unregistered": [],
    "pendingReview": []
  }
}

GET /api/v1/partners/v1/kb/rules/{id}

규칙 카드 하나와 원전 출처를 돌려줍니다. 무료입니다.

curl https://api.hrum.ai/api/v1/partners/v1/kb/rules/R-SAJU-REL-004 \
  -H "X-Partner-Api-Key: your-api-key"
{
  "success": true,
  "data": {
    "id": "R-SAJU-REL-004",
    "kind": "definition",
    "statement": "丁과 壬은 천간합 관계이고, 이 짝의 합화 오행은 목(木)이다.",
    "status": "verified",
    "schools": [],
    "variantOf": null,
    "sources": [
      { "sourceId": "SRC-SMTH", "location": "『三命通會』 卷二 論十干化氣",
        "quote": "故丁與壬合 丁壬化木", "match": "EXACT", ... }
    ],
    "engineRef": "..."
  }
}

GET /api/v1/partners/v1/kb/release?domain=saju

현재 공개된 해석 DB 판(版)을 돌려줍니다. 무료입니다. domain은 생략하면 saju입니다(예: saju · ziwei · astro · dream · tarot).

curl "https://api.hrum.ai/api/v1/partners/v1/kb/release?domain=saju" \
  -H "X-Partner-Api-Key: your-api-key"
{
  "success": true,
  "data": {
    "version": "2026.10.09-3",
    "hash": "sha256:24b9a2d9...",
    "publishedAt": "2026-10-09T00:00:00.000Z",
    "channel": "stable",
    "counts": { "rules": 412, "interps": 1236 }
  }
}

위 숫자는 예시입니다. 결과를 저장해 둔다면 hash가 바뀌었을 때 다시 받으세요.

에러 코드

공통 에러 외에:

HTTP코드언제
404kb_saju_explain_not_enabled신청하지 않은 계정 (세 경로 공통)
400INVALID_LUNAR_DATE · INVALID_LOCATIONexplain — 없는 음력 날짜 / 위도·경도 중 하나만 보냄
400KB_DOMAIN_UNKNOWNrelease — 지원하지 않는 domain
404KB_RULE_NOT_FOUNDrules — 없는 규칙 id
503KB_NOT_LOADED해석 DB 판이 아직 올라오지 않음. 잠시 뒤 다시 시도

explain은 성공한 호출만 과금하며, 실패한 호출은 ₩0으로 기록됩니다.

인증 API

콘솔 계정 관리를 위한 인증 API입니다.

POST /api/v1/partners/v1/console/auth/login

계정으로 로그인합니다. 이메일/비밀번호 또는 소셜 로그인을 지원합니다.

요청 (이메일)

curl -X POST https://api.hrum.ai/api/v1/partners/v1/console/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "email",
    "email": "[email protected]",
    "password": "your-password"
  }'

요청 (소셜)

curl -X POST https://api.hrum.ai/api/v1/partners/v1/console/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "google",
    "identityToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ..."
  }'

응답

{
  "success": true,
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "id": "user-123",
      "email": "[email protected]",
      "displayName": "John Doe",
      "provider": "email"
    }
  }
}

user.provider는 email · apple · google 중 하나입니다.

POST /api/v1/partners/v1/console/auth/register

새로운 계정을 등록합니다.

요청

curl -X POST https://api.hrum.ai/api/v1/partners/v1/console/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "secure-password",
    "displayName": "John Doe",
    "agreements": { "terms": true, "privacy": true }
  }'

password는 8~128자, displayName은 2~50자입니다. agreements.terms·agreements.privacy는 반드시 true여야 하며(없거나 false면 400), agreements.aiTraining(boolean)은 선택입니다.

응답

{
  "success": true,
  "data": {
    "message": "인증 메일을 발송했습니다"
  }
}

POST /api/v1/partners/v1/console/auth/verify-email

이메일 인증을 완료합니다. 인증 메일의 토큰과 가입할 때 정한 비밀번호를 함께 보냅니다.

요청

curl -X POST https://api.hrum.ai/api/v1/partners/v1/console/auth/verify-email \
  -H "Content-Type: application/json" \
  -d '{
    "token": "인증-메일의-토큰",
    "password": "secure-password"
  }'

응답

{
  "success": true,
  "data": {
    "message": "이메일 인증이 완료되었습니다"
  }
}

토큰이 틀렸거나 만료됐거나 비밀번호가 맞지 않으면 400 EMAIL_VERIFY_FAILED입니다. 인증 메일 링크(GET /api/v1/partners/v1/console/auth/verify-email?token=…)는 인증을 하지 않고 https://console.hrum.ai/verify-email.html?token=…로 302 리다이렉트만 합니다 — 그 페이지에서 비밀번호를 입력하면 위 POST가 호출됩니다.

조회 API

크레딧 잔액과 사용 현황을 조회하는 API입니다. 헤더에 X-Partner-Api-Key를 포함해야 합니다.

GET /api/v1/partners/v1/credits/balance

현재 크레딧 잔액을 조회합니다.

요청

curl -X GET https://api.hrum.ai/api/v1/partners/v1/credits/balance \
  -H "X-Partner-Api-Key: your-api-key"

응답

{
  "success": true,
  "data": {
    "balanceWon": 25000,
    "lastUpdated": "2026-10-09T05:12:30.000Z",
    "estimatedMonthEndBalance": 15000,
    "warningThreshold": 5000,
    "lowBalanceActive": false
  }
}

warningThreshold는 파트너마다 다릅니다 — max(₩5,000, 최근 유료 충전액의 20%). lowBalanceActive는 잔액이 이 기준 아래로 내려가 저잔액 경고 상태인지입니다. API로 충전하는 엔드포인트는 없습니다 — 충전은 콘솔 > 결제에서 합니다. 이 API만의 에러 코드는 없습니다 — 공통 에러만 나옵니다.

GET /api/v1/partners/v1/usage/logs?limit=50&offset=0

API 호출 기록을 최신순으로 조회합니다. 각 항목은 id·timestamp·endpoint(짧은 이름, 예: manseryeok)·statusCode·costWon·responseTimeMs(현재 항상 null)이고, 해당할 때만 idempotencyKey·model·endUserId·cachedTokens가 붙습니다.

요청 파라미터

파라미터 타입 설명
limit number 조회할 항목 수 (기본: 50, 최대: 200)
offset number 오프셋 (기본: 0)
endpoint string 엔드포인트 짧은 이름으로 거르기 (예: manseryeok). all 또는 생략 시 전체
dateFrom · dateTo string 기간 (YYYY-MM-DD). 생략 시 제한 없음

요청

curl "https://api.hrum.ai/api/v1/partners/v1/usage/logs?limit=50&offset=0&endpoint=manseryeok" \
  -H "X-Partner-Api-Key: your-api-key"

응답

{
  "success": true,
  "data": {
    "logs": [
      {
        "id": "12345",
        "timestamp": "2026-08-10T14:30:00.000Z",
        "endpoint": "manseryeok",
        "statusCode": 200,
        "costWon": 50,
        "responseTimeMs": null,
        "idempotencyKey": "order-8812"
      }
    ],
    "total": 1234,
    "pagination": {
      "limit": 50,
      "offset": 0,
      "hasMore": true
    }
  }
}

GET /api/v1/partners/v1/usage/summary?dateFrom=2026-08-01&dateTo=2026-08-31

기간별 사용 통계를 조회합니다. 날짜를 생략하면 이번 달 전체입니다. dailyBreakdown의 날짜는 한국 시간(KST) 기준입니다.

요청 파라미터

파라미터 타입 설명
dateFrom string 시작 날짜 (YYYY-MM-DD)
dateTo string 종료 날짜 (YYYY-MM-DD)

요청

curl "https://api.hrum.ai/api/v1/partners/v1/usage/summary?dateFrom=2026-08-01&dateTo=2026-08-31" \
  -H "X-Partner-Api-Key: your-api-key"

응답

{
  "success": true,
  "data": {
    "dateRange": { "from": "2026-08-01", "to": "2026-08-31" },
    "totalCalls": 1234,
    "totalCostWon": 6170,
    "byEndpoint": [
      {
        "endpoint": "manseryeok",
        "calls": 1234,
        "costWon": 6170,
        "percentOfTotal": 100
      }
    ],
    "dailyBreakdown": [
      {
        "date": "2026-08-10",
        "calls": 150,
        "costWon": 750
      }
    ]
  }
}

에러 코드 (사용량 로그·요약)

공통 에러 외에, 날짜가 YYYY-MM-DD 형식이 아니거나 limit·offset이 음수면 코드 없는 400입니다. 조회는 무료입니다.

GET /api/v1/partners/v1/status/upstream

HRUM이 쓰는 AI 제공사(OpenAI · Google Gemini · Anthropic Claude)의 공개 상태를 모아 돌려줍니다. API 키가 필요 없고 무료이며, 60초 동안 캐시됩니다. 상태 페이지가 이 값을 씁니다.

curl https://api.hrum.ai/api/v1/partners/v1/status/upstream

{
  "success": true,
  "data": {
    "providers": [
      { "name": "OpenAI", "indicator": "none", "description": "All Systems Operational", "url": "https://status.openai.com", "checkedAt": "2026-10-12T03:00:00.000Z" },
      ...
    ],
    "checkedAt": "2026-10-12T03:00:00.000Z"
  }
}

indicator: none(정상) · minor · major · critical · unknown(상태를 읽지 못함). 이 API만의 에러 코드는 없습니다.

게스트 체험

로그인 없이 사주를 체험할 수 있는 무료 API입니다.

POST /api/v1/partners/v1/playground/manseryeok

인증 없이 만세력을 계산합니다. 게스트 엔드포인트(만세력·네이탈 차트) 합산 IP당 월 5회 무료이며, 초과 시 429 GUEST_QUOTA_EXCEEDED가 반환됩니다.

요청

curl -X POST https://api.hrum.ai/api/v1/partners/v1/playground/manseryeok \
  -H "Content-Type: application/json" \
  -d '{
    "year": 1990,
    "month": 1,
    "day": 15,
    "hour": 14,
    "minute": 30,
    "timezone": "Asia/Seoul",
    "gender": "male"
  }'

응답

유료 만세력 API와 같은 원국·분석 필드(wonkuk·ohaengBalance·yongshin·daeun·seun …)에 남은 횟수 guestRemaining이 data 안에 붙습니다. 음력(calendar·isLeapMonth)·위치 보정(좌표·country)·zaSiRule·birthTimePrecision은 유료와 같이 반영되고, 음력 입력이면 lunarInput이 붙습니다. 체험용이라 lang 번역·glossary와 identity·ohaengDeficient는 붙지 않습니다.

{
  "success": true,
  "data": {
    "wonkuk": {
      "year": { "stem": "기", "branch": "사", "stemHanja": "己", "branchHanja": "巳", ... },
      "month": { ... }, "day": { ... }, "hour": { ... }
    },
    "trueSolarTime": null,
    "ohaengBalance": { ... },
    "yongshin": "...",
    ...,
    "guestRemaining": 4
  }
}

한도 초과 (429)

{
  "success": false,
  "statusCode": 429,
  "code": "GUEST_QUOTA_EXCEEDED",
  "message": "게스트 체험 한도(월 5회)를 초과했습니다. API 키를 발급받아 사용해주세요.",
  "timestamp": "..."
}

에러 코드

HTTP코드언제
429GUEST_QUOTA_EXCEEDED같은 IP에서 이번 달 5회를 넘김 (네이탈 차트 게스트와 합산)
400INVALID_LUNAR_DATE · INVALID_LOCATION없는 음력 날짜 / 위도·경도 중 하나만 보냄. 이 경우에도 횟수가 1회 차감됩니다
400(없음)본문 검사 실패 (공통 에러와 같음)

POST /api/v1/partners/v1/playground/natal-chart

인증 없이 네이탈 차트를 계산합니다. 요청 본문은 유료 /natal-chart와 같고, 응답도 같은 차트에 guestRemaining이 붙습니다. 풀이 문단(readings)은 제공하지 않습니다. 한도는 만세력 게스트와 합산(IP당 월 5회)입니다.

curl -X POST https://api.hrum.ai/api/v1/partners/v1/playground/natal-chart \
  -H "Content-Type: application/json" \
  -d '{ "birthDate": "1990-05-15", "birthTime": "12:00", "birthLatitude": 37.5665, "birthLongitude": 126.978, "birthTimezoneOffset": 540 }'

{
  "success": true,
  "data": {
    "sun": { ... },
    "moon": { ... },
    ...,
    "guestRemaining": 3
  }
}

에러: 한도 초과 429 GUEST_QUOTA_EXCEEDED, 그 밖에는 공통 에러와 같습니다.

유효값

천간 (Cheongan)

갑, 을, 병, 정, 무, 기, 경, 신, 임, 계

지지 (Jiji)

자, 축, 인, 묘, 진, 사, 오, 미, 신, 유, 술, 해

오행 (Ohaeng)

목(목), 화(불), 토(흙), 금(쇠), 수(물)

말결 (candor)

t, f(기본) — 대소문자는 구분하지 않습니다. 예전 값 blunt는 t로, balanced·gentle은 f로 정규화됩니다. 그 밖의 값은 400 (말결 (가칭))

에러 코드

모든 API는 아래 공통 에러를 같은 방식으로 돌려줍니다. 각 API 절의 에러 표에는 그 API에서만 나오는 코드만 적었습니다. 요청은 다음 순서로 검사되며, 앞 단계에서 걸리면 뒤 단계는 실행되지 않습니다.

  1. 서비스 보호 상한 — 같은 IP에서 오는 요청 전체 분당 상한 (429, code 없음)
  2. API 키 — X-Partner-Api-Key 확인 (401)
  3. 기능 열림 여부 — 계정에 열리지 않은 기능 (404 *_not_enabled)
  4. 요청 제한 — 콘솔에서 정한 초당·일일 제한 (429 RATE_LIMIT_EXCEEDED)
  5. 본문 검사 — 필드 이름·타입·범위 (400)
  6. 잔액·계산·호출 — 잔액 부족(402), API별 입력 오류, 외부 모델 오류

공통 에러

HTTP 코드 언제 과금
400(없음)본문 검사 실패 — 표에 없는 필드를 보냈거나 타입·범위가 틀림. message는 문자열 배열입니다없음
401(없음)X-Partner-Api-Key 누락, 잘못된 키, 폐기한 키, 비활성 계정없음
402INSUFFICIENT_BALANCE크레딧 잔액 부족. 스트리밍 응답은 event: error에 같은 code와 status가 담깁니다없음
402(없음)차감 순간 잔액이 모자람 (호출 전 잔액 검사가 없는 API, 또는 호출 중 다른 요청이 잔액을 쓴 경우). message에 필요한 금액과 남은 잔액이 담깁니다없음
403(본문 HTML)보안 규칙 차단 (error 1010) — 기본 User-Agent가 막힌 HTTP 클라이언트. Quickstart의 User-Agent 주의를 보세요없음
404*_not_enabled계정에 열리지 않은 기능 (예: ziwei_not_enabled, dream_not_enabled)없음
404(없음)없는 경로. 본문 message는 "Not Found"뿐입니다없음
429(없음)서비스 보호 상한 초과 (API 키당 분당 300회 등). Retry-After 없음 — 최대 1분 뒤 다시 시도없음
429RATE_LIMIT_EXCEEDED콘솔에서 정한 초당·일일 제한 초과. 본문 retryAfterSeconds와 Retry-After 헤더가 붙습니다없음
500(없음)서버 오류. 이 요청에서 이미 차감된 금액은 응답 뒤 돌려드립니다환불
503SERVICE_DEGRADED일시적인 데이터베이스 연결 문제. Retry-After: 30 — 30초 뒤 다시 시도환불

오류 응답 본문 형식: {"success": false, "statusCode": 400, "code": "INVALID_LOCATION", "message": "...", "timestamp": "..."} — code는 있을 때만 붙습니다. 분기는 statusCode와 code로 하고, message 문구에 의존하지 마세요.

// 401 — 키 누락·오류
{ "success": false, "statusCode": 401, "message": "Invalid or missing API key", "error": "Unauthorized", "timestamp": "2026-10-12T03:00:00.000Z" }

// 400 — 본문 검사 실패 (message는 배열)
{ "success": false, "statusCode": 400, "message": ["property foo should not exist", "month must not be greater than 12"], "error": "Bad Request", "timestamp": "2026-10-12T03:00:00.000Z" }

// 404 — 계정에 열리지 않은 기능
{ "success": false, "statusCode": 404, "code": "ziwei_not_enabled", "message": "아직 열리지 않은 기능입니다", "timestamp": "2026-10-12T03:00:00.000Z" }

// 429 — 콘솔에서 정한 요청 제한
{ "success": false, "statusCode": 429, "code": "RATE_LIMIT_EXCEEDED", "message": "초당 요청 제한을 초과했습니다. 콘솔 > API 키 > 요청 제한에서 조정할 수 있습니다.", "retryAfterSeconds": 1, "timestamp": "2026-10-12T03:00:00.000Z" }

API별 코드 가운데 자주 보는 것: 422 처리할 수 없는 입력(PROMPT_TOO_LONG, EXTRACTION_FAILED), 503 일시적으로 이용 불가(예: 풀이 문단을 불러오지 못함, 과금 없음), 504 외부 모델 시간 초과. 게스트 체험 한도는 429 GUEST_QUOTA_EXCEEDED입니다.

Rate Limit

서비스 보호용 상한이 항상 걸려 있습니다: API 키당 분당 300회(같은 IP에서 오는 키 요청 전체는 분당 3,000회). 넘으면 429가 반환되며, 이 경우 응답에 code와 Retry-After가 없습니다 — 잠시 뒤(최대 1분) 다시 시도하세요. 이보다 낮은 제한은 파트너가 직접 정합니다 — 콘솔 API 키 > 요청 제한에서 초당 요청과 일일 요청(KST 기준)을 설정하면 키 유출이나 무한 루프로부터 잔액을 지킬 수 있습니다. 비워 두면 위 서비스 보호 상한만 적용됩니다.

과금

모든 API 호출은 크레딧에서 차감됩니다. 신규 가입 시 무료 ₩1,000 크레딧을 제공합니다(부정 가입 방지 조건에 걸리면 지급이 보류될 수 있습니다). 게스트는 월 5회까지 무료로 테스트 가능합니다.

API 요금
manseryeok (만세력) ₩50/호출
compat (궁합) ₩20/호출
lifetime (평생 사주) 전체 ₩100/호출 · 주제 1개 ₩20/호출 · 별자리·자미두수 층 +₩20/층
ziwei-chart (자미두수 명반) ₩50/호출 · readings: true +₩20 (합계 ₩70)
identity (사주 정체성, 구 엔드포인트) ₩10/호출 — 새 연동은 만세력의 identity 필드(추가 요금 없음)
today (오늘의 사주) ₩10/호출
dream (꿈해몽) ₩30/호출 (결과 없으면 ₩0)
natal-chart (네이탈 차트) ₩20/호출 · readings: true ₩40/호출
personality-type (성격유형) - 무료 ₩0 (무료)
personality-type (성격유형) - 프리미엄 ₩10/회
reading (AI 사주 리딩) 토큰 기반 (사용 토큰 수에 따라 상이)
image (사주 이미지) ₩200/이미지
image_generation (이미지 생성 · 모델 리셀) 모델·해상도·품질별 장당 고정가 — 가격표

게스트 테스트

멱등성

모든 성공한 요청은 각각 과금됩니다. 같은 요청을 다시 보내면 다시 계산·생성하고 다시 과금하므로, 재시도 중복이나 동일 입력의 재사용은 클라이언트가 응답을 저장해 처리하세요 (예: 요청 해시를 키로 한 캐시).

X-Idempotency-Key 헤더(최대 128자 — 더 길면 앞 128자만 기록)는 여전히 받을 수 있지만 과금을 막지 않습니다. 보낸 키는 사용량 로그의 idempotencyKey에 그대로 기록되어, 어떤 요청이 어떤 호출로 처리됐는지 파트너 쪽 주문·작업 ID와 대조하는 용도로 쓸 수 있습니다.

지원

연동 중 막히는 부분이나 응답이 문서와 다른 경우 알려주세요.

문의연락처
기술 지원 · 장애 신고 · 제휴 [email protected]

문의 시 아래를 함께 보내주시면 확인이 빠릅니다.

약관·개인정보 처리에 관한 사항은 이용약관과 개인정보처리방침을 참고하세요.