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 도 그대로 동작하니 이미 연동했다면 바꾸지 않아도 됩니다.
- API 키 발급 - 콘솔에 가입한 뒤 API 키 화면에서 발급받습니다(키는 발급 직후 한 번만 보입니다). 가입부터 에러 처리까지의 순서는 개발자 시작 가이드에 정리되어 있습니다.
- 요청 보내기 - 아래의 예제를 따라 POST 요청을 보냅니다.
- 응답 처리 - 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_FLAGGED | 400 | 모더레이션 차단 (ridm: system+user, 리셀: user 메시지) |
INSUFFICIENT_BALANCE | 402 | 잔액 부족 (요청한 출력 전체를 미리 예약해 검사) |
MODEL_NOT_FOUND | 400 | 설정되지 않은 모델 |
INVALID_TOOLS / TOOLS_UNSUPPORTED_FOR_MODEL | 400 | tools 관련 필드 불일치 / Claude 모델에 도구 호출 요청 |
FILE_NOT_FOUND | 400 | fileIds에 없는 파일(또는 다른 파트너 파일) 포함 |
FEATURE_DISABLED | 403 | youtube·files·emotion 기능이 꺼진 계정에서 해당 옵션 사용 |
RATE_LIMITED | HTTP 400, 본문 status 429 | 상류 속도 제한, 또는 리셀 일일 지출 상한 초과 (파트너당 기본 ₩100,000/일) |
UPSTREAM_TIMEOUT | HTTP 400, 본문 status 504 | 상류 응답 시간 초과 (60초) |
UPSTREAM_INVALID_REQUEST | 400 | 상류 모델이 요청을 거절 (상류 400·422) |
INVALID_RESPONSE_FORMAT | 400 | response_format: json_object인데 메시지에 "json"이라는 말이 없음 (OpenAI 계열) |
REPORT_ONLY_MODEL | 400 | ridm/saju-report 지정 — 사주 리포트 API 전용 |
| (없음) | 400 | ridm/* 모델에 user 누락 · systemPrompt 8,000자 초과 · 지원하지 않는 모델 접두사 |
provider_not_configured | 503 | Claude 모델이 지금 연결되어 있지 않음 |
주의사항
- 유튜브 자막 주입: youtube:true일 때, 마지막 메시지의 유튜브 링크에서 자막을 추출해 입력 토큰에 포함합니다. 자막을 못 얻으면 주입 없이 대화가 그대로 진행되며 응답의
youtubeUsed가 빈 배열로 옵니다 (단독 유튜브 요약 API만 400 무과금). - 원본 모델 호환: model 값에 openai/* 또는 google/*, anthropic/*를 지정하면 원본 모델이 메모리 기능 없이 그대로 호출됩니다 (원가 × 환율 × 1.10).
- 프롬프트 캐시 할인: OpenAI 계열 모델(openai/*, ridm/gpt-*)은 같은 앞부분(1,024토큰 이상)을 반복해 보내면 업스트림 캐시에 맞은 입력 토큰을 입력가의 30%로 과금합니다. 응답
usage.prompt_tokens_details.cached_tokens와GET /usage/logs의cachedTokens로 확인할 수 있고, 캐시 적중 여부는 업스트림 정책에 따라 호출마다 달라질 수 있습니다. Google 모델은 캐시가 없어 전체 입력가가 적용됩니다. Claude 모델도 캐시를 요청하지 않으므로 전체 입력가가 적용됩니다. - REPORT_ONLY_MODEL 제외: ridm/saju-report는 메모리 없는 원샷 전용이라 이 엔드포인트에서 사용 불가 (400).
커스텀 프로필 (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만의 에러 코드는 없습니다 — 공통 에러만 나옵니다.
상한 및 제약
- 필드 상한: 엔드유저당 최대 100개 필드. 초과 시 가장 오래된 필드부터 제거됩니다.
- 값 길이: 필드당 기본 150자, 최대 500자. profileValueMaxChars로 조정 가능합니다.
- 누적 저장: extractProfileFields로 추출한 값은 누적되며, 비어 있거나 null인 값은 건너뛰고 기존 값을 유지합니다. 필드 삭제는 위 DELETE API(또는 100개 초과 시 오래된 순 자동 제거)로만 됩니다. endUserProfile로 지정한 값은 추출값을 덮어씁니다.
- 값 길이 기준은 요청 단위: profileValueMaxChars는 저장되지 않고 그 요청에서 저장되는 값에만 적용됩니다.
- Soft Update: 스키마(필드명)가 바뀌어도 기존 값은 보존되며, 매 호출마다 새로운 값과 합쳐집니다.
- ridm/* 전용: 리셀(openai/*, google/*)은 프로필을 무시합니다.
LLM API (리셀)
openai/*, google/* 등 원본 LLM 모델을 리셀(메모리 없이 원본 응답 그대로). 리셀 모델 요청의 user, systemPrompt, recallCount, memoryCap, summaryEvery, emotion, candor는 무시되며(시스템 지시는 messages에 {"role":"system"}으로), 응답에 메모리 필드(recalledMemories 등)가 없습니다. 가격 = 원가 × 환율 × 1.10.
제공 모델 (GET /api/v1/partners/v1/models와 같은 목록)
- OpenAI (23종):
openai/gpt-6-astra·openai/gpt-6-sol·openai/gpt-6-luna·openai/gpt-5.6-sol·openai/gpt-5.6-terra·openai/gpt-5.6-luna·openai/gpt-5.5·openai/gpt-5.4·openai/gpt-5.4-mini·openai/gpt-5.4-nano·openai/gpt-5.2·openai/gpt-5.1·openai/gpt-5·openai/gpt-5-mini·openai/gpt-5-nano·openai/gpt-4.1·openai/gpt-4.1-mini·openai/gpt-4.1-nano·openai/gpt-4o·openai/gpt-4o-mini·openai/o3·openai/o3-mini·openai/o4-mini - Google (3종):
google/gemini-3.5-flash·google/gemini-3.5-flash-lite·google/gemini-3.1-pro - Anthropic (13종): 아래 Claude 모델 항목 참고
- RIDM: 위 모델마다 같은 이름에
ridm/접두사를 붙인 기억형 모델(리셀가 × 1.3)과, 사주 리포트 전용ridm/saju-report(/reading/report로만 호출)가 있습니다.
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)을 지원하지 않아 400TOOLS_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 | 해석 텍스트 |
sajuSource | computed(birth로 계산) | provided(saju 입력 사용) |
sajuMismatch | birth와 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 | 코드 |
|---|---|
| 400 | MISSING_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 하나만 따로) |
| 402 | INSUFFICIENT_BALANCE — 호출 전 maxLength × 2 토큰(value 등급 또는 한국어가 아닌 질문은 +4,000)의 출력 비용을 미리 예약하므로, 최종 청구액이 더 작아도 거절될 수 있습니다 |
| 403 | FEATURE_DISABLED — 계정에서 꺼진 기능(youtube·files·emotion) 사용 |
포함 항목 — 자미두수 · 점성술 · 꿈해몽 · 타로
사주 AI 안에 사주·자미두수·점성술·타로·꿈해몽 AI가 다 들어 있습니다. 오늘 일진은 따로 부르지 않아도 모든 풀이에 기본으로 반영됩니다(추가 요금 없음). 통합으로 한 번 부르면 포함된 항목의 계산 결과를 근거로 함께 풀이하고, 빼고 싶은 항목은 exclude로 뺍니다. 필요한 AI 하나만 부르려면 only를 씁니다. 계산 결과만 필요하면 같은 항목을 계산형 엔드포인트(/ziwei-chart · /astrology(점성술 토탈) · /dream · /tarot)로 하나씩 따로 불러 직접 앱을 만들 수도 있습니다. /reading/memory에서도 같은 필드로 씁니다.
| 항목 | 언제 포함되나 | 근거로 넣는 것 | 요금 (= 단독 엔드포인트 1회 가격) |
|---|---|---|---|
자미두수 ziwei | 생년월일 + 출생 시각(hour)이 있으면 기본 | 명반 요약 — 명궁·신궁·삼방사정, 생년사화, 현재 대한·유년 | /ziwei-chart |
점성술 astro | 생년월일이 있으면 기본 (시각 없어도 됨) | 네이탈 차트 요약 — 태양·달·수성~토성의 별자리와 도수, 상승궁(가능할 때) | ₩20 (점성술 단독 상품은 /astrology) |
꿈해몽 dream | dream에 꿈 내용을 보내면 | 꿈 내용 + 꿈에서 찾은 상징 낱말 | /dream (후보가 없으면 0원) |
타로 tarot | tarot을 보내면 | 이 요청에서 뽑힌 카드와 정·역방향 뜻 (응답 tarot에 이미지 주소와 함께) | /tarot |
- 과금: 기존 토큰 가격(질문 + persona + 포함 항목 근거 블록 글자수 × 0.25 입력 토큰, 출력 토큰)에 포함된 항목마다 그 항목의 단독 엔드포인트 1회 가격을 더합니다. 근거 블록은 항목마다 1,500자 안쪽의 요약입니다. 해당 기능이 계정에서 꺼져 있거나 가격이 없는 항목은 포함되지 않습니다. 항목별 요금과 예상 1회 범위는 요금에 있습니다.
- 4기둥(
saju)만 보낸 요청에는 생년월일이 없으므로 자미두수·점성술이 포함되지 않습니다(꿈해몽·타로는 포함됩니다). tools.astrology와의 관계: 점성술 항목이 포함되면 같은 네이탈 차트를 두 번 넣지 않도록tools.astrology의 차트 주입은 건너뜁니다. 점성술 항목이 빠지면(exclude또는 기능 꺼짐)tools.astrology는 지금처럼 동작합니다.- 꿈해몽 안전장치:
dream내용이 위기 표현이면 AI를 부르지 않고 도움 안내(support)를costPoints: 0으로 돌려줍니다. - 기능 스위치: 지금 켜져 있습니다. 서버 설정이 꺼지면
exclude·dream·tarot을 보내도 무시되고, 풀이·가격·응답이 포함 항목이 없던 때와 같아집니다(modulesUsed·moduleFees·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 | 자미두수 AI | birth + 출생 시각(hour) | 토큰 가격 + /ziwei-chart 1회 가격 |
astro | 점성술 AI | birth (시각 없어도 됨) | 토큰 가격 + ₩20 (점성술 항목 고정 요금) |
tarot | 타로 AI | tarot 옵션 — birth 불필요 | 토큰 가격 + /tarot 1회 가격 |
dream | 꿈해몽 AI | dream 꿈 내용 — birth 불필요 | 토큰 가격 + /dream 1회 가격 |
- 과금: 토큰 가격(질문 + persona + 그 AI의 근거 블록 하나만, 글자수 × 0.25 입력 토큰, 출력 토큰) + 그 항목 고정 요금 1개. 도메인별 예상 1회 가격은 요금에 있습니다.
exclude와 함께 쓸 수 없습니다: 둘 다 보내면 400ONLY_WITH_EXCLUDE. 그 AI에 필요한 입력이 없으면 400ONLY_REQUIRES_INPUT, 계정에서 그 기능이 꺼져 있으면 400ONLY_DOMAIN_UNAVAILABLE— 사주 풀이로 대신 답하지 않습니다. 명반·차트를 계산할 수 없는 날짜면 400ONLY_INPUT_INVALID.tarot·dream은 생년월일이 필요 없습니다.birth·saju없이 질문과 카드(또는 꿈 내용)만 보내면 됩니다.saju가 아닌only는 사주를 쓰지 않으므로 응답에sajuSource가 없습니다.- 기능 스위치: 포함 항목과 같은 서버 설정을 따르며 지금 켜져 있습니다. 설정이 꺼지면
only는 무시되고 기존 사주 리딩과 같게 동작합니다.
요청 예시 — 타로 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
}
}
주의사항
- 해석 결과만 반환됨: 응답에는 생성된 운세 해석 텍스트만 포함되며, 내부 프롬프트나 사주 이론은 노출되지 않습니다.
candorApplied:candor를t로 보낸 경우에만 응답에 실제 적용된 말결(t|f)이 붙습니다./reading/memory도 같습니다 (말결 (가칭)).- 과금 기준: 입력 = (question + persona 글자수) × 0.25 토큰(+
/reading에서 주입된 유튜브 자막 글자수 × 0.25), 출력 = 실제 completion 토큰. 사주 주입·시스템 프롬프트는 과금하지 않으며,usage.inputTokens는 참고용입니다. 포함 항목(오늘의 운세 · 자미두수 · 점성술 · 꿈해몽 · 타로)이 켜지면 그 근거 블록과 항목별 고정 요금이 더해집니다. costPoints가 실제 차감액입니다. - 응답 언어: 한국어가 아닌 질문에는 질문과 같은 언어로 답합니다.
- 출생시간 미상: hour 생략 시 시주를 빼고 년·월·일 3주 기준으로 해석합니다.
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 | 코드 | 언제 |
|---|---|---|
| 400 | MISSING_ENDUSER_ID | endUserId가 비었거나 공백뿐 |
| 400 | MISSING_SUBJECT_INPUT · INVALID_SAJU_PILLAR · INVALID_LUNAR_DATE | 출생 정보·사주 입력 오류 (리딩 API와 같음) |
| 400 | UNKNOWN_TOOL · INVALID_TOOL_MODE · INVALID_FILE_IDS · FILE_IDS_TOO_MANY · INVALID_FILE_ID_TYPE · FILE_NOT_FOUND | tools·fileIds 오류. 이 경로는 tools.emotion·tools.summaryEvery도 받습니다 |
| 400 | ONLY_WITH_EXCLUDE · ONLY_REQUIRES_INPUT · ONLY_DOMAIN_UNAVAILABLE · ONLY_INPUT_INVALID | AI 하나만 따로 관련 오류 |
| 402 | INSUFFICIENT_BALANCE | 출력 비용을 미리 예약하지 못함 (리딩 API와 같은 방식) |
| 403 | FEATURE_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 | 코드 | 언제 |
|---|---|---|
| 404 | END_USER_NOT_FOUND | 처음 보는 endUserId (네 경로 공통) |
| 400 | MISSING_PARAM | 검색 — endUserId 누락 |
| 400 | MISSING_TAGS | 검색 — tags가 비어 있음 |
주의사항
- 1.3배 단가: 메모리 호스팅으로 인해 입력/출력 토큰 단가가 AI 사주 리딩의 1.3배입니다 (1M 토큰당 입력 3,754원 · 출력 30,030원, 부가세 별도).
- endUserId 필수: 각 엔드유저는 고유한 ID를 가져야 하며, 같은 ID로 호출 시 과거 맥락이 유지됩니다.
- 메모리 보관: 저장된 메모리는 명시적 삭제 시까지 보관됩니다.
사주 리포트 API
질문 없이 버튼 한 번으로 총평·재물·연애·대운까지 모든 사주 해석이 한 번에 나옵니다. 대화형(reading)과 별개의 단품 결제형 상품. 전용 모델 ridm/saju-report(RIDM 자체 리포트 특화 모델 — 원샷 상품 특성상 메모리 미장착)가 기본입니다. 리딩 API의 응답 상한은 2,000자입니다 — 그 이상의 장문 해설(최대 12섹션, 약 6,000자)은 이 리포트 API를 사용하세요.
POST /api/v1/partners/v1/reading/report
생년월일(또는 사주)을 넣으면 섹션별 마크다운 사주 리포트를 반환합니다.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| birth | object | birth 또는 saju 중 하나 | {year, month, day, hour?, minute?, gender, calendar?} — calendar: "lunar"는 양력으로 변환해 계산 |
| saju | object | birth 또는 saju 중 하나 | 4기둥 직접 입력 {"year":{"stem":"경","branch":"오"},"month":{…},"day":{…},"hour"?:{…}} |
| year / month / day / gender | number / string | 선택 | 평면 필드(레거시, 양력). 네 값이 모두 있을 때만 birth로 인정. birth·saju 모두 없으면 400 MISSING_SUBJECT_INPUT |
| hour / minute | number | 선택 | hour 생략 시 시주를 빼고 년·월·일 3주 기준으로 해석합니다. minute은 hour 없이 보내면 무시 |
| tone | string | 선택 | warm(기본) | casual | formal |
| candor | string | 선택 | 말결 — t | f(기본). 좋은 점과 어려운 점의 배치·강도만 바뀌고 섹션 구성·의료·법률·투자 확답 금지는 그대로. 과금 없음. 말결 (가칭) 참고 |
| persona | string | 선택 | 말투·캐릭터 지시 (최대 2,000자 — 이 길이만 입력 과금) |
| sections | string[] | 선택 | 섹션 제목 목록 (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)
}
}
- 사주 주입 무료: 만세력 계산·주입과 시스템 프롬프트는 과금하지 않습니다. 입력 과금은 persona 길이만, 출력은 실사용 토큰입니다.
- 실측 비용: 기본 8섹션(약 3,000자) 리포트 1회 약 85P (출력 ₩38,077/1M 기준).
- 재시도는 다시 과금됩니다. 응답을 받은 뒤 같은 입력을 재사용하려면 클라이언트에서 저장하세요 (멱등성).
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | MISSING_SUBJECT_INPUT | birth·saju가 모두 없음 |
| 400 | INVALID_SAJU_PILLAR | saju의 기둥이 60갑자가 아님 |
| 400 | INVALID_LUNAR_DATE | 없는 음력 날짜 |
| 402 | INSUFFICIENT_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"
}
}
주의사항
- 자막 필수: 자막이 없으면 TRANSCRIPT_UNAVAILABLE 400 (무과금).
- 에러: 잘못된 URL·ID 400
INVALID_YOUTUBE_URL/ 요약 생성 실패 400LLM_GENERATION_FAILED/ 잔액 부족 402INSUFFICIENT_BALANCE. - 실측 비용: 20~30분 영상 1회 약 80P (토큰 포함).
- 리딩 연동: AI 사주 리딩 API(
/reading)에서tools: { "youtube": true }로 질문 속 링크의 자막을 주입할 수 있습니다 (최상위youtube필드는 무시). 유튜브 주입은 /reading에서만 지원(/reading/memory는 아직 미지원).
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | INVALID_YOUTUBE_URL | URL에서 영상 ID를 찾지 못함 |
| 400 | TRANSCRIPT_UNAVAILABLE | 자막이 없는 영상 (과금 없음) |
| 400 | LLM_GENERATION_FAILED | 요약 생성 실패 또는 빈 결과 |
| 402 | INSUFFICIENT_BALANCE | 요약 비용을 미리 예약하지 못함 |
| 403 | FEATURE_DISABLED | 계정에서 유튜브 기능이 꺼져 있음 |
기능 켜고 끄기
유튜브 요약 API는 파트너 계정 및 요청 단위로 옵트인할 수 있습니다. 콘솔의 "기능 설정"에서 전역 활성화 상태를 관리하며, chat은 최상위 youtube:true, reading은 tools: { "youtube": true }를 명시해 요청별로 활성화합니다.
- 전역 비활성: 403 { code: 'FEATURE_DISABLED', feature: 'youtube' } 응답
- 요청 단위:
youtube:true미지정 또는 false = 유튜브 주입 안 함
문서 처리 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는 자동 회상된 파일이 있을 때만 붙습니다(비어 있으면 생략).
요금 및 과금
- 업로드 및 추출: 무료 (텍스트 추출 토큰 미과금)
- AI 분석 (analyze=true): 경계 탐지 + 구간별 요약 + 전체 요약의 실제 토큰 사용량을 분석 모델 단가로 과금. 동기 ≤30,000자, 비동기 >30,000자 (status:
analyzing) - Chat 주입: 문서 텍스트가 입력 토큰으로 계산되어 일반 chat 요청과 동일하게 과금
- 로그: 콘솔의 사용 로그에서
file_upload(무료),file_analyze(유료, 실사용 토큰) 분류로 확인 가능
기능 켜고 끄기
문서 처리 기능은 파트너 계정 수준에서 활성화/비활성화할 수 있습니다. 콘솔의 "기능 설정"에서 관리하며, 비활성 시 모든 파일 요청에 대해 403 응답이 반환됩니다.
- 전역 비활성: 403 { code: 'FEATURE_DISABLED', feature: 'files' } 응답
- 요청 단위:
fileIds미지정 = 문서 주입 없음 (정상 처리)
분석 상태 및 청크
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 |
분석 실패 (실패 사유는 응답에 포함되지 않음 — 다시 업로드하세요) |
주의사항
- 형식 검증: 지원하지 않는 형식은 400 { code: 'INVALID_FILE_TYPE' } 반환
- 기타 에러: 파일 누락 400
FILE_NOT_PROVIDED/ 10MB 초과 400FILE_TOO_LARGE/ 없는 파일 404FILE_NOT_FOUND/:id가 UUID가 아니면 400 - 분석 필수: analyze=true일 때 endUserId가 없으면 400 { code: 'END_USER_REQUIRED' } 반환
- 추출 실패: 문서 추출 중 오류 시 422 { code: 'EXTRACTION_FAILED' } 반환
- 잔액 확인: 분석 요청 전 파트너 크레딧 충분성 검사. 부족하면 402 반환
- 지식베이스 자동 적재: analyze=true인 경우, 각 구간의 요약과 태그가 지식베이스에 자동 저장되어 이후 auto-recall 대상이 됩니다. endUserId 지정 시 해당 유저의 기억 계층에도 추가됩니다.
- 재업로드 = 새 파일: 업로드할 때마다 새 파일(새 id)이 만들어지고 analyze=true면 다시 분석·과금됩니다. 같은 문서는 한 번만 업로드하고 받은 id를
fileIds로 재사용하세요.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | FILE_NOT_PROVIDED | 업로드 — file 파트 없음 |
| 400 | INVALID_FILE_TYPE | 업로드 — 지원하지 않는 형식 |
| 400 | FILE_TOO_LARGE | 업로드 — 10MB 초과 (경우에 따라 코드 없는 413) |
| 400 | END_USER_REQUIRED | 업로드 — analyze=true인데 endUserId 없음 |
| 400 | (없음) | 상세·삭제 — :id가 UUID가 아님 |
| 402 | INSUFFICIENT_BALANCE | 업로드 — 분석 비용을 미리 예약하지 못함 |
| 403 | FEATURE_DISABLED | 계정에서 문서 처리 기능이 꺼져 있음 (네 경로 공통) |
| 404 | FILE_NOT_FOUND | 상세·삭제 — 없는 파일 또는 다른 계정의 파일 |
| 422 | EXTRACTION_FAILED | 업로드 — 문서에서 텍스트를 뽑지 못함 |
감정 상태 분석 (Emotion)
Chat API에서 요약 사이클에 편승하여 사용자와 AI의 감정 상태를 분석합니다. 별도의 LLM 호출이 없으며, 요약 프롬프트에만 감정 분석 블록을 추가하므로 추가 과금이 없습니다. ridm/* 모델에서만 지원됩니다.
개요
활성화 조건:
- Global:
partner_emotion_enabled✓ - Partner: 콘솔 기능 설정에서 "감정 상태 분석" 활성화
- Request:
emotion: true명시 - Model:
ridm/*계열만 지원
요약 주기 (summaryEvery)
감정 분석은 요약 사이클마다 자동으로 실행됩니다. summaryEvery 파라미터로 사이클 주기를 조절할 수 있습니다 (기본값: 5, 범위: 1–20).
- summaryEvery=1: 거의 매 턴마다 감정 업데이트 (요약은 응답 뒤 비동기로 실행)
- summaryEvery=5: 5턴 주기 (권장)
- summaryEvery=20: 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)
- improving: 비긍정 감정(sadness+anger+fear+disgust)이 50 이상 감소
- stable: 변화 50 미만
- declining: 비긍정 감정이 50 이상 증가
- null: 이전 사이클 데이터 부재
스트리밍 응답 예시 (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]
비용 안내
- 별도 과금 없음: 감정 분석은 요약 사이클 프롬프트에 포함되어 진행되며, 요약·감정 사이클의 토큰은 파트너에게 과금되지 않습니다. 과금은 채팅 턴 자체의 입력·출력 토큰뿐입니다.
주의사항
- 첫 사이클 전: 첫 요약 사이클이 실행되기 전에는 emotion 필드가 없습니다 (정상 동작).
- 기능 OFF:
emotion: true요청인데 전역 또는 파트너 기능 설정이 비활성이면 403 { code: 'FEATURE_DISABLED', feature: 'emotion' } 응답 (YouTube·문서 처리와 동일).emotion을 보내지 않거나 false면 필드만 생략됩니다. - ridm/* 모델에서만 지원됩니다. 감정 기능이 켜진 계정이면 리셀(openai/*·google/*) 요청의 emotion 필드는 무시되고, 꺼진 계정이면 모델과 무관하게
emotion:true는 403FEATURE_DISABLED입니다. - 스트리밍: 스트리밍 응답에서는 message_done SSE 이벤트에 emotion이 포함됩니다.
사주 이미지 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
}
}
주의사항
- 고정 가격: 모든 이미지 생성은 ₩200이 과금됩니다.
- 생성 시간: 이미지 생성에는 20~30초 소요됩니다. 타임아웃을 충분히 설정해주세요.
- Base64 인코딩: 반환된 imageDataUri는 base64로 인코딩된 PNG 이미지이며, HTML
<img src="{imageDataUri}">태그에 직접 사용할 수 있습니다. - 스타일: background(배경 아트)·icon(미니멀 아이콘)·portrait(초상 일러스트)는 분위기만 바꿉니다. 크기는 모든 스타일이 같습니다(기본 1024×1024 정사각형).
- 타임아웃: 상류가 80초 안에 이미지를 돌려주지 않으면 504
IMAGE_UPSTREAM_TIMEOUT을 반환하며 과금하지 않습니다. 다시 시도하세요. - 출생시간 미상: hour 생략 시 시주를 빼고 년·월·일 3주 기준으로 해석합니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 402 | INSUFFICIENT_BALANCE | 잔액이 이미지 1장 가격보다 적음 |
| 504 | IMAGE_UPSTREAM_TIMEOUT | 80초 안에 이미지를 받지 못함 (과금 없음) |
이미지 생성 API (모델 리셀)
POST /api/v1/partners/v1/images/generations
OpenAI Images API와 같은 형식으로 GPT-Image·Nano Banana 모델을 하나의 키로 호출합니다. 모델·해상도·품질마다 장당 가격이 정해져 있고, 표의 금액이 그대로 차감됩니다(생성된 장수 × 장당 가격). 프롬프트 입력 토큰 비용도 장당 가격에 포함되어 있어 별도로 붙지 않습니다.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
model | string | O | openai/gpt-image-2, openai/gpt-image-1-mini, google/nano-banana-pro, google/nano-banana, fal/nano-banana-pro, fal/nano-banana |
prompt | string | O | 생성할 이미지 설명 (최대 1,000자). 초과 시 422 PROMPT_TOO_LONG(잔액 예약 뒤 검사하므로 잔액이 부족하면 402가 먼저 옴), 빈 문자열은 400 EMPTY_PROMPT |
size | string | GPT-Image: 1024x1024(기본) · 1024x1536(세로) · 1536x1024(가로) · auto(모델이 선택 — 해당 품질의 가장 비싼 크기 가격으로 과금). 이전 값 256x256·512x512는 1024x1024, 1792x1024는 1536x1024, 1024x1792는 1024x1536으로 생성·과금됩니다. Nano Banana에서는 비율로만 쓰입니다 — aspect_ratio를 권장합니다. | |
quality | string | GPT-Image 전용: low · medium(기본) · high. 이전 값 standard=medium, hd=high로 그대로 동작합니다. Nano Banana에 low/medium/high를 보내면 400입니다. | |
aspect_ratio | string | Nano Banana 전용: 21:9 · 16:9 · 3:2 · 4:3 · 5:4 · 1:1(기본) · 4:5 · 3:4 · 2:3 · 9:16. 비율은 가격에 영향이 없습니다. size보다 우선합니다. | |
resolution | string | Nano Banana 전용: Nano Banana는 1K만, Nano Banana Pro는 1K(기본) · 2K · 4K. 대문자 K만 허용합니다. | |
n | number | 생성 장수 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": "..."
}
- GPT-Image·Google 직접 모델은
b64_json, FAL 모델은url로 이미지를 돌려줍니다. costWon= 장당 가격 × 실제 생성된 장수. 업스트림이 이미지를 하나도 돌려주지 않으면 과금하지 않습니다.- GPT-Image 응답의
usage(토큰 수)는 참고용이며 가격에는 쓰이지 않습니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | MODEL_NOT_FOUND | 가격표에 없는 모델 |
| 400 | INVALID_MODEL | openai/·google/·fal/ 이외의 접두사 |
| 400 | INVALID_PARAMETER | 모델이 받지 않는 옵션 (GPT-Image에 aspect_ratio·resolution, Nano Banana에 quality, 지원하지 않는 해상도) |
| 400 | EMPTY_PROMPT | 프롬프트가 공백뿐 |
| 400 | INVALID_PROMPT | 상류 모델이 프롬프트를 거절 |
| 400 | UPSTREAM_ERROR | 상류 오류 또는 이미지 0장 (과금 없음) |
HTTP 400, 본문 status 429 | RATE_LIMITED | 상류 속도 제한 — 잠시 뒤 다시 시도 |
| 402 | INSUFFICIENT_BALANCE | 장당 가격 × n이 잔액보다 큼 |
| 422 | PROMPT_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"이 됩니다. 보정 결과 형태는 아래 주의사항의 음력 입력 예시를 보세요.
주의사항
- 복잡한 응답: 만세력 응답은 원국뿐만 아니라 대운, 세운, 격국, 신살 등 상세 정보를 포함합니다. 필요한 필드만 선택해 사용하세요.
- 선택적 필드 — 출생시간 미상 계약:
hour·minute는 생략할 수 있습니다.hour를 생략하면 정오로 가정하지 않고 시주(時柱)를 제외합니다. 응답은 다음처럼 달라집니다.wonkuk.hour=null,trueSolarTime=nullohaengBalance= 년·월·일 3주(천간+지지 6글자) 기준으로 재계산branchRelations·cheonEulGwiIn= 3주 지지만으로 성립하는 항목만 남김- 최상위
birthHour추가:{ known: false, hourPillar: null, basis: "three_pillars", excludedFields: [], note }. 분석 필드(yongshin·strength·gyeokguk·hapChungReport·ilganSummary·seun등)는 제거되지 않고 년·월·일 3주(6글자) 기준으로 계산된 값으로 내려갑니다 (글자 수에 의존하는 임계치도 6글자 기준으로 환산). 정오 참고값이 아니라 3주만으로 산출한 값입니다 - 미상 여부 판별은
birthHour.known을 사용하십시오 —excludedFields.length로 판별하지 마십시오. 이 배열은 이제 항상 비어 있으며 구버전 클라이언트 호환을 위해서만 유지됩니다 minute는hour없이 보내면 무시됩니다 (hour가 있을 때만minute ?? 0)hour를 보낸 요청에는birthHour필드가 없고 계산 결과도 이전과 같습니다(2026-10-10부터 붙는identity·ohaengDeficient제외). 항상hour를 보내던 기존 클라이언트는 영향이 없습니다. 짧게 존재했던 "생략 시 정오 계산" 동작에 맞춰 만든 클라이언트는birthHour.known === false를 확인해 시주 칸을 "미상"으로 표시하십시오.strength·gyeokguk등은 3주 기준 값으로 항상 내려갑니다(2026-09-16 이전에는 제거됐습니다). 상담 계열(/reading·/reading/memory·/reading/report)도 같은 규칙으로 모델에 3주 기준 값을 전달합니다
- 한글 필드명: 모든 필드명(천간, 지지, 오행 등)은 한글로 표기됩니다.
lang을 보내도 필드명은 그대로이고 값(용신·일간 설명·대운 방향·신살 이름 등)만 번역됩니다. 천간·지지·오행 코드(stem·branch·ohaeng)와ohaengBalance의 키는 모든 언어에서 한글 그대로입니다. - 추가 옵션은 전부 선택입니다 (2026-10-08):
lang·calendar·isLeapMonth·birthLatitude·birthLongitude·country·zaSiRule·birthTimePrecision를 하나도 보내지 않으면 계산 결과는 이전과 같습니다. 단 2026-10-10부터identity·ohaengDeficient가 항상 응답 끝에 추가됩니다(기존 키의 값·순서는 그대로). 요금도 그대로 ₩50/호출입니다. - 음력 입력:
calendar: "lunar"로 보내면 응답 최상위에 변환 내역lunarInput이 붙습니다. 진태양시 보정 결과는trueSolarTime에 들어갑니다. 실제 엔진 출력 예 (음력 1990-07-15 14:30, 부산 좌표,lang: "en"):{ "year": 1990, "month": 7, "day": 15, "hour": 14, "minute": 30, "timezone": "Asia/Seoul", "gender": "male", "calendar": "lunar", "birthLatitude": 35.1796, "birthLongitude": 129.0756, "lang": "en" } → "lunarInput": { "calendar": "lunar", "lunarDate": "1990-07-15", "isLeapMonth": false, "leapMonthApplied": false, "solarDate": "1990-09-03" }, "trueSolarTime": { "originalLocal": "14:30", "correctedLocal": "14:07", "longitudeCorrection": -23.6976, "equationOfTime": 0.3241, "totalCorrectionMinutes": -23.4, "dayShift": 0, "dstApplied": false, "basis": "coords", ... }, "ilganSummary": "辛 Metal — Jewel energy. Sharp intellect and refined aesthetic sense.", "daeunDirection": "Forward" - 입력 오류: 존재하지 않는 음력 날짜(예: 29일까지인 달의 30일)는 400
INVALID_LUNAR_DATE, 위도/경도 중 하나만 보낸 요청은 400INVALID_LOCATION이며 과금되지 않습니다.timezone누락 등 형식 오류도 400(과금 없음)입니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | INVALID_LUNAR_DATE · INVALID_LOCATION | 없는 음력 날짜 / 위도·경도 중 하나만 보냄 (과금 없음) |
| 402 | (없음) | 잔액이 호출 요금보다 적음 — 이 API는 호출 전 잔액 검사가 없어 코드 없는 402가 옵니다 |
평생 사주 API
POST /api/v1/partners/v1/lifetime
생년월일시로 원국을 계산하고, 차트에 해당하는 부품(일주·일간×월지·일간×신강약·일간×용신·오행 과다/부족 최대 3개·가장 센 십성 그룹)의 풀이 문단을 성격·재물·직업·연애·건강·인간관계·조언 7개 주제로 묶어 돌려줍니다. HRUM 사주이론으로 작성한 문단이며 같은 입력은 항상 같은 결과입니다. 원국 판정은 만세력 API와 동일합니다.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
year · month · day | number | O | 출생년(1900-2100)·월·일 (기본 양력, calendar: "lunar"면 음력) |
hour · minute | number | 출생시각. hour 생략 시 만세력 API와 같은 출생시간 미상 규칙(년·월·일 3주 기준)으로 계산합니다 | |
timezone | string | O | 타임존 (예: Asia/Seoul) |
gender | string | O | "male" 또는 "female" |
topic | string | 한 주제만 받을 때: personality, wealth, career, love, health, relationships, advice. 생략 시 7개 주제 전체 | |
lang | string | ko(기본) · en · ja · th | |
layers | string[] | 추가 풀이 층: "astro"(별자리 — 태양·달·상승궁) · "ziwei"(자미두수 — 명궁 주성 + 주제별 궁 주성). 각 주제의 사주 문단 뒤에 별자리 → 자미두수 순으로 붙습니다. 생략하거나 []면 응답이 지금과 똑같습니다 | |
calendar · isLeapMonth | string · boolean | "lunar"면 year·month·day를 음력으로 받아 양력으로 바꿔 계산합니다(기본 "solar"). isLeapMonth: true는 윤달이며, 그해에 그 윤달이 없으면 평달로 계산합니다. 음력으로 보내면 응답에 lunarInput(만세력 API와 같은 형식)이 붙습니다 | |
birthLatitude · birthLongitude | number | 출생지 좌표(둘 다 함께). 사주 문단과 layers가 같은 기준으로 진태양시 보정을 합니다. 표준 자오선은 timezone의 표준시(서머타임 제외)로 정합니다. 상승궁은 출생시각과 좌표가 모두 있어야 나옵니다 | |
country | string | 출생 국가 2글자 코드. 좌표 없이 KR·JP·TH를 보내면 그 나라 대표 경도로 보정합니다(그 외 국가는 보정 없음). layers도 이 국가를 씁니다(생략하면 lang으로 추정) | |
zaSiRule | string | 자시(23시 이후) 일주: "traditional"(기본, 다음 날 일주) · "yajaSi"(야자시, 당일 일주). 사주 문단에만 쓰입니다(자미두수는 23시부터 다음 날로 고정) | |
birthTimePrecision | string | exact · 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 | 코드 | 언제 |
|---|---|---|
| 404 | lifetime_not_enabled | 계정에 기능이 열려 있지 않음 |
| 400 | INVALID_LUNAR_DATE · INVALID_LOCATION | 없는 음력 날짜 / 위도·경도 중 하나만 보냄 |
| 402 | INSUFFICIENT_BALANCE | 요청한 층까지 포함한 금액을 미리 확인했을 때 잔액 부족 |
| 503 | lifetime_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 · day | number | O | 출생년(1900-2100)·월·일 |
hour | number | O | 출생 시(0-23). 자미두수는 출생 시각 없이 명반을 세울 수 없습니다 — 생략하면 400 birth_time_required |
minute | number | 출생 분(0-59), 생략 시 0 | |
gender | string | O | "male" 또는 "female" (대한 진행 방향을 정합니다) |
calendar | string | solar(기본) 또는 lunar | |
isLeapMonth | boolean | 음력 윤달이면 true. 양력 입력에서는 무시합니다. 그 해에 해당 윤달이 없으면 평달로 계산되고 chart.lunarDate.isLeapMonth가 false로 나옵니다 | |
birthTimePrecision | string | exact(진태양시 보정) 또는 approximate(보정 없음 + notes.approximateTimeNote). 생략 시 보정 없음 | |
birthLatitude · birthLongitude | number | 출생지 좌표 (exact 보정용) | |
countryCode | string | 출생 국가 ISO 2자리, 대문자만(예: KR — kr은 400). 좌표가 없을 때 대표 경도로 보정 | |
birthTimezoneOffset | number | 출생 시각의 UTC 오프셋(분, 540 = KST) | |
referenceDate | string | 대한·유년 기준일 YYYY-MM-DD (기본: 오늘, 한국 시간) | |
lang | string | ko(기본) · en · ja · th | |
timezone | string | 만세력 API와의 호환용 (계산에 쓰이지 않음) | |
readings | boolean | true면 응답에 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(계정에 기능 미개방) · 404ziwei_readings_not_enabled(풀이 문단 미개방) · 503ziwei_readings_unavailable(풀이 문단을 불러오지 못함).
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 404 | ziwei_not_enabled | 계정에 기능이 열려 있지 않음 |
| 404 | ziwei_readings_not_enabled | readings: true인데 풀이 문단이 열려 있지 않음 |
| 400 | invalid_birth_date | 없는 양력 날짜, 또는 음력 일·윤달 범위 밖 |
| 400 | birth_time_required | hour 없음 — 위 예시처럼 partial이 함께 옵니다 |
| 400 | reference_date_before_birth | referenceDate가 출생일보다 앞섬 |
| 402 | INSUFFICIENT_BALANCE | 잔액 부족 |
| 503 | ziwei_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/호출(계정에 열려 있을 때만, 미개방 시 404identity_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 | 코드 | 언제 |
|---|---|---|
| 404 | identity_not_enabled | 계정에 열려 있지 않음 |
| 400 | INVALID_LUNAR_DATE · INVALID_LOCATION | 없는 음력 날짜 / 위도·경도 중 하나만 보냄 |
| 402 | INSUFFICIENT_BALANCE | 잔액 부족 |
오늘의 사주 API
POST /api/v1/partners/v1/today
지정한 날(기본: 한국 시간 오늘)의 일진과 내 사주를 맞춰 하루 요약(일진·오늘의 십성·오늘 모드 MBTI·올해 흐름 점수·발현 신살·삼재)과 오늘의 운(일진×일간 관계, 행운 색·음식·키워드, 좋은 시간·피할 시간, 추천 활동, 한 줄 메시지)을 돌려줍니다. INTA 앱 홈의 "오늘" 카드와 같은 계산입니다. 여기에 활동 적합도(taskAffinity)와 오늘의 흐름 한 줄(summary.daily_flow)이 항상 붙고, 기온·날씨를 보내면 오늘의 옷차림(fashion), mbtiBehavior: true면 오늘 모드 MBTI 행동 패턴(mbtiBehavior)이 더해집니다.
요청 파라미터
만세력 API와 같은 출생 정보와 선택 옵션(lang·음력·좌표·zaSiRule 등)을 받습니다. 추가로:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
date | string | 볼 날짜 YYYY-MM-DD(1900-2100). 생략하면 한국 시간 기준 오늘 | |
temperatureC | number | 현재 기온(℃, −40~50). 보내면 응답에 fashion(아침·낮·저녁 옷차림)이 붙습니다. 생략하면 fashion 없음(추정하지 않음) | |
weather | string | 날씨: clear · cloudy · rain · snow · storm | |
humidity | number | 상대 습도(%, 0~100). temperatureC·weather·humidity 세 값을 모두 보내야 fashion.mood(시간대별 한 줄 날씨 코멘트)가 붙습니다 | |
mbtiBehavior | boolean | true면 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 | 코드 | 언제 |
|---|---|---|
| 404 | today_not_enabled | 계정에 기능이 열려 있지 않음 |
| 400 | INVALID_LUNAR_DATE · INVALID_LOCATION | 없는 음력 날짜 / 위도·경도 중 하나만 보냄 |
| 402 | INSUFFICIENT_BALANCE | 잔액 부족 |
꿈해몽 API
POST /api/v1/partners/v1/dream
엔드유저가 말한 꿈 이야기에서 상징 낱말(예: 바다, 물고기)을 찾아 두드러진 순서로 돌려줍니다. 낱말은 HRUM이 직접 만든 꿈 상징 어휘집(일상 명사 1,000여 개와 그 동의어·활용형·입말)에서 찾습니다. 어휘집에는 낱말만 있고 해몽 뜻이나 길흉 판단은 없습니다. 돌려주는 낱말은 모두 꿈 내용에 그 낱말이나 동의어·활용형이 실제로 나온 것입니다.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
dream | string | ✓ | 꿈 이야기 (최대 1,000자) |
feeling | string | 꿈에서 느낀 감정 (최대 300자, 예: "무섭고 답답했어요"). 꿈 내용과 합쳐 태그를 뽑습니다(합친 길이는 1,000자까지 사용) | |
lang | string | 응답 언어. 현재 ko만 지원합니다(사전이 한국어 전용). 다른 값은 400 | |
includeNegative | boolean | 준비 중 고전 근거에 흉한 판단(죽음·병·잃음 등)의 기록도 넣을지. 기본 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 | 코드 | 언제 |
|---|---|---|
| 404 | dream_not_enabled | 계정에 기능이 열려 있지 않음 |
| 402 | INSUFFICIENT_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, modelVersion | string | ✓ | 좌표를 낸 모델. 허용 목록 밖이면 400 MEASURE_MODEL_UNSUPPORTED / MEASURE_MODEL_VERSION_MISMATCH. 현재: face_landmarker · float16/[email protected] |
normalization | string | ✓ | face-norm-1 |
landmarks | number[][] | ✓ | 정확히 478개 [x, y] 또는 [x, y, z] (이미지 폭·높이로 나눈 값, −0.02~1.02) |
imageWidth, imageHeight | number | ✓ | 픽셀 크기 |
hairline | [number, number] | 얼굴 가운데 발제선 한 점. 서버는 추정하지 않습니다 — 없으면 上停 관련 키가 null(사유 hairline_missing). 사용자가 이마 위 머리 시작점을 탭하는 입력을 권합니다(머리카락 분할 모델은 참고용) | |
quality | object | yawDeg·pitchDeg·rollDeg·blendshapes(MediaPipe 계수 이름→점수). 보내야 품질 검사가 됩니다 | |
includeNegative | boolean | 흉한 판단의 기록(polarity negative)도 넣을지. 기본 false (꿈해몽과 같은 이름·뜻) |
POST /api/v1/partners/v1/palm-measure
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
modelName, modelVersion | string | ✓ | 현재: hand_landmarker · float16/[email protected] |
normalization | string | ✓ | palm-norm-1 |
landmarks | number[][] | ✓ | 정확히 21개 |
imageWidth, imageHeight | number | ✓ | 픽셀 크기 |
handedness | string | ✓ | left | right — 피사체의 실제 손(사용자가 고르게 하세요) |
mirrored | boolean | 좌우 반전 사진(셀카 미리보기)이면 true | |
creases | object[] | 손금 선 폴리라인 (최대 64개, 선마다 점 2~512). 항목: points(✓), label(life·head·heart·fate·sun·mercury·simian·unknown), kind(segment | branch), autoLabel. 없으면 선 관련 키가 null(creases_missing) | |
autoLabel | boolean | 이름 없는 선 전부에 서버 공용 휴리스틱으로 이름을 붙입니다. 선 하나에만 쓰려면 그 항목의 autoLabel. 직접 붙인 label이 항상 우선합니다 | |
includeNegative | boolean | 얼굴과 같음. 기본 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는 원문이 그렇게 적었다는 기록입니다. 이용자에게는 "이런 경향이 있다고 적혀 있다 + 이렇게 대처해 볼 수 있다"로 전하고, 사람을 깎아내리거나 성격을 낙인찍는 말로 바꾸지 마세요.
오류 코드
| HTTP | code | 뜻 |
|---|---|---|
| 400 | MEASURE_MODEL_UNSUPPORTED | modelName이 허용 목록에 없음 |
| 400 | MEASURE_MODEL_VERSION_MISMATCH | modelVersion이 허용 목록에 없음 (현재 float16/[email protected]만) |
| 400 | MEASURE_NORMALIZATION_MISMATCH | normalization이 face-norm-1 / palm-norm-1이 아님 |
| 400 | MEASURE_LANDMARK_COUNT | 좌표 개수가 얼굴 478 / 손 21이 아님 |
| 400 | MEASURE_LANDMARK_SHAPE · MEASURE_COORD_NAN · MEASURE_COORD_RANGE · MEASURE_IMAGE_SIZE · MEASURE_DEGENERATE | 점 형식·숫자·범위(−0.02~1.02)·이미지 크기 오류 |
| 400 | MEASURE_HANDEDNESS · MEASURE_CREASE_LIMIT · MEASURE_CREASE_SHAPE · MEASURE_CREASE_LABEL | 손·손금 선 입력 오류 |
| 400 | MEASURE_QUALITY_POSE · MEASURE_QUALITY_FACE_SIZE | 고개가 10° 넘게 돌아감 · 얼굴이 너무 작음 — 다시 찍기 |
| 400 | (검증 오류) | 문서에 없는 필드를 보냄 — 허용된 필드만 보내세요 |
| 402 | INSUFFICIENT_BALANCE | 크레딧 잔액 부족 |
| 404 | face_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 해석은 없습니다 — 뜻은 카드에 정해진 문장 그대로입니다. 그래서 쓰는 방법이 셋입니다.
- 타로만 불러 앱 만들기 — 응답의 카드 이름·뜻·이미지를 그대로 화면에 그립니다.
- 타로 + LLM으로 해석 — 뽑힌 카드를 LLM API 입력으로 넘겨 직접 풀이를 씁니다.
- 사주 AI와 조합 — AI 사주 리딩 요청에
tarot을 넣으면 사주 풀이와 함께 타로를 읽어 줍니다(포함 항목).
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
count | number | △ | 뽑을 장수 1~10 (정수). count와 spread 중 하나만 보냅니다. 둘 다 보내거나 둘 다 없으면 400 |
spread | string | △ | 스프레드. 장수와 위치 이름이 정해져 있습니다: single(오늘의 한 장, 1장) · past-present-future(과거·현재·미래, 3장) · choice(선택: 현재 상황·장애물·결과, 3장) · celtic-condensed(켈틱 축약, 5장) · future-prediction(미래 예측, 5장) |
reversed | boolean | 역방향 허용 (기본값 true). false면 모두 정방향 | |
seed | string | 재현용 문자열 (1~200자). 같은 seed + 같은 count/spread + 같은 reversed면 언제나 같은 카드가 나옵니다. 없으면 매번 무작위(암호학적 난수) | |
question | string | 엔드유저 질문 (최대 500자). 해석에 쓰지 않고 응답에 그대로 돌려줍니다 — 다음 LLM 단계로 넘길 때 편하라고 둔 필드입니다 | |
lang | string | 카드 이름·키워드·뜻·스프레드 이름의 언어: 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
}
}
cards[]:position(1부터, 뽑은 순서 = 스프레드 위치) ·positionLabel(스프레드 위치 이름,count로 뽑으면null) ·id(언어와 무관한 카드 고유값) ·name(요청 언어) ·nameEn·arcana(major|wands|cups|swords|pentacles) ·reversed·keywords·meaning(뽑힌 방향의 것) ·imageUrl.spread:count로 뽑으면null입니다.- 카드 이미지:
imageUrl·cardBackUrl은 WebP(가로 600px)이며 주소에 버전(v2)이 들어 있어 바뀌지 않습니다 — 받아서 캐시해 두고 그대로 써도 됩니다. 역방향 카드는 이미지를 180도 돌려서 보여 주세요(이미지 자체는 정방향 하나뿐입니다). - 그림 출처: 1909 Rider-Waite(Pamela Colman Smith, 퍼블릭 도메인) 원판 스캔입니다. 카드 78장과 뒷면 모두 Wikimedia Commons의 1909 원판 스캔이며, 파일별 출처·라이선스는 출처 원장에 있습니다. 예전 주소(
v1)도 같은 그림을 그대로 돌려주므로, 이미 저장해 둔 주소를 바꿀 필요는 없습니다. costPoints: 이번 호출의 차감액(₩).
요금
- 호출당 고정 요금 1번 — 1장을 뽑든 10장을 뽑든 같습니다. 가격은 요금의 계산형 표에 있습니다. 성공한 호출만 과금되고, 같은 입력의 반복 호출도 각각 과금됩니다 (멱등성).
- 입력 오류(400)·잔액 부족(402)은 과금되지 않습니다. 사용량 로그 endpoint는
tarot입니다. - 아직 열리지 않은 상태에서 호출하면 404
tarot_not_enabled(과금 없음)입니다.
Waite 원전 근거 kb 준비 중
열리면 응답에 kb가 붙습니다. 뽑힌 카드마다 A. E. Waite, The Pictorial Key to the Tarot(1910, 퍼블릭 도메인) 원문에서 그 카드의 정·역방향 뜻을 규칙 ID(R-TAROT-*)와 원문 인용으로 돌려줍니다. 위의 meaning·keywords는 그대로 두고 kb만 더해지며, 요금은 같습니다.
kb.cards[]:position·id·reversed·drawn(뽑힌 방향의 뜻) ·upright·reversedMeaning·positionRule. 뜻마다ruleId·status·statement(한국어) ·quotes[](quote원문 영어,location장절,foundAt원문 줄) ·variants[](Waite가 적은 다른 설명alternative, 제3부 §4 추가 의미additional).status는 KB 표시를 그대로 씁니다(verified= 원문 인용 있음). Waite가 뜻을 적지 않은 자리(컵 2 역방향)는ruleId: null,status: "disputed"입니다.method: "celtic-cross": Waite가 책에 적은 켈트식 점법(10장).count·spread대신 보내며, 10장 자리마다 Waite의 자리 뜻이positionRule에 옵니다. 다른spread는 Waite 원문에 없는 배열이라 자리 근거가 없습니다(kb.method: null).kb.caveats: 자리에 따라 뜻이 바뀐다는 Waite의 말과 이용 원칙(점의 도구이며 건강·법률·재정 결정을 대신하지 않음).- 열리기 전
method를 보내면 404tarot_kb_not_enabled(과금 없음)입니다.
{
"method": "celtic-cross",
"seed": "user-1042:2026-10-11"
}
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 404 | tarot_not_enabled | 계정에 기능이 열려 있지 않음 |
| 404 | tarot_kb_not_enabled | method를 보냈는데 원전 근거가 아직 열리지 않음 |
| 402 | INSUFFICIENT_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(과금 없음).
주의사항
- 가격: ₩20 / 호출 (종량제) ·
readings: true면 ₩20 추가 (합계 ₩40) - 날짜 검증: 잘못된 날짜(예: 2월30일)나 잘못된 좌표/타임존은 400 에러를 반환하며 과금되지 않습니다.
- 상승궁 계산: 출생 시각 +
birthTimePrecision(exact/approximate) + 위도/경도가 모두 필요합니다. 하나라도 없으면ascendant는null입니다. - 풀이 문단 오류:
readings: true인데 계정에 미개방이면 404natal_readings_not_enabled, 문단을 불러오지 못하면 503natal_readings_unavailable— 둘 다 과금되지 않습니다. - 태양/달/상승궁: 각 행성 및 각도는 트로피컬 황도(서양 점성술 표준)를 기준으로 합니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 404 | natal_readings_not_enabled | readings: true인데 풀이 문단이 열려 있지 않음 |
| 503 | natal_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(정오 기준)입니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
houseSystems | string[] | placidus · whole_sign · equal · porphyry 중 고른 것. 생략하면 ["placidus"]입니다. 극권 출생지(위도 약 66.56° 이상, angles.polar)에서는 플라시두스 대신 포르피리를 계산하고 그 결과에 "fallbackFrom": "placidus"를 붙입니다(플라시두스를 직접 지정해도 같음) | |
minorAspects | boolean | 세미섹스타일(30°)·세미스퀘어(45°)·세스퀴쿼드레이트(135°)·퀸컹스(150°) 포함(네이탈·시너스트리). 기본 false — 메이저 5각만 | |
orbs | string | 네이탈·시너스트리 오브 표: leo1928(기본 — 합·충 해–달 12°, 해/달–행성 10°, 행성끼리 8°, 스퀘어·트라인 8°, 섹스타일 7°, 세미스퀘어·세스퀴 4°, 세미섹스타일·퀸컹스 2°) · heindel1919(메이저 단일 6°, 보수적) | |
node | string | 규칙 키의 Nn(북교점)·Sn(남교점 = 북교점 + 180°)이 따를 교점: mean(기본, 평균 교점) · true(진 교점). features의 node_sign Nn_…·Sn_…와 교점 합 키(Mo_Nn_cj·Nn_Asc_cj 등)에 적용됩니다. MNn_…·TNn_… 키는 이 값과 상관없이 둘 다 나옵니다 | |
lang | string | signName(별자리 이름) 언어 — ko(기본) · en · ja · th | |
timing | object | 보내면 트랜싯 + 2차 진행을 계산해 응답 timing에 넣습니다. 아래 표 | |
partner | object | 상대의 출생 입력(위와 같은 필드, lang 제외). 보내면 두 사람 사이 각·별자리 관계를 응답 synastry에 넣습니다. 상대의 출생 시각과 좌표(birthLatitude·birthLongitude)가 있으면 상대 상승점(Asc)·중천(MC)도 계산해 교차 각에 넣습니다 |
timing 블록 안:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
orbDegrees | number | O | 트랜싯→출생, 진행→출생 각의 오브(도) 0.5~10. 모든 천체 쌍에 같은 값을 씁니다(메이저 5각만) |
at | string | 보고 싶은 시점. YYYY-MM-DD(= 그날 00:00 UTC) 또는 오프셋을 붙인 일시(2026-10-11T09:00:00+09:00, ...Z). 오프셋 없는 일시는 400. 생략하면 지금 | |
progressions | boolean | 2차 진행 계산 (기본 true) | |
yearLength | string | 경과 시간을 "인생 몇 년"으로 바꿀 때 쓸 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 키 자체가 없습니다.
bodies[]: 태양·달·수성·금성·화성·목성·토성·천왕성·해왕성·명왕성.longitude는 황경(0° = 춘분점, 트로피컬),speed는 도/일 — 음수면retrograde: true. 천체마다signName(lang언어 별자리 이름) ·degreeInSign(별자리 안 도수 0~30°) ·house(요청한 하우스 체계별 하우스 번호,houses[체계].bodies와 같은 값 — 하우스를 계산하지 않으면{})가 바로 붙어 있어 이것만으로 차트 표를 그릴 수 있습니다.dignities: 태양~토성의 품위(dom본궁 ·exa고양 ·det손상 ·fal추락, 해당 상태 전부).bodies[].dignities와 같은 값입니다.nodes[]:MeanNode(평균 교점) ·TrueNode(진 교점) = 북교점,south= 남교점(+180°).angles: ASC·MC·DSC·IC. 출생 시각(exact/approximate)과 좌표가 둘 다 있을 때만 계산합니다. 없으면null이고anglesOmitted가 이유(time_unknown|coordinates_missing)를 알려 줍니다. 이때 하우스도 계산하지 않아houses는{}입니다.houses: 요청한 체계별cusps(1~12하우스 시작 황경) +bodies(천체별 하우스 번호).houseSystems·houses: 실제로 계산한 체계. 극권 출생지(angles.polar= 위도가 약 66.56° 이상)에서는 플라시두스가 정의되지 않을 수 있어 포르피리로 대신 계산하고houses.porphyry.fallbackFrom = "placidus"로 표시합니다(몰래 바꾸지 않습니다). 판정은 출생지 위도로 하므로 같은 출생지는 시각과 상관없이 늘 같은 체계를 받습니다.housesExcluded는 그 밖에 계산할 수 없었던 체계와 사유입니다(에러 응답이 아닙니다).aspects[]:orb= 정각에서 벗어난 도수,strength=exact(2° 이내) ·close(한계의 절반 이내) ·wide,applying= 정각을 향해 다가가는 중이면true(모르면null),outOfSign= 별자리 경계를 넘은 각(한 단계 약하게 읽습니다).moonSignUncertain: 시각 미상일 때 그날 안에 달 별자리가 바뀌면true— 이때features의 달planet_sign에"uncertain": true가 붙습니다.readings(항상 포함, 추가 요금 없음): HRUM이 미리 써 둔 풀이 문단.dignities[]= 품위(태양~토성, 천체마다 가장 센 상태 하나),aspects[]= 출생 천체 사이 각을 오브가 좁은 순서로. 각 문단은key(features·aspects[].key와 같은 키) ·text·lang(실제 문단 언어 — 요청 언어 문단이 없으면ko) ·deviation(각만, 정각에서 벗어난 도수). 미리 써 둔 문단이 있는 키만 붙고 나머지는 건너뜁니다(즉석으로 지어내지 않음) — 천왕성·해왕성·명왕성이 낀 각과 마이너 각은 문단이 없어 빠집니다. 출생 시각이 없으면 달이 낀 각, 달 별자리가 불확실하면 달 품위는 붙지 않습니다. 위 예시(1990-05-15 14:30 서울)는 애스펙트 16개 중 7개 + 품위 4개 = 11문단입니다.readingsUnavailable: 풀이 문단을 불러오지 못한 경우true— 이때도 차트 계산 결과는 그대로 오고readings는 빈 배열이며 요금(₩50)도 그대로입니다.features[]: 규칙 DB 매칭용 키.planet_signSu_ari·planet_retrogradeMe_rx·dignityMa_dom·node_signMNn_leo·TNn_leo·Nn_leo·Sn_aqu(Nn·Sn은node옵션의 교점) ·angle_signAsc_sco·planet_houseVe_h7(+system) ·aspectSu_Mo_cj(교점은 합만 3° —Mo_Nn_cj·Sa_Sn_cj·Nn_Asc_cj, 상승점 쌍은 출생 시각이 있을 때만) ·house_cusp_signh12_ari(12하우스 커스프가 양자리) ·house_emptyh6(행성 10개 중 하나도 없는 하우스) ·house_ruler_househ1_h7(1하우스 주인이 7하우스에) ·house_ruler_signh7_lib(7하우스 주인이 천칭자리에) — 하우스 주인은 커스프 별자리의 전통 주인(태양~토성 7행성) ·planet_risingJu(1하우스, 또는 12하우스에서 상승점 6° 이내) — 이 다섯 종류는+system, 출생 시각과 좌표가 있을 때만 ·planet_solarVe_cazimi(태양과 17′ 이내) /Me_combust(17′~8°30′, 태양과 같은 별자리) /Ma_beams(그 밖 17° 이내) — 세 단계는 겹치지 않습니다 ·moon_phasewaxing·waning·metatime_known(시각 있음 —exact·approximate) /time_hour_level(approximate일 때 추가) /time_unknown. 각 키(aspect·transit·progressed·syn_aspect)에는orb(정각에서 벗어난 도수, 소수 둘째 자리)와, 각이 별자리 경계를 넘을 때만"outOfSign": true가 붙습니다 — 규칙 키 속성[orb=…]·[outOfSign=true]와 맞추는 값입니다. 출생 시각을 모르면 달이 낀 출생 각에"uncertain": true가 붙습니다. 천체 약어Su Mo Me Ve Ma Ju Sa Ur Ne Pl, 교점MNn TNn Nn Sn, 각 코드cj(0°)sx(60°)sq(90°)tr(120°)op(180°) · 마이너ssx(30°)ssq(45°)sqq(135°)qcx(150°).
요청 예시 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
}
}
- 트랜싯 대상(
natalPoints): 출생 천체 10개 + 출생 시각·좌표가 있으면 상승점(Asc)·중천(MC). 키는t(트랜싯)·n(출생) 접두사 —tSa_nSu_cj= 트랜싯 토성이 출생 태양과 합. transit_housetSa_h7(트랜싯 토성이 출생 7하우스에) ·progressed_housepMo_h4(진행 달이 출생 4하우스에): 행성 10개씩. 출생 하우스는 기본 체계(플라시두스, 극권이면 포르피리 +"fallbackFrom": "placidus")이고 키에"system"이 붙습니다 —houseSystems를 다르게 보내도 이 두 키는 기본 체계 기준입니다. 출생 시각이나 좌표가 없으면 나오지 않습니다.- 진행→출생 각은 출생 행성만 대상입니다. 키
pSu_nJu_tr= 진행 태양이 출생 목성과 트라인.instant는 진행 차트로 쓴 하늘의 시각(출생 +ageYears일)입니다. progressions: false면timing.progressions는null입니다.yearLength를 생략하면tropical(365.24219일)입니다.progressions가 켜진 상태에서timing.at이 출생 시각보다 앞서면 400at_before_birth(과금 없음).- 시각 미상이면 출생 차트를 정오 기준으로 잡습니다. 출생 달이 대상인 키(
…_nMo_…)와 진행 달 키(pMo_…)에는"uncertain": true가 붙습니다(진행 달은 출생 달 위치를 그대로 물려받아 정오 기준만큼 흔들립니다).
요청 예시 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
}
}
- 키 접두사
a= 요청한 본인,b=partner.aVe_bMa_cj= 본인 금성이 상대 화성과 합. 본인 천체 10개(+상승점·중천) × 상대 천체 10개(+상승점·중천) 전부를 봅니다(방향이 있어aSu_bMo와aMo_bSu는 다른 키). - 상승점(
Asc)·중천(MC)은 그 사람의 출생 시각과 좌표가 둘 다 있을 때만 계산합니다(본인과 같은 규칙, 상대는synastry.partner.angles/anglesOmitted). 앵글이 낀 교차 각은 합·충만 6°로 봅니다. 단 상승점끼리의 합(aAsc_bAsc_cj)만 17°까지 냅니다(프톨레마이오스 『테트라비블로스』 — 두 상승점이 17° 안이면 결속이 강하다, 규칙[orb=17]). 실제 벌어진 도수는orb로 옵니다. 앵글은 그 앵글을 가진 사람의 시각만 확정이면 계산합니다. 하강점·천저는 상승점·중천의 정반대라 따로 키를 내지 않습니다. astro_syn: 고전 6쌍(태양–달, 금성–화성, 달–금성, 태양–금성, 달–화성, 금성–토성)의 별자리 관계 중 가장 강한 2개. 두 방향을 한 키로 접습니다(Su_Mo_se= 한쪽 태양과 다른 쪽 달이 같은 원소).syn_sign: 본인 점 × 상대 점 전부의 별자리 관계(aAsc_bAsc_ss). 두 사람 모두 출생 시각이exact이면 행운점(PoF, 낮/밤 공식 반전)도 점에 넣습니다(aPoF_bPoF_ss) — 행운점은 상승점과 함께 4분에 약 1°씩 움직여 시 단위(approximate)나 시각 미상에서는 내지 않습니다. 관계 코드ss(같은 별자리) ·op(대극) ·se(같은 원소) ·es(원소 지지) ·sm(같은 양상) ·ef(원소 마찰) ·nt(무관).- 오브 표와 마이너 각 여부는 네이탈과 같은
orbs·minorAspects를 씁니다. 두 출생 차트 사이라 접근·분리가 없어applying은 항상null입니다. - 어느 한쪽이라도 출생 시각을 모르면 그 사람 차트는 정오 기준입니다. 그 사람의 달이 낀 각에는
features에"uncertain": true가 붙습니다(달은 하루 약 13° 움직여 오브보다 넓게 흔들립니다). 상대 앵글과 짝지은 각(aAsc_bMo_…, 반대 방향aMo_bAsc_…)도 같습니다. - 상대의 하우스는 계산하지 않습니다(시너스트리 하우스 겹침 미제공).
기본값과 직접 골라야 하는 값
- 하우스 체계(기본
placidus): 1819·1929 사전이 "통용"이라 적은 체계입니다. 극권 출생지에서는 포르피리로 대신하고fallbackFrom으로 표시합니다. 다른 체계가 필요하면houseSystems로 고르세요. - 진행 1년 길이(기본
tropical365.24219일): 원전의 "생일→다음 생일" 1년입니다. 율리우스년(julian)과의 차이는 80세에 진행 시각 약 2.5분입니다. - 트랜싯·진행 오브(
timing.orbDegrees,timing을 보내면 필수): 원전끼리 다릅니다(정각 전 8° 대 10° 구간). 비워 두면 400입니다. - 명왕성·교점·앵글이 낀 네이탈 각은 기본 오브 표에서 빠집니다: 레오(1928)·하인델(1919) 오브 표가 다루지 않는 점입니다(명왕성은 1930년 발견). 그래서 네이탈
aspects에는 이 점들이 나오지 않습니다(위치·하우스에는 나옵니다). 시너스트리의 앵글 교차 각만 합·충 6°(상승점끼리 합만 17°)로 봅니다.
제공하지 않는 것
- 마이너 품위(삼분·경계 term·얼굴 face), 리셉션, 네이탈 행운점(시너스트리
syn_sign에만 씀), 천왕성·해왕성·명왕성 품위, 패럴렐·중간점 - 진행 앵글(진행 ASC·MC) — 원전이 방법을 정하지 않음
- DSC·IC는 값은 주지만
features키는 내지 않음(남교점은Sn키로 나옴) - 신월·보름·상현·하현 달 위상 키(
moon_phase는waxing·waning만), 태양 동방·서방(oriental·occidental), 진행 앵글 키 - 시너스트리 하우스 겹침
요금
- 만세력과 같은 1회 ₩50 —
timing·partner블록을 추가해도, 하우스 체계·마이너 각 여부와 상관없이 같은 ₩50 한 번입니다. 응답costPoints가 이번 차감액(₩)입니다. 사용량 로그 endpoint는astrology입니다. - 계산에 성공했을 때만 차감합니다. 입력 오류(400)·잔액 부족(402)은 과금되지 않습니다.
- 아직 열리지 않은 상태에서 호출하면 404
astrology_not_enabled(과금 없음)입니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 404 | astrology_not_enabled | 계정에 기능이 열려 있지 않음 |
| 400 | at_before_birth | timing.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
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
birthDate | string | O | YYYY-MM-DD (1900~2100). calendar 기준 날짜 |
birthTime | string | null | HH:mm. 없으면 시주 미상 — 여섯 글자 기준으로 계산하고 hourKnown: false | |
calendar | string | O | solar · lunar (음력은 양력으로 바꿔 계산) |
isLeapMonth | boolean | 음력 윤달 (기본 false) | |
gender | string | null | male · female. 없으면 대운 층이 없습니다(daeun: [], daeunIndex: null) | |
timezone | string | Asia/Seoul만 받습니다 | |
range | object | O | { "fromYear", "toYear" } 세운 범위(양력 연도, 최대 101년). 출생연도 ~ 출생연도+100을 한 번 받아 두고 화면에서 잘라 쓰는 방식을 권합니다 |
monthsOfYear | integer | null | 월운 12칸을 계산할 사주 연도(그해 입춘부터 다음 해 입춘 전까지). 생략하면 지금(한국 시각)이 속한 사주 연도 — 1월 1일~입춘 전에는 지난해. null이면 월운 층 없음(months: []) | |
lang | string | ko · 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
}
}
필드 읽는 법
line: 누적값 — 대운은 원국 + 대운, 세운은 원국 + 그해 대운 + 세운. 매 단계 ±100으로 자르고 잘렸으면clamped: true(R-SAJU-UN-033). 월운은 누적선이 없고 그 달 운 몫(delta)만 줍니다.share: 글자 하나의 몫 분해(지원·통제·지장간·뿌리). 층 무게는 대운·세운·월운 1:1:1(R-SAJU-UN-030), 운 지지 뿌리는 상한 없음(R-SAJU-UN-031).shinRole: 그 글자 오행의 5신 이름(용신·희신·기신·구신·한신, R-SAJU-YONG-170) — 점수가 아니라 이름입니다. 모든 명식에 5신 표가 나옵니다(null없음).relations: 운 글자와 원국 글자의 형충회합 목록(점수에 넣지 않음).ruleId는 짝 표 규칙,decisionRuleId는 운에도 적는 근거 규칙(UN-035 월운, UN-036 자형, UN-037 반합)입니다.withDaeun은 세운 ↔ 그해 대운입니다. 시주 미상이면 시주와의 관계는 내지 않습니다.triples: 대운·세운 글자가 끼어 원국과 함께 삼합·방합 세 글자가 찬 경우(R-SAJU-UN-037).hapPulim: 대운·세운 글자가 건드린 원국 합(합기·풀림·깨짐)과 새로 맺은 합(R-SAJU-HAP-021~027). 월운은 넣지 않습니다. 화면 문구는reasonCode와params(incoming들어온 운 글자,origindaeun|seun, 필요할 때target·partner)로 앱 언어에 맞게 직접 만드세요.reasonFallback은 엔진이 쓴 한국어 문장으로 항상 들어 있으며, 앱이 모르는reasonCode가 왔을 때만 대신 보여 주는 용도입니다.reasonCode는 지금 7가지입니다.stem_hap_kept_jaenghap: 원국 천간합이 그대로 유지되고, 같은 양간 운 글자(incoming)는 쟁합이라 끼어들지 못함.stem_hap_kept_tuhap: 원국 천간합이 그대로 유지되고, 같은 음간 운 글자(incoming)는 투합이라 끼어들지 못함.branch_hap_gi: 운 글자(incoming)가 원국 육합의 한 글자(target)를 충해 합이 일어남(합기).branch_hap_released: 운 글자(incoming)가 원국 육합의 한 글자를 형해 합이 풀림.branch_hap_broken: 원국 육합의 한 글자와 같은 글자(incoming)가 운으로 들어와 합이 깨짐.new_stem_hap: 운 천간(incoming)과 원국의 빈 천간(partner)이 새 천간합을 맺음.new_branch_yukhap: 운 지지(incoming)와 원국 지지(partner)가 새 육합을 맺음.
startAge: 대운수(첫 대운 나이). 날수는 출생일(한국 날짜) 0시부터 절입까지 세고 태어난 시·분은 넣지 않으며, 3일 = 1년으로 반올림합니다(R-SAJU-UN-038).kbVersion2026.10.11-2부터 원장에 있는 규칙입니다.ipchunNote: 조회 시점(한국 시각)이 그해 입춘 전이면, 올해 칸에만true. 1월 1일~입춘 전에는 아직 지난해 세운이 이어지고 있다는 표시입니다. 세운은 그해 입춘 간지입니다(R-SAJU-UN-016).birthBeforeIpchun: 1월 1일~입춘 전에 태어나 원국 연주가 앞 해 간지인 경우true(최상위 필드).natalAmbiguous: 시각 미상인데 출생일에 절입이 있으면{ "pillar": "month" | "year", "termAt" }— 엔진은 정오로 판정하므로 월주(입춘이면 연주까지)가 실제와 다를 수 있다는 표시입니다.compatInputs: 여러 사람을 겹쳐 볼 때 앱이 집합 교차만 하도록 주는 값(용신 오행, 겉 글자 오행 개수, 겉에 보이지 않는 오행). 사람끼리 합산·순위는 만들지 않습니다.
요금
- 준비 중인 기능입니다. 열리지 않은 상태에서 호출하면 404
saju_flow_not_enabled(과금 없음)입니다. - 계산에 성공했을 때만 차감합니다. 입력 오류(400 — 없는 날짜, 범위 101년 초과 등)는 과금되지 않습니다. 응답
costPoints가 이번 차감액(₩), 사용량 로그 endpoint는saju-flow입니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 404 | saju_flow_not_enabled | 계정에 기능이 열려 있지 않음 |
| 400 | INVALID_DATE | birthDate 형식 오류 또는 없는 날짜 |
| 400 | INVALID_LUNAR_DATE | 없는 음력 날짜 |
| 400 | INVALID_RANGE | toYear가 fromYear보다 앞섬, 또는 101년 초과 |
| 400 | INVALID_TIME | birthTime이 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 / personABirthLongitudepersonBBirthLatitude / personBBirthLongitude |
number | 출생지 좌표(둘 다 함께). 상승궁 계산과 사주 진태양시 보정에 같이 쓰입니다(2026-10-11부터 사주에도 적용). 없으면 상승궁은 personXCountry, 그것도 없으면 lang 의 나라 대표 좌표로 추정(ko→한국, ja→일본, th→태국 / en은 상승궁 생략) |
|
personABirthTimezoneOffset / personBBirthTimezoneOffset |
number | UTC 오프셋(분), 기본 540(KST). 좌표와 함께 보내면 사주 보정의 표준 자오선도 이 값으로 정합니다(생략하면 경도에서 가장 가까운 시간대) | |
personACalendar · personAIsLeapMonthpersonBCalendar · 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 그대로).
- 시각 보정: 한 사람의 사주(일주·8글자 점수·종합 점수·속궁합 기호표·띠·권위자 분석·
yearFlow·관계 서술)는 한 번 계산해 모든 항목에 같이 씁니다. 좌표·personXCountry·정확도exact를 보내지 않으면 입력 시각 그대로 계산합니다(만세력 API와 동일). INTA 앱(한국어, 시각 직접 입력)과 같은 궁합이 필요하면 두 사람 모두personXCountry: "KR"(+personXBirthTimePrecision: "exact")를 보내세요. 출생 시각이 없는 사람은 보정하지 않습니다. - 2026-10-11 변경: 좌표를 보내면 사주도 진태양시 보정을 합니다(그전에는 별자리 궁합에만 썼습니다). 한 응답 안에서 사주와 별자리가 같은 시각 기준을 쓰도록 맞춘 수정이라, 좌표를 보내던 호출은 사주 점수·일주가 바뀔 수 있습니다. 새 필드와 좌표를 보내지 않는 호출의 응답은 그대로입니다.
응답 예시
아래는 실제 엔진 출력입니다 (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"
}
}
saju(사주 궁합)이 메인입니다. 일주를 계산할 수 없는 드문 경우null— 그때는tti만 보여 주세요saju.score.total: 0~100. 일주 궁합표 점수와 8글자 분석 점수의 평균(breakdown에 각각).grade는 쉬운 말 등급 — 아주 잘 맞아요(85+)·잘 맞아요(75+)·무난해요(65+)·맞춰 가면 좋아요(55+)·서로 많이 달라요. 이 값은 예전과 같습니다 — 기호표·별자리까지 합친 점수는headerBreakdown.averagesaju.axes: 끌림·안정·성장·소통·열정·지속 6축(0~100),purpose에 따라 달라짐.saju.pattern: 관계 유형 이름과 설명(없으면null)saju.elements: 두 사람 원국 전체(시간을 모르면 6글자 —basis: "six")의 오행 분포로 계산.bProvidesToA= A에게 없는 오행 중 B가 가진 것saju.tenGods: A 기준으로 본 B의 십신(aView)과 그 반대(bView).highlights: 8글자에서 찾은 교차 신살 문장(최대 5개, 없으면 빈 배열)classic1995: 참고 문헌(1995) 남녀 일주 속궁합표의 기호(●대길 100 ·◎중길 90 ·○길 80 ·△무해무덕 70 ·×흉 60 ·××대흉 50). 이 표는 남자 일주 × 여자 일주 방향표라, 동성이거나 성별을 안 보내면 두 방향(A를 남자로 · B를 남자로)을 모두 읽어score는 두 점수의 평균(예 95),symbol·grade는 그 평균을 10점 단위로 내린 등급(예 95 →◎중길)으로 옵니다. 두 방향이 같으면 그 기호 그대로.conflictFrame: 기호는 길한데 띠 관계는 흉하거나 그 반대일 때true. 일주를 못 구하면 전체nullastroSynastry: 별자리 궁합.layers= 태양·달·상승궁 3층 비교,purposeBodies= 용도별 우선 행성(연애: 금성·화성, 결혼: +토성, 우정: 수성·목성 …).relation=same_sign·opposite·same_element·element_support·same_modality·element_friction·neutral,text는 그 뜻풀이(lang).score0~100.caveats/caveatTexts: 상승궁·달 별자리가 추정이면 그 사실. 시간을 모르면 상승궁 층이 빠집니다astroReadings: 두 사람 별자리에서 가장 두드러진 접점(태양–달, 금성–화성 등) 최대 2개에 대해 미리 써 둔 풀이 문단. 없으면 빈 배열. 각 문단의lang은 실제 문장 언어(요청 언어 문장이 없으면ko)headerBreakdown: 6축 평균 · 권위자(authority) · 8글자(full8) · 기호표(classic) · 별자리(natal) 다섯 점수를purpose별 가중치(weights)로 합친 종합 점수average(0~100). 빠진 점수는 남은 것끼리 가중치를 다시 나눕니다. INTA 앱 궁합 화면의 큰 숫자와 같은 값. 일주 궁합표를 못 찾으면null- 생년월일의 연도는 1900~2100 — 벗어나면 400
INVALID_BIRTH_YEAR(과금 없음). 날짜 입력 칸에 연도를 두 자리로 치면0090처럼 들어가니 화면에서도 막으세요. 궁합표에서 결과를 못 찾으면 400COMPAT_UNAVAILABLE(과금 없음) tti.tier: 참고 문헌(1995) 띠궁합의 등급 문자열(예: 대길·길·중길·보통·주의·흉).tti.relation.type= 두 띠 지지의 관계 —충·원진·형·해·육합·반합·파중 하나(여러 관계가 겹치면 이 순서로 앞선 것), 없으면null.detail은 반합일 때만(예: 수국(水局)).tti.relation.relations= 겹치는 관계 전부(같은 순서, 예: 소·양 띠["충","형"], 없으면 빈 배열)tti에는 띠·관계·등급만 옵니다. 참고 문헌의 띠궁합 문장은 싣지 않습니다(2026-10-11부터).categoryNarrative: 이 관계(purpose)에서 두 사람이 어떤지 풀어 쓴 2~3문장. 두 사람의 일주 조합(1,830쌍)과 관계 종류 6가지마다 미리 써 둔 문장이라 같은 입력엔 항상 같은 문장이 나옵니다. 문장 속나= A,상대= B (영어You/Your partner, 일본어あなた/相手, 태국어คุณ/คู่ของคุณ).lang으로 4개 언어, 문장을 찾지 못하면null(과금은 동일)- 항상 포함 (2026-10-10~):
saju.relationRatings= 관계 5종별{ key, stars }(key예: 연애, 결혼·동거, 친구, 사업 파트너, 장기 관계 / 별 1~5) ·saju.meetingKeywords= 만남 키워드 3개 ·saju.authority= 권위자 궁합 판정(finalScore·grade·gradeTier·pattern·summary·levels(L1~L4)·factors·flags·climateBalance) ·tti.conflictFrame=classic1995.conflictFrame과 같은 값 yearFlow.years[].tier:great·good·neutral·caution.dayFlow.days[].tier:golden·good·neutral·dim·hard(dayFlow.golden/hard는 해당 날짜 목록)
옵션 예시 (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 }, ... }
}
}
주의사항
- 기본 정보: 두 사람의 출생일은 필수입니다. 시간과 성별은 선택사항입니다. 성별이 없거나 같아도 속궁합 기호표는 나오고(두 방향 합산), 별자리 궁합의 상승궁은 출생 시간이 있어야 나옵니다.
- 결과 구성: 별자리 궁합은 트로피컬 황도 기준입니다. 관계별 서술은
categoryNarrative, 별자리 문단은astroReadings로 함께 오고, 더 긴 맞춤 풀이가 필요하면 결과를/reading질문에 넣어 쓰세요. - 호환: 2026-10 추가 필드(
classic1995·astroSynastry·astroReadings·headerBreakdown·lang, 2026-10-10의saju.relationRatings·saju.meetingKeywords·saju.authority·tti.conflictFrame)는lang을 보내든 안 보내든 항상 덧붙습니다. 기존 필드의 값은 그대로입니다. 단, 2026-10-11부터 참고 문헌(1995)의 문장을 옮긴 필드 — 띠 궁합 한 줄 풀이와 일주 사전의 애정운·건강운 — 는 응답에서 빠졌습니다. - 비용: 호출당 ₩20이 차감됩니다 (추가 필드·
details·flow·MBTI 옵션 포함, 추가 요금 없음).
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | INVALID_BIRTH_YEAR | 두 사람 중 출생 연도가 1900~2100 밖 |
| 400 | INVALID_LUNAR_DATE | 없는 음력 날짜 |
| 400 | COMPAT_UNAVAILABLE | 이 일주 조합의 궁합 자료가 없음 |
| 402 | INSUFFICIENT_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
}
}
가격
- 무료 버전 (free): 24문항, ₩0 (무료)
- 프리미엄 (premium): 93문항, ₩10/회
- 없는
questionId나 1~5 밖의answer는 400이며 과금되지 않습니다.
에러 코드
공통 에러(401·400 본문 검사·402·429·503 등) 외에 이 API에서 나오는 코드:
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | (없음) | 문항 조회 — tier가 free·premium이 아님 / 제출 — 없는 questionId |
| 402 | INSUFFICIENT_BALANCE | 제출 — premium인데 잔액 부족 |
답이 1~5 밖이면 공통 400입니다. 모두 과금되지 않습니다.
해석 DB (KB) API
신청한 계정에서만 열리는 API입니다. 열리지 않은 계정은 세 경로 모두 404 kb_saju_explain_not_enabled를 받습니다(과금 없음). 사용하려면 지원으로 문의하세요.
사주 원국에서 성립하는 관계(합·충 등)와 구조(십신·지장간 등)를 규칙 카드와 해설 문장으로 풀어 줍니다. LLM을 쓰지 않아 같은 입력에는 항상 같은 결과가 나옵니다.
POST /api/v1/partners/v1/kb/saju/explain
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
birth | object | O | 만세력 API 요청 본문과 같은 필드 (year·month·day·hour?·minute?·timezone·gender·calendar? …). birth.lang은 무시됩니다 |
sections | string[] | relations · structure · all. 생략하면 전부(relations → structure 순) | |
level | string | L1(기본) · L3. L3 해설이 없는 항목은 L1로 채우고 fallbackLevel을 붙입니다 | |
lang | string | ko만 지원 | |
channel | string | stable(기본, 검수된 해설만) · 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": []
}
}
interp는 해설 칸(meaning뜻 ·why근거 ·pitfall주의)입니다. 칸마다{text, reviewed, reviewedBy?, notes?}이고,stable에서는 검수된 칸만 나옵니다.unregistered: 성립했지만 해설이 아직 없는 항목.pendingReview: 해설은 있지만 검수 전이라stable에서 숨긴 항목.- 규칙 카드의 원전 출처는 아래
GET /kb/rules/{id}로 조회합니다.
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 | 코드 | 언제 |
|---|---|---|
| 404 | kb_saju_explain_not_enabled | 신청하지 않은 계정 (세 경로 공통) |
| 400 | INVALID_LUNAR_DATE · INVALID_LOCATION | explain — 없는 음력 날짜 / 위도·경도 중 하나만 보냄 |
| 400 | KB_DOMAIN_UNKNOWN | release — 지원하지 않는 domain |
| 404 | KB_RULE_NOT_FOUND | rules — 없는 규칙 id |
| 503 | KB_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 | 코드 | 언제 |
|---|---|---|
| 429 | GUEST_QUOTA_EXCEEDED | 같은 IP에서 이번 달 5회를 넘김 (네이탈 차트 게스트와 합산) |
| 400 | INVALID_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에서만 나오는 코드만 적었습니다. 요청은 다음 순서로 검사되며, 앞 단계에서 걸리면 뒤 단계는 실행되지 않습니다.
- 서비스 보호 상한 — 같은 IP에서 오는 요청 전체 분당 상한 (429,
code없음) - API 키 —
X-Partner-Api-Key확인 (401) - 기능 열림 여부 — 계정에 열리지 않은 기능 (404
*_not_enabled) - 요청 제한 — 콘솔에서 정한 초당·일일 제한 (429
RATE_LIMIT_EXCEEDED) - 본문 검사 — 필드 이름·타입·범위 (400)
- 잔액·계산·호출 — 잔액 부족(402), API별 입력 오류, 외부 모델 오류
공통 에러
| HTTP | 코드 | 언제 | 과금 |
|---|---|---|---|
| 400 | (없음) | 본문 검사 실패 — 표에 없는 필드를 보냈거나 타입·범위가 틀림. message는 문자열 배열입니다 | 없음 |
| 401 | (없음) | X-Partner-Api-Key 누락, 잘못된 키, 폐기한 키, 비활성 계정 | 없음 |
| 402 | INSUFFICIENT_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분 뒤 다시 시도 | 없음 |
| 429 | RATE_LIMIT_EXCEEDED | 콘솔에서 정한 초당·일일 제한 초과. 본문 retryAfterSeconds와 Retry-After 헤더가 붙습니다 | 없음 |
| 500 | (없음) | 서버 오류. 이 요청에서 이미 차감된 금액은 응답 뒤 돌려드립니다 | 환불 |
| 503 | SERVICE_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 기준)을 설정하면 키 유출이나 무한 루프로부터 잔액을 지킬 수 있습니다. 비워 두면 위 서비스 보호 상한만 적용됩니다.
- 제한을 설정하면 응답에
X-RateLimit-Limit-Second/X-RateLimit-Remaining-Second/X-RateLimit-Limit-Day/X-RateLimit-Remaining-Day헤더가 붙습니다. - 파트너가 설정한 제한을 넘으면
429+{"code":"RATE_LIMIT_EXCEEDED","retryAfterSeconds":N},Retry-After헤더. 차단된 요청은 과금하지 않습니다. - 게스트(키 없음)는 월 5회 무료 테스트로 고정됩니다.
과금
모든 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 (이미지 생성 · 모델 리셀) | 모델·해상도·품질별 장당 고정가 — 가격표 |
게스트 테스트
- 만세력·네이탈 차트 게스트 엔드포인트: 합산 월 5회 무료 (IP 기반 제한)
- 로그인 없이 플레이그라운드에서 테스트 가능
- 잔여 횟수는 응답에 포함됨 (guestRemaining)
멱등성
모든 성공한 요청은 각각 과금됩니다. 같은 요청을 다시 보내면 다시 계산·생성하고 다시 과금하므로, 재시도 중복이나 동일 입력의 재사용은 클라이언트가 응답을 저장해 처리하세요 (예: 요청 해시를 키로 한 캐시).
X-Idempotency-Key 헤더(최대 128자 — 더 길면 앞 128자만 기록)는 여전히 받을 수 있지만 과금을 막지 않습니다. 보낸 키는 사용량 로그의 idempotencyKey에 그대로 기록되어, 어떤 요청이 어떤 호출로 처리됐는지 파트너 쪽 주문·작업 ID와 대조하는 용도로 쓸 수 있습니다.
지원
연동 중 막히는 부분이나 응답이 문서와 다른 경우 알려주세요.
| 문의 | 연락처 |
|---|---|
| 기술 지원 · 장애 신고 · 제휴 | [email protected] |
문의 시 아래를 함께 보내주시면 확인이 빠릅니다.
- 호출한 엔드포인트와 시각(타임존 포함)
- API 키 앞 12자리 (
float_live_포함, 전체 키는 절대 보내지 마세요) - 받은 응답의 HTTP 상태코드와 본문