API 레퍼런스
기본 URL: https://trendy.storydot.kr/superapp/api ·
인증: X-Client-Id + X-API-Key 헤더(인증 참고) ·
모든 응답은 { "success", "data", "message" } 표준 봉투(envelope)로 감쌉니다.
pairwiseSub)입니다.
내부 식별자(civilId/userId)는 이 API 표면에 절대 나타나지 않습니다.파트너(FI) 엔드포인트
POST/fi/score/query — 단건 점수 조회
ACTIVE 동의가 있는 주체의 점수·등급·사유코드를 반환합니다(소프트 풀). 동의가 없으면 거부됩니다.
요청
{
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"purpose": "UNDERWRITING"
}
purpose 예: UNDERWRITING / ACCOUNT_REVIEW / PREQUALIFY
응답 200
{
"success": true,
"data": {
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"score": 712,
"scoreMin": 0,
"scoreMax": 850,
"tier": "GOOD",
"pullType": "SOFT",
"reasonCodes": [
{
"code": "RC_LOW_UTILITY_PAYMENT_RELIABILITY",
"labelEn": "Low utility bill payment reliability",
"labelLocalized": "전기·수도 등 공과금을 제때 낸 기록이 부족함",
"category": "PAYMENT",
"impactPoints": 38,
"rank": 1
},
{
"code": "RC_DID_VC_WEAK",
"labelEn": "Limited verified digital identity",
"labelLocalized": "디지털 신분증(DID)과 인증서 기록이 적음",
"category": "IDENTITY",
"impactPoints": 21,
"rank": 2
}
],
"modelVersion": "scorecard-v2.1",
"scoredAt": "2026-08-20 14:32:11",
"decisionHash": "9f2ab1c4e5d6...64hex",
"environment": "LIVE",
"confidence": "HIGH",
"dataSufficiency": {
"featuresObserved": 18,
"featuresTotal": 23,
"coveragePercent": 78.3
}
},
"message": "Score retrieved"
}
| 필드 | 설명 |
|---|---|
tier | EXCELLENT(750–850) · GOOD(650–749) · FAIR(550–649) · BELOW_AVERAGE(450–549) · POOR(0–449) |
reasonCodes | 사유코드(카탈로그 31종 중), 최대 4건, 영향 순 랭킹 |
decisionHash | 결정 프로비넌스 해시(있을 때만). 해시 대상 canonical 페이로드는 불변 — 메타데이터 필드는 해시 대상 밖 |
environment | "LIVE" | "SANDBOX" — 인증된 키의 환경 |
confidence | "HIGH" | "MEDIUM" | "LOW" — 피처 스냅샷 결측 기반. 스냅샷이 없으면 LOW이고 dataSufficiency 생략 |
contributions | 동의 데이터 카테고리에 contributions가 포함된 경우에만 존재하는 피처 기여 상세 |
거부: 동의 없음/철회/만료/주체 없음은 에러 응답(에러)로 거부됩니다. 어떤 사유든 FI가 주체 존재를 추론할 수 없도록 동일하게 처리됩니다.
POST/fi/decision/evaluate — 정책 판정·대출 조건 산출
동의 게이트를 통과한 점수를 기관이 미리 등록해 둔 정책 규칙과 대출 조건표에 대입해
APPROVE / REVIEW / DECLINE 판정과 조건을 돌려줍니다.
점수 조회 경로를 그대로 재사용하므로 동의·접근로그 규칙은 /fi/score/query와 완전히 동일합니다
(동의가 없으면 여기서도 같은 방식으로 거부).
요청
{
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"purpose": "UNDERWRITING",
"productCode": "MICRO_LOAN_12M"
}
| 필드 | 필수 | 제약 |
|---|---|---|
pairwiseSub | 필수 | 파트너 전용 익명 ID |
purpose | 필수 | 조회 목적. 동의는 (클라이언트, 목적) 단위로 확인됩니다 |
productCode | 필수 | 대문자·숫자·밑줄만([A-Z0-9_]), 1~40자. 위반 시 SW1001 |
응답 200 — 승인
{
"success": true,
"data": {
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"productCode": "MICRO_LOAN_12M",
"segmentKey": "KR:MICRO_LOAN_12M",
"decision": "APPROVE",
"score": 712,
"tier": "GOOD",
"confidence": "HIGH",
"terms": {
"depositRatePct": 10.00,
"creditLimit": 3000000,
"currency": "KRW",
"maxTenorMonths": 12,
"interestAprPct": 14.50
},
"firedRules": [],
"reasonCodes": [
{
"code": "RC_LOW_UTILITY_PAYMENT_RELIABILITY",
"labelEn": "Low utility bill payment reliability",
"labelLocalized": "전기·수도 등 공과금을 제때 낸 기록이 부족함",
"category": "PAYMENT",
"impactPoints": 38,
"rank": 1
}
],
"modelVersion": "scorecard-v2.1",
"scoredAt": "2026-08-20 14:32:11",
"environment": "LIVE"
},
"message": "Decision evaluated"
}
응답 200 — 즉시 거절 (terms는 아예 생략됩니다)
{
"success": true,
"data": {
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"productCode": "MICRO_LOAN_12M",
"segmentKey": "GLOBAL",
"decision": "DECLINE",
"score": 412,
"tier": "POOR",
"confidence": "MEDIUM",
"firedRules": [
{
"ruleCode": "KO_SCORE_FLOOR",
"ruleType": "KNOCKOUT",
"messageEn": "Score below product floor"
}
],
"reasonCodes": [
{
"code": "RC_CASHFLOW_INSTABILITY",
"labelEn": "Cash-flow instability",
"labelLocalized": "수입·지출 흐름이 불안정함",
"category": "FINANCIAL",
"impactPoints": 52,
"rank": 1
}
],
"modelVersion": "scorecard-v2.1",
"scoredAt": "2026-08-20 14:32:11",
"environment": "LIVE"
},
"message": "Decision evaluated"
}
| 필드 | 설명 |
|---|---|
decision | APPROVE · REVIEW(수동 심사) · DECLINE |
segmentKey | 실제로 적용된 세그먼트(예: KR:MICRO_LOAN_12M, KR, GLOBAL) |
terms | 조건표에서 채택된 조건. DECLINE이거나 조건표에 매칭 행이 없으면 생략 |
terms.depositRatePct | 보증금(선납금) 비율 % |
terms.creditLimit · terms.currency | 한도 금액과 3자리 통화코드 |
terms.maxTenorMonths | 최대 대출 기간(개월) |
terms.interestAprPct | 연이율(APR) % |
firedRules | 발화한 규칙 목록(ruleCode, ruleType=KNOCKOUT|REVIEW, messageEn). 없으면 빈 배열 |
score·tier·confidence·reasonCodes·modelVersion·scoredAt | /fi/score/query와 동일한 값(같은 점수 응답에서 그대로 옵니다) |
environment | "LIVE" | "SANDBOX" |
판정 순서
판정은 결정적(deterministic)입니다 — 같은 점수와 같은 설정이면 항상 같은 결과입니다.
- 동의 확인 + 점수 조회:
/fi/score/query와 동일한 경로. 동의가 없으면 같은 방식으로 거부됩니다. - 세그먼트 체인 결정:
{국가}:{productCode}→{국가}→GLOBAL순으로, 구체적인 것이 먼저입니다. 국가 컨텍스트가 없으면GLOBAL하나만 사용됩니다. - 즉시 거절 규칙(KNOCKOUT): 등록 순서대로 평가하여 첫 매치에서 즉시
DECLINE. 조건표는 보지 않고,terms없이 해당 규칙 1건만firedRules에 담아 반환합니다. - 수동 심사 규칙(REVIEW): 매칭된 규칙을 모두
firedRules에 누적합니다(여기서 반환하지는 않음). - 대출 조건표(terms matrix): 세그먼트 체인 순서로, 각 세그먼트마다 기관 전용 행을 먼저 찾고
없으면 공용 행을 찾습니다. 행의 조건은
minScore ≤ score ≤ maxScore그리고confidence가 그 행의minConfidence이상(LOW < MEDIUM < HIGH)입니다. 첫 매치를 채택합니다. - 조건표가 구성되어 있지 않거나 매칭 행이 없으면
REVIEW— 조건 없이 수동 심사로 안전하게 처리합니다 (임의로 승인하지 않습니다). - 채택된 행의 판정을 사용하되, REVIEW 규칙이 하나라도 발화한 상태에서 행이
APPROVE면REVIEW로 낮춥니다. 행이DECLINE이면terms는 생략됩니다.
/admin/hacs/fi-policy-rules,
/admin/hacs/fi-terms). 기관 전용 설정이 없으면 공용 설정이 적용됩니다.규칙 조건에 쓸 수 있는 항목
| 항목(field) | 비교 연산자 | 값 |
|---|---|---|
score | LT · LTE · GT · GTE · EQ · NEQ | 숫자 |
coveragePercent | LT · LTE · GT · GTE · EQ · NEQ | 숫자(응답의 dataSufficiency.coveragePercent) |
tier | EQ · NEQ는 이름 비교, 나머지는 등급 순서 비교(POOR < BELOW_AVERAGE < FAIR < GOOD < EXCELLENT) | 등급 문자열 |
confidence | EQ · NEQ는 이름 비교, 나머지는 LOW < MEDIUM < HIGH 순서 비교 | LOW · MEDIUM · HIGH |
reasonCode | CONTAINS(해당 사유코드 포함) · EQ · NEQ | 사유코드 문자열 |
한 규칙 안의 조건은 모두 만족해야 발화합니다(AND). 형식이 잘못된 규칙은 매치되지 않고 조용히 건너뜁니다.
샌드박스 동작
샌드박스 키로 호출하면 점수는 합성값(샌드박스의 고정 주체 표와 동일)이고, 규칙과 조건표는 실제로 등록된 기관 설정이 그대로 적용됩니다 — 정책을 라이브 전에 그대로 시험해 볼 수 있습니다.
GLOBAL 하나뿐입니다.
국가별 규칙·조건표를 시험하려면 GLOBAL 세그먼트에 동일한 설정을 두고 확인하세요.
SANDBOX-NO-CONSENT·SANDBOX-EXPIRED는 점수 조회와 똑같이 거부됩니다.curl -s https://trendy.storydot.kr/superapp/api/fi/decision/evaluate \
-H "X-Client-Id: YOUR_CLIENT_ID" \
-H "X-API-Key: YOUR_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-d '{ "pairwiseSub": "SANDBOX-TIER-C", "purpose": "UNDERWRITING", "productCode": "MICRO_LOAN_12M" }'
사용량 집계에서는 DECISION 버킷으로 계량되며 호출당 2유닛입니다(GET /fi/usage 참고).
POST/fi/score/batch — 배치 점수 조회
한 번의 호출로 1~100건을 조회합니다(100건 초과 시 검증 에러 SW1001).
건별로 단건과 동일한 동의 검사가 적용되며, 거부 건은 status="DENIED" 한 가지 라벨로만 표시됩니다.
요청
{
"subjects": ["b41c9d2f-8e77-4a10-9c3e-example", "SANDBOX-NO-CONSENT"],
"purpose": "ACCOUNT_REVIEW"
}
응답 200
{
"success": true,
"data": {
"batchId": "3f8a1b62-90cd-4e5f-a217-7c9d1e2f3a4b",
"summary": { "requested": 2, "returned": 1, "denied": 1 },
"results": [
{
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"status": "OK",
"score": 712,
"tier": "GOOD",
"reasonCodes": [
{
"code": "RC_LOW_UTILITY_PAYMENT_RELIABILITY",
"labelEn": "Low utility bill payment reliability",
"labelLocalized": "Low utility bill payment reliability",
"category": "PAYMENT",
"impactPoints": 40,
"rank": 1
}
],
"modelVersion": "scorecard-v2.1",
"scoredAt": "2026-08-20 14:32:11",
"decisionHash": "9f2ab1c4e5d6...64hex"
},
{
"pairwiseSub": "SANDBOX-NO-CONSENT",
"status": "DENIED"
}
]
},
"message": "Batch completed"
}
POST /fi/consent/requests를 사용하세요.POST/fi/verify — 신용등급 증명서(어테스테이션) 검증
사용자가 제시한(QR 스캔 등) 증명서가 발급기관 원본과 일치하는지 확인합니다.
동의 불필요 — 제시받은 값 이상은 아무것도 공개되지 않습니다.
위·변조 증명서도 에러가 아닌 200 응답에 valid=false로 표시됩니다.
요청 — vcId/reputationHash 중 최소 1개 필수, 나머지는 선택 교차검증
{
"vcId": "vc-8c3a51d0-example",
"reputationHash": "0x4e5f6a7b...",
"tierCommitment": "0x1a2b3c...",
"tier": "GOOD"
}
응답 200
{
"success": true,
"data": {
"valid": true,
"reason": "VALID",
"tier": "GOOD",
"status": "ANCHORED",
"issuedAt": "2026-07-01 10:00:00",
"expiresAt": "2026-09-29 10:00:00",
"anchoredTxHash": "0x7d1e...",
"modelVersion": "scorecard-v2.1"
},
"message": "Attestation verification completed"
}
reason | 의미 |
|---|---|
| VALID | 발급 원본과 일치하고 유효기간(발급 후 90일) 이내 |
| NOT_FOUND | 제시된 vcId/reputationHash와 일치하는 발급 기록 없음 |
| REVOKED | 발급 기록이 폐기됨 |
| EXPIRED | 유효기간(90일)이 지남 |
| COMMITMENT_MISMATCH | 제시된 tierCommitment가 발급 기록과 다름(변조 의심) |
| TIER_MISMATCH | 제시된 tier가 발급 기록과 다름 |
GET/fi/score/history — 점수 이력 조회
GET /fi/score/history?pairwiseSub=&fromDate=&toDate= (조회 구간 최대 365일)
동의의 데이터 카테고리에 history가 포함되어야 합니다. 없으면 동의 없음 계열로 거부됩니다.
응답 200
{
"success": true,
"data": {
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"entries": [
{
"score": 712,
"tier": "GOOD",
"modelVersion": "scorecard-v2.1",
"scoredAt": "2026-08-20 14:32:11",
"decisionHash": "9f2ab1c4e5d6...64hex",
"anchorTxHash": "0x7d1e..."
},
{
"score": 688,
"tier": "GOOD",
"modelVersion": "scorecard-v2.0",
"scoredAt": "2026-07-19 09:12:45",
"decisionHash": "5b6c7d8e9f0a...64hex",
"anchorTxHash": null
}
]
},
"message": "History retrieved"
}
GET/fi/whoami — 내 클라이언트 확인
인증만 필요하고 동의는 필요 없습니다. 키가 어떤 클라이언트·환경에 결속됐는지 반환합니다.
{
"success": true,
"data": {
"clientId": "bank-busan-001",
"name": "Example Bank",
"environment": "SANDBOX",
"rateLimitTier": "STANDARD",
"status": "ACTIVE"
},
"message": "OK"
}
GET/fi/usage — 사용량 자기조회
GET /fi/usage?fromDate=&toDate= (조회 구간 최대 92일)
일별·엔드포인트별 호출/유닛 집계를 반환합니다. 유닛 가중치: score/query=1, batch 건당=1, verify=1, history=2, decision=2.
{
"success": true,
"data": {
"clientId": "bank-busan-001",
"days": [
{ "date": "2026-08-20", "endpoint": "QUERY", "callCount": 41, "unitCount": 41 },
{ "date": "2026-08-20", "endpoint": "BATCH", "callCount": 2, "unitCount": 130 },
{ "date": "2026-08-20", "endpoint": "HISTORY", "callCount": 3, "unitCount": 6 },
{ "date": "2026-08-20", "endpoint": "DECISION", "callCount": 9, "unitCount": 18 }
]
},
"message": "Usage retrieved"
}
endpoint 값: QUERY | BATCH | VERIFY | HISTORY | DECISION.
집계는 매일 03:30에 전일 접근로그 기준으로 계산됩니다.
POST/fi/consent/requests — 동의 요청
사용자 앱으로 점수 공유 동의를 요청하는 인앱 알림을 보냅니다.
{
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"purpose": "UNDERWRITING"
}
응답 200: data=null, message="Consent request sent"
GET/fi/consent/{consentReceiptId} — 동의 상태 조회
{
"success": true,
"data": {
"consentReceiptId": "cr-1f2e3d4c-example",
"pairwiseSub": "b41c9d2f-8e77-4a10-9c3e-example",
"clientId": "bank-busan-001",
"purpose": "UNDERWRITING",
"scope": "score",
"dataCategories": "score,tier,reason-codes",
"status": "ACTIVE",
"grantedAt": "2026-08-01 11:20:00",
"expiresAt": "2026-11-01 11:20:00"
},
"message": "Consent status retrieved"
}
status: ACTIVE | REVOKED | EXPIRED. dataCategories는 CSV 문자열이며 기본값은
score,tier,reason-codes, 선택 확장으로 contributions, history,
monitoring이 있습니다(동의 모델 참고).
GET/fi/health — 상태 확인 (공개)
인증 불필요. 개인정보 없음.
{
"success": true,
"data": { "status": "UP", "pullTypes": "SOFT" },
"message": "OK"
}
관리자(운영) 전용 엔드포인트
아래 엔드포인트는 파트너 키가 아닌 관리자 JWT(ADMIN)로만 접근 가능한 내부 운영 API입니다. 파트너 통합에는 필요하지 않으며, 계약의 완전성을 위해 기재합니다.
| 메서드·경로 | 설명 |
|---|---|
GET·POST /api/v1/admin/hacs/fi-policy-rulesPUT·DELETE /api/v1/admin/hacs/fi-policy-rules/{id} | 판정 규칙(KNOCKOUT·REVIEW) 조회·등록·수정·삭제 — /fi/decision/evaluate가 사용하는 설정 |
GET·POST /api/v1/admin/hacs/fi-termsPUT·DELETE /api/v1/admin/hacs/fi-terms/{id} | 대출 조건표 행 조회·등록·수정·삭제 |
GET /api/v1/admin/hacs/fi-usage?clientId=&fromDate=&toDate= | 클라이언트별 사용량 조회 |
POST /api/v1/admin/hacs/fi-usage/aggregate?date= | 특정 일자 수동 재집계 |
GET /api/v1/admin/hacs/fi-webhooks | 웹훅 엔드포인트 목록 |
POST /api/v1/admin/hacs/fi-webhooks | 생성(url, events, clientId) — 서명 시크릿은 서버 생성 후 1회 평문 반환 |
PUT /api/v1/admin/hacs/fi-webhooks/{id} | 상태 변경·재활성(연속 실패 카운터 리셋) |
DELETE /api/v1/admin/hacs/fi-webhooks/{id} | 삭제 |
POST /api/v1/admin/hacs/fi-webhooks/{id}/test | 테스트 이벤트 발송 |
GET /api/v1/admin/hacs/fi-webhooks/{id}/deliveries | 최근 전달 기록 50건 |
GET /api/v1/admin/hacs/fi-access-logs/export?clientId=&fromDate=&toDate= | 접근로그 CSV(헤더: accessedAt,clientId,subjectPairwiseSub,purpose,outcome,returnedTier,modelVersion,environment,batchId,entryHash) |
GET /api/v1/admin/hacs/fi-consents/export?clientId= | 동의 CSV(헤더: grantedAt,revokedAt,clientId,pairwiseSub,purpose,dataCategories,status,receiptId) |