← Developer Reference

API & CLI

This reference targets APD v1.0.x. Last updated: 2026-02-21.

REST API endpoints, helper functions, WP-CLI commands, and the module system for building add-ons.

REST API

Endpoints

All endpoints use namespace apd/v1. The base URL is /wp-json/apd/v1/.

Listings

GET /wp-json/apd/v1/listings
POST /wp-json/apd/v1/listings
GET /wp-json/apd/v1/listings/{id}
PUT /wp-json/apd/v1/listings/{id}
DELETE /wp-json/apd/v1/listings/{id}

Taxonomies

GET /wp-json/apd/v1/categories
GET /wp-json/apd/v1/categories/{id}
GET /wp-json/apd/v1/tags
GET /wp-json/apd/v1/tags/{id}

Favorites

GET /wp-json/apd/v1/favorites
POST /wp-json/apd/v1/favorites
DELETE /wp-json/apd/v1/favorites/{id}
GET /wp-json/apd/v1/favorites/listings
POST /wp-json/apd/v1/favorites/toggle/{id}

Reviews

GET /wp-json/apd/v1/reviews
POST /wp-json/apd/v1/reviews
GET /wp-json/apd/v1/reviews/{id}
PUT /wp-json/apd/v1/reviews/{id}
DELETE /wp-json/apd/v1/reviews/{id}
GET /wp-json/apd/v1/listings/{id}/reviews

Inquiries

GET /wp-json/apd/v1/inquiries
GET /wp-json/apd/v1/inquiries/{id}
DELETE /wp-json/apd/v1/inquiries/{id}
POST /wp-json/apd/v1/inquiries/{id}/read
POST /wp-json/apd/v1/inquiries/{id}/unread
GET /wp-json/apd/v1/listings/{id}/inquiries

The full permissions matrix:

Endpoint Methods Auth Required Ownership / Capability Nonce Required Typical Error Codes
/listingsGETNoNo
/listingsPOSTYesYes401, 403
/listings/{id}GETNoNo
/listings/{id}PUTYesOwner or edit_others_postsYes401, 403
/listings/{id}DELETEYesOwner or delete_others_postsYes401, 403
/categoriesGETNoNo
/categories/{id}GETNoNo
/tagsGETNoNo
/tags/{id}GETNoNo
/favoritesGETYesNo401
/favoritesPOSTYesYes401, 403
/favorites/{id}DELETEYesYes401, 403
/favorites/listingsGETYesNo401
/favorites/toggle/{id}POSTYesYes401, 403
/reviewsGETNoNo
/reviewsPOSTYesYes401, 403
/reviews/{id}GETNoNo
/reviews/{id}PUTYesAuthorYes401, 403
/reviews/{id}DELETEYesAuthor or moderate_commentsYes401, 403
/listings/{id}/reviewsGETNoNo
/inquiriesGETYesNo401
/inquiries/{id}GETYesListing ownerNo401, 403
/inquiries/{id}DELETEYesListing ownerYes401, 403
/inquiries/{id}/readPOSTYesListing ownerYes401, 403
/inquiries/{id}/unreadPOSTYesListing ownerYes401, 403
/listings/{id}/inquiriesGETYesListing ownerNo401, 403

Authentication

What's required depends on the endpoint and HTTP method:

Include the nonce header in all authenticated and write requests:

JavaScript
fetch( '/wp-json/apd/v1/favorites', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': wpApiSettings.nonce,
    },
    body: JSON.stringify( { listing_id: 42 } ),
} );

cURL Examples

Public (no auth)
# Public GET — no authentication needed
curl https://example.com/wp-json/apd/v1/listings
Authenticated read (cookie + no nonce)
# Authenticated GET — pass WordPress auth cookie
curl https://example.com/wp-json/apd/v1/favorites \
  -b wordpress_logged_in_xxx=COOKIE_VALUE
Authenticated write (cookie + nonce)
# State-changing POST — cookie + X-WP-Nonce required
curl -X POST https://example.com/wp-json/apd/v1/favorites \
  -b wordpress_logged_in_xxx=COOKIE_VALUE \
  -H "X-WP-Nonce: NONCE_VALUE" \
  -H "Content-Type: application/json" \
  -d '{"listing_id": 42}'

