Skip to content

Commissies (Committees) API Documentation

This document describes how to use the Rondo Club REST API to manage commissies (committees).

All endpoints are relative to your WordPress installation:

https://your-site.com/wp-json/

The API supports two authentication methods:

Section titled “Method 1: Application Password (Recommended for External Integrations)”

Use HTTP Basic Authentication with a WordPress Application Password. This is the recommended method for scripts, external services, and API integrations.

  1. Generate an Application Password in WordPress: Users → Profile → Application Passwords
  2. Use your WordPress username and the generated password (with spaces)
Terminal window
curl -X GET "https://your-site.com/wp-json/wp/v2/commissies" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx"

Or with the Authorization header:

Terminal window
curl -X GET "https://your-site.com/wp-json/wp/v2/commissies" \
-H "Authorization: Basic $(echo -n 'username:xxxx xxxx xxxx xxxx xxxx xxxx' | base64)"

For requests from the Rondo Club frontend (same browser session), use the REST nonce:

X-WP-Nonce: {nonce_value}

The nonce is available in window.rondoConfig.nonce when logged in to Rondo Club.


Access Control: Kader users can read commissies. Commissie identity and hierarchy are synced from Sportlink and remain read-only for non-administrators. Administrators and users with the rondo_bestuur role may update the Rondo-local information fields through the dedicated info endpoint.


MethodEndpointDescription
GET/rondo/v1/my-committeesOwn current committee rosters; contacts only for their chairperson
GET/wp/v2/commissiesList all accessible commissies
GET/wp/v2/commissies/{id}Get single commissie
POST/wp/v2/commissiesCreate new commissie
PUT/wp/v2/commissies/{id}Update commissie
DELETE/wp/v2/commissies/{id}Delete commissie
GET/rondo/v1/commissies/{id}/peopleList current and former commissie members
POST/rondo/v1/commissies/{id}/infoUpdate Rondo-local commissie information (board/admin only)

GET /rondo/v1/my-committees

This read-only endpoint resolves the authenticated user’s own current committee memberships through their published, non-former linked person. It does not require the full-directory commissies capability. Anonymous requests receive HTTP 401; users without a qualifying committee receive HTTP 403, including administrators without an own membership. Request parameters cannot select another user, person or committee. Verenigingsbreed is excluded from membership and contact grants.

The response is an array of { id, name, can_view_contacts, members }, sorted by committee name. Members are sorted by name and contain { id, name, thumbnail, roles }. Only a current exact Voorzitter role in that committee (trimmed, case-insensitive) adds emails, phones and mobile_phones. Other members receive no contact keys. Parent contacts and general person fields are never returned. Ending a role or membership changes the next response.

Responses send Cache-Control: private, no-store. See My Committees for date rules, contact allowlists, exclusions, client freshness and source tests. This endpoint is separate from the directory member endpoint below.

GET /rondo/v1/commissies/456/people

The response groups members into current and former. Each member summary includes id, name, thumbnail, work-history details, and email. The email value is the first valid address from the person’s email_1 and email_2 fields, or an empty string when neither field contains a valid address. The commissie detail page uses the current members’ unique addresses for its E-mail leden mail link.

POST /rondo/v1/commissies/456/info
Content-Type: application/json
X-WP-Nonce: {nonce}

This endpoint is available to administrators and users with the rondo_bestuur role. It accepts only the seven Rondo-local information fields; attempts to change the name, parent, website, contact information, or another field are rejected.

{
"fields": {
"lange_omschrijving": "Stimuleert sportief gedrag binnen de vereniging.",
"taakomschrijving": "Behandelt vragen en ondersteunt teams.",
"uren_aantal": 2.5,
"uren_periode": "week",
"dagen_flexibel": "Flexibel",
"max_leden": 8,
"max_wachtlijst": 3
}
}

The response contains the commissie ID and the complete canonical fields object after the update.


