발급기관 온보딩

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)를 발급할 수 있습니다. 학생증·수료증처럼 기관이 원래 발급하던 증서를 지갑 안의 디지털 증명서로 발급하는 용도입니다.

이 페이지의 대상: 발급기관으로 등록하려는 기관의 담당자와 개발자. 점수 조회(FI) API와는 별개 표면이지만, 인증 헤더·응답 봉투·에러 규칙은 완전히 같습니다. 기본 URL도 동일합니다: https://trendy.storydot.kr/superapp/api

전체 절차

단계누가무엇을
① 신청기관POST /public/issuer-onboarding/apply — 인증 없이 접수. 접수번호(id)를 돌려받습니다
② 심사운영자(관리자)신청 내용 검토 후 승인 또는 반려. 반려 시 사유가 기록됩니다
③ 승인운영자(관리자)승인과 동시에 API 키가 1회만 발급되어 기관에 전달됩니다 + 발급자 DID 등재(수동 단계)
④ 발급기관POST /issuer/credentials/issue — 기관 명의로 증명서 발급
③의 발급자 DID 등재는 자동이 아닙니다. 승인만으로는 발급이 되지 않습니다 — 운영자가 기관의 발급자 DID를 신뢰 목록에 등재하고 신청 건에 연결해야 비로소 발급 API가 열립니다. 등재 전에 발급을 시도하면 거부됩니다(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 키는 승인 시 단 한 번만 전달됩니다

평문 API 키는 승인 시점의 응답에 단 한 번만 나타납니다. 서버는 키를 해시로만 보관하므로 다시 조회할 수 없습니다. 전달받은 즉시 안전한 비밀 저장소에 보관하세요. 분실 시 재발급(기존 키 폐기) 외에 방법이 없습니다.

승인 시 기관 전용 파트너 클라이언트가 만들어지고, 발급 권한(credential:issue 스코프)이 부여됩니다. 기관이 받는 것은 clientId와 API 키 한 쌍이며, 이후 모든 발급 호출에 이 두 값을 헤더로 넣습니다 (인증과 동일한 방식).

X-Client-Id: <승인 시 전달받은 clientId>
X-API-Key:   <승인 시 1회 전달받은 API 키>

발급자 DID 등재 — 수동 단계

승인 다음에 운영자의 수동 작업이 한 번 더 필요합니다. 기관의 발급자 DID와 공개키(JWK)를 발급자 신뢰 목록에 등재하고, 그 DID를 신청 건에 연결하는 작업입니다. 이 단계는 현재 프로그램으로 자동화되어 있지 않습니다 — 승인 응답에도 이 수동 절차를 안내하는 registryMessage 문구가 함께 담깁니다.

기관 유형등재 시 신뢰 등급
GOVERNMENTVERIFIED(검증됨)
그 외(UNIVERSITY · ASSOCIATION · COMPANY)PROVISIONAL(잠정)

등재가 끝났는지는 기관이 직접 확인할 수 있습니다 — GET /issuer/whoami의 issuerDid에 값이 채워져 있으면 등재·연결이 완료된 것입니다. null이면 아직 발급할 수 없습니다.

기본 차단(fail-closed)

발급 API는 조건이 하나라도 갖춰지지 않으면 발급하지 않습니다. 부분 발급이나 임시 발급은 없습니다.

차단 조건errorCodeHTTP
인증 헤더 누락 또는 키 불일치SW2102401
클라이언트가 정지 상태SW2101403
credential:issue 권한 없음SW2304403
클라이언트에 연결된 신청 건 없음 / 신청이 APPROVED가 아님SW2304403
승인 목록에 없는 credentialTypeSW2304403
발급자 DID 미등재 또는 신뢰 목록에 없음SW1701500
보유자 DID(holderDid) 없음SW1402404
보유자 DID가 활성 상태가 아님(폐기 등)SW1403403
보유자 DID에 연결된 사용자 기록 없음SW1002404
입력 검증 실패SW1001400
속도 제한(60 req/min) 초과SW1004429
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일)이 적용됩니다
예약된 claim 이름: 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증명서의 공개 식별자
statusACTIVE · 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 버킷으로 집계됩니다. 규칙은 다음과 같습니다.

기관은 같은 인증 헤더로 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 상태를 확인하도록 안내하세요