To obtain a nonce for cURL testing, call /wp-json/apd/v1/ with a valid cookie and read the X-WP-Nonce header from the response, or generate one via WP-CLI: wp eval 'echo wp_create_nonce("wp_rest");'

Error Responses

Missing or invalid authentication returns standard WordPress REST API error codes:

HTTP Status Error Code Meaning
401rest_not_logged_inThe request requires an authenticated user but no user session was found.
403rest_nonce_invalidThe X-WP-Nonce header is missing, expired, or invalid.
403rest_forbiddenThe user does not have the required capability or ownership for this resource.

Adding Custom Endpoints

Hook into apd_register_rest_routes to add your own endpoints under the apd/v1 namespace. Use permission_authenticated_with_nonce for write endpoints and permission_authenticated for read-only private endpoints:

PHP
add_action( 'apd_register_rest_routes', function( $controller ) {
    // Read-only private endpoint (auth required, no nonce)
    register_rest_route( 'apd/v1', '/custom', [
        'methods'             => 'GET',
        'callback'            => 'my_custom_read',
        'permission_callback' => [ $controller, 'permission_authenticated' ],
    ] );

    // Write endpoint (auth + nonce required)
    register_rest_route( 'apd/v1', '/custom', [
        'methods'             => 'POST',
        'callback'            => 'my_custom_write',
        'permission_callback' => [ $controller, 'permission_authenticated_with_nonce' ],
    ] );
} );

function my_custom_read( WP_REST_Request $request ) {
    return apd_rest_response( [ 'data' => 'value' ] );
}

function my_custom_write( WP_REST_Request $request ) {
    // Safe to mutate state — nonce already verified
    return apd_rest_response( [ 'created' => true ], 201 );
}

Response Helpers

APD provides helper functions for building consistent API responses:

PHP
// Success response
return apd_rest_response( $data, 200, $headers );

// Error response
return apd_rest_error( 'error_code', 'Error message', 400 );

// Paginated response
return apd_rest_paginated_response( $items, $total, $page, $per_page );

Functions

Helper Functions

Global helper functions available in your theme and plugins. All are prefixed with apd_.

Listings

PHP
apd_get_listing_field( $listing_id, $field_name, $default );
apd_set_listing_field( $listing_id, $field_name, $value );
apd_get_listing_views( $listing_id );
apd_increment_listing_views( $listing_id );
apd_get_related_listings( $listing_id, $limit, $args );

Taxonomies

PHP
apd_get_listing_categories( $listing_id );
apd_get_listing_tags( $listing_id );
apd_get_category_listings( $category_id, $args );
apd_get_categories_with_count( $args );
apd_get_category_icon( $category );
apd_get_category_color( $category );

Favorites

PHP
apd_add_favorite( $listing_id, $user_id );
apd_remove_favorite( $listing_id, $user_id );
apd_toggle_favorite( $listing_id, $user_id );
apd_is_favorite( $listing_id, $user_id );
apd_get_user_favorites( $user_id );
apd_get_favorites_count( $user_id );
apd_get_listing_favorites_count( $listing_id );

Reviews

PHP
apd_get_listing_reviews( $listing_id, $args );
apd_get_listing_rating( $listing_id );
apd_get_listing_review_count( $listing_id );
apd_create_review( $listing_id, $data );
apd_update_review( $review_id, $data );
apd_delete_review( $review_id );
apd_current_user_has_reviewed( $listing_id );

Settings

PHP
apd_get_setting( $key, $default );
apd_set_setting( $key, $value );
apd_get_all_settings();
apd_reviews_enabled();
apd_favorites_enabled();
apd_contact_form_enabled();
apd_format_price( $amount );

Templates

PHP
apd_get_template( $template, $args );
apd_get_template_part( $slug, $name, $args );
apd_get_template_html( $template, $args );
apd_template_exists( $template );
apd_locate_template( $template );

Caching

PHP
apd_cache_remember( $key, $callback, $expiration );
apd_cache_get( $key );
apd_cache_set( $key, $value, $expiration );
apd_cache_delete( $key );
apd_cache_clear_all();

CLI

WP-CLI Commands

APD includes WP-CLI commands for managing demo data without the browser.

Bash
# Generate all demo data with default quantities
wp apd demo generate

# Generate with custom quantities
wp apd demo generate --users=10 --listings=50

# Generate only specific data types
wp apd demo generate --types=categories,tags,listings

# Show current demo data counts
wp apd demo status

# Show counts as JSON
wp apd demo status --format=json

# Delete all demo data (with confirmation)
wp apd demo delete

# Delete without confirmation prompt
wp apd demo delete --yes

Generate Options

Option Default Description
--types=<types>allComma-separated list of data types: users, categories, tags, listings, reviews, inquiries, favorites, all
--users=<count>5Number of demo users to create (max 20)
--tags=<count>10Number of tags to create (max 10)
--listings=<count>25Number of listings to create (max 100)

Status Options

Option Default Description
--format=<format>tableOutput format: table, json, csv, yaml

Delete Options

Option Description
--yesSkip the confirmation prompt

Notes

Modules

Module System

External plugins can register themselves as APD modules via the apd_modules_init action hook, which fires at init priority 1.

Module Configuration

Each module is defined by a configuration array with these properties:

Property Type Default Description
namestringModule display name (required)
descriptionstring''Short description of the module
versionstring'1.0.0'Module version number
authorstring''Module author name
requiresarray[]Dependencies, e.g. [ 'core' => '1.0.0' ]
featuresarray[]Feature flags, e.g. [ 'link_checker', 'favicon_fetcher' ]
hidden_fieldsarray[]Core fields to hide when module is active
iconstring'dashicons-admin-plugins'Dashicon class for admin UI
priorityint10Load order priority (lower loads first)

Registration Functions

PHP
// Register a module with array configuration
apd_register_module( $slug, $config );

// Register a class-based module (implements ModuleInterface)
apd_register_module_class( $module );

// Unregister a module
apd_unregister_module( $slug );

// Get a registered module by slug
apd_get_module( $slug );

// Get all registered modules (supports orderby, order, feature args)
apd_get_modules( $args );

// Check if a module is registered
apd_has_module( $slug );

// Get modules that support a specific feature
apd_get_modules_by_feature( $feature );

// Check if module requirements are met
apd_module_requirements_met( $requires );

Developing Modules

To create an APD module, build a standard WordPress plugin that hooks into apd_modules_init. Here is a minimal example:

PHP
/**
 * Plugin Name: My APD Module
 * Description: Custom module for DamDir Directory.
 * Requires Plugins: damdir-directory
 */

add_action( 'apd_modules_init', function() {
    apd_register_module( 'my-module', [
        'name'        => 'My Module',
        'description' => 'Adds custom functionality to APD.',
        'version'     => '1.0.0',
        'author'      => 'Your Name',
        'requires'    => [ 'core' => '1.0.0' ],
        'features'    => [ 'my_feature' ],
        'icon'        => 'dashicons-admin-generic',
    ] );
} );

For more complex modules, implement ModuleInterface and use class-based registration:

PHP
use APD\Module\ModuleInterface;

class My_Module implements ModuleInterface {

    public function get_slug(): string {
        return 'my-module';
    }

    public function get_name(): string {
        return 'My Module';
    }

    public function get_description(): string {
        return 'Adds custom functionality to APD.';
    }

    public function get_version(): string {
        return '1.0.0';
    }

    public function get_config(): array {
        return [
            'requires' => [ 'core' => '1.0.0' ],
            'features' => [ 'my_feature' ],
            'icon'     => 'dashicons-admin-generic',
        ];
    }

    public function init(): void {
        // Hook into WordPress and APD actions/filters here.
    }
}

add_action( 'apd_modules_init', function() {
    apd_register_module_class( new My_Module() );
} );

Things to keep in mind: