Skip to content

Custom Post Type (Beta)

For sites that want jobs to live as native WordPress posts, the plugin can create a job_post custom post type. This gives you full access to WordPress queries, templates, REST API, and plugin integrations for your job data.

When enabled, the plugin registers a custom post type called job_post. A cron endpoint syncs jobs from the RecMan API into WordPress posts. Each sync cycle:

  1. Fetches all active jobs from the RecMan API
  2. Creates new WordPress posts for jobs not yet in the database
  3. Updates existing posts if the RecMan updated timestamp is newer
  4. Re-publishes posts that were previously marked as private (e.g. a job was temporarily removed and then re-added)
  5. Marks as private any published posts whose API ID no longer appears in the feed (expired/removed jobs are never deleted)

Each post stores the RecMan API job ID in the job_post_id meta field and a last_updated timestamp to avoid unnecessary updates.

PropertyValue
Post type slugjob_post
URL slug (rewrite)/jobs/ — e.g. yoursite.com/jobs/job-title/
Archive URLyoursite.com/jobs/
PublicYes
Show in RESTYes (available via /wp-json/wp/v2/job_post)
GutenbergYes
Supportstitle, editor, author, thumbnail, excerpt, comments, custom-fields
  1. Go to RecMan Jobs > Settings in your WordPress admin
  2. Enable Custom Post Type
  3. Set up a cron job to trigger the sync URL (provided in the settings)

The Custom Post Type sync is triggered via a secret cron URL:

https://yoursite.com/bonsy-recman-cron/{secret-key}

The secret key is generated automatically when the plugin is activated and can be found (and regenerated) in the plugin settings. Set up a server cron or external service (like EasyCron or cron-job.org) to hit this URL on a regular schedule.

The endpoint returns a JSON response with a log of actions taken (created, updated, expired, skipped).

Every synced job post stores its data as WordPress post meta. You can access these fields with get_post_meta() in your templates.

Meta KeyDescriptionExample Value
job_post_idRecMan API job ID (internal)12345
last_updatedUnix timestamp of last sync1710000000
nameJob nameSenior Developer
titleJob titleSenior Developer
companyNameCompany nameAcme Corp
workplaceWorkplace nameOslo Office
Meta KeyDescription
startDateJob start date
endDateJob end date
deadlineApplication deadline
createdWhen the job was created in RecMan
updatedWhen the job was last updated
Meta KeyDescription
numberOfPositionsNumber of open positions
accessionAccession/entry information
salarySalary information
sectorJob sector
typeEmployment type
positionPosition level
positionTypePosition type
branchCategoryPrimary branch category
branchPrimary branch
secondaryBranchCategorySecondary branch category
secondaryBranchSecondary branch
Meta KeyDescription
address1Street address line 1
address2Street address line 2
postalCodePostal/ZIP code
cityCity
countryCountry
location_cityMapped location city
location_countryMapped location country
location_regionMapped location region
locationsStructured location data (array)
Meta KeyDescription
departmentIdRecMan department ID
corporationIdRecMan corporation ID
departmentDepartment name
corporationCorporation name
Meta KeyDescription
logoLogo URL
videoUrlVideo URL
urlJob post URL on RecMan
applyApplication URL
websiteCompany website
facebookFacebook URL
linkedInLinkedIn URL
twitterTwitter URL
imagesImage data (array)
Meta KeyDescription
some_titleSocial media title
some_descriptionSocial media description
some_imageSocial media image URL
some_image_fullFull-size social media image URL
Meta KeyDescription
companyDescriptionCompany description text
contactsContact persons (array)
skillsRequired skills (array)
similarJobsRelated/similar jobs (array)
isExpiredWhether the job is expired
$jobs = new WP_Query([
'post_type' => 'job_post',
'posts_per_page' => 10,
'post_status' => 'publish',
]);
while ($jobs->have_posts()) {
$jobs->the_post();
$company = get_post_meta(get_the_ID(), 'companyName', true);
$deadline = get_post_meta(get_the_ID(), 'deadline', true);
$city = get_post_meta(get_the_ID(), 'city', true);
// Render your template...
}
wp_reset_postdata();
$jobs = new WP_Query([
'post_type' => 'job_post',
'posts_per_page' => -1,
'meta_query' => [
[
'key' => 'city',
'value' => 'Oslo',
'compare' => '=',
],
],
]);
$jobs = new WP_Query([
'post_type' => 'job_post',
'posts_per_page' => -1,
'meta_query' => [
[
'key' => 'departmentId',
'value' => '42',
'compare' => '=',
],
],
]);

Since show_in_rest is enabled, job posts are available at:

GET /wp-json/wp/v2/job_post
GET /wp-json/wp/v2/job_post/{id}

Custom meta fields are not exposed in the REST API by default. To expose specific fields, register them with show_in_rest:

add_action('init', function () {
register_post_meta('job_post', 'companyName', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
]);
register_post_meta('job_post', 'deadline', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
]);
// Add more fields as needed...
});

WordPress looks for template files in this order for single job posts:

  1. single-job_post.php (in your theme)
  2. single.php
  3. singular.php
  4. index.php

And for the archive:

  1. archive-job_post.php (in your theme)
  2. archive.php
  3. index.php

Create single-job_post.php in your theme to build a custom job detail page:

<?php get_header(); ?>
<?php while (have_posts()) : the_post(); ?>
<article class="job-post">
<h1><?php the_title(); ?></h1>
<?php if ($company = get_post_meta(get_the_ID(), 'companyName', true)) : ?>
<p class="job-company"><?= esc_html($company) ?></p>
<?php endif; ?>
<?php if ($deadline = get_post_meta(get_the_ID(), 'deadline', true)) : ?>
<p class="job-deadline">Deadline: <?= esc_html($deadline) ?></p>
<?php endif; ?>
<div class="job-content">
<?php the_content(); ?>
</div>
<?php if ($apply_url = get_post_meta(get_the_ID(), 'apply', true)) : ?>
<a href="<?= esc_url($apply_url) ?>" class="btn">Apply Now</a>
<?php endif; ?>
<?php
$contacts = get_post_meta(get_the_ID(), 'contacts', true);
if (is_array($contacts) && !empty($contacts)) : ?>
<h2>Contact</h2>
<ul>
<?php foreach ($contacts as $contact) : ?>
<li><?= esc_html($contact['name'] ?? '') ?> <?= esc_html($contact['email'] ?? '') ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</article>
<?php endwhile; ?>
<?php get_footer(); ?>

The Custom Post Type is useful when you need:

  • Jobs to appear in WordPress search results
  • Jobs in sitemaps generated by SEO plugins (Yoast, Rank Math, etc.)
  • Integration with other WordPress plugins that work with custom post types (Elementor, ACF, FacetWP, etc.)
  • REST API access to job data
  • Full control over templating with theme template files

For most use cases, the standard caching + shortcode/template approach is simpler and recommended.