FieldTypeDescription
titleobjectCommissie name (rendered as title.rendered)
contentobjectCommissie description (rendered as content.rendered)
featured_mediaintegerLogo/image attachment ID
parentintegerParent commissie ID (for hierarchical structure)
FieldTypeDescriptionFormat
fields.websitestringCommissie website URLFull URL
fields.contact_infoarrayContact methods (repeater)See below
fields.lange_omschrijvingstringUitgebreide omschrijving van de commissiePlain multi-line text
fields.taakomschrijvingstringWat een lid van deze commissie doetPlain multi-line text
fields.uren_aantalnumber|nullGeschat aantal uren tijdsinvesteringNumber, send null to clear
fields.uren_periodestring|nullPeriode voor uren_aantalweek, maand, or null
fields.dagen_flexibelstringVaste dagen of flexibelFree text
fields.max_ledennumber|nullMaximum aantal ledenNumber, send null to clear
fields.max_wachtlijstnumber|nullMaximum aantal op de wachtlijstNumber, send null to clear

Note: The seven fields from lange_omschrijving through max_wachtlijst are Rondo-local and are not synced from Sportlink. Administrators and board users edit them through POST /rondo/v1/commissies/{id}/info, with a body shaped as { "fields": { ... } }. uren_periode accepts week, maand, or null; numeric fields accept a number or null.

"fields": {
"contact_info": [
{
"contact_type": "email",
"contact_label": "Voorzitter",
"contact_value": "[email protected]"
},
{
"contact_type": "phone",
"contact_label": "Secretaris",
"contact_value": "+31 6 12345678"
}
]
}

Contact Types:

  • phone - Telefoon
  • email - E-mailadres
  • address - Adres
  • other - Anders
FieldTypeDescriptionValues
acf._visibilitystringWho can see this commissieprivate, workspace, shared
acf._assigned_workspacesarrayWorkspace term IDs[1, 2, 3]

Commissies support parent-child relationships. Use the parent field to create subcommissies:

FieldTypeDescription
parentintegerParent commissie ID (0 for top-level)
menu_orderintegerDisplay order within siblings

Request:

POST /wp/v2/commissies
Content-Type: application/json
X-WP-Nonce: {nonce}

Body:

{
"status": "publish",
"title": "Jeugdcommissie",
"content": "Verantwoordelijk voor alle jeugdactiviteiten en -evenementen.",
"parent": 0,
"acf": {
"website": "https://club.nl/jeugd",
"contact_info": [
{
"contact_type": "email",
"contact_label": "Voorzitter",
"contact_value": "[email protected]"
}
],
"_visibility": "private"
}
}

Response (201 Created):

{
"id": 456,
"date": "2026-01-26T10:00:00",
"slug": "jeugdcommissie",
"status": "publish",
"type": "commissie",
"title": {
"rendered": "Jeugdcommissie"
},
"content": {
"rendered": "<p>Verantwoordelijk voor alle jeugdactiviteiten en -evenementen.</p>"
},
"author": 1,
"parent": 0,
"featured_media": 0,
"acf": {
"website": "https://club.nl/jeugd",
"contact_info": [...],
"_visibility": "private",
"_assigned_workspaces": []
}
}

To create a subcommissie, set the parent field to the parent commissie’s ID:

Request:

POST /wp/v2/commissies
Content-Type: application/json
X-WP-Nonce: {nonce}

Body:

{
"status": "publish",
"title": "Jeugd A-selectie",
"content": "Commissie voor de A-junioren",
"parent": 456,
"menu_order": 1,
"acf": {
"contact_info": [
{
"contact_type": "email",
"contact_label": "Coordinator",
"contact_value": "[email protected]"
}
],
"_visibility": "private"
}
}

Request:

PUT /wp/v2/commissies/456
Content-Type: application/json
X-WP-Nonce: {nonce}

Body (partial update):

{
"content": "Nieuwe beschrijving van de commissie",
"acf": {
"website": "https://club.nl/jeugd/nieuw"
}
}

