REST API

Listora exposes 58 REST endpoints under the listora/v1 namespace. Every customer-facing surface (frontend listing UI, submission wizard, user dashboard, search, reviews, claims, favorites) is REST-driven; AJAX is reserved for admin-only operations (per the plugin's REST-first architecture rule).

Base URL: <your-site>/wp-json/listora/v1/

Auth model:

  • Public - GET reads (listings, search, single listing). No authentication required.
  • Auth - requires a valid user session (cookies + nonce) OR a WordPress Application Password.
  • Owner - only the listing's author (or a user with the listing's edit capability) can modify.
  • Admin - requires manage_options or manage_listora_settings.

Nonce header for browser clients: X-WP-Nonce: <wp_create_nonce("wp_rest")>. Apps using Application Passwords don't need this.

Response envelope (lists):

{
"listings": [ /* array of resource objects */ ],
"total": 247,
"pages": 21,
"has_more": true,
"cursor": "WyJsaXN0aW5nIiwxMjM0XQ==",
"next_cursor": "WyJsaXN0aW5nIiwxNDQ0XQ=="
}

Error contract:

{
"code": "listora_invalid_field",
"message": "Field 'price' is required",
"data": { "status": 400 }
}

Permission-denial codes: since 1.1.0, denied requests return structured codes instead of the generic rest_forbidden - listora_unauthorized with HTTP 401 when the caller is not authenticated, and listora_forbidden with HTTP 403 when an authenticated caller lacks the required capability or ownership. This applies to the Settings + notification-log endpoints (Free) and the Analytics + Photo Reviews endpoints (Pro); ownership failures correctly return 403.

Generated from audit/manifest.json. Re-run /wp-plugin-onboard --refresh after non-trivial commits to regenerate.

Listings (12)

Method Route Auth Handler Purpose
GET /listora/v1/dashboard/listings logged_in_permissions Dashboard_Controller::get_listings User's listings (cursor pagination)
GET /listora/v1/listings Public Listings_Controller::get_items List published listings (cursor pagination)
POST /listora/v1/listings/bulk Public Listings_Controller::get_bulk Fetch up to 50 listings by ID (offline cache)
DELETE /listora/v1/listings/{id} delete_listing_permissions Listings_Controller::delete_listing Owner soft-delete
POST /listora/v1/listings/{id}/deactivate deactivate_listing_permissions Listings_Controller::deactivate_listing Owner hides their listing from the directory (sets listor…
GET /listora/v1/listings/{id}/detail Public Listings_Controller::get_listing Single listing detail (card or full)
POST /listora/v1/listings/{id}/feature feature_listing_permissions Listings_Controller::feature_listing Upgrade listing to Featured
GET /listora/v1/listings/{id}/related Public Listings_Controller::get_related Related listings
POST /listora/v1/listings/{id}/renew renew_listing_permissions Listings_Controller::renew_listing Renew expired listing
GET /listora/v1/listings/{id}/renewal-quote renew_listing_permissions Listings_Controller::get_renewal_quote Renewal pricing/status
GET, POST /listora/v1/listings/{listing_id}/services __return_true / create_service_permissions Services_Controller::get_listing_services / create_service Listing services list/create
POST /listora/v1/listings/{listing_id}/services/reorder create_service_permissions Services_Controller::reorder_services Reorder services

Listings - Lifecycle (1)

Method Route Auth Handler Purpose
POST /listora/v1/listings/{id}/reactivate reactivate_listing_permissions Listings_Controller::reactivate_listing Owner restores a deactivated listing back to its prior pu…

Listings - Moderation (1)

Method Route Auth Handler Purpose
POST /listora/v1/listings/bulk-moderate Owner WBListora\REST\Listings_Controller::bulk_moderate Bulk moderation - approve/reject/feature/unfeature/trash …

Listings - Contact (1)

Method Route Auth Handler Purpose
POST /listora/v1/listings/(?P<id>[\d]+)/contact-form __return_true (anonymous allowed; nonce + honeypot + Anti_Spam pipeline gate inside handler) WBListora\Contact_Form::handle_rest_submission Free's listing contact form. Per-IP-per-listing 3/hour ca…

Reviews (5)

Method Route Auth Handler Purpose
GET /listora/v1/dashboard/reviews logged_in_permissions Dashboard_Controller::get_reviews User's reviews received/written
PUT, DELETE /listora/v1/reviews/{id} update_review_permissions / delete_review_permissions Reviews_Controller::update_review / delete_review Update/delete review
POST /listora/v1/reviews/{id}/helpful logged_in_permissions Reviews_Controller::vote_helpful Helpful vote
POST /listora/v1/reviews/{id}/reply owner_reply_permissions Reviews_Controller::owner_reply Listing owner reply
POST /listora/v1/reviews/{id}/report logged_in_permissions Reviews_Controller::report_review Report inappropriate review

Reviews (per-listing) (1)

Method Route Auth Handler Purpose
GET, POST /listora/v1/listings/{listing_id}/reviews Auth Reviews_Controller::get_listing_reviews / create_review List reviews / submit new review

Search (2)

Method Route Auth Handler Purpose
GET /listora/v1/search Public Search_Controller::search Filtered/geo/fulltext/facet search
GET /listora/v1/search/suggest Public Search_Controller::suggest Autocomplete suggestions

Submission (2)

Method Route Auth Handler Purpose
POST /listora/v1/submission/resend-verification Public Submission_Controller::resend_verification_endpoint Resend email verification. Deprecated in 1.3.0 with guest submission; submission now requires a logged-in account, so this endpoint is no longer used.
GET /listora/v1/submission/verify Public Submission_Controller::verify_endpoint REST mirror of email verify URL. Deprecated in 1.3.0 with guest submission; submission now requires a logged-in account, so this endpoint is no longer used.

Claims (3)

Method Route Auth Handler Purpose
GET, POST /listora/v1/claims admin_permissions / logged_in_permissions Claims_Controller::get_claims / submit_claim List all claims (admin) / submit claim
PUT /listora/v1/claims/{id} admin_permissions Claims_Controller::update_claim Approve/reject claim
GET /listora/v1/dashboard/claims logged_in_permissions Dashboard_Controller::get_my_claims User's claim requests

Favorites (2)

Method Route Auth Handler Purpose
GET, POST /listora/v1/favorites logged_in_permissions Favorites_Controller::get_favorites / add_favorite List/add favorites
DELETE /listora/v1/favorites/{listing_id} logged_in_permissions Favorites_Controller::remove_favorite Remove favorite

Services (1)

Method Route Auth Handler Purpose
GET, PUT, DELETE /listora/v1/services/{id} scoped Services_Controller::get_service / update_service / delete_service CRUD single service

User Dashboard (4)

Method Route Auth Handler Purpose
GET /listora/v1/dashboard/notifications logged_in_permissions Dashboard_Controller::get_notifications In-app notifications
PUT /listora/v1/dashboard/notifications/read logged_in_permissions Dashboard_Controller::mark_notifications_read Mark notifications read
GET, PUT /listora/v1/dashboard/profile logged_in_permissions Dashboard_Controller::get_profile / update_profile Dashboard profile (name, bio)
GET /listora/v1/dashboard/stats logged_in_permissions Dashboard_Controller::get_stats User dashboard stats (60s transient)

Listing Types (4)

Method Route Auth Handler Purpose
GET, POST /listora/v1/listing-types __return_true / create_item_permissions_check Listing_Types_Controller::get_items / create_item List/create listing types
GET, PUT, DELETE /listora/v1/listing-types/{slug} scoped Listing_Types_Controller::get_item / update_item / delete_item CRUD single listing type
GET /listora/v1/listing-types/{slug}/categories Public Listing_Types_Controller::get_categories Categories scoped to a listing type
GET /listora/v1/listing-types/{slug}/fields Public Listing_Types_Controller::get_fields Type fields schema

Settings (9)

Method Route Auth Handler Purpose
GET, PUT, DELETE /listora/v1/settings Admin Settings_Controller::get_all_settings / update_settings / reset_settings Plugin settings CRUD
GET /listora/v1/settings/app-config Public Settings_Controller::get_app_config Public bootstrap config (app/frontend)
GET /listora/v1/settings/export Admin Settings_Controller::export_settings Export settings JSON
POST /listora/v1/settings/import Admin Settings_Controller::import_settings Import settings JSON
GET /listora/v1/settings/maps Public Settings_Controller::get_map_settings Public map config
GET, DELETE /listora/v1/settings/notifications/log Admin Settings_Controller::get_notification_log / clear_notification_log View/clear notification log
GET /listora/v1/settings/notifications/log/export Admin Settings_Controller::export_notification_log Download notification log as CSV/JSON for archiving
POST /listora/v1/settings/notifications/log/retention Admin Settings_Controller::set_notification_retention Set notification log retention policy (days)
POST /listora/v1/settings/notifications/test Admin Settings_Controller::send_test_notification Send test notification email

Miscellaneous (7)

Method Route Auth Handler Purpose
GET /listora/v1/export/csv Admin Import_Export_Controller::export_csv Export listings CSV
POST /listora/v1/import/csv Admin Import_Export_Controller::import_csv Import CSV
POST /listora/v1/import/geojson Admin Import_Export_Controller::import_geojson Import GeoJSON with geo
POST /listora/v1/import/json Admin Import_Export_Controller::import_json Import JSON
POST /listora/v1/submit submit_listing_permissions Submission_Controller::submit_listing Frontend listing submission
POST /listora/v1/submit/check-duplicate logged_in_permissions Submission_Controller::check_duplicate_endpoint Pre-submit duplicate check
PUT /listora/v1/submit/{id} Owner Submission_Controller::edit_listing Owner edit listing

Authentication examples

Cookie + nonce (logged-in browser session)

WordPress core localizes the REST nonce automatically. Read it from wp.apiFetch (when using @wordpress/api-fetch) or the page's localized wpApiSettings.nonce:

// In a block's view.js (uses the apiFetch helper):
import apiFetch from '@wordpress/api-fetch';
const data = await apiFetch( { path: '/listora/v1/listings?per_page=12' } );

// Plain fetch with manual nonce:
const res = await fetch( '/wp-json/listora/v1/favorites', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.wpApiSettings.nonce },
body: JSON.stringify( { listing_id: 42 } )
} );

Application Password (apps / scripts)

# Generate an Application Password under wp-admin → Users → Your Profile → Application Passwords
curl -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{"title":"My Listing","type":"restaurant"}' \
https://yoursite.com/wp-json/listora/v1/submit

Pro endpoints

When wb-listora-pro is active, every route below registers under the same listora/v1 namespace and respects the same auth + nonce + rate-limit rules as Free. Permission per route is annotated as public (anyone), auth (logged-in only), cap:foo (requires that capability), or pro-toggle (feature toggle must be on at Settings → Features, where Pro toggles register since 1.1.0).

Needs (Reverse Listings)

Method Route Permission Purpose
GET /needs public Public needs feed (with filters: type, location, status)
GET /needs/{id} public Single need detail
POST /needs auth Submit a new need
PUT /needs/{id} auth + owner Edit a need
DELETE /needs/{id} auth + owner Delete a need
GET /needs/matching/{listing_id} auth + listing owner Needs matching a listing's type / location
GET /dashboard/needs auth "My Needs" + "My Responses" dashboard data

Credits & Plans

Method Route Permission Purpose
GET /credits/plans public Available pricing plans + entitlements
GET /credits/credit-packs public Credit packs available for purchase
POST /credits/purchase-plan auth Purchase / activate a plan (Hold-and-Commit flow)
POST /credits/admin-add cap:manage_listora_settings Manually grant credits to a user

Coupons

Method Route Permission Purpose
POST /coupons/validate public Validate a coupon code (returns discount + eligibility)
POST /coupons/generate-code cap:manage_listora_settings Auto-generate a unique coupon code

Badges & Verification

Method Route Permission Purpose
GET /listings/{listing_id}/badges public Badges assigned to a listing
POST /listings/{listing_id}/badges/{badge_id} cap:manage_listora_settings Assign a badge
DELETE /listings/{listing_id}/badges/{badge_id} cap:manage_listora_settings Remove a badge

Outgoing Webhooks

Method Route Permission Purpose
GET /webhooks cap:manage_listora_settings List configured outgoing webhooks
POST /webhooks cap:manage_listora_settings Create a webhook
GET /webhooks/{id} cap:manage_listora_settings Get webhook config
PUT /webhooks/{id} cap:manage_listora_settings Update webhook
DELETE /webhooks/{id} cap:manage_listora_settings Delete webhook
POST /webhooks/{id}/test cap:manage_listora_settings Fire a test payload
GET /webhooks/{id}/log cap:manage_listora_settings Recent delivery log
POST /webhooks/payment public + HMAC Inbound payment webhook receiver (Stripe + PayPal direct + WooCommerce / WooSubscriptions / MemberPress / PMPro / WooMemberships bridges)

Moderators

Method Route Permission Purpose
GET /moderators cap:manage_listora_moderators List configured moderators
POST /moderators/{user_id}/activate cap:manage_listora_moderators Promote a WP user to moderator
POST /moderators/{user_id}/deactivate cap:manage_listora_moderators Demote a moderator
GET /moderators/{user_id}/queue moderator or above Items assigned to this moderator
POST /moderators/reassign cap:manage_listora_moderators Re-route queue items to a different moderator
GET /moderators/stats moderator or above Throughput / SLA stats per moderator

Migration (Competitor)

Method Route Permission Purpose
GET /migration/detect cap:manage_listora_settings Detect installed source plugin + row counts
GET /migration/fields cap:manage_listora_settings Source-field schema for the picked source
POST /migration/preview cap:manage_listora_settings Dry-run preview of N rows
POST /migration/run cap:manage_listora_settings Run the migration (batched, queueable)

Google Import

Method Route Permission Purpose
GET /import/google/search cap:manage_listora_settings Google Places text search
GET /import/google/details cap:manage_listora_settings Get Place Details for a place_id
POST /import/google/import cap:manage_listora_settings Import a Place as a listing
GET /import/google/test cap:manage_listora_settings Smoke-test the API key

Visual / Bulk Import

Method Route Permission Purpose
POST /import/upload cap:manage_listora_settings Upload a CSV / JSON file for visual mapping
GET /import/fields cap:manage_listora_settings Auto-detected source fields
POST /import/preview cap:manage_listora_settings Preview rows with the proposed mapping
POST /import/start cap:manage_listora_settings Kick off the batched import
GET /import/status/{batch_id} cap:manage_listora_settings Poll batch progress
POST /import/cancel/{batch_id} cap:manage_listora_settings Abort a running batch
GET /import/templates cap:manage_listora_settings Saved mapping templates
GET/PUT/DELETE /import/templates/{id} cap:manage_listora_settings Manage a saved template

Compare Listings

Method Route Permission Purpose
GET /compare public Compare 2-4 listings side by side (returns merged data)
POST /compare/preview public Preview comparison set (used by Quick Compare modal)

Services Discovery

Method Route Permission Purpose
GET /services/search public Cross-listing service search ("find SEO services near me")
POST /services/compare public Compare service offerings across multiple listings

Audit Log

Method Route Permission Purpose
GET /audit-log cap:manage_listora_settings Recent audit entries (with filters)
GET /audit-log/export cap:manage_listora_settings CSV export of the current view
GET /audit-log/stats cap:manage_listora_settings Per-actor / per-event aggregates

Analytics

Method Route Permission Purpose
GET /analytics/overview cap:manage_listora_settings Site-wide views / clicks / submissions over time
GET /analytics/listing/{id} listing owner or admin Per-listing analytics
POST /analytics/track public Beacon endpoint - record a view / phone-click / website-click

Saved Searches

Method Route Permission Purpose
GET /saved-searches auth List my saved searches
POST /saved-searches auth Save the current search
PUT /saved-searches/{id} auth + owner Rename or update frequency
DELETE /saved-searches/{id} auth + owner Delete

Lead Forms (Pro contact)

Method Route Permission Purpose
POST /listings/{id}/contact public + nonce Pro lead form (replaces Free /contact-form when lead_form toggle is on)

Filters & extensibility

Every endpoint that returns a resource also fires a wb_listora_rest_prepare_{resource} filter so Pro / themes / third-party code can inject custom fields without forking the controller:

add_filter( 'wb_listora_rest_prepare_listing', function ( $data, $post, $request ) {
$data['my_field'] = get_post_meta( $post->ID, '_my_field', true );
return $data;
}, 10, 3 );

See Hooks Reference → REST Response Filters for the full list of wb_listora_rest_prepare_* filters.

Rate limits

Public-write endpoints (POST /submissions, POST /listings/{id}/reviews, POST /claims, POST /listings/{id}/contact-form) are rate-limited per IP via sliding-window counters. Since 1.1.0, when Listora Pro is active its public read-only endpoints (credit packs, pricing plans, needs feed, comparisons, service search, badges) are also throttled - a per-IP cap of 60 requests/minute that fails open. See Rate Limiting & Abuse Controls for the default caps + per-endpoint windows + how to tune.

Related