Utfärda Open Badges från WordPress (utan en tung plugin)
Utfärda Open Badges från valfri WordPress-kurs-/LMS-plugin (LearnDash, LifterLMS, Tutor) genom att anropa Badges Ninja-API:et vid slutförande — ingen separat märkesplugin att installera.
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.
Kör du kurser på WordPress känner du redan igen mönstret: någon slutför en modul i LearnDash, LifterLMS eller Tutor LMS, LMS:et markerar den som “slutförd”, och sedan… händer inget verifierbart. Kanske mejlar du en PDF. Kanske installerar du en dedikerad WordPress-märkesplugin som lägger till egna databastabeller, ett eget bildbibliotek och en egen uppfattning om vad ett “märke” är — en som ingen utanför din webbplats kan verifiera.
Det finns en enklare väg. WordPress kursplugins avfyrar redan en action hook i samma stund som någon slutför en kurs. Du kan fånga den hooken och göra ett enda HTTP-anrop till Badges Ninja-API:et. Ingen extra plugin, inget extra hosting, inget proprietärt märkesformat — bara en standardkompatibel Open Badge v2.0-credential som mottagaren kan verifiera var som helst, lägga till på LinkedIn och ladda ner som PDF.
Det här inlägget går igenom mönstret en gång, och visar sedan den specifika hooken för LearnDash, LifterLMS och Tutor LMS.
Varför inte bara använda en WordPress-märkesplugin?
Dedikerade WordPress-märkesplugins löser ett smalare problem än de ser ut att lösa. De är bra på att rendera en bild på en “mina prestationer”-sida på din webbplats. De är oftast mycket svagare på det som faktiskt spelar roll för en credential:
- Portabilitet. Ett märke som bara existerar som en shortcode på din WordPress-webbplats är inte en credential — det är en dekoration. I samma stund som mottagaren byter jobb, byter inkorg, eller din webbplats går ner, försvinner “beviset” med den.
- Verifiering. Vem som helst ska kunna kontrollera att en credential är äkta utan att logga in i din WordPress-admin. Open Badge v2.0 ger dig en publik verifieringsadress och maskinläsbar JSON-LD per design.
- Underhållsyta. Varje WordPress-plugin är ytterligare en sak att patcha, ytterligare en databastabell att säkerhetskopiera, ytterligare en felpunkt vid en kärnuppdatering.
Att anropa ett externt API vid kursens slutförande kringgår alla tre. WordPress förblir ansvarigt för det det är bra på — innehållsleverans och framstegsspårning — och själva credentialen finns i ett system byggt specifikt för att utfärda, verifiera och dela den.
Mönstret: en hook, ett HTTP-anrop
Varje större WordPress LMS-plugin avfyrar en åtgärd när en användare slutför en kurs. Integrationen är alltid samma tre steg:
- Koppla in dig på slutförande-åtgärden för ditt specifika LMS.
- Slå upp (eller skapa) mottagarens identitet — vanligtvis bara deras WordPress-kontos e-post och visningsnamn.
- Anropa
POST /awardspå Badges Ninja-API:et medwp_remote_post(), och skicka med märkes-ID:t och mottagarens uppgifter.
Du behöver en API-nyckel först. Generera en från din dashboard under API Keys — se guiden för API-nycklar för hela genomgången. Nycklar visas bara en gång vid skapandet, så spara den i wp-config.php som en konstant snarare än i databasen:
define( 'BADGES_NINJA_API_KEY', 'bws_your_32_hex_key_here' );
define( 'BADGES_NINJA_BADGE_ID', 'your-badge-id' );

