WordPressからOpen Badgesを発行する方法(重いプラグイン不要)
完了時にBadges Ninja APIを呼び出すことで、あらゆるWordPressコース・LMSプラグイン(LearnDash、LifterLMS、Tutor)からOpen Badgesを発行できます — 別途バッジプラグインをインストールする必要はありません。
WordPressでコースを運営しているなら、この流れはもうお馴染みでしょう。誰かがLearnDash、LifterLMS、またはTutor LMSでモジュールを完了し、LMSが「完了」とマークします。そして……何も検証可能なことは起こりません。PDFをメールで送るかもしれません。あるいは、独自のデータベーステーブル、独自の画像ライブラリ、そして「バッジ」とは何かについての独自の考え方を持つ専用のWordPressバッジプラグインをインストールするかもしれません — サイトの外にいる誰にも検証できないものです。
もっとシンプルな方法があります。WordPressのコースプラグインは、誰かがコースを完了した瞬間にすでにアクションフックを発火しています。そのフックを捕捉して、Badges Ninja APIへHTTPリクエストを1回送るだけで済みます。追加のプラグインも、追加のホスティングも、独自のバッジ形式も不要です — 受信者がどこでも検証でき、LinkedInに追加でき、PDFとしてダウンロードできる、標準準拠のOpen Badge v2.0クレデンシャルだけがあれば十分です。
この記事ではまずパターンを一度説明し、その後LearnDash、LifterLMS、Tutor LMSそれぞれの具体的なフックを紹介します。
なぜWordPressバッジプラグインをそのまま使わないのか?
専用のWordPressバッジプラグインは、見た目ほど広い問題を解決してくれるわけではありません。サイト内の「実績」ページに画像を表示することは得意です。しかし、クレデンシャルにとって本当に重要なことに対しては、たいてい弱いのです。
- 可搬性。 あなたのWordPressサイト上のショートコードとしてしか存在しないバッジは、クレデンシャルではなく「装飾」です。受信者が転職したり、メールアドレスを変えたり、あなたのサイトがダウンしたりした瞬間、その「証明」も一緒に消えてしまいます。
- 検証。 誰でも、あなたのWordPress管理画面にログインすることなく、クレデンシャルが本物かどうかを確認できるべきです。Open Badge v2.0は設計上、公開の検証URLと機械可読なJSON-LDを提供します。
- 保守対象。 WordPressのプラグインが1つ増えるごとに、パッチを当てる対象、バックアップすべきデータベーステーブル、コアアップデート時の障害点がもう1つ増えます。
コース完了時に外部APIを呼び出すことで、この3つすべてを回避できます。WordPressは、コンテンツ配信と進捗トラッキングという得意分野を担当し続け、クレデンシャル自体は発行・検証・共有のために特化して作られたシステムに存在します。
パターン:フック1つ、HTTP呼び出し1つ
主要なWordPress LMSプラグインはすべて、ユーザーがコースを完了したときにアクションを発火します。統合手順は常に同じ3ステップです。
- あなたが使っているLMS固有の完了アクションにフックする。
- 受信者のIDを特定(または作成)する — 通常は単にWordPressアカウントのメールアドレスと表示名で十分です。
wp_remote_post()を使って、バッジIDと受信者情報を渡し、Badges Ninja APIのPOST /awardsを呼び出す。
まずAPIキーが必要です。ダッシュボードの「API Keys」から生成してください — 詳しい手順はAPIキーガイドを参照してください。キーは作成時に一度だけ表示されるので、データベースではなくwp-config.php内に定数として保存しましょう。
define( 'BADGES_NINJA_API_KEY', 'bws_your_32_hex_key_here' );
define( 'BADGES_NINJA_BADGE_ID', 'your-badge-id' );

