Skip to content

Custom Fields API Documentation

This document describes how to use the Rondo Club REST API to manage custom field definitions. Custom fields allow administrators to extend person and team records with additional data types.

All endpoints are relative to your WordPress installation:

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

Admin Endpoints (manage_options capability required)

Section titled “Admin Endpoints (manage_options capability required)”

Admin endpoints require the manage_options capability (WordPress administrator role). Use HTTP Basic Authentication with a WordPress Application Password:

Terminal window
curl -X GET "https://your-site.com/wp-json/rondo/v1/custom-fields/person" \
-u "admin:xxxx xxxx xxxx xxxx xxxx xxxx"

The /metadata endpoint is available to any logged-in user and provides read-only access to field definitions for display purposes.

Terminal window
curl -X GET "https://your-site.com/wp-json/rondo/v1/custom-fields/person/metadata" \
-H "X-WP-Nonce: {nonce}"

MethodEndpointDescription
GET/custom-fields/{post_type}List all custom fields
POST/custom-fields/{post_type}Create new field
GET/custom-fields/{post_type}/{key}Get single field
PUT/custom-fields/{post_type}/{key}Update field
DELETE/custom-fields/{post_type}/{key}Deactivate field (soft delete)
PUT/custom-fields/{post_type}/orderReorder fields
MethodEndpointDescription
GET/custom-fields/{post_type}/metadataRead-only field metadata

Note: {post_type} must be either person or team.


TypeDescriptionKey Options
textSingle line textplaceholder, maxlength
textareaMulti-line textplaceholder, maxlength
numberNumeric inputmin, max, step, prepend, append
urlURL with validationplaceholder
emailEmail with validationplaceholder
selectDropdown selectionchoices, allow_null, multiple, ui
checkboxMultiple selection checkboxeschoices, layout, toggle, allow_custom
radioSingle selection radio buttonschoices, layout
true_falseBoolean toggleui_on_text, ui_off_text
dateDate pickerdisplay_format, return_format, first_day
imageImage uploadpreview_size, library, min_width, max_width
fileFile uploadlibrary, mime_types, min_size, max_size
relationshipLink to other postsrelation_post_types, filters
color_pickerColor selectionenable_opacity

ParameterTypeDescription
labelstringField label displayed to users
typestringACF field type (see supported types above)
ParameterTypeDefaultDescription
namestringautoField name/key (auto-generated from label)
instructionsstring""Help text displayed below field
requiredbooleanfalseWhether field is required
default_valuemixed-Default value for new posts
placeholderstring""Placeholder text for empty field
ParameterTypeDescription
minnumberMinimum allowed value
maxnumberMaximum allowed value
stepnumberStep increment
prependstringText displayed before input (e.g., ”$“)
appendstringText displayed after input (e.g., “kg”)
ParameterTypeDefaultDescription
display_formatstring"F j, Y"PHP date format for display
return_formatstring"Y-m-d"PHP date format for storage
first_dayinteger1First day of week (0=Sunday, 1=Monday)
ParameterTypeDefaultDescription
choicesobject-Key-value pairs: {"key": "Label"}
allow_nullbooleanfalseAllow empty selection (Select)
multiplebooleanfalseAllow multiple selections (Select)
uibooleantrueUse enhanced UI
layoutstring"vertical""vertical" or "horizontal"
togglebooleanfalseShow “toggle all” checkbox
allow_custombooleanfalseAllow custom values
save_custombooleanfalseSave custom values to choices
ParameterTypeDescription
maxlengthintegerMaximum character length
ParameterTypeDefaultDescription
ui_on_textstring"Ja"Text for ON state
ui_off_textstring"Nee"Text for OFF state
ParameterTypeDescription
preview_sizestringPreview size: thumbnail, medium, large
librarystringMedia filter: all or uploadedTo
min_widthintegerMinimum image width in pixels
max_widthintegerMaximum image width in pixels
min_heightintegerMinimum image height in pixels
max_heightintegerMaximum image height in pixels
min_sizestringMinimum file size (e.g., “1MB”)
max_sizestringMaximum file size (e.g., “5MB”)
mime_typesstringAllowed MIME types (comma-separated)
ParameterTypeDescription
relation_post_typesarrayAllowed post types: ["person"], ["team"], or ["person", "team"]
filtersarraySearch filters: ["search", "post_type"]
ParameterTypeDefaultDescription
enable_opacitybooleanfalseEnable alpha/opacity slider
ParameterTypeDefaultDescription
show_in_list_viewbooleanfalseShow field as column in list view
list_view_orderinteger999Column order (lower = leftmost)
ParameterTypeDefaultDescription
uniquebooleanfalseEnforce unique values per post type

Request:

POST /rondo/v1/custom-fields/person
Content-Type: application/json
Authorization: Basic {credentials}

Body (text field example):

{
"label": "LinkedIn URL",
"type": "url",
"instructions": "Full LinkedIn profile URL",
"placeholder": "https://linkedin.com/in/username",
"show_in_list_view": true,
"list_view_order": 10
}

Body (select field example):

{
"label": "Contact Status",
"type": "select",
"choices": {
"active": "Actief",
"inactive": "Inactief",
"prospect": "Prospect"
},
"default_value": "active",
"allow_null": false,
"ui": true
}

Body (number field example):

{
"label": "Budget",
"type": "number",
"min": 0,
"max": 1000000,
"step": 100,
"prepend": "$",
"instructions": "Annual budget in USD"
}

Response (200 OK):

{
"key": "field_cf_linkedin_url",
"name": "linkedin_url",
"label": "LinkedIn URL",
"type": "url",
"instructions": "Full LinkedIn profile URL",
"placeholder": "https://linkedin.com/in/username",
"show_in_list_view": true,
"list_view_order": 10,
"active": 1,
"menu_order": 0
}

Request:

GET /rondo/v1/custom-fields/person/linkedin_url
Authorization: Basic {credentials}

Response:

{
"key": "field_cf_linkedin_url",
"name": "linkedin_url",
"label": "LinkedIn URL",
"type": "url",
"instructions": "Full LinkedIn profile URL",
"placeholder": "https://linkedin.com/in/username",
"show_in_list_view": true,
"list_view_order": 10,
"active": 1,
"menu_order": 0
}

Request:

PUT /rondo/v1/custom-fields/person/linkedin_url
Content-Type: application/json
Authorization: Basic {credentials}

Body (partial update):

{
"label": "LinkedIn Profile",
"instructions": "Link naar LinkedIn profiel",
"show_in_list_view": false
}

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

Note: The type cannot be changed after field creation to protect existing data integrity.


Request:

GET /rondo/v1/custom-fields/person
Authorization: Basic {credentials}

Query Parameters:

ParameterTypeDefaultDescription
include_inactivebooleanfalseInclude deactivated fields

Response:

[
{
"key": "field_cf_linkedin_url",
"name": "linkedin_url",
"label": "LinkedIn URL",
"type": "url",
"active": 1,
"menu_order": 0
},
{
"key": "field_cf_contact_status",
"name": "contact_status",
"label": "Contact Status",
"type": "select",
"choices": {
"active": "Actief",
"inactive": "Inactief"
},
"active": 1,
"menu_order": 1
}
]

Request:

DELETE /rondo/v1/custom-fields/person/linkedin_url
Authorization: Basic {credentials}

Response (200 OK):

{
"success": true,
"field": {
"key": "field_cf_linkedin_url",
"name": "linkedin_url",
"active": 0
}
}

Note: This is a soft delete. The field is deactivated but not removed from the database. Existing data is preserved and can be restored by reactivating the field.


Request:

PUT /rondo/v1/custom-fields/person/order
Content-Type: application/json
Authorization: Basic {credentials}

Body:

{
"order": ["contact_status", "linkedin_url", "budget"]
}

Response (200 OK):

{
"success": true
}

This read-only endpoint is available to any logged-in user. It returns only the display-relevant properties needed to render custom field values in the UI.

Request:

GET /rondo/v1/custom-fields/person/metadata
X-WP-Nonce: {nonce}

Response:

[
{
"key": "field_cf_linkedin_url",
"name": "linkedin_url",
"label": "LinkedIn URL",
"type": "url",
"instructions": ""
},
{
"key": "field_cf_contact_status",
"name": "contact_status",
"label": "Contact Status",
"type": "select",
"instructions": "",
"choices": {
"active": "Actief",
"inactive": "Inactief",
"prospect": "Prospect"
}
},
{
"key": "field_cf_is_vip",
"name": "is_vip",
"label": "VIP Contact",
"type": "true_false",
"instructions": "",
"ui_on_text": "Ja",
"ui_off_text": "Nee"
}
]

Properties returned:

  • Core: key, name, label, type, instructions
  • Select/Checkbox/Radio: choices
  • True/False: ui_on_text, ui_off_text
  • Date: display_format
  • Image/File/Relationship: return_format
  • Relationship: post_type
  • Number: prepend, append
  • Display: show_in_list_view, list_view_order

400 Bad Request:

{
"code": "invalid_field_type",
"message": "Unsupported field type: invalid",
"data": { "status": 400 }
}

403 Forbidden:

{
"code": "rest_forbidden",
"message": "Sorry, you are not allowed to manage custom fields.",
"data": { "status": 403 }
}

404 Not Found:

{
"code": "not_found",
"message": "Field not found",
"data": { "status": 404 }
}

const API_BASE = 'https://your-site.com/wp-json/rondo/v1';
// Create a custom field (admin only)
async function createCustomField(postType, fieldConfig) {
const response = await fetch(`${API_BASE}/custom-fields/${postType}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa('admin:xxxx xxxx xxxx xxxx xxxx xxxx'),
},
body: JSON.stringify(fieldConfig),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return response.json();
}
// Usage
const field = await createCustomField('person', {
label: 'Department',
type: 'select',
choices: {
sales: 'Sales',
marketing: 'Marketing',
engineering: 'Engineering',
},
show_in_list_view: true,
});
console.log('Created field:', field.name);
Terminal window
# Create a text field
curl -X POST "https://your-site.com/wp-json/rondo/v1/custom-fields/person" \
-u "admin:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"label": "Employee ID",
"type": "text",
"unique": true,
"instructions": "Unique employee identifier"
}'
# Create a select field
curl -X POST "https://your-site.com/wp-json/rondo/v1/custom-fields/team" \
-u "admin:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"label": "Industry",
"type": "select",
"choices": {
"tech": "Technology",
"finance": "Finance",
"healthcare": "Healthcare"
}
}'
# Update a field
curl -X PUT "https://your-site.com/wp-json/rondo/v1/custom-fields/person/employee_id" \
-u "admin:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"instructions": "Werknemersnummer (uniek)"
}'
# Delete (deactivate) a field
curl -X DELETE "https://your-site.com/wp-json/rondo/v1/custom-fields/person/employee_id" \
-u "admin:xxxx xxxx xxxx xxxx xxxx xxxx"
# Get field metadata (any logged-in user)
curl -X GET "https://your-site.com/wp-json/rondo/v1/custom-fields/person/metadata" \
-H "X-WP-Nonce: {nonce}"

  1. Field Key Generation: The field key is auto-generated from the label with prefix field_cf_. For example, “LinkedIn URL” becomes field_cf_linkedin_url.

  2. Soft Delete (Deactivation): Deleting a field only deactivates it. The field definition and all stored data are preserved in the database. This allows restoration if needed.

  3. Type Immutability: Once created, a field’s type cannot be changed. This protects existing data integrity. Create a new field if you need a different type.

  4. Post Type Separation: Custom fields for person and team are stored separately. A field created for person is not available for team and vice versa.

  5. ACF Integration: Custom fields are stored as ACF field groups and integrate seamlessly with ACF’s validation and display systems.

  6. Unique Validation: Fields with unique: true will reject duplicate values within the same post type. Useful for employee IDs, serial numbers, etc.

  7. List View Display: Fields with show_in_list_view: true appear as columns in the person/team list. Use list_view_order to control column position.


Documentation generated: 2026-01-26