Håll den här nyckeln på serversidan. Allt i den här guiden körs i PHP på din WordPress-server — wp_remote_post() är ett serversidesanrop, och wp-config.php skickas aldrig till webbläsaren. Det är precis vad du vill: nyckeln färdas server-till-server över HTTPS, och ingen besökare på webbplatsen kan läsa den. Flytta aldrig det här anropet till klientsidans JavaScript — ett fetch() från webbläsaren med nyckeln inbakad skulle exponera den för vem som helst via webbläsarens utvecklarverktyg. Två vanor är värda att behålla: se till att din värdtjänst serverar wp-config.php som körd PHP (aldrig som klartext, och lämna inte kvar en wp-config.php.bak-kopia i webbroten), och skapa en dedikerad API-nyckel för den här integrationen så att du kan återkalla den separat om den någonsin komprometteras — utan att ha sönder dina andra verktyg.
En delad hjälpfunktion håller varje LMS-hook tunn:
function badges_ninja_issue_award( $recipient_email, $recipient_name, $badge_id ) {
$response = wp_remote_post( 'https://api.badges.ninja/awards', array(
'headers' => array(
'X-Api-Key' => BADGES_NINJA_API_KEY,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'badgeId' => $badge_id,
'recipient' => array(
'name' => $recipient_name, // must be at least 5 characters
'email' => $recipient_email,
),
'issuedOn' => round( microtime( true ) * 1000 ), // epoch milliseconds
) ),
'timeout' => 15,
) );
if ( is_wp_error( $response ) ) {
error_log( 'Badges Ninja award failed: ' . $response->get_error_message() );
return false;
}
$code = wp_remote_retrieve_response_code( $response );
if ( $code >= 400 ) {
error_log( 'Badges Ninja award failed with HTTP ' . $code . ': ' . wp_remote_retrieve_body( $response ) );
return false;
}
return json_decode( wp_remote_retrieve_body( $response ), true );
}
Mottagarens e-post SHA-256-hashas på serversidan så snart den når Badges Ninja, och den returnerade payloaden innehåller belöningens verifieringsadress — praktiskt om du vill visa en länk “visa din credential” direkt där i WordPress efter att anropet lyckats.
LearnDash
LearnDash avfyrar learndash_course_completed med ett enda array-argument som innehåller user och course:
add_action( 'learndash_course_completed', function( $data ) {
$user = $data['user'];
$course = $data['course'];
// Map LearnDash course ID → your Badges Ninja badge ID.
// A simple approach: store the badge ID in a course custom field.
$badge_id = get_post_meta( $course->ID, 'badges_ninja_badge_id', true );
if ( ! $badge_id ) {
return; // no badge configured for this course
}
badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
} );
Att lagra märkes-ID:t som kursmetadata (i stället för en enda global BADGES_NINJA_BADGE_ID-konstant) är rätt val så snart du har fler än en kurs — varje kurs mappas till sin egen märkesdesign.
LifterLMS
LifterLMS avfyrar lifterlms_course_completed med $user_id och $course_id:
add_action( 'lifterlms_course_completed', function( $user_id, $course_id ) {
$user = get_userdata( $user_id );
$badge_id = get_post_meta( $course_id, 'badges_ninja_badge_id', true );
if ( ! $user || ! $badge_id ) {
return;
}
badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
}, 10, 2 );
Tutor LMS
Tutor LMS avfyrar tutor_course_complete_after med kurs-ID:t, och du hämtar den aktuella användaren från sessionen:
add_action( 'tutor_course_complete_after', function( $course_id ) {
$user_id = get_current_user_id();
$user = get_userdata( $user_id );
$badge_id = get_post_meta( $course_id, 'badges_ninja_badge_id', true );
if ( ! $user || ! $badge_id ) {
return;
}
badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
} );
Om din Tutor LMS-installation avfyrar slutförande från en bakgrundsprocess (t.ex. via en autobedömd quiz), byt ut get_current_user_id() mot det användar-ID som din specifika hook tillhandahåller — kontrollera payloaden med error_log( print_r( func_get_args(), true ) ) en gång under installationen för att bekräfta argumenten.
Designa märket innan du kopplar in hooken
Innan något av detta avfyras behöver du ett märke att dela ut. Den visuella designern täcker det här utan att du behöver en designer i teamet — 80+ formmallar, egna färger, ikonbibliotek och logotypuppladdning, allt med automatisk rutnätsjustering. Om du är ny på designern går Designing Your First Verifiable Digital Certificate igenom hur du väljer en mall och levererar något mottagarna faktiskt vill dela.
När märket väl finns, hämta dess ID från dashboarden (eller via GET /badges) och lägg det i kursens anpassade fält.
Vad mottagaren faktiskt får
Det här är delen en hemmasnickrad WordPress-märkesplugin inte kan replikera. När POST /awards lyckas får mottagaren:
- En unik verifieringsadress som vem som helst kan öppna — ingen WordPress-inloggning krävs — som visar att credentialen är äkta, vem som utfärdat den och när.
- En QR-kod och ett A4 PDF-certifikat, genererade automatiskt.
- En Add to LinkedIn Profile-knapp, om din utfärdare har ett LinkedIn-organisations-ID inställt — se Add a LinkedIn “Add to Profile” Button för engångsinställningen.
- Tillgång till sin egen mottagarportal på
badges.ninja/me, via inloggning med magisk länk — inget lösenord att glömma, inget separat konto att hantera på din WordPress-webbplats. - Standardkompatibel Open Badge v2.0 JSON-LD, så att alla OB-medvetna verifierare (inte bara Badges Ninja) kan validera den.
Inget av detta är WordPress uppgift att bygga, och inget av det bör bara finnas i din WordPress-databas.
Hantera fel på ett smidigt sätt
Kurs-slutförande-hooks avfyras under en live sidbegäran, och ett långsamt eller misslyckat API-anrop bör inte blockera elevens “grattis”-skärm. Två praktiska justeringar när du går bortom ett proof of concept:
Låt inte en misslyckad belöning blockera gränssnittet. Hjälpfunktionen ovan loggar redan och returnerar false i stället för att kasta ett fel — LearnDash, LifterLMS och Tutor fortsätter alla sitt normala slutförandeflöde oavsett vad badges_ninja_issue_award() returnerar.
Köa det i stället för att anropa inline, i stor skala. Om du kör stora kohorter genom en enda kurs samtidigt (ett kohortstartdatum, upplåsning av en webinarrepris), avfyra en schemalagd WP-Cron-händelse från hooken i stället för att anropa API:et synkront:
add_action( 'learndash_course_completed', function( $data ) {
wp_schedule_single_event( time() + 30, 'badges_ninja_deferred_award', array(
$data['user']->user_email,
$data['user']->display_name,
get_post_meta( $data['course']->ID, 'badges_ninja_badge_id', true ),
) );
} );
add_action( 'badges_ninja_deferred_award', 'badges_ninja_issue_award', 10, 3 );
Det här sprider ut belastningen och hindrar en enda långsam begäran från att bli elevens problem. För genuint stora batchar (en hel tidigare kohort, migrerad i ett svep), hoppa över WordPress-hook-vägen helt och använd massutfärdande via CSV — ladda upp en gång, så paginerar plattformen utfärdandet på serversidan med paus/återuppta inbyggt.
En notering om idempotens
Om ditt LMS någonsin avfyrar sin slutförande-hook mer än en gång för samma användare (ett vanligt LearnDash-specialfall med quiz-omförsök, eller en Tutor LMS-omsynkronisering), vill du inte dela ut samma märke två gånger. Det enklaste skyddet är en WordPress user-meta-flagga som du sätter direkt efter ett lyckat anrop:
if ( get_user_meta( $user_id, 'badges_ninja_awarded_' . $course_id, true ) ) {
return; // already issued
}
$result = badges_ninja_issue_award( $user->user_email, $user->display_name, $badge_id );
if ( $result ) {
update_user_meta( $user_id, 'badges_ninja_awarded_' . $course_id, true );
}
Om du utfärdar i verklig volym och vill hantera det centralt i stället för per webbplats, titta på webhook-driven händelsehantering eller den fullständiga API-snabbstarten för begärans-/svarsformerna över varje endpoint.
När mönstret växer ur en enda kurs
Exemplen ovan förutsätter ett märke per kurs. När du väl kör stapelbara credentials — ett spårmärke som låses upp efter flera kursmärken, eller ett avslutande märke (capstone) som beror på flera förkunskapskrav — flytta mappningen “vilket märke, för vilket slutförande” från post meta till en liten uppslagstabell, eller driv den från en kalkylarksbaserad automatisering (Airtable eller Google Sheets) som avfyrar samma POST /awards-anrop. Själva API-anropet ändras inte; bara det som avgör när det ska göras gör det.
Felsöka integrationen
En handfull problem står för det mesta av friktionen folk stöter på när de kopplar in det här för första gången:
Hooken avfyras aldrig. Bekräfta att du kopplar in dig på rätt åtgärd för din plugins faktiska version — LearnDash i synnerhet har ändrat namnen på slutförande-hooks mellan större versioner (äldre installationer förlitar sig ibland fortfarande på att learndash_course_completed avfyras från en annan kodväg än nyare gör). Lägg till en tillfällig error_log() högst upp i din callback och håll koll på wp-content/debug.log medan du slutför en testkurs som en engångsanvändare.
wp_remote_post() får timeout. Delade hostingmiljöer blockerar ibland utgående HTTPS till obekanta värdar, eller begränsar begärans-timeouts under de 15 sekunder som anges ovan. Om anrop konsekvent får timeout, kontrollera med din värdtjänst om utgående anslutningar till api.badges.ninja är tillåtna, och bekräfta att din PHP curl-tillägg har aktuella CA-certifikat — en föråldrad certifikatbunt är ett vanligt tyst fel på äldre delade hosting-stackar.
400 Bad Request. Payloaden saknar ett obligatoriskt fält eller har fel form. POST /awards förväntar sig badgeId, ett inbäddat recipient-objekt med name och email, samt en issuedOn-tidsstämpel — hjälparen ovan skickar exakt det. Den vanligaste fallgropen är mottagarens namn: det måste vara minst 5 tecken, så ett WordPress-visningsnamn som “Jo” avvisas. Falla tillbaka till kontots fullständiga namn (eller e-postens lokaldel) när visningsnamnet är för kort.
401 Unauthorized. API-nyckelkonstanten laddas inte, eller så regenererades den efter att konstanten sattes. Nycklar visas bara en gång vid skapandet och kan inte hämtas igen — om du tappat räkningen på vilken nyckel som är aktiv, radera den gamla och utfärda en ny i stället för att gissa.
Märke utfärdat, men med fel märkesdesign. Det här är nästan alltid ett föråldrat badges_ninja_badge_id-post-meta-värde kvarlämnat från testning. Eftersom mappningen ligger på kursinlägget är det lätt att glömma att du ändrade den efter att ha duplicerat en kurs för en ny kohort — värt en snabb koll varje gång du klonar en kurs.
Dubbla belöningar till samma person. Täckt ovan med user-meta-skyddet, men värt att upprepa: LMS-slutförande-hooks garanteras inte avfyras exakt en gång. Behandla “redan utdelat” som ett normalfall att kontrollera för, inte ett specialfall att ignorera.
Vanliga frågor
Fungerar det här med WooCommerce-kurspaket? Ja — om ett paketköp ska utlösa ett märke oberoende av kursens slutförande (t.ex. ett “certifierad medlem”-märke till alla som köper ett specifikt paket), koppla in dig på woocommerce_order_status_completed i stället för en LMS-slutförandeåtgärd, och slå upp märkes-ID:t från produktens metadata i stället för en kurs.
Kan mottagare få märket utan ett WordPress-konto? Ja, och det är hela poängen. Belöningen är knuten till deras e-postadress i Badges Ninja, inte till en WordPress-användarpost. Om din webbplats tillåter gästutcheckning eller anonymt quiz-slutförande, se bara till att du fångar en riktig e-postadress att skicka till badges_ninja_issue_award().
Vad händer om jag vill granska belöningar innan de skickas ut? Hoppa över att avfyra anropet direkt från slutförande-hooken. Skriv i stället den väntande belöningen (användare, kurs, tidsstämpel) till en anpassad tabell eller posttyp, och utfärda den från en schemalagd uppgift eller en admin “Approve & Award”-knapp som anropar samma hjälpfunktion på begäran.
Behöver jag ett separat märke per kurs, eller kan ett täcka flera? Båda fungerar. Vissa utfärdare skapar ett märke per kurs för granularitet; andra skapar ett enda “programmet slutfört”-märke och delar bara ut det när varje obligatorisk kurs är klar, och kontrollerar förkunskapskravens slutförande inuti hooken innan API:et anropas.
Redo att utfärda din första verifierbara credential? Kom igång gratis på badges.ninja — visuell designer, publik verifieringssida, PDF-certifikat, Open Badge v2.0-utdata. Inget kreditkort krävs.
Så här skapades artikeln
Vissa inlägg på den här bloggen skrivs med hjälp av en AI-assistent och granskas, faktakontrolleras och redigeras därefter av Badges Ninja-teamet före publicering. Varje kodexempel och pris kontrolleras mot den faktiska produkten. Läs mer om vår redaktionella process och AI-process på vår sida om redaktionell process .

Om författaren
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.