このキーはサーバーサイドに保管してください。 このガイドの内容はすべて、あなたのWordPressサーバー上でPHPとして実行されます — wp_remote_post() はサーバーサイドの呼び出しであり、wp-config.php がブラウザに送信されることはありません。それがまさに望ましい状態です。キーはHTTPS経由でサーバー間を移動し、サイトの訪問者は誰もそれを読み取ることができません。この呼び出しをクライアントサイドのJavaScriptに移すことは絶対にしないでください — キーを埋め込んだブラウザからのfetch()は、誰でもブラウザの開発者ツールから読み取れてしまいます。守っておくべき習慣が2つあります。ホストがwp-config.phpを実行済みPHPとして提供していること(決してプレーンテキストとしてではなく、またwp-config.php.bakのコピーをwebroot内に残さないこと)を確認すること、そしてこの統合専用の専用APIキーを作成し、万が一漏洩した場合に他のツールを壊すことなく独立して失効できるようにすることです。
共有のヘルパー関数を使えば、各LMSのフックを薄く保てます。
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 );
}
受信者のメールアドレスは、Badges Ninjaに到達した時点でサーバーサイドでSHA-256ハッシュ化され、レスポンスにはその発行の検証URLが含まれます。呼び出しが成功した後、WordPress内でその場に「あなたのクレデンシャルを見る」リンクを表示したい場合に便利です。
LearnDash
LearnDashは、userとcourseを含む単一の配列引数とともに learndash_course_completed を発火します。
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 );
} );
バッジIDを(単一のグローバルな BADGES_NINJA_BADGE_ID 定数ではなく)コースのメタとして保存しておくのは、コースが複数になった時点で正しい選択です — 各コースはそれぞれ独自のバッジデザインに対応します。
LifterLMS
LifterLMSは、$user_idと$course_idとともに lifterlms_course_completed を発火します。
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は、コースIDとともに tutor_course_complete_after を発火し、現在のユーザーはセッションから取得します。
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 );
} );
もしあなたのTutor LMS環境がバックグラウンドプロセス(クイズの自動採点など)から完了を発火する場合は、get_current_user_id() を、その特定のフックが提供するユーザーIDに置き換えてください — セットアップ時に一度、error_log( print_r( func_get_args(), true ) ) でペイロードを確認し、引数を確かめておきましょう。
フックを組む前にバッジをデザインする
これらすべてが発火する前に、授与するバッジが必要です。ビジュアルデザイナーなら、専属デザイナーがいなくても対応できます — 80種類以上の形状テンプレート、カスタムカラー、アイコンライブラリ、ロゴアップロードがすべてグリッドスナップで揃っています。デザイナーが初めての方は、検証可能なデジタル証明書を初めてデザインするでテンプレートの選び方から、受信者が実際に共有したくなるものを仕上げるまでを解説しています。
バッジができたら、ダッシュボード(または GET /badges)からそのIDを取得し、コースのカスタムフィールドに設定します。
受信者が実際に手にするもの
これこそ、自作のWordPressバッジプラグインでは再現できない部分です。POST /awards が成功すると、受信者は以下を得られます。
- 誰でも開ける一意の検証URL — WordPressへのログインは不要 — クレデンシャルが本物であること、誰が発行したか、いつ発行されたかを表示します。
- 自動生成されるQRコードとA4 PDF証明書。
- 発行者にLinkedIn組織IDが設定されている場合の**「LinkedInプロフィールに追加」**ボタン — 一度限りのセットアップについてはLinkedInの「プロフィールに追加」ボタンを設置するを参照してください。
- マジックリンクサインインによる、自分専用の受信者ポータル(
badges.ninja/me)へのアクセス — 忘れるパスワードも、WordPressサイト上で別途管理するアカウントも不要です。 - 標準準拠のOpen Badge v2.0 JSON-LD — Badges Ninjaに限らず、OB対応のあらゆる検証ツールで検証できます。
そのどれも、WordPressが構築すべき仕事ではありませんし、WordPressのデータベース内だけに存在すべきものでもありません。
失敗を適切に処理する
コース完了フックはライブなページリクエスト中に発火するため、遅いAPI呼び出しや失敗したAPI呼び出しが、受講者の「おめでとうございます」画面をブロックしてはいけません。概念実証の段階を過ぎたら、実用的な調整が2つあります。
失敗した授与でUIをブロックしない。 上記のヘルパー関数は、すでに例外を投げるのではなくログに記録して false を返すようになっています — badges_ninja_issue_award() が何を返そうと、LearnDash、LifterLMS、Tutorはいずれも通常の完了フローを継続します。
規模が大きい場合は、インラインで呼び出す代わりにキューに入れる。 大人数のコホートを一度に単一のコースに通す場合(コホートの開始日、ウェビナーのリプレイ解禁など)は、APIを同期的に呼び出す代わりに、フックからWP-Cronのスケジュールイベントを発火させましょう。
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 );
これによって負荷が分散され、1つの遅いリクエストが受講者の問題になることを防げます。本当に大規模なバッチ(一括で移行する過去のコホート全体など)の場合は、WordPressフックの経路をまるごとスキップしてCSVによる一括授与を使ってください — 一度アップロードすれば、プラットフォーム側でサーバーサイドのページネーションと一時停止・再開機能によって発行が処理されます。
冪等性に関する注意
もしあなたのLMSが、同じユーザーに対して完了フックを2回以上発火させることがある場合(クイズの再受験に関するよくあるLearnDashのエッジケースや、Tutor LMSの再同期など)、同じバッジを二重に授与したくはないはずです。最もシンプルな対策は、呼び出しが成功した直後に設定するWordPressのユーザーメタフラグです。
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 );
}
本当に大量に発行していて、サイトごとではなく一元的にこれを処理したい場合は、Webhook駆動のイベント処理や、全エンドポイントのリクエスト・レスポンス形式をまとめたAPIクイックスタートを確認してください。
このパターンが単一コースを超えて成長するとき
上記の例はすべて、コースごとに1つのバッジを前提としています。積み重ね式のクレデンシャル運用に移行したら — 複数のコースバッジの後に解除されるトラックバッジや、複数の前提条件に依存するキャップストーンなど — 「どのバッジを、どの完了に対して」というマッピングをポストメタから小さなルックアップテーブルに移すか、同じ POST /awards 呼び出しを発火するスプレッドシート駆動の自動化(AirtableやGoogleスプレッドシート)から動かすようにしましょう。API呼び出し自体は変わりません — 変わるのは、いつそれを呼び出すかを決める部分だけです。
統合のトラブルシューティング
初めてこれを組み込む際に、ほとんどの人がつまずく問題はいくつかに絞られます。
フックが一度も発火しない。 あなたが使っているプラグインの実際のバージョンに対して、正しいアクションにフックしているか確認してください — 特にLearnDashは、メジャーリリースをまたいで完了フックの名前を変更してきた経緯があります(古いインストールでは、learndash_course_completed が新しいものとは異なるコードパスから発火し続けている場合があります)。コールバックの先頭に一時的な error_log() を追加し、使い捨てのテストユーザーとしてテストコースを完了させながら wp-content/debug.log を確認してください。
wp_remote_post() がタイムアウトする。 共有ホスティング環境では、見慣れないホストへの送信HTTPS通信をブロックしたり、上記で設定した15秒よりも短くリクエストタイムアウトを制限したりすることがあります。呼び出しが一貫してタイムアウトする場合は、api.badges.ninja への送信接続が許可されているかホストに確認し、PHPの curl 拡張が最新のCA証明書を持っているか確認してください — 古い証明書バンドルは、古い共有ホスティング環境でよくあるサイレント障害の原因です。
400 Bad Request。 ペイロードに必須フィールドが欠けているか、形式が間違っています。POST /awards は badgeId、nameとemailを含むネストされた recipient オブジェクト、issuedOn タイムスタンプを期待します — 上記のヘルパーはまさにそれを送信します。最もよくあるつまずきは受信者の name です。5文字以上でなければならないため、「Jo」のようなWordPressの表示名は拒否されます。表示名が短すぎる場合は、アカウントのフルネーム(またはメールのローカルパート)にフォールバックしてください。
401 Unauthorized。 APIキーの定数が読み込まれていないか、定数を設定した後にキーが再生成されました。キーは作成時に一度だけ表示され、再取得はできません — どのキーが有効か分からなくなった場合は、推測するのではなく、古いキーを削除して新しいキーを発行してください。
バッジは発行されたが、意図しないバッジデザインになっている。 これはほぼ常に、テスト時に残った古い badges_ninja_badge_id のポストメタ値が原因です。マッピングがコースの投稿に紐づいているため、新しいコホート用にコースを複製した後にそれを変更したことを忘れがちです — コースを複製するたびに一度確認する価値があります。
同じ人物への重複授与。 上記のユーザーメタガードでカバー済みですが、改めて強調しておきます。LMSの完了フックは正確に一度だけ発火することが保証されているわけではありません。「すでに授与済み」を、無視すべきエッジケースではなく、確認すべき通常のケースとして扱ってください。
よくある質問
WooCommerceのコースバンドルでも動作しますか? はい — バンドルの購入がコース完了とは独立してバッジを付与すべき場合(特定のバンドルを購入した全員に「認定メンバー」バッジを付与するなど)は、LMSの完了アクションの代わりに woocommerce_order_status_completed にフックし、コースではなく製品メタからバッジIDを取得してください。
受信者はWordPressアカウントなしでバッジを受け取れますか? はい、それがまさにポイントです。この授与は、WordPressのユーザーレコードではなく、Badges Ninja内のメールアドレスに紐づいています。あなたのサイトがゲストチェックアウトや匿名でのクイズ完了を許可している場合は、badges_ninja_issue_award() に渡す本物のメールアドレスを取得できていることだけ確認してください。
授与を送信する前にレビューしたい場合は? 完了フックから直接呼び出しを発火するのはやめましょう。代わりに、保留中の授与(ユーザー、コース、タイムスタンプ)をカスタムテーブルや投稿タイプに書き込み、スケジュールされたタスクや、同じヘルパー関数をオンデマンドで呼び出す管理画面の「承認して授与」ボタンから発行してください。
コースごとに別々のバッジが必要ですか、それとも1つのバッジで複数をカバーできますか? どちらも可能です。粒度を重視してコースごとに1つのバッジを作成する発行者もいれば、単一の「プログラム完了」バッジを作成し、必要なすべてのコースが完了した時点で一度だけ授与する発行者もいます。その場合はフック内でAPIを呼び出す前に前提条件の完了を確認します。
最初の検証可能なクレデンシャルを発行する準備はできましたか?badges.ninjaで無料スタート — ビジュアルデザイナー、公開検証ページ、PDF証明書、Open Badge v2.0出力。クレジットカードは不要です。
この記事の作成方法
このブログの記事の一部は、AIアシスタントの支援を受けて下書きされた後、公開前にBadges Ninjaチームがレビュー、ファクトチェック、編集を行っています。すべてのコードサンプルと価格は実際の製品と照合して検証済みです。編集方針とAIプロセスについて詳しくは、 編集プロセスページ .

著者について
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.