Important: When updating repeater fields (contact_info), you must send the complete array. Partial updates will replace the entire field.

Response (200 OK): Returns the full updated commissie object.


Request:

GET /wp/v2/commissies/456
X-WP-Nonce: {nonce}

Response:

{
"id": 456,
"title": { "rendered": "Jeugdcommissie" },
"content": { "rendered": "<p>Verantwoordelijk voor alle jeugdactiviteiten.</p>" },
"parent": 0,
"featured_media": 0,
"acf": {
"website": "https://club.nl/jeugd",
"contact_info": [...],
"_visibility": "private",
"_assigned_workspaces": []
}
}

Request:

GET /wp/v2/commissies?per_page=20&page=1
X-WP-Nonce: {nonce}

Query Parameters:

ParameterTypeDefaultDescription
per_pageint10Items per page (max: 100)
pageint1Page number
searchstring-Search in name
parentint-Filter by parent ID (0 for top-level)
orderbystringdateSort by: date, title, modified, menu_order
orderstringdescSort order: asc or desc
_fieldsstring-Limit fields returned (comma-separated)

Example - Get top-level commissies only:

GET /wp/v2/commissies?parent=0&orderby=menu_order&order=asc

Example - Get subcommissies of a parent:

GET /wp/v2/commissies?parent=456

Example - Search for commissies:

GET /wp/v2/commissies?search=jeugd&per_page=50

Example - Get only IDs and names (faster):

GET /wp/v2/commissies?_fields=id,title,parent,acf.website

Request:

DELETE /wp/v2/commissies/456
X-WP-Nonce: {nonce}

Note: Deleting a parent commissie does not automatically delete its subcommissies. Child commissies will become orphaned (parent=0).

Response (200 OK):

{
"deleted": true,
"previous": { ... }
}

Common Error Responses:

401 Unauthorized:

{
"code": "rest_not_logged_in",
"message": "You are not currently logged in.",
"data": { "status": 401 }
}

403 Forbidden:

{
"code": "rest_forbidden",
"message": "Sorry, you are not allowed to edit this commissie.",
"data": { "status": 403 }
}

404 Not Found:

{
"code": "rest_post_invalid_id",
"message": "Invalid commissie ID.",
"data": { "status": 404 }
}

const API_BASE = 'https://your-site.com/wp-json';
const nonce = window.rondoConfig?.nonce || 'your-nonce';
// Create a commissie
async function createCommissie(data) {
const response = await fetch(`${API_BASE}/wp/v2/commissies`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce,
},
credentials: 'include',
body: JSON.stringify({
status: 'publish',
title: data.name,
content: data.description || '',
parent: data.parentId || 0,
acf: {
website: data.website,
contact_info: data.contacts || [],
},
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
// Get commissie hierarchy (top-level with children)
async function getCommissieTree() {
// Get all commissies
const response = await fetch(
`${API_BASE}/wp/v2/commissies?per_page=100&orderby=menu_order&order=asc`,
{
headers: { 'X-WP-Nonce': nonce },
credentials: 'include',
}
);
const commissies = await response.json();
// Build tree structure
const topLevel = commissies.filter((c) => c.parent === 0);
return topLevel.map((parent) => ({
...parent,
children: commissies.filter((c) => c.parent === parent.id),
}));
}
// Update a commissie
async function updateCommissie(id, data) {
const response = await fetch(`${API_BASE}/wp/v2/commissies/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce,
},
credentials: 'include',
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
// Usage
const newCommissie = await createCommissie({
name: 'Technische Commissie',
description: 'Verantwoordelijk voor technische zaken',
website: 'https://club.nl/technisch',
contacts: [
{ contact_type: 'email', contact_label: 'TC', contact_value: '[email protected]' }
],
});
console.log('Created commissie:', newCommissie.id);
// Create a subcommissie
const subCommissie = await createCommissie({
name: 'Scouting',
description: 'Scouting en talentidentificatie',
parentId: newCommissie.id,
});
console.log('Created subcommissie:', subCommissie.id);
<?php
// Create a commissie programmatically
$commissie_id = wp_insert_post([
'post_type' => 'commissie',
'post_status' => 'publish',
'post_title' => 'Jeugdcommissie',
'post_content' => 'Verantwoordelijk voor alle jeugdactiviteiten.',
'post_parent' => 0, // Top-level
'menu_order' => 1,
'post_author' => get_current_user_id(),
]);
if ($commissie_id && !is_wp_error($commissie_id)) {
// Set ACF fields
update_field('website', 'https://club.nl/jeugd', $commissie_id);
// Set contact info (repeater)
update_field('contact_info', [
[
'contact_type' => 'email',
'contact_label' => 'Voorzitter',
'contact_value' => '[email protected]',
],
], $commissie_id);
// Set visibility
update_field('_visibility', 'private', $commissie_id);
}
// Create a subcommissie
$sub_commissie_id = wp_insert_post([
'post_type' => 'commissie',
'post_status' => 'publish',
'post_title' => 'Jeugd A-selectie',
'post_content' => 'Commissie voor de A-junioren',
'post_parent' => $commissie_id,
'menu_order' => 1,
'post_author' => get_current_user_id(),
]);
// Get all commissies for current user
$commissies = get_posts([
'post_type' => 'commissie',
'posts_per_page' => -1,
'author' => get_current_user_id(),
'orderby' => 'menu_order',
'order' => 'ASC',
]);
// Build hierarchical structure
$tree = [];
foreach ($commissies as $commissie) {
if ($commissie->post_parent === 0) {
$tree[$commissie->ID] = [
'commissie' => $commissie,
'children' => [],
];
}
}
foreach ($commissies as $commissie) {
if ($commissie->post_parent !== 0 && isset($tree[$commissie->post_parent])) {
$tree[$commissie->post_parent]['children'][] = $commissie;
}
}
Terminal window
# Create a commissie
curl -X POST "https://your-site.com/wp-json/wp/v2/commissies" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"status": "publish",
"title": "Jeugdcommissie",
"content": "Verantwoordelijk voor alle jeugdactiviteiten.",
"parent": 0,
"acf": {
"website": "https://club.nl/jeugd",
"contact_info": [
{
"contact_type": "email",
"contact_label": "Voorzitter",
"contact_value": "[email protected]"
}
]
}
}'
# Create a subcommissie
curl -X POST "https://your-site.com/wp-json/wp/v2/commissies" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"status": "publish",
"title": "Jeugd A-selectie",
"parent": 456,
"menu_order": 1,
"acf": {
"contact_info": [
{
"contact_type": "email",
"contact_label": "Coordinator",
"contact_value": "[email protected]"
}
]
}
}'
# Update a commissie
curl -X PUT "https://your-site.com/wp-json/wp/v2/commissies/456" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"content": "Bijgewerkte beschrijving"
}'
# List top-level commissies
curl -X GET "https://your-site.com/wp-json/wp/v2/commissies?parent=0&orderby=menu_order&order=asc" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx"
# List subcommissies of a parent
curl -X GET "https://your-site.com/wp-json/wp/v2/commissies?parent=456" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx"
# Delete a commissie
curl -X DELETE "https://your-site.com/wp-json/wp/v2/commissies/456" \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx"

  1. Title Field: Commissies use the standard WordPress post title, not ACF fields.

  2. Description: Commissie descriptions use the WordPress content field (editor), not an ACF field.

  3. Hierarchical Structure: Commissies support parent-child relationships. Use the parent field to create subcommissies.

  4. Menu Order: Use menu_order to control the display order of commissies within the same level.

  5. Repeater Fields: When updating contact_info, always send the complete array. WordPress will replace the entire field.

  6. Access Control: Each user only sees commissies they created. Use visibility settings and sharing to extend access.

  7. Deleting Parents: Deleting a parent commissie does not cascade to children. Subcommissies become orphaned.


Documentation generated: 2026-01-26