Je winkelwagen is leeg
Producten die je toevoegt, verschijnen hier.
Bouw je eerste WordPress Ability: een AI-powered SEO meta generator. Stap-voor-stap tutorial met werkende code voor WordPress 6.9+.
Samenvatting: WordPress Ability tutorial
In deze tutorial bouw je een werkende SEO meta generator — een WordPress Ability die AI gebruikt om titels en meta descriptions te genereren. Je leert de structuur van abilities (inputs, outputs, permissions), integreert AI via de WP AI Client SDK of OpenAI, en test je ability via WP-CLI en REST API. Alle code is copy-paste klaar.
Je wilt AI in WordPress
KeurigOnline zegtWordPress
KeurigOnline zegtAPI
In ons artikel over de Abilities API legden we uit wát abilities zijn en waarom ze belangrijk zijn. Nu gaan we er daadwerkelijk één bouwen.
Download de complete plugin
Alle code uit deze tutorial is beschikbaar als complete WordPress plugin op GitHub:
Na dit artikel heb je:

Onze SEO meta generator is een WordPress Ability die content analyseert en AI gebruikt om geoptimaliseerde SEO titels en meta descriptions te genereren. Hier zie je hoe de onderdelen samenwerken:
De ability doet het volgende:
SEO meta generator in 3 stappen
Dit is een ideaal eerste project omdat het:
Na deze tutorial kun je de ability aanroepen via REST API:
POST /wp-json/wp-abilities/v1/abilities/keurigonline/generate-seo-meta/run
{
"input": {
"content": "Dit artikel gaat over de beste WordPress hosting providers...",
"language": "nl"
}
}
// Response:
{
"seo_title": "Beste WordPress hosting 2025: vergelijking & tips",
"meta_description": "Ontdek de beste WordPress hosting providers van 2025. Vergelijk snelheid, prijs en support. Plus: tips voor de juiste keuze."
}
De plugin bevat ook een gebruiksvriendelijke admin interface:

Voordat we beginnen, zorg dat je het volgende hebt:
Vereisten
Optioneel maar handig:
Tip: Werk altijd in een development omgeving voordat je code naar productie deployt. Een staging site of lokale installatie (met Local of DDEV) is ideaal. Zorg ook dat je WordPress goed is geoptimaliseerd — zie onze WordPress sneller maken gids.
We beginnen met een complete plugin structuur. Maak een nieuwe map ko-seo-meta-generator in wp-content/plugins/ en maak daarin het bestand ko-seo-meta-generator.php:
<?php
/**
* Plugin Name: KeurigOnline SEO Meta Generator
* Plugin URI: https://github.com/keurigonline/ko-seo-meta-generator
* Description: WordPress Ability van KeurigOnline die AI gebruikt om SEO titels en meta descriptions te genereren.
* Version: 1.0.0
* Author: KeurigOnline
* Author URI: https://keurigonline.nl
* License: GPL v2 or later
* Requires at least: 6.9
* Requires PHP: 8.0
* Text Domain: keurigonline-seo-meta-generator
*/
declare( strict_types = 1 );
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
Abilities worden gegroepeerd in categorieën. We registreren eerst een "content" categorie:
/**
* Registreer de content ability categorie
*/
add_action( 'wp_abilities_api_categories_init', 'keurigonline_register_seo_ability_category' );
function keurigonline_register_seo_ability_category(): void {
wp_register_ability_category(
'content',
array(
'label' => __( 'Content', 'keurigonline-seo-meta-generator' ),
'description' => __( 'Abilities die content genereren of wijzigen.', 'keurigonline-seo-meta-generator' ),
)
);
}
Nu registreren we de daadwerkelijke ability met input/output schema's:
/**
* Registreer de ability
*/
add_action( 'wp_abilities_api_init', 'keurigonline_register_seo_ability' );
function keurigonline_register_seo_ability(): void {
wp_register_ability(
'keurigonline/generate-seo-meta',
array(
'label' => __( 'Genereer SEO Meta', 'keurigonline-seo-meta-generator' ),
'description' => __( 'Genereert een SEO titel en meta description voor de gegeven content met behulp van AI. Gemaakt door KeurigOnline.', 'keurigonline-seo-meta-generator' ),
'category' => 'content',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'content' => array(
'type' => 'string',
'description' => __( 'De content waarvoor SEO meta gegenereerd moet worden.', 'keurigonline-seo-meta-generator' ),
),
'language' => array(
'type' => 'string',
'description' => __( 'Doeltaal (nl, en, de).', 'keurigonline-seo-meta-generator' ),
'default' => 'nl',
'enum' => array( 'nl', 'en', 'de' ),
),
),
'required' => array( 'content' ),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'seo_title' => array(
'type' => 'string',
'description' => __( 'Geoptimaliseerde SEO titel (max 60 karakters).', 'keurigonline-seo-meta-generator' ),
),
'meta_description' => array(
'type' => 'string',
'description' => __( 'Meta description (max 160 karakters).', 'keurigonline-seo-meta-generator' ),
),
),
'required' => array( 'seo_title', 'meta_description' ),
),
'execute_callback' => 'keurigonline_execute_seo_generator',
'permission_callback' => function(): bool {
return current_user_can( 'edit_posts' );
},
'meta' => array(
'show_in_rest' => true,
),
)
);
}
| Parameter | Functie | Voorbeeld |
|---|---|---|
label |
Leesbare naam voor de UI | "Genereer SEO meta" |
description |
Uitleg wat de ability doet | Wordt getoond in documentatie |
category |
Groepering van abilities | "content", "media", "users" |
input_schema |
JSON Schema voor validatie van input | Definieert verwachte parameters |
output_schema |
JSON Schema voor de response | Definieert wat je terugkrijgt |
execute_callback |
De functie die het werk doet | Wordt aangeroepen bij uitvoering |
permission_callback |
Wie mag de ability gebruiken | Capability check |
show_in_rest |
Maakt ability beschikbaar via REST API | true/false |
Tip: De input_schema en output_schema gebruiken JSON Schema formaat. Dit zorgt voor automatische validatie van input en maakt de ability self-documenting.1
Nu voegen we de execute callback toe die de AI aanroept. We gebruiken een aparte system prompt en user prompt voor betere AI resultaten.
De system prompt definieert de "persoonlijkheid" en expertise van de AI:
/**
* Haal de system prompt op voor SEO generatie
*/
function keurigonline_get_system_prompt(): string {
return 'Je bent een ervaren SEO specialist met expertise in het schrijven van pakkende titels en meta descriptions die hoog scoren in zoekmachines.
Je kernvaardigheden:
- Je begrijpt zoekintentie en weet welke woorden mensen aanzetten tot klikken
- Je plaatst het belangrijkste keyword altijd vooraan in de titel
- Je schrijft meta descriptions die nieuwsgierigheid wekken en een duidelijke call-to-action bevatten
- Je houdt je strikt aan karakterlimieten: titels max 60 tekens, descriptions max 160 tekens
- Je antwoordt ALLEEN in valid JSON formaat, zonder extra tekst of uitleg
Output formaat (strict JSON):
{"seo_title": "jouw titel", "meta_description": "jouw description"}';
}
De user prompt bevat de specifieke content en taal:
/**
* Bouw de user prompt voor SEO generatie
*/
function keurigonline_build_user_prompt( string $content, string $language ): string {
$lang_names = array(
'nl' => 'Nederlands',
'en' => 'English',
'de' => 'Deutsch',
);
$lang_name = $lang_names[ $language ] ?? 'Nederlands';
return sprintf(
'Genereer een SEO titel en meta description voor onderstaande content.
Taal: %s
Content:
%s',
$lang_name,
substr( $content, 0, 2000 )
);
}
Nu de belangrijkste functie: de execute callback. Deze probeert eerst de WP AI Client, dan OpenAI, en heeft ook een demo modus voor testen:
/**
* Execute callback met fallback ondersteuning
*/
function keurigonline_execute_seo_generator( array $input ): array|WP_Error {
$content = $input['content'] ?? '';
$language = $input['language'] ?? 'nl';
if ( empty( $content ) ) {
return new WP_Error( 'empty_content', __( 'Content mag niet leeg zijn.', 'keurigonline-seo-meta-generator' ) );
}
// Probeer eerst WP AI Client (WordPress 6.9+)
if ( class_exists( 'WordPress\\AI\\AI_Client' ) ) {
return keurigonline_generate_with_wp_ai_client( $content, $language );
}
// Fallback naar OpenAI
if ( defined( 'OPENAI_API_KEY' ) || get_option( 'keurigonline_seo_meta_openai_key' ) ) {
return keurigonline_generate_with_openai( $content, $language );
}
// Demo modus: geef voorbeeld output terug voor testen
if ( defined( 'SEO_META_GENERATOR_DEMO_MODE' ) && SEO_META_GENERATOR_DEMO_MODE ) {
return keurigonline_generate_demo_output( $content, $language );
}
return new WP_Error(
'no_ai_provider',
__( 'Geen AI provider geconfigureerd. Installeer WP AI Client of configureer een OpenAI API key.', 'keurigonline-seo-meta-generator' )
);
}
De directe OpenAI integratie met system en user messages:
/**
* Genereer met directe OpenAI API
*/
function keurigonline_generate_with_openai( string $content, string $language ): array|WP_Error {
$api_key = defined( 'OPENAI_API_KEY' ) ? OPENAI_API_KEY : get_option( 'keurigonline_seo_meta_openai_key' );
$system_prompt = keurigonline_get_system_prompt();
$user_prompt = keurigonline_build_user_prompt( $content, $language );
$response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', array(
'timeout' => 30,
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'model' => 'gpt-5-nano',
'messages' => array(
array( 'role' => 'system', 'content' => $system_prompt ),
array( 'role' => 'user', 'content' => $user_prompt ),
),
) ),
) );
if ( is_wp_error( $response ) ) {
return $response;
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( isset( $body['error'] ) ) {
return new WP_Error( 'openai_error', $body['error']['message'] );
}
$ai_response = $body['choices'][0]['message']['content'] ?? '';
return keurigonline_parse_ai_response( $ai_response );
}
Voor de officiële WordPress AI Client SDK:2
/**
* Genereer met WP AI Client SDK
*/
function keurigonline_generate_with_wp_ai_client( string $content, string $language ): array|WP_Error {
$prompt = keurigonline_build_seo_prompt( $content, $language );
try {
$response = \WordPress\AI\AI_Client::prompt()
->text( $prompt )
->run();
return keurigonline_parse_ai_response( $response );
} catch ( Exception $e ) {
return new WP_Error( 'ai_client_error', $e->getMessage() );
}
}
/**
* Bouw de volledige prompt voor WP AI Client (geen system message support)
*/
function keurigonline_build_seo_prompt( string $content, string $language ): string {
return keurigonline_get_system_prompt() . "\n\n" . keurigonline_build_user_prompt( $content, $language );
}
Een robuuste functie om de AI response te parsen:
/**
* Parse de AI response naar gestructureerde output
*/
function keurigonline_parse_ai_response( string $response ): array|WP_Error {
// Probeer JSON te extracten uit de response
$json_match = array();
if ( preg_match( '/\{[^}]+\}/', $response, $json_match ) ) {
$response = $json_match[0];
}
$result = json_decode( $response, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return new WP_Error(
'parse_error',
__( 'Kon AI response niet parsen als JSON: ', 'keurigonline-seo-meta-generator' ) . json_last_error_msg()
);
}
if ( ! isset( $result['seo_title'] ) || ! isset( $result['meta_description'] ) ) {
return new WP_Error( 'invalid_response', __( 'AI response mist vereiste velden.', 'keurigonline-seo-meta-generator' ) );
}
return array(
'seo_title' => sanitize_text_field( $result['seo_title'] ),
'meta_description' => sanitize_text_field( $result['meta_description'] ),
);
}
Let op: API keys veilig opslaan
wp-config.php: define( 'OPENAI_API_KEY', 'sk-...' );wp-config.php toe aan je .gitignoreLees meer over veilig omgaan met credentials in onze WordPress beveiligingsgids.
Tijdens development wil je niet steeds de AI API aanroepen. Voeg een demo modus toe:
/**
* Genereer demo output voor testen zonder AI
*/
function keurigonline_generate_demo_output( string $content, string $language ): array {
$title_base = wp_trim_words( wp_strip_all_tags( $content ), 8, '' );
$titles = array(
'nl' => $title_base . ' | Complete Gids',
'en' => $title_base . ' | Complete Guide',
'de' => $title_base . ' | Vollständiger Leitfaden',
);
$descriptions = array(
'nl' => 'Ontdek alles wat je moet weten over dit onderwerp. Praktische tips, voorbeelden en meer.',
'en' => 'Discover everything you need to know about this topic. Practical tips, examples and more.',
'de' => 'Erfahren Sie alles, was Sie über dieses Thema wissen müssen. Praktische Tipps, Beispiele und mehr.',
);
return array(
'seo_title' => substr( $titles[ $language ] ?? $titles['nl'], 0, 60 ),
'meta_description' => substr( $descriptions[ $language ] ?? $descriptions['nl'], 0, 160 ),
);
}
Activeer demo modus in wp-config.php:
// Enable demo mode for SEO Meta Generator testing
define( 'SEO_META_GENERATOR_DEMO_MODE', true );
Nu je ability geregistreerd is, kun je hem op verschillende manieren testen.
WP-CLI
KeurigOnline zegtWP-CLI
# Bekijk alle geregistreerde abilities
wp abilities list
# Bekijk details van je ability
wp abilities get keurigonline/generate-seo-meta
# Voer de ability uit
wp abilities run keurigonline/generate-seo-meta --input='{"content": "Dit is een test artikel over WordPress hosting en de beste providers van 2025.", "language": "nl"}'
De output zou er zo uit moeten zien:
{
"seo_title": "Beste WordPress hosting 2025: top providers",
"meta_description": "Ontdek de beste WordPress hosting providers van 2025. Vergelijk snelheid, betrouwbaarheid en prijs voor jouw website."
}
Abilities met show_in_rest => true zijn automatisch beschikbaar via de REST API.
Belangrijk: De REST API verwacht de input in een input wrapper object:
curl -X POST "https://jouwsite.nl/wp-json/wp-abilities/v1/abilities/keurigonline/generate-seo-meta/run" \
-H "Authorization: Bearer JOUW_APP_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"input": {
"content": "Dit artikel bespreekt de voordelen van managed WordPress hosting...",
"language": "nl"
}
}'
Let op: input wrapper
De REST API vereist dat je parameters in een input object wrapt. Dit is anders dan WP-CLI waar je de parameters direct meegeeft. Zonder deze wrapper krijg je de fout: "Ability has invalid input. Reason: input is not of type object."
Authenticatie
De REST API vereist authenticatie. Maak een Application Password aan via je WordPress profiel (Gebruikers → Profiel → Application Passwords) en gebruik deze als Bearer token.
Voor een gebruiksvriendelijke interface voegen we een instellingenpagina toe met een generator formulier:
/**
* Registreer instellingen pagina
*/
add_action( 'admin_menu', 'keurigonline_seo_meta_generator_admin_menu' );
function keurigonline_seo_meta_generator_admin_menu(): void {
add_options_page(
__( 'KeurigOnline SEO Meta Generator', 'keurigonline-seo-meta-generator' ),
__( 'SEO Meta Generator', 'keurigonline-seo-meta-generator' ),
'manage_options',
'keurigonline-seo-meta-generator',
'keurigonline_seo_meta_generator_settings_page'
);
}
De volledige admin pagina met post selector, API
KeurigOnline zegtAPI
De plugin bevat ook een uitgebreide "Integratie & API" sectie met documentatie:

WordPress biedt wp.apiFetch voor REST API calls vanuit JavaScript
KeurigOnline zegtJavaScriptinput wrapper:
// In een Gutenberg block of admin script
wp.apiFetch( {
path: '/wp-abilities/v1/abilities/keurigonline/generate-seo-meta/run',
method: 'POST',
data: {
input: {
content: 'Dit is de content waarvoor we SEO willen genereren...',
language: 'nl'
}
}
} ).then( ( result ) => {
console.log( 'SEO titel:', result.seo_title );
console.log( 'Meta description:', result.meta_description );
} ).catch( ( error ) => {
console.error( 'Ability error:', error.message );
} );
Tip: wp.apiFetch handelt automatisch authenticatie af via de WordPress nonce. Je hoeft geen Bearer token mee te geven als je ingelogd bent.
Je kunt ook de ability schema's ophalen om dynamische formulieren te bouwen:
// Haal ability details op (inclusief input/output schema)
wp.apiFetch( {
path: '/wp-abilities/v1/abilities/keurigonline/generate-seo-meta'
} ).then( ( ability ) => {
console.log( 'Label:', ability.label );
console.log( 'Input schema:', ability.input_schema );
console.log( 'Output schema:', ability.output_schema );
} );
// Haal alle beschikbare abilities op
wp.apiFetch( {
path: '/wp-abilities/v1/abilities'
} ).then( ( abilities ) => {
abilities.forEach( ( ability ) => {
console.log( ability.name, '-', ability.description );
} );
} );
Binnen WordPress kun je abilities ook direct aanroepen met PHP
KeurigOnline zegtPHP
// Haal de ability op
$ability = wp_get_ability( 'keurigonline/generate-seo-meta' );
if ( $ability ) {
// Voer uit
$result = $ability->execute( array(
'content' => get_the_content(),
'language' => 'nl',
) );
if ( ! is_wp_error( $result ) ) {
echo 'Titel: ' . esc_html( $result['seo_title'] );
echo 'Description: ' . esc_html( $result['meta_description'] );
}
}
Waarom meerdere manieren?
Je hebt nu een werkende WordPress Ability gebouwd. Dit is pas het begin.
Geïnspireerd? Hier zijn ideeën voor je volgende ability:
De Abilities API is onderdeel van WordPress
KeurigOnline zegtWordPress
De kosten hangen af van je AI provider. Met GPT-5-nano (de standaard in onze code) kost een typische SEO generatie nog minder dan $0.001 per request. Bij 1000 generaties per maand is dat minder dan $1.
Download de complete plugin
Alle code uit deze tutorial is beschikbaar als complete WordPress plugin:
Deze tutorial is gebaseerd op officiële WordPress documentatie:
Let op: De code in dit artikel is getest met WordPress
KeurigOnline zegtWordPress
KeurigOnline zegtAPI