프로그램
배지를 발급할 활동입니다. 교육, 행사, 공모전 등이 해당합니다.
각 가이드는 구현 순서와 API 요청 예시를 함께 안내합니다.
키를 연동 서버의 환경 변수에 저장합니다.
GET /organization으로 인증과 조직 범위를 확인합니다.
첫 배지 발급, 플랫폼 사용자 배지 표시, 수료·수상 시 배지 발급 중 필요한 가이드로 이동합니다.
프로그램에 배지를 연결해 발급하면 수신자별로 발급된 배지(Assertion)가 만들어집니다.
배지를 발급할 활동을 만듭니다.
이름, 기준과 이미지를 연결합니다.
한 명 이상에게 발급을 요청합니다.
수신자별 발급 결과를 조회합니다.
배지를 발급할 활동입니다. 교육, 행사, 공모전 등이 해당합니다.
여러 수신자에게 발급할 이름, 설명, 기준과 이미지입니다.
배지를 한 명 이상에게 발급하는 비동기 요청입니다.
특정 수신자에게 실제로 발급된 디지털 배지입니다. 배지 이미지와 검증 URL을 포함하며, 필요하면 PDF로 다운로드할 수 있습니다.
프로그램과 배지에 연결하는 업로드 파일입니다.
GET /badges발급에 사용할 배지 목록GET /assertions사용자에게 발급된 배지 목록API 키를 연동 서버의 환경 변수에 저장한 뒤 GET /organization으로 연결을 확인합니다.
https://api.certi.world/public/v3써티 콘솔의 조직 API 키 관리 안내 에서 Manager 이상 권한으로 키를 발급합니다. 키 원문은 처음 한 번만 표시되므로 비밀 저장소에 보관합니다.
.env에 키 저장프로젝트 루트의 서버 환경 변수 파일이나 배포 서비스의 비밀 저장소에 넣습니다. 이 파일은 Git에 올리지 않습니다.
프로젝트 루트/.envCERTI_API_KEY=your-key-from-certi-console아래는 Node.js 예시입니다. 사용하는 언어와 관계없이 API 키를 서버에만 보관하고 인증·오류 처리를 한 곳에서 공통으로 적용하세요.
lib/certi.js// lib/certi.js — 서버에서만 사용합니다.
const BASE_URL = "https://api.certi.world/public/v3";
export async function certi(path, options = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
"X-API-KEY": process.env.CERTI_API_KEY,
"Content-Type": "application/json",
...options.headers,
},
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.error?.code ?? response.status}: ${result.error?.message ?? "Certi API request failed"}`);
}
return result.data;
}GET /organization은 읽기 권한만으로 호출할 수 있어 첫 연결 확인에 적합합니다. 응답의 조직 이름이 예상과 같은지 확인하세요.
curl -X GET "https://api.certi.world/public/v3/organization" \
-H "X-API-KEY: $CERTI_API_KEY"200 · application/json{
"success": true,
"message": null,
"data": {
"id": "01946e3c-72dc-7a42-a40f-f36df9b73d7a",
"name": "Certi Labs",
"roleCode": "ORG_MANAGER"
}
}브라우저 번들, 모바일 앱, 공개 저장소에 키를 포함하지 마세요.
브라우저는 연동 서버를 호출하고, 연동 서버만 써티 API 키를 사용합니다. 아래 파일명은 Node.js·Next.js 예시이며 같은 원칙을 다른 서버 언어에도 적용할 수 있습니다.
연동 서버의 API만 호출합니다. 써티 API 키는 알지 못합니다.
권한을 확인하고 환경 변수의 키로 써티 API를 호출합니다.
프로그램·배지·발급 작업·발급된 배지 데이터를 반환합니다.
필요하면 플랫폼 사용자·프로그램 ID와 써티 리소스 ID의 연결 정보를 저장합니다.
발급 요청, 상태 재확인, 대량 처리는 큐나 스케줄러에서 처리합니다.
CERTI_API_KEY는 서버 환경 변수나 클라우드 Secret Manager에 보관합니다.
.envAPI 키 저장서버 런타임만lib/certi.js인증·오류 처리 공통 함수서버 API와 작업 큐app/api/…/route.js화면에 필요한 데이터만 가공브라우저·LMSjobs/…발급 요청·상태 확인·대량 처리큐·스케줄러// lib/certi.js — 서버에서만 사용합니다.
const BASE_URL = "https://api.certi.world/public/v3";
export async function certi(path, options = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
"X-API-KEY": process.env.CERTI_API_KEY,
"Content-Type": "application/json",
...options.headers,
},
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.error?.code ?? response.status}: ${result.error?.message ?? "Certi API request failed"}`);
}
return result.data;
}모든 요청에 유효한 조직 키를 X-API-KEY로 보내며 키의 조직과 역할에 따라 접근 범위가 정해집니다. 개인 API 키는 V2·V3에서 지원하지 않습니다.
X-API-KEY: $CERTI_API_KEYORG_VIEWER조직·프로그램·배지·발급 작업·발급된 배지 조회ORG_MANAGERVIEWER 권한 + 생성·수정·삭제·발급·취소·이미지 업로드ORG_OWNER써티 API에서는 MANAGER와 동일한 전체 권한브라우저 번들, 모바일 앱, 공개 저장소에 키를 포함하지 마세요. 노출이 의심되면 키를 교체하고 기존 키를 폐기하세요.
날짜는 ISO-8601, ID는 UUID를 사용하며 정상 응답은 HTTP 200입니다.
page는 1부터, size는 기본 20·최대 100입니다. sort는 반복 가능하며 기본값은 createdAt,desc와 id,desc입니다.
JSON 단건 응답은 success · message · data, 목록은 items · pagination을 포함합니다. PDF 다운로드는 파일 바이너리를 바로 반환합니다.
프로그램·배지 생성과 수정은 PUBLISHED 상태로 처리되며, PATCH는 생략한 필드를 유지합니다.
써티 API의 프로그램·배지 생성과 수정 요청에는 publishStatusCode를 보내지 않습니다. 응답은 PUBLISHED 상태입니다.
요청에서 생략한 필드는 유지합니다. null과 빈 배열의 의미는 각 엔드포인트의 Request body 설명을 확인하세요.
직접 발급에는 1~300자의 Idempotency-Key가 필요합니다. 같은 조직·배지 범위의 키와 정규화 요청이 같으면 기존 발급을 반환합니다. 이메일·전화번호는 정규화하지만 수신자 순서는 유지합니다. 같은 절대 시각의 issuedAt은 offset 표현이 달라도 같으며, 생략과 null은 동일합니다. 명시한 발급 시각의 추가·제거·변경은 다른 요청입니다. 정규화 요청이 달라지면 409 · ISS057로 응답합니다.
2026-08-01T00:00:00Z 또는 2026-08-01T09:00:00+09:00처럼 UTC나 offset을 반드시 포함해야 합니다. 응답은 UTC Instant 문자열로 반환됩니다.issuedAt은 발급일, createdAt은 실제 생성 시각입니다. V3 직접 발급 요청의 issuedAt은 선택값이며 생략/null이면 현재 시각을 사용합니다. 상대 만료 정책은 발급일의 서울 날짜에 개월 수를 더한 날짜의 00:00 서울 시간으로 계산합니다. startAt·endAt 조회와 createdAt 정렬은 생성 시각 기준을 유지하며 issuedAt 검색·정렬은 지원하지 않습니다.JSON API 표에는 아래 공통 구조를 제외하고 data 또는 data.items[]의 실제 필드만 표시합니다. PDF 다운로드 응답은 이 구조를 사용하지 않습니다.
successmessagedatasuccessdata.itemsdata.paginationpagesizetotalElementtotalPagesfirstlastemptyAPI 오류는 HTTP 상태, 오류 코드, 메시지와 요청 경로를 포함한 JSON 구조로 반환됩니다. 분기 처리는 메시지가 아닌 고정된 오류 코드를 기준으로 구현하세요.
error.code와 발급된 배지의 errorCode는 별도입니다. 비동기 처리 후에는 각 배지의 stateCode와 errorCode를 확인하세요. 예를 들어 EMAIL_SEND_FAILED, KAKAO_SEND_FAILED, NOTIFICATION_DELIVERY_FAILED는 알림 오류이며, DUPLICATE_ISSUANCE는 중복 발급입니다.COM002파라미터 형식 오류타입·UUID·날짜 형식을 확인하세요.COM006요청 값 검증 실패필수값과 필드 제약을 확인하세요.COM020지원하지 않는 정렬이 API에서 허용하는 sort 필드만 사용하세요.AST008잘못된 생성 일시 범위startAt이 endAt보다 늦지 않도록 입력하세요.AST024수신자 필터 충돌email, phoneNumber, sourcedId 중 하나만 사용하세요.ISS056멱등성 키 오류1~300자의 Idempotency-Key를 추가하세요.ISS058발급 시각 형식 또는 시간대 오류issuedAt에 Z 또는 offset을 포함한 ISO-8601 문자열을 보내세요.ISS059발급 시각이 미래서버 현재 시각보다 미래인 issuedAt을 보내지 마세요.ISS061계산한 만료 시각이 이미 지남배지 정책과 과거 발급일을 확인하세요.SEC065API 키 인증 실패X-API-KEY 헤더와 키 상태를 확인하세요. 노출된 키는 교체하세요.ORG004권한 부족필요한 역할이 VIEWER+인지 MANAGER+인지 확인하세요.PRG001프로그램을 찾을 수 없음ID와 API 키의 조직 범위를 확인하세요.BDG001배지를 찾을 수 없음ID와 API 키의 조직 범위를 확인하세요.ISS001발급 작업을 찾을 수 없음ID와 API 키의 조직 범위를 확인하세요.AST001발급된 배지를 찾을 수 없음ID와 API 키의 조직 범위를 확인하세요.BDG064FIXED 만료 정책 배지만료 정책이 없거나 상대 만료 개월 수를 사용하는 배지로 발급하세요.ISS057멱등성 키 충돌같은 정규화 요청으로 재시도하세요. issuedAt의 추가·제거·변경도 충돌 대상입니다. 다른 발급에는 새 키를 사용하세요.AST022취소할 수 없는 상태취소 가능한 상태인지 확인하세요.PAY054발급 크레딧 부족발급 가능 여부를 확인하세요.{
"success": false,
"error": {
"timestamp": "2026-07-23T01:24:31Z",
"code": "SEC065",
"message": "유효한 조직 API Key가 필요합니다.",
"status": 401,
"path": "/external/v3/organization",
"errors": []
}
}연동 서비스의 플랫폼 사용자 이메일과 recipient.email이 일치하는 발급된 배지를 조회해 openbadgeImageUrl과 credentialUrl을 표시합니다. 아래는 Next.js·React 구현 예시입니다.
빠른 시작의 Node.js 예시를 사용하거나, 같은 인증·오류 처리 원칙을 사용하는 서버 언어로 구현하세요.
lib/certi.js// lib/certi.js — 서버에서만 사용합니다.
const BASE_URL = "https://api.certi.world/public/v3";
export async function certi(path, options = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
"X-API-KEY": process.env.CERTI_API_KEY,
"Content-Type": "application/json",
...options.headers,
},
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.error?.code ?? response.status}: ${result.error?.message ?? "Certi API request failed"}`);
}
return result.data;
}email은 앞뒤 공백과 대소문자를 정규화한 뒤 완전 일치로 조회합니다. External API V3는 운영 발급만 반환하며, 상태 필터는 서비스의 화면 표시 정책에 맞게 조정하세요. 목록 응답의 badge.image를 표시용 이미지로 사용할 수 있고, 검증 URL과 OpenBadge 이미지는 상세 응답에서 가져옵니다.
app/api/my-badges/route.js// app/api/my-badges/route.js — 홈페이지 서버 API
import { certi } from "@/lib/certi";
import { getCurrentUser } from "@/lib/auth"; // 사용 중인 인증 함수로 바꾸세요.
export async function GET() {
const user = await getCurrentUser();
const email = user?.email?.trim().toLowerCase();
if (!email) return Response.json({ message: "sign in required" }, { status: 401 });
const page = await certi(
`/assertions?${new URLSearchParams({ email, page: "1", size: "100" })}`,
);
// email은 정규화 후 완전 일치하며 External API V3는 운영 발급만 반환합니다.
// 아래 상태 필터는 화면 표시 정책 예시입니다.
const issued = page.items.filter((item) =>
item.recipient.email?.toLowerCase() === email &&
["ISSUED", "RECEIVED"].includes(item.stateCode),
);
const items = await Promise.all(issued.map(async (item) => {
const detail = await certi(`/assertions/${item.id}`);
return {
id: detail.id,
name: detail.badge.name,
imageUrl: detail.openbadgeImageUrl ?? item.badge.image?.urls.medium ?? item.badge.image?.urls.original,
credentialUrl: detail.credentialUrl,
};
}));
return Response.json({ items });
}프론트엔드는 여러분의 /api/my-badges만 호출합니다. 서버가 선택한 이미지와 검증 링크를 그대로 표시합니다.
components/BadgeList.jsx// components/BadgeList.jsx — 홈페이지 화면
export function BadgeList({ badges }) {
return (
<ul className="badge-list">
{badges.map((badge) => (
<li key={badge.id}>
{badge.imageUrl && <img src={badge.imageUrl} alt={`${badge.name} 디지털 배지`} />}
<strong>{badge.name}</strong>
{badge.credentialUrl && <a href={badge.credentialUrl} target="_blank" rel="noreferrer">
배지 검증
</a>}
</li>
))}
</ul>
);
}issuedAt으로 표시합니다. REISSUED는 재발급으로 대체된 원본이므로 현재 배지 목록 예제에서 제외합니다. credentialUrl이나 openbadgeImageUrl이 null이면 해당 링크나 이미지를 표시하지 않습니다.recipient.email을 서버에서 다시 비교합니다.수료·수상·승인 결과가 확정되면 발급 작업을 만들고, 발급 작업의 처리 상태가 COMPLETED가 될 때까지 확인합니다. 아래는 Node.js 작업 큐 구현 예시입니다.
badgeId 발급할 배지programId 연결할 프로그램sourcedId로 사용할 외부 시스템 수신자 ID화면 요청을 처리하는 동안 발급 작업이 끝날 때까지 기다리지 마세요. 수료 확정, 수상자 등록, 승인 완료 시점에 작업 큐로 내부 결과 ID와 수신자 정보를 전달합니다.
issuedAt에 UTC 또는 offset이 있는 과거·현재 시각을 전달할 수 있습니다. 같은 수료 건을 재시도할 때는 발급일도 같은 값으로 유지하세요. 생략/null이면 최초 요청에서 서버 현재 시각으로 확정합니다.같은 결과를 다시 처리해도 중복 발급되지 않도록 내부 결과 ID에서 같은 Idempotency-Key를 만듭니다. V3에서는 같은 수신자를 다시 조회할 수 있도록 외부 시스템의 사용자 ID를 sourcedId에도 보냅니다.
jobs/issue-completion-badge.js// jobs/issue-completion-badge.js — 수료 처리 후 실행되는 서버 작업
import { createHash } from "node:crypto";
import { certi } from "@/lib/certi";
export async function issueCompletion({ completionId, badgeId, programId, learner, issuedAt }) {
// 같은 수료 건에는 같은 키를 사용해 중복 발급을 막습니다.
const key = "completion:" + createHash("sha256").update(completionId).digest("hex");
return certi(`/badges/${badgeId}/issuances`, {
method: "POST",
headers: { "Idempotency-Key": key },
body: JSON.stringify({
programId,
...(issuedAt == null ? {} : { issuedAt }),
recipients: [{
name: learner.name,
email: learner.email,
sourcedId: learner.id,
receiveType: "EMAIL",
}],
}),
});
}직접 발급 응답의 data.id를 내부 결과 건에 저장합니다. PROCESSING이면 같은 ID를 다시 조회하고, COMPLETED 후에는 발급된 배지 목록의 stateCode와 errorCode를 확인합니다. 완료 상태는 모든 수신자의 성공을 보장하지 않습니다.
jobs/check-issuance.js// jobs/check-issuance.js — 백그라운드 작업에서 주기적으로 확인
import { certi } from "@/lib/certi";
export async function checkIssuance(issuanceId) {
const issuance = await certi(`/issuances/${issuanceId}`);
if (issuance.processingStatus === "PROCESSING") {
return { done: false };
}
if (issuance.processingStatus === "FAILED") {
throw new Error("발급 작업이 실패했습니다. issuanceId로 발급된 배지 목록과 errorCode를 확인하세요.");
}
const page = await certi(
`/assertions?${new URLSearchParams({ issuanceId, page: "1", size: "100" })}`,
);
return { done: true, assertions: page.items };
}PROCESSING정책에 따라 재확인COMPLETED발급된 배지 조회RECEIVED수신자별 성공 확인이미지 업로드부터 발급된 배지 확인까지, 배지 한 개를 실제로 발급하는 전 과정을 순서대로 따라 합니다.
POST /media-files/images로 배지 이미지를 업로드합니다.
curl -X POST "https://api.certi.world/public/v3/media-files/images" \
-H "X-API-KEY: $CERTI_API_KEY" \
-F "image=@badge.png"{
"success": true,
"message": null,
"data": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
}
}이 단계가 끝나면: 응답의 data.id를 imageId로 사용합니다.
1단계의 imageId를 사용해 교육, 행사, 공모전처럼 배지를 받을 활동을 만듭니다.
curl -X POST "https://api.certi.world/public/v3/programs" \
-H "X-API-KEY: $CERTI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "AI Literacy Program",
"description": "데이터 리터러시를 배우는 교육 과정입니다.",
"url": "https://example.com/programs/ai-literacy",
"categoryCode": "EDUCATION",
"startsAt": "2026-08-01T00:00:00Z",
"endsAt": "2026-08-31T09:00:00Z",
"isVisibleAtOrganizationPage": true,
"imageId": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"badgeIds": []
}'{
"success": true,
"message": null,
"data": {
"id": "0194775c-6402-7b98-baa4-1c8f8af208b8",
"name": "AI Literacy Program",
"description": "데이터 리터러시를 배우는 교육 과정입니다.",
"url": "https://example.com/programs/ai-literacy",
"categoryCode": "EDUCATION",
"startsAt": "2026-08-01T00:00:00Z",
"endsAt": "2026-08-31T09:00:00Z",
"publishStatusCode": "PUBLISHED",
"isVisibleAtOrganizationPage": true,
"image": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
},
"badgeIds": [
"01947a42-1b58-7cb1-91d7-b386d629db96"
],
"createdAt": "2026-07-23T01:24:31Z",
"updatedAt": "2026-07-23T01:24:31Z"
}
}이 단계가 끝나면: 응답의 data.id를 programId로 사용합니다.
imageId와 2단계의 programId를 포함해 이름, 발급 기준, 카테고리와 스킬을 정의합니다.
curl -X POST "https://api.certi.world/public/v3/badges" \
-H "X-API-KEY: $CERTI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "AI Literacy Certificate",
"description": "데이터 리터러시 교육 이수를 인증합니다.",
"criteria": [
{
"category": "COMPLETION",
"value": "필수 과정 이수"
}
],
"categoryCode": "CERTIFICATION",
"expirationPolicy": {
"relativeExpirationMonths": 12
},
"imageId": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"skills": [
{
"sourceType": "CUSTOM",
"skillName": "AI literacy"
}
],
"programIds": [
"0194775c-6402-7b98-baa4-1c8f8af208b8"
]
}'{
"success": true,
"message": null,
"data": {
"id": "01947a42-1b58-7cb1-91d7-b386d629db96",
"name": "AI Literacy Certificate",
"description": "데이터 리터러시 교육 이수를 인증합니다.",
"criteria": [
{
"category": "COMPLETION",
"value": "필수 과정 이수"
}
],
"categoryCode": "CERTIFICATION",
"publishStatusCode": "PUBLISHED",
"expirationPolicy": {
"relativeExpirationMonths": 12
},
"image": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
},
"signatureImage": null,
"skills": [
{
"sourceType": "CUSTOM",
"skillName": "AI literacy"
}
],
"programIds": [
"0194775c-6402-7b98-baa4-1c8f8af208b8"
],
"createdAt": "2026-07-23T01:24:31Z",
"updatedAt": "2026-07-23T01:24:31Z"
}
}이 단계가 끝나면: 응답의 data.id를 badgeId로 사용합니다.
uuidgen 등으로 요청마다 멱등성 키를 만들고 수신자 목록을 보냅니다.
curl -X POST "https://api.certi.world/public/v3/badges/01947a42-1b58-7cb1-91d7-b386d629db96/issuances" \
-H "X-API-KEY: $CERTI_API_KEY" \
-H "Idempotency-Key: $CERTI_IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{
"name": "Kim Certi",
"email": "recipient@example.com",
"sourcedId": "2026000123",
"receiveType": "EMAIL"
}
],
"programId": "0194775c-6402-7b98-baa4-1c8f8af208b8",
"issuedAt": "2026-07-23T01:24:31Z"
}'{
"success": true,
"message": null,
"data": {
"id": "01947b37-9c9d-7e8c-981a-a6f8cdf95a6e",
"badgeId": "01947a42-1b58-7cb1-91d7-b386d629db96",
"programId": "0194775c-6402-7b98-baa4-1c8f8af208b8",
"processingStatus": "PROCESSING",
"assertionCount": 1,
"createdAt": "2026-07-23T01:24:31Z",
"updatedAt": "2026-07-23T01:24:31Z"
}
}이 단계가 끝나면: 응답의 data.id를 issuanceId로 사용합니다.
GET /issuances/{issuanceId}로 처리 상태를 조회합니다. PROCESSING이면 같은 ID로 다시 확인하고, COMPLETED 후에는 각 수신자의 stateCode와 errorCode를 확인합니다. 직접 발급 성공 건은 RECEIVED이며, 만료 시점이 지나면 EXPIRED일 수 있습니다.
curl -X GET "https://api.certi.world/public/v3/issuances/01947b37-9c9d-7e8c-981a-a6f8cdf95a6e" \
-H "X-API-KEY: $CERTI_API_KEY"{
"success": true,
"message": null,
"data": {
"id": "01947b37-9c9d-7e8c-981a-a6f8cdf95a6e",
"badgeId": "01947a42-1b58-7cb1-91d7-b386d629db96",
"programId": "0194775c-6402-7b98-baa4-1c8f8af208b8",
"processingStatus": "PROCESSING",
"assertionCount": 1,
"createdAt": "2026-07-23T01:24:31Z",
"updatedAt": "2026-07-23T01:24:31Z"
}
}이 단계가 끝나면: processingStatus가 COMPLETED인지 확인했습니다.
발급 작업 ID로 발급된 배지 목록을 좁혀 조회하고, 수신자별 id와 stateCode를 확인합니다.
curl -X GET "https://api.certi.world/public/v3/assertions?page=1&size=20&sort=createdAt,desc" \
-H "X-API-KEY: $CERTI_API_KEY"{
"success": true,
"data": {
"items": [
{
"id": "01947b8e-54d7-79a7-835d-7ea2376bc4d2",
"serialNumber": "CERTI-2026-000042",
"badge": {
"id": "01947a42-1b58-7cb1-91d7-b386d629db96",
"name": "AI Literacy Certificate",
"image": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
}
},
"program": {
"id": "0194775c-6402-7b98-baa4-1c8f8af208b8",
"name": "AI Literacy Program",
"image": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
}
},
"issuanceId": "01947b37-9c9d-7e8c-981a-a6f8cdf95a6e",
"recipient": {
"name": "Kim Certi",
"email": "recipient@example.com",
"phoneNumber": null,
"sourcedId": "2026000123",
"receiveType": "EMAIL"
},
"stateCode": "RECEIVED",
"errorCode": null,
"expiredAt": "2027-07-22T15:00:00Z",
"issuedAt": "2026-07-23T01:24:31Z",
"createdAt": "2026-07-23T01:24:31Z"
}
],
"pagination": {
"page": 1,
"size": 20,
"totalElement": 1,
"totalPages": 1,
"first": true,
"last": true,
"empty": false
}
}
}이 단계가 끝나면: data.items[0].id를 assertionId로 사용합니다.
발급된 배지 상세 응답에서 OpenBadge 이미지 URL인 openbadgeImageUrl과 배지 검증 URL인 credentialUrl을 가져옵니다.
curl -X GET "https://api.certi.world/public/v3/assertions/01947b8e-54d7-79a7-835d-7ea2376bc4d2" \
-H "X-API-KEY: $CERTI_API_KEY"{
"success": true,
"message": null,
"data": {
"id": "01947b8e-54d7-79a7-835d-7ea2376bc4d2",
"serialNumber": "CERTI-2026-000042",
"badge": {
"id": "01947a42-1b58-7cb1-91d7-b386d629db96",
"name": "AI Literacy Certificate",
"image": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
}
},
"program": {
"id": "0194775c-6402-7b98-baa4-1c8f8af208b8",
"name": "AI Literacy Program",
"image": {
"id": "01947a1b-66d1-7a51-8d7c-67b80b4162db",
"urls": {
"original": "https://cdn.certi.world/media/badge.png",
"medium": "https://cdn.certi.world/media/badge-medium.png",
"small": "https://cdn.certi.world/media/badge-small.png"
}
}
},
"issuanceId": "01947b37-9c9d-7e8c-981a-a6f8cdf95a6e",
"recipient": {
"name": "Kim Certi",
"email": "recipient@example.com",
"phoneNumber": null,
"sourcedId": "2026000123",
"receiveType": "EMAIL"
},
"stateCode": "RECEIVED",
"errorCode": null,
"expiredAt": "2027-07-22T15:00:00Z",
"issuedAt": "2026-07-23T01:24:31Z",
"createdAt": "2026-07-23T01:24:31Z",
"credentialUrl": "https://api.certi.world/v1/openbadge/v3/credentials/01947b8e-54d7-79a7-835d-7ea2376bc4d2",
"openbadgeImageUrl": "https://api.certi.world/v1/openbadge/v3/credentials/01947b8e-54d7-79a7-835d-7ea2376bc4d2/image"
}
}이 단계가 끝나면: OpenBadge 이미지 URL(openbadgeImageUrl)과 배지 검증 URL(credentialUrl)을 사용할 수 있습니다.
발급된 배지의 상태와 유효 시간, OpenBadge 형식 및 무결성을 검증합니다.
/assertions/{assertionId}/validationassertionId필수공통 단건 응답의 data에 포함되는 필드입니다.
isValidFormatisTamperProofisValidBadgedetailMessageopenBadgeData