발급기관 온보딩
Issuer onboarding — apply, get reviewed, receive an API key once, then issue credentials under your own institution's name through the delegated issuance API.
교육부·대학·협회 같은 기관이 SuperWallet의 발급기관으로 등록하면, 자기 기관 명의로 증명서(VC)를 발급할 수 있습니다. 학생증·수료증처럼 기관이 원래 발급하던 증서를 지갑 안의 디지털 증명서로 발급하는 용도입니다.
https://trendy.storydot.kr/superapp/api
전체 절차
| 단계 | 누가 | 무엇을 |
|---|---|---|
| ① 신청 | 기관 | POST /public/issuer-onboarding/apply — 인증 없이 접수. 접수번호(id)를 돌려받습니다 |
| ② 심사 | 운영자(관리자) | 신청 내용 검토 후 승인 또는 반려. 반려 시 사유가 기록됩니다 |
| ③ 승인 | 운영자(관리자) | 승인과 동시에 API 키가 1회만 발급되어 기관에 전달됩니다 + 발급자 DID 등재(수동 단계) |
| ④ 발급 | 기관 | POST /issuer/credentials/issue — 기관 명의로 증명서 발급 |
SW1701). 아래 기본 차단(fail-closed) 참고.① 신청
POST/public/issuer-onboarding/apply — 발급기관 신청
인증이 필요 없습니다. 누구나 호출할 수 있는 공개 접수 창구이며, 다른 공개 엔드포인트와 같은 속도 제한(60 req/min, 호출 IP 기준)이 적용됩니다.
요청
{
"orgName": "국립○○대학교",
"orgType": "UNIVERSITY",
"countryCode": "KR",
"contactEmail": "did-admin@univ.example.ac.kr",
"requestedCredentialTypes": ["StudentCard", "CompletionCertificate"],
"purposeNote": "재학생 학생증과 수료증을 디지털 증명서로 발급하려 합니다."
}
응답 200
{
"success": true,
"data": { "id": 41 },
"message": "Issuer onboarding application received"
}
id는 접수번호입니다. 심사 결과는 contactEmail로 연락드리며,
승인 시 clientId와 API 키를 함께 전달합니다.
필드
| 필드 | 필수 | 규칙 |
|---|---|---|
orgName | 필수 | 기관명. 최대 200자. 앞뒤 공백은 제거됩니다 |
orgType | 필수 | GOVERNMENT · UNIVERSITY · ASSOCIATION · COMPANY 중 하나 |
countryCode | 선택 | 영문 2자리(예: KR, LA, VN). 대문자로 저장됩니다 |
contactEmail | 필수 | 이메일 형식, 최대 255자. 소문자로 저장됩니다 |
requestedCredentialTypes | 필수 | 1~10개. 각 값은 최대 100자, [A-Za-z][A-Za-z0-9_.:-]* 형식(영문자로 시작). 중복 금지. 배열 전체의 저장 길이 제한(JSON 500자)도 있으므로 짧은 이름을 쓰세요 |
purposeNote | 선택 | 발급 목적. 최대 1000자 |
credentialType으로 발급을 시도하면 거부됩니다(SW2304).
나중에 종류를 추가하려면 운영팀에 문의하세요.검증 실패: SW1001(HTTP 400). 형식·길이·개수 위반, 중복, 이메일 형식 오류가 모두 여기에 해당합니다.
② 심사 · ③ 승인
심사는 운영자(관리자)가 진행하며, 기관이 호출하는 API는 없습니다. 신청 상태는 4가지입니다.
| status | 의미 |
|---|---|
APPLIED | 접수됨 — 심사 대기. 이 상태에서만 승인·반려가 가능합니다 |
APPROVED | 승인됨 — API 키 발급 완료. 발급자 DID까지 등재되면 발급 API 사용 가능 |
REJECTED | 반려됨 — 반려 사유가 반드시 기록됩니다 |
SUSPENDED | 정지됨 — 발급 API 사용 불가 |
API 키는 승인 시 단 한 번만 전달됩니다
승인 시 기관 전용 파트너 클라이언트가 만들어지고, 발급 권한(credential:issue 스코프)이 부여됩니다.
기관이 받는 것은 clientId와 API 키 한 쌍이며, 이후 모든 발급 호출에 이 두 값을 헤더로 넣습니다
(인증과 동일한 방식).
X-Client-Id: <승인 시 전달받은 clientId>
X-API-Key: <승인 시 1회 전달받은 API 키>
발급자 DID 등재 — 수동 단계
승인 다음에 운영자의 수동 작업이 한 번 더 필요합니다. 기관의 발급자 DID와
공개키(JWK)를 발급자 신뢰 목록에 등재하고, 그 DID를 신청 건에 연결하는 작업입니다.
이 단계는 현재 프로그램으로 자동화되어 있지 않습니다 — 승인 응답에도 이 수동 절차를 안내하는
registryMessage 문구가 함께 담깁니다.
| 기관 유형 | 등재 시 신뢰 등급 |
|---|---|
GOVERNMENT | VERIFIED(검증됨) |
그 외(UNIVERSITY · ASSOCIATION · COMPANY) | PROVISIONAL(잠정) |
등재가 끝났는지는 기관이 직접 확인할 수 있습니다 — GET /issuer/whoami의 issuerDid에
값이 채워져 있으면 등재·연결이 완료된 것입니다. null이면 아직 발급할 수 없습니다.
기본 차단(fail-closed)
발급 API는 조건이 하나라도 갖춰지지 않으면 발급하지 않습니다. 부분 발급이나 임시 발급은 없습니다.
| 차단 조건 | errorCode | HTTP |
|---|---|---|
| 인증 헤더 누락 또는 키 불일치 | SW2102 | 401 |
| 클라이언트가 정지 상태 | SW2101 | 403 |
credential:issue 권한 없음 | SW2304 | 403 |
클라이언트에 연결된 신청 건 없음 / 신청이 APPROVED가 아님 | SW2304 | 403 |
승인 목록에 없는 credentialType | SW2304 | 403 |
| 발급자 DID 미등재 또는 신뢰 목록에 없음 | SW1701 | 500 |
보유자 DID(holderDid) 없음 | SW1402 | 404 |
| 보유자 DID가 활성 상태가 아님(폐기 등) | SW1403 | 403 |
| 보유자 DID에 연결된 사용자 기록 없음 | SW1002 | 404 |
| 입력 검증 실패 | SW1001 | 400 |
| 속도 제한(60 req/min) 초과 | SW1004 | 429 |
SW1701은 기관의 잘못이 아니라 등재가 아직 안 끝난 상태를 뜻합니다.
같은 요청을 반복해도 결과는 같습니다 — 재시도하지 말고 GET /issuer/whoami로 issuerDid를
확인한 뒤 운영팀에 등재 완료를 요청하세요.④ 기관 명의 발급 API
아래 3개 엔드포인트 모두 X-Client-Id + X-API-Key 헤더 인증이며,
응답은 { "success", "data", "message" } 표준 봉투로 감쌉니다.
issuedAt·expiresAt은
ISO-8601 형식(2026-08-26T10:00:00)입니다. 점수(FI) 표면의 yyyy-MM-dd HH:mm:ss와
표기가 다르니 파서를 분리하세요.POST/issuer/credentials/issue — 기관 명의로 증명서 발급
요청
{
"holderDid": "did:sw:0x8f2a41c9d27b6e5a1c0f3d94ab77e5c621d0a4b3",
"credentialType": "StudentCard",
"claims": {
"studentNo": "2026-000412",
"faculty": "Computer Engineering",
"enrollmentStatus": "ENROLLED"
},
"validityDays": 365
}
| 필드 | 필수 | 규칙 |
|---|---|---|
holderDid | 필수 | 증명서를 받을 사용자의 DID 주소(did:sw:0x…). 최대 255자. 활성 상태여야 합니다 |
credentialType | 필수 | 최대 100자, [A-Za-z][A-Za-z0-9_.:-]*. 승인된 목록 안의 값이어야 합니다 |
claims | 필수 | 문자열 → 문자열 맵. 최대 20개, 키 최대 100자, 값 최대 500자. 값에 null 불가 |
validityDays | 선택 | 1~3650. 생략하면 서버 기본값(365일)이 적용됩니다 |
credentialType과 delegatedIssuer는
서버가 발급 시점에 직접 채웁니다. 같은 이름으로 값을 보내면 덮어써집니다.
delegatedIssuer에는 기관의 발급자 DID가 들어가며, 이 값이 "누가 발급했는지"를 나타냅니다.응답 200
{
"success": true,
"data": { "credentialId": 10427 },
"message": "Credential issued"
}
credentialId는 발급된 증명서의 식별자입니다. 발급이 성공하면 사용자의 지갑에서 해당 증명서를 볼 수 있습니다.
주의: 이 API에는 멱등키가 없습니다. 같은 요청을 두 번 보내면 증명서가 두 건 발급됩니다.
네트워크 오류로 응답을 받지 못했다면, 재시도 전에 GET /issuer/credentials로 이미 발급되었는지 확인하세요.
GET/issuer/credentials?page=0 — 우리 기관이 발급한 목록
기관 명의로 발급된 증명서만 조회합니다(다른 기관 발급분은 보이지 않습니다).
페이지 크기는 20건 고정이며, page는 0부터 시작합니다(음수는 SW1001).
응답 200
{
"success": true,
"data": {
"page": 0,
"size": 20,
"totalElements": 137,
"totalPages": 7,
"content": [
{
"credentialId": 10427,
"credentialUid": "vc_9f2ab1c4e5d6",
"holderDid": "did:sw:0x8f2a41c9d27b6e5a1c0f3d94ab77e5c621d0a4b3",
"credentialType": "StudentCard",
"status": "ACTIVE",
"issuedAt": "2026-08-26T10:00:00",
"expiresAt": "2027-08-26T10:00:00"
}
]
},
"message": "Issuer credentials retrieved"
}
| 필드 | 설명 |
|---|---|
credentialUid | 증명서의 공개 식별자 |
status | ACTIVE · PENDING_ANCHOR · ANCHOR_DEFERRED · SUSPENDED · REVOKED · EXPIRED 등 증명서 상태 |
totalPages | 발급분이 없으면 0입니다. 범위를 넘는 page는 빈 content를 돌려줍니다 |
GET/issuer/whoami — 기관 정보와 발급 가능 종류
발급자 DID 등재가 끝났는지 확인하는 용도로도 씁니다. 이 엔드포인트는 등재 전에도 호출할 수 있습니다
(issuerDid가 null로 나옵니다).
응답 200
{
"success": true,
"data": {
"applicationId": 41,
"clientId": "pc_7d1e4a9b",
"orgName": "국립○○대학교",
"orgType": "UNIVERSITY",
"countryCode": "KR",
"contactEmail": "did-admin@univ.example.ac.kr",
"issuerDid": "did:sw:0x3c7b21e8a4d05f9b6c1e7a2d8f4b03c95e6a1d72",
"status": "APPROVED",
"credentialTypes": ["StudentCard", "CompletionCertificate"]
},
"message": "Issuer information retrieved"
}
credentialTypes가 발급 가능한 종류의 전부입니다 — 여기에 없는 값으로는 발급되지 않습니다.
사용량 집계(ISSUE)
발급 호출은 ISSUE 버킷으로 집계됩니다. 규칙은 다음과 같습니다.
- 성공한 발급만 집계됩니다. 거부·검증 실패 호출은 집계되지 않습니다.
- 가중치는 1건당 1 유닛입니다(조회
QUERY와 동일). - 집계는 발급 시점에 즉시 기록되며, 야간 재집계에도
ISSUE값은 보존됩니다. - 집계 실패가 발급을 되돌리지는 않습니다 — 발급은 이미 성공한 것으로 확정됩니다.
기관은 같은 인증 헤더로 GET /fi/usage?fromDate=&toDate=를 호출해 자기 사용량을 직접 확인할 수 있습니다
(최대 92일 구간).
{
"success": true,
"data": {
"clientId": "pc_7d1e4a9b",
"days": [
{ "date": "2026-08-26", "endpoint": "ISSUE", "callCount": 18, "unitCount": 18 }
]
},
"message": "Usage retrieved"
}
자주 겪는 상황
| 증상 | 원인과 대응 |
|---|---|
승인받았는데 발급이 SW1701로 거부됨 | 발급자 DID 등재(수동 단계)가 아직 안 끝났습니다. GET /issuer/whoami의 issuerDid가 null인지 확인하고 운영팀에 문의하세요. 재시도해도 결과는 같습니다 |
발급이 SW2304로 거부됨 | 승인 목록에 없는 credentialType이거나, 클라이언트에 발급 권한이 없습니다. whoami의 credentialTypes와 대조하세요 |
| API 키를 분실함 | 서버는 키를 해시로만 보관하므로 다시 알려드릴 수 없습니다. 운영팀에 재발급(기존 키 폐기)을 요청하세요 |
| 같은 증명서가 두 건 발급됨 | 멱등키가 없어 재시도가 곧 중복 발급입니다. 재시도 전 GET /issuer/credentials로 확인하세요 |
보유자 DID로 발급이 SW1402/SW1403로 거부됨 | 해당 DID가 없거나 활성 상태가 아닙니다. 사용자에게 앱에서 DID 상태를 확인하도록 안내하세요 |