আপনার ওয়েবসাইটে ব্যাজ যাচাইকরণ এমবেড করুন (iframe ছাড়া, SDK ছাড়া)
আপনার সাইটে একটি ছোট JavaScript স্নিপেট (বা সার্ভার-সাইড fetch) যোগ করুন, যা পাবলিক JSON-LD endpoint দিয়ে যেকোনো Open Badge যাচাই করে। কোনো iframe নেই, কোনো থার্ড-পার্টি ট্র্যাকার নেই।
Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure.
আপনি যদি credential ইস্যু করেন, তাহলে আজ হোক কাল হোক কেউ জিজ্ঞেস করবেই: “এই ব্যাজ যে আসল, তা বুঝব কীভাবে?” এর সৎ উত্তর কখনোই “PNG-কে বিশ্বাস করুন” হওয়া উচিত নয়। Open Badge v2.0 এর সমাধান দেয় একটি পাবলিক, মেশিন-রিডেবল যাচাইকরণ endpoint দিয়ে, আর আপনার নিজের সাইটে একটি “Verified” উইজেট বসাতে লাগে প্রায় দশ লাইন কোড — কোনো iframe নয়, কোনো থার্ড-পার্টি এমবেড স্ক্রিপ্ট নয়, কোনো SDK-ও নয়।
এই পোস্টে দেখানো হয়েছে endpoint-টি ঠিক কীভাবে কল করবেন এবং ফলাফল কীভাবে দেখাবেন — ভ্যানিলা JavaScript-এ, এবং সার্ভারে Node ও Python-এ। এছাড়া আলোচনা করা হয়েছে “যাচাই” আসলে কী কী পরীক্ষা করে, badges.ninja প্রাপকদের জন্য যে মানুষ-পাঠযোগ্য পেজ আগে থেকেই দেয় তার পাশে এই endpoint-এর স্থান কোথায়, এবং কোন ভুলগুলোর কারণে যাচাইকরণ উইজেট সম্পূর্ণ দেখালেও আসলে কিছুই প্রমাণ করে না।
“যাচাইকৃত” আসলে কী বোঝায়
একটি Open Badge v2.0 assertion হলো নির্দিষ্ট কাঠামোর একটি JSON-LD ডকুমেন্ট: assertion নিজে (কে পেয়েছেন, কবে), BadgeClass-এর একটি রেফারেন্স (এটি কী, মানদণ্ড কে নির্ধারণ করে), এবং ইস্যুকারী প্রোফাইলের একটি রেফারেন্স (কে এর পক্ষে জামিন দিচ্ছে)। আপনি যখন কোনো ব্যাজ “যাচাই” করেন, আসলে তিনটি আলাদা বিষয় পরীক্ষা করছেন:
- Assertion-টি আছে কি এবং resolve হয় কি। award ID-টি আসল, আন্দাজ করা বা বিকৃত করা নয়।
- এটি বাতিল (revoked) হয়েছে কি। ইস্যুকারী পরে ব্যাজ বাতিল করতে পারেন — ভুলে ইস্যু হওয়া credential, নীতি লঙ্ঘন, মেয়াদোত্তীর্ণ সার্টিফিকেশন — আর সঠিক ভেরিফায়ারকে তা দেখাতে হবে, শুধু রেকর্ডটি যে আছে তা নিশ্চিত করলে চলবে না।
- শৃঙ্খলটি একসঙ্গে মেলে কি। Assertion-এর badge ও issuer রেফারেন্স আসল, পরস্পর-মিলে-যাওয়া রেকর্ডে resolve হয়, পরিত্যক্ত বা অমিল ID-তে নয়।
যে যাচাইকরণ উইজেট শুধু একটি URL-এ ping করে 200 স্ট্যাটাস দেখে, সে ২ নম্বর বিষয়টি পুরোপুরি এড়িয়ে যায়। বাতিল হওয়া assertion-ও ঠিকঠাক resolve হয় — শুধু payload-এ একটি revocation ফ্ল্যাগ থাকে। আপনার উইজেট যদি কেবল “রিকোয়েস্ট সফল হলো কি না” দেখে, তাহলে বাতিল credential-ও যাচাইকৃত হিসেবে দেখাবে — যা কোনো উইজেট না থাকার চেয়েও খারাপ।
মানুষের জন্য পাবলিক পেজ বনাম API endpoint
https://badges.ninja-এ ইস্যু হওয়া প্রতিটি award একটি পাবলিক URL পায়, যা প্রাপকরা সরাসরি শেয়ার করতে পারেন; দেখতে কোনো সাইন-আপ লাগে না:

অধিকাংশ প্রাপক LinkedIn, রেজিউমে বা ইমেইল সিগনেচার থেকে এই পেজেরই লিংক দেবেন — এটি মানুষের জন্য তৈরি, ব্যাজের ছবি, ইস্যুকারী ও ইস্যুর তারিখ দেখায়, আর কাজ করতে আপনার কাছ থেকে কিছুই লাগে না। নিচের API endpoint একই assertion স্ট্রাকচার্ড JSON-LD হিসেবে ফেরত দেয় — আপনি যখন শুধু আমাদের পেজে লিংক না দিয়ে নিজের UI বানাচ্ছেন, তখন এটাই দরকার।
Endpoint
GET https://api.badges.ninja/certify-badge/award/{guid}
কোনো auth header লাগে না — এটি ইচ্ছাকৃতভাবেই পাবলিক, কারণ Open Badges-এর পুরো উদ্দেশ্যই হলো যে কেউ (নিয়োগকর্তা, রিক্রুটার, লাইসেন্সিং বোর্ড) অ্যাকাউন্ট ছাড়াই credential যাচাই করতে পারবে। ফেরত আসা assertion-এর ঠিক ফিল্ডের নামসহ পূর্ণ চুক্তির বিবরণ আছে যাচাইকরণ ডকুমেন্টেশনে।
award-এর GUID পাঠান, যা আপনি পাবেন নিজের ইস্যু-রেকর্ড থেকে, অথবা কোনো প্রাপকের শেয়ার করা badges.ninja/awards/<guid> লিংক থেকে বের করে নিয়ে।
ভ্যানিলা JS: fetch করে রেন্ডার করা
এটাই পুরো উইজেট। কোনো বিল্ড স্টেপ নেই, কোনো npm প্যাকেজ নেই, যেকোনো পেজে সরাসরি বসিয়ে দিন:
<div id="badge-verify"></div>
<script>
async function renderBadgeVerification(awardId, mountId) {
const el = document.getElementById(mountId);
el.textContent = "Checking badge status";
try {
const res = await fetch(
`https://api.badges.ninja/certify-badge/award/${awardId}`
);
if (!res.ok) {
el.textContent = "Credential not found.";
return;
}
const assertion = await res.json();
// —— revocation check first, a resolvable assertion isn't enough on its own
if (assertion.revoked) {
el.textContent = `Revoked: ${assertion.revocationReason || "no reason given"}`;
return;
}
el.textContent = `Verified, issued ${new Date(assertion.issuedOn).toLocaleDateString()}`;
} catch (err) {
el.textContent = "Verification check failed.";
}
}
renderBadgeVerification("REPLACE_WITH_AWARD_ID", "badge-verify");
</script>
REPLACE_WITH_AWARD_ID-এর জায়গায় একটি আসল award GUID বসান। ক্লায়েন্ট-সাইড ইন্টিগ্রেশন এটুকুই: CSS নিয়ে লড়াই করার মতো কোনো iframe-সীমানা নেই, আর যাচাইকরণের পাশাপাশি নিজস্ব অ্যানালিটিক্স বা ট্র্যাকিং পিক্সেল টেনে আনা কোনো থার্ড-পার্টি স্ক্রিপ্টও নেই।
সার্ভার-সাইড: Node
আপনি যদি ব্যাকএন্ডে যাচাই করতে চান — ধরুন, আপনার নিয়ন্ত্রিত প্রোফাইল পেজে “Verified Partner” ব্যাজ দেখানোর আগে, বা অনবোর্ডিং ফ্লোতে প্রমাণ হিসেবে credential গ্রহণ করার আগে — fetch একই, শুধু DOM অংশটি বাদ:
async function verifyAward(awardId) {
const res = await fetch(
`https://api.badges.ninja/certify-badge/award/${awardId}`
);
if (!res.ok) return { verified: false, reason: "not_found" };
const assertion = await res.json();
if (assertion.revoked) {
return { verified: false, reason: assertion.revocationReason || "revoked" };
}
return { verified: true, issuedOn: assertion.issuedOn, badge: assertion.badge };
}
সার্ভার-সাইড: Python
import requests
# —— resolve the assertion, then check revocation before trusting it
def verify_award(award_id):
res = requests.get(f"https://api.badges.ninja/certify-badge/award/{award_id}")
if res.status_code != 200:
return {"verified": False, "reason": "not_found"}
assertion = res.json()
if assertion.get("revoked"):
return {"verified": False, "reason": assertion.get("revocationReason", "revoked")}
return {"verified": True, "issued_on": assertion.get("issuedOn"), "badge": assertion.get("badge")}
দুটো উদাহরণেই non-200 রেসপন্সকে exception না ছুড়ে “not found” হিসেবে ধরা হয়েছে। পাবলিক যাচাইকরণ endpoint-এ ভাঙা বা আন্দাজ করা award ID একটি প্রত্যাশিত ইনপুট, ব্যতিক্রমী ঘটনা নয়; তাই এটিকে ধরা-পড়া error না ভেবে স্বাভাবিক একটি branch হিসেবে সামলান।
অন্যের UI এমবেড না করে ফলাফলে নিজের স্টাইল দেওয়া
যেহেতু উপরের উইজেট আপনার নিজের DOM-এ একটি fetch কল মাত্র, প্রতিটি পিক্সেল আপনার নিয়ন্ত্রণে। রেসপন্স থেকে সরাসরি তিনটি ভিজ্যুয়াল অবস্থা ম্যাপ করা একটি প্রচলিত প্যাটার্ন:
- Verified (যাচাইকৃত) — সবুজ টিক চিহ্নসহ “Verified” ও ইস্যুর তারিখ, আপনার সাইটের badge/tag কম্পোনেন্টের সঙ্গে মিলিয়ে স্টাইল করা।
- Revoked (বাতিল) — ম্লান বা লাল অবস্থা, যেখানে বাতিলের কারণ লুকানোর বদলে দেখানো হয়। প্রাপক ও তৃতীয় পক্ষ — দুই পক্ষই নীরব ব্যর্থতার চেয়ে সৎ “কেন”-টা চায়।
- Not found (পাওয়া যায়নি) — দৃশ্যত “revoked”-এর মতোই সামলান (ভুয়া সাফল্যের অবস্থা দেখাবেন না), তবে লেখা আলাদা, কারণ ভুল ID আর ইচ্ছাকৃত বাতিল — পেজ যিনি পড়ছেন তাঁর কাছে দুটো ভিন্ন পরিস্থিতি।
এর কোনোটার জন্যই কম্পোনেন্ট লাইব্রেরি লাগে না। verified / revoked / not_found-এর ওপর একটি switch আর তিনটি CSS ক্লাসই যথেষ্ট।
ক্যাশিং, যাতে endpoint-কে অযথা চাপ না দেন
যাচাইকরণ endpoint পাবলিক ও unauthenticated, অর্থাৎ প্রতি রিকোয়েস্টে ক্রেডিট খরচের চিন্তা নেই — তবু ক্লায়েন্ট-সাইডে ক্যাশ ছাড়া কল করলে প্রতিটি পেজ লোডে একটি নেটওয়ার্ক রাউন্ড-ট্রিপ হয়। যে পেজ প্রতিটি ভিজিটরকে একই ব্যাজ দেখায় (যেমন ফুটারে “Verified Partner” ব্যাজ), সেখানে প্রতিটি রিকোয়েস্টে নতুন করে fetch না করে ফলাফল সার্ভার-সাইডে ছোট TTL দিয়ে ক্যাশ করুন — পাঁচ থেকে পনেরো মিনিটই যথেষ্ট, কারণ বাতিল হওয়া দ্রুত বদলানো সংকেত নয়:
const cache = new Map();
async function verifyAwardCached(awardId, ttlMs = 10 * 60 * 1000) {
const hit = cache.get(awardId);
if (hit && Date.now() - hit.at < ttlMs) return hit.result;
const result = await verifyAward(awardId);
cache.set(awardId, { result, at: Date.now() });
return result;
}
ভিজিটর-প্রতি উইজেটের ক্ষেত্রে (কোনো প্রাপক নিজের ব্যক্তিগত সাইটে নিজের credential এমবেড করছেন) ক্যাশ বাদ দিন এবং পেজ লোডে সরাসরি কল করুন — ওই মাত্রায় বাড়তি রিকোয়েস্ট নগণ্য।
badges.ninja প্রাপকদের আগে থেকেই কী দেয়
সম্পূর্ণ কাস্টম এমবেড বানানোর আগে জেনে রাখা ভালো, আগে থেকে কী পাওয়া যায়। প্রতিটি award-এর পাবলিক পেজে তৈরি করা শেয়ার ও এমবেড স্নিপেট থাকে, যা প্রাপকরা কোনো কোড না লিখেই সরাসরি কপি করতে পারেন:

আপনার প্রয়োজন যদি হয় “প্রাপককে দেখাতে দিন যে তাঁর ব্যাজ আসল”, তাহলে তাঁর badges.ninja/awards/<guid> পেজে লিংক দেওয়া প্রায়ই কাস্টম উইজেট বানানোর চেয়ে সহজ — এটি আগে দেখানো সেই মানুষ-পাঠযোগ্য পেজ, আর verified/revoked পার্থক্য এটি আগে থেকেই সামলায়। এই পোস্টের কাস্টম উইজেট বানান তখনই, যখন যাচাইকরণের অবস্থা আপনার নিজের প্রোডাক্টের UI-এর ভেতরে দেখাতে হবে — পার্টনার ডিরেক্টরি, অভ্যন্তরীণ অডিট ড্যাশবোর্ড, প্রার্থীর দাবি করা সার্টিফিকেশন যাচাই করা হায়ারিং টুল — যেখানে অন্য সাইটে লিংক দেওয়া সঠিক UX নয়।
কেন iframe বা ভেন্ডর উইজেট নয়?
কিছু প্ল্যাটফর্ম যাচাইকরণ অবস্থা এমবেড করতে <iframe src="https://vendor.example.com/verify-widget">-এর মতো স্নিপেট দেয়। এটি কাজ করে, কিন্তু বাস্তব মূল্য আছে: আপনার সাইটের সঙ্গে মিলিয়ে ভেতরের কনটেন্ট রিস্টাইল করা যায় না (ভেন্ডরের পাঠানো CSS-ই মানতে হয়), এটি একটি cross-origin ডকুমেন্ট আনে যা আপনার Content-Security-Policy-কে স্পষ্টভাবে অনুমতি দিতে হয়, আর ভিজিটর যাচাইকরণ অবস্থা নিয়ে আগ্রহী হোক বা না হোক, প্রতিটি পেজ লোডে তৃতীয় পক্ষের iframe হোস্টে রিকোয়েস্ট যায়। দুই-কলের fetch উইজেট এই তিনটিই এড়ায় — এটি আপনার DOM, আপনার স্টাইলিং, এবং আপনি যখন কল করবেন বলে ঠিক করবেন কেবল তখনই চলে।
পুরো যাচাইকরণ শৃঙ্খল ধরে হাঁটা
আপনি যদি স্ট্যাটাস উইজেটের চেয়ে গভীর কিছু বানান — যেমন একগুচ্ছ credential পরীক্ষা করা অডিট টুল বা কমপ্লায়েন্স রিপোর্ট — তাহলে assertion-এ থেমে না গিয়ে পুরো শৃঙ্খলটি অনুসরণ করুন। Assertion একটি badge URL রেফারেন্স করে (BadgeClass: নাম, বিবরণ, মানদণ্ড), আর BadgeClass আবার একটি issuer URL রেফারেন্স করে (প্রতিষ্ঠানের প্রোফাইল)। কঠোর ভেরিফায়ার তিনটিই resolve করে এবং নিশ্চিত করে সেগুলো পরস্পর সামঞ্জস্যপূর্ণ — ব্যাজটি সত্যিই দাবি করা ইস্যুকারীর, assertion সত্যিই দাবি করা ব্যাজকে রেফারেন্স করছে — শুধু এটুকু নয় যে শীর্ষ-স্তরের assertion 200 ফেরত দেয়। একক-credential স্ট্যাটাস উইজেটের জন্য এটি প্রয়োজনের চেয়ে গভীর, তবু কোনো পার্টনার বা অডিটর যদি কখনো জিজ্ঞেস করেন “আপনাদের যাচাইকরণ আসলে কতটা গভীর?”, তখন এই কাঠামোর অস্তিত্ব জানা থাকা ভালো।
এড়িয়ে চলার সাধারণ ভুল
- শুধু HTTP স্ট্যাটাস দেখা। আগেই বলা হয়েছে, বাতিল assertion-ও resolve হয়। সবসময়
assertion.revokedপড়ুন; শুধু স্ট্যাটাস কোড দেখে যাচাই ধরে নেবেন না। - শেয়ার করা কম্পোনেন্টে award ID হার্ডকোড করা। উইজেট যদি বহু প্রাপকের পেজে পুনর্ব্যবহারযোগ্য হওয়ার কথা হয়, award ID প্যারামিটার হিসেবে নিন (data attribute, query string, prop), স্ক্রিপ্টে বসানো লিটারেল হিসেবে নয়।
- নেটওয়ার্ক error নীরবে গিলে ফেলা। যে
try/catchকিছুই লগ করে না এবং সত্যিই না-থাকা ব্যাজের মতো একই “not found” লেখা দেখায়, পরে ডিবাগিং কষ্টকর করে। অন্তত লগে “নেটওয়ার্ক কল ব্যর্থ হয়েছে” আর “API না বলেছে”-র পার্থক্য রাখুন। - ক্লায়েন্ট-সাইড অ্যাপে প্রতিটি render-এ আবার যাচাই করা। উইজেট যদি এমন React বা Vue কম্পোনেন্টের ভেতরে থাকে যা ঘন ঘন re-render হয়, তাহলে প্রতিটি re-render-এ নয়, প্রতি mount-এ একবার (বা ক্যাশিং অংশ অনুযায়ী প্রতি ক্যাশ TTL উইন্ডোতে একবার) যাচাই করুন।
এতে কি ক্রেডিট খরচ হয়, আর এটি কি ফ্রি প্ল্যানে চলে?
না। যাচাইকরণ endpoint ইচ্ছাকৃতভাবেই ইস্যু নিয়ন্ত্রণকারী ক্রেডিট সিস্টেমের বাইরে — ব্যাজ বৈধ কি না তা পরীক্ষা করতে ইস্যুকারীর কোনো খরচ হয় না, আর কলকারী ভিজিটরের অ্যাকাউন্ট, API key বা কোনো প্ল্যান টিয়ারও লাগে না। ব্যাজটি Free, Starter বা Pro — যে প্ল্যানেই ইস্যু হোক, এটি সত্য: যাচাইকরণ credential-এর নিজস্ব বৈশিষ্ট্য, ইস্যুকারীর সাবস্ক্রিপশনে আটকানো কোনো ফিচার নয়। তাই অনির্দিষ্ট ট্রাফিকের পাবলিক পেজে (ক্যারিয়ার পেজ, পার্টনার ডিরেক্টরি) এই উইজেট নিশ্চিন্তে বসানো যায়, আপনার অ্যাকাউন্টের সঙ্গে যুক্ত rate limit-এর চিন্তা ছাড়াই — আপনি একটি read-only পাবলিক endpoint কল করছেন, authenticated API route নয়।
একটি বিষয় স্পষ্ট করে বলা দরকার: endpoint-এর ফেরত দেওয়া assertion JSON-এ প্রাপকের কাঁচা ইমেইল ঠিকানা থাকে না। প্রাপকের ইমেইল ব্যাকএন্ডে hash করে রাখা হয়, ঠিক যাতে পাবলিক যাচাইকরণ রেসপন্স স্ক্র্যাপ করে ব্যক্তিগত যোগাযোগের তথ্য পাওয়া না যায় — যা পাবেন তা credential নিশ্চিত করতে যথেষ্ট (ব্যাজ, ইস্যুকারী, তারিখ, বাতিলের অবস্থা), অথচ award ID ঘুরে ঘুরে যে কেউ লিড সংগ্রহ করতে পারবে এমন টুল এটি হয়ে ওঠে না।
আপনার প্রথম যাচাইযোগ্য credential ইস্যু করতে প্রস্তুত?
badges.ninja-তে বিনামূল্যে শুরু করুন — ভিজ্যুয়াল ডিজাইনার, পাবলিক যাচাইকরণ পেজ, PDF সার্টিফিকেট, Open Badge v2.0 আউটপুট। ক্রেডিট কার্ড লাগবে না।
assertion-এর প্রতিটি ফিল্ডের পূর্ণ টীকাসহ কাঠামোর জন্য দেখুন JSON-LD ফিল্ড রেফারেন্স। আর আপনি যদি শুধু যাচাই না করে প্রোগ্রাম্যাটিকভাবে credential ইস্যু করেন, তাহলে API কুইকস্টার্ট-এ অথেন্টিকেশন এবং সেই award-তৈরির কল আছে, যা থেকে এই endpoint-এর আউটপুট আসে।
এই নিবন্ধটি যেভাবে তৈরি হয়েছে
এই ব্লগের কিছু পোস্ট এআই সহায়কের সাহায্যে খসড়া করা হয় এবং প্রকাশের আগে Badges Ninja টিম তা পর্যালোচনা, তথ্য যাচাই ও সম্পাদনা করে। প্রতিটি কোড নমুনা ও মূল্য লাইভ প্রোডাক্টের সাথে যাচাই করা হয়। আমাদের সম্পাদকীয় ও এআই প্রক্রিয়া সম্পর্কে আরও পড়ুন আমাদের সম্পাদকীয় প্রক্রিয়া পৃষ্ঠা .

লেখক সম্পর্কে
Nacho Coll
Founder & Engineer at Badges Ninja
Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure. Writes about the Open Badges spec, credential verification, and running a credentialing platform serverless on AWS, from the operator side of the wire.

