Developer guide
The Reach Vacancy API is a RESTful toolkit for surfacing live recruitment data across your career site or back-office workflows. Each request is built from simple URL segments, making it easy to fetch vacancies, metadata and supporting content in the format your application needs.
Every account has a unique API key that is supplied to them upon request. During development you can use our dummy data key in lieu of recieving the live data key.
a83d66d29041c407e8abf4187c533053
.
The API key forms part of the hostname, all requests should be over HTTPS. For example, development requests can target
https://a83d66d29041c407e8abf4187c533053.reach-ats.com/
.
A condensed reference to the most commonly used JSON endpoints.
All live vacancies
External feed (use internalListing
for internal sites).
https://a83d66d29041c407e8abf4187c533053.reach-ats.com/get/job/listing/-/-/-/-/JSON
Radius search listing
Add postcode and range; internal variant available via internalListingByPostcode
.
https://a83d66d29041c407e8abf4187c533053.reach-ats.com/get/job/listingByPostcode/B610GD/250/-/-/-/-/JSON
Vacancy information
Use internalInformation
for intranet flows.
https://a83d66d29041c407e8abf4187c533053.reach-ats.com/get/job/information/129436/JSON
Vacancy advert text
Raw HTML copy for the advert.
View advert JSONhttps://a83d66d29041c407e8abf4187c533053.reach-ats.com/get/job/advert/129436/JSON
Vacancy files
Supporting documents and metadata.
View file listhttps://a83d66d29041c407e8abf4187c533053.reach-ats.com/get/job/fileurls/129436/JSON
Embed via iframe to collect job alert registrations.
Load iframe demohttps://a83d66d29041c407e8abf4187c533053.reach-ats.com/jobalert/load/6/2
Populate dropdowns and filters with live account metadata.
https://a83d66d29041c407e8abf4187c533053.reach-ats.com/get/job/offices
Also available: locations
, roles
, types
, counties
.
You can query the API from any modern language, including PHP, Python, .NET, Perl and Java. and recieve the payload in the format that works best for you.
Returns all live jobs in XML format.
View exampleN.B. View the page source in your browser; XML responses render as raw markup.
Each request is constructed from five segments:
get
.job
.listing
or advert
.-
) for empty values.XML
, JSON
, CSV
, PIPE
or RAW
. This is always the last parameter of the URL.
Example: /get/job/listing/-/-/-/-/JSON
fetches the job listing,
skips optional filters, and requests JSON.
<response generated="2011-06-30T16:48:07+01:00" records="3"> <record> <id><![CDATA[25618]]></id> <title><![CDATA[Assistant Store Manager - Camden]]></title> <category><![CDATA[Sales]]></category> <role><![CDATA[Sales]]></role> <type><![CDATA[Permanent]]></type> <hours><![CDATA[35]]></hours> <salarydescription><![CDATA[25,000]]></salarydescription> <closingdate><![CDATA[2013-01-04]]></closingdate> </record> </response>
The vacancy module exposes all live job data. Calls are grouped into packages, with the
job
package covering everything from vacancy listings to supporting media.
Use filters and alternate encodings to power different channels such as career sites, aggregators or partner feeds.
Endpoint | Purpose | Key parameters |
---|---|---|
/get/job/listing/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Default vacancy feed (XML by default). | All filters optional; use - to skip and append the desired format. |
/get/job/listingByPostcode/POSTCODE/RANGE/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Radius search by full or partial postcode. | Provide postcode (max 15 chars) and range in miles; remaining filters mirror the base listing. |
/get/job/listingBySource/SOURCES/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Limit results to one or more sources. | First segment accepts comma-separated source IDs or short codes. |
/get/job/listingBySourceAndPostcode/SOURCES/POSTCODE/RANGE/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Combines source filters with postcode radius. | Adds postcode and range parameters to the source listing signature. |
/get/job/internalListing/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Surfacing vacancies for internal audiences. | Internal variants exist for postcode and source-based filtering. |
/get/job/internalListingByPostcode/POSTCODE/RANGE/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Internal-only vacancies filtered by postcode radius. | Matches the public postcode signature but limits sources to internal types. |
/get/job/internalListingBySource/SOURCES/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Internal jobs available through specific sources. | Accepts comma-separated internal source IDs or short codes. |
/get/job/internalListingBySourceAndPostcode/SOURCES/POSTCODE/RANGE/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Hybrid filter for internal roles by source and geography. | Combines the source list with postcode radius parameters. |
/get/job/listingCounty/KEYWORDS/COUNTY/TYPE/ROLE/ENCODING | County-specific vacancy search. | Swap LOCATION for COUNTY to align with data surfaced by /get/lists/counties . |
/get/job/referralListing/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Feed of roles assigned to employee referral programmes. | Filters vacancies that have referral apply sources. |
/get/job/speculativeListing/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Speculative opportunities open for general interest applications. | Ideal when pairing with /get/lists/types filters. |
/get/job/openDayListing/KEYWORDS/LOCATION/TYPE/ROLE/ENCODING | Events and open day listings published as vacancies. | Keeps the core filter arguments while surfacing event-specific metadata. |
Endpoint | Purpose | Notes |
---|---|---|
/get/job/information/{JOB_ID}/ENCODING | Full vacancy record, including contact and workflow data. | Defaults to XML; pass /JSON etc. to change format. |
/get/job/advert/{JOB_ID}/ENCODING | Advert copy returned as RAW HTML. | Aliases: /text , /description . Default encoding is RAW |
/get/job/fileurls/{JOB_ID}/ENCODING | Supporting documents with display names. | XML by default; use /JSON for client apps. |
/get/job/files/{JOB_ID}/ENCODING | Detailed metadata for each vacancy file. | Pairs with /file/{FILE_UID} to download content. |
/get/job/webimage/{JOB_ID}/ENCODING | Retrieves the primary job image. | Streams the binary directly for hero imagery. Default encoding is RAW |
/get/job/video/{JOB_ID}/ENCODING | Job video embed markup. | Use /video_embed for iframe-ready content. Default encoding is RAW |
/get/job/applyurl/{JOB_ID}/ENCODING | External application URL. | See /applyurlsource/{JOB_ID}/{TYPE} for internal vs external sources. Default encoding is RAW |
/get/job/referralurl/{JOB_ID}/ENCODING | Referral programme application link. | Targets referral source type. Default encoding is RAW |
Endpoint | Returns | Notes |
---|---|---|
/get/job/locations | Distinct location values across all jobs. | Use for search filters and job alerts. |
/get/job/counties | Unique county list. | Pairs with listingCounty for search. |
/get/job/roles | Distinct job roles. | Provides options for discipline selectors. |
/get/job/types | Distinct employment types. | Excludes the registration placeholder. |
/get/job/live_locations | Locations with currently live vacancies. | Restrict filters to active options. |
/get/job/live_roles | Roles with live vacancies. | Ideal for job alert pickers. |
/get/job/regions | Distinct regions from office addresses. | Useful for multi-region sites. |
/get/job/offices | Office records including logos and CDN URLs. | Power branded job cards or directory pages. |
/get/job/groups | Department or group identifiers. | Map vacancies to internal teams. |
/get/job/all_categories | Managed category list with IDs. | Returns inactive options too (status 0 filters applied). |
The lists module provides lightweight
endpoints for populating filters, dropdowns and form pickers. Each call mirrors a metadata query from the Job controller but exposes it under the
/get/lists
namespace, returning JSON by default for easy client-side consumption.
Endpoint | Returns | Notes |
---|---|---|
/get/lists/categories | Distinct job categories currently in use. | Pulls from live job data so it always reflects published vacancies. |
/get/lists/locations | Unique list of locations. | Use for geography filters on search forms. |
/get/lists/counties | Distinct counties across the account. | Ideal for regional breakdowns alongside locations. |
/get/lists/roles | All role names referenced by vacancies. | Populate speciality/discipline selectors. |
/get/lists/types | Distinct job types (e.g. Permanent, Part Time). | Excludes the registration placeholder. |
Use the live_*
endpoints to limit filter options to values that have active vacancies.
Endpoint | Returns | Notes |
---|---|---|
/get/lists/live_locations | Locations associated with live jobs. | Ideal for map filters or alert sign-up forms. |
/get/lists/live_roles | Role values that have at least one live vacancy. | Keeps drop-downs free of empty categories. |
Endpoint | Returns | Notes |
---|---|---|
/get/lists/offices | Full office records including branding assets. | Perfect for office directories or branded cards. |
/get/lists/regions | Distinct regions defined in office addresses. | Align regional career pages with data. |
/get/lists/groups | Department or group IDs with display names. | Synchronise internal team filters. |
/get/lists/all_categories | Managed category options (ID and label). | Includes inactive catalogue entries for administration. |
listing
) to offer contextual filtering.Job alert sign-up forms typically ask candidates to opt into roles, locations or employment types they are interested in. The vacancy metadata endpoints provide the lookup data you need to build these filters dynamically, ensuring your alert preferences always reflect the current catalogue.
Reach ships a responsive HTML page for job alert sign-up. Embed it inside an iframe or surface it within a modal/popup to collect alert registrations.
Preview alert pageIt may be possible for us to create a bespoke sign up template for you mimicing your branding styles, please let us know if this is something you require.
The Jobalert
module exposes helper routes for pre-built signup forms that you can iFrame or load in a modal. Swap the rows
, version
, client
, or group
parameters as needed.
Endpoint | Purpose | Key Options |
---|---|---|
/jobalert/load/{ROWS}/{VERSION} | Default alert form with type/role filters. | ROWS controls number item in the multple select boxes; VERSION picks template (1 legacy, 2 updated). |
/jobalert/loadWithLocation/{ROWS}/{VERSION} | Signup including location dropdown. | VERSION 1 (legacy), 2 (jobalertlocation2 ), 3 (jobalertlocation3 ). |
/jobalert/loadWithTown/{ROWS}/{VIEW} | Signup using town list instead of region. | Set VIEW to a template name (e.g. jobalertlocation2 ) or leave numeric for defaults. |
Bespoke template endpoints | ||
/jobalert/loadClient/{ROWS}/{CLIENT}/{LIVE} | Serve a named client template with live or cached data. | CLIENT view name (e.g. jobalert2 ); set LIVE=true for lists to only use current live vacancy data. |
/jobalert/loadClientGroup/{ROWS}/{GROUP}/{CLIENT} | Pre-filter form by group/department. | GROUP slug or - for all; CLIENT selects the view template. |
The PHP snippets below demonstrate how to consume listing data and specific vacancy detail. Swap in your account’s API key and adjust the rendering code to match your templating approach.
<?php class ReachApiClient { private string $host; public function __construct(private readonly string $apiKey) { $this->host = sprintf('https://%s.reach-ats.com', $apiKey); } public function get(string $path): string { $url = $this->host . $path; if (ini_get('allow_url_fopen')) { $response = @file_get_contents($url); } elseif (function_exists('curl_init')) { $curl = curl_init($url); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($curl); curl_close($curl); } else { throw new RuntimeException('Unable to fetch data; enable allow_url_fopen or install cURL.'); } if ($response === false) { throw new RuntimeException("Reach ATS request failed for {$url}"); } return $response; } public function getJson(string $path): array { return json_decode($this->get($path), true, 512, JSON_THROW_ON_ERROR); } public static function renderOptions(array $values, ?string $selected = null): string { $items = array_unique(array_filter(array_map('strval', $values))); sort($items, SORT_NATURAL | SORT_FLAG_CASE); return implode('', array_map(static function (string $value) use ($selected): string { $isSelected = $selected !== null && strcasecmp($selected, $value) === 0; return sprintf( '<option value="%s"%s>%s</option>', rawurlencode($value), $isSelected ? ' selected' : '', htmlspecialchars($value, ENT_QUOTES, 'UTF-8') ); }, $items)); } public function listing( string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/listing/', [ $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function listingByPostcode( string $postcode, string $range, string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/listingByPostcode/', [ $postcode, $range, $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function listingBySource( string $sources, string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/listingBySource/', [ $sources, $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function listingBySourceAndPostcode( string $sources, string $postcode, string $range, string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/listingBySourceAndPostcode/', [ $sources, $postcode, $range, $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function internalListing( string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/internalListing/', [ $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function internalListingByPostcode( string $postcode, string $range, string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/internalListingByPostcode/', [ $postcode, $range, $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function internalListingBySource( string $sources, string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/internalListingBySource/', [ $sources, $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function internalListingBySourceAndPostcode( string $sources, string $postcode, string $range, string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/internalListingBySourceAndPostcode/', [ $sources, $postcode, $range, $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function listingByCounty( string $keywords = '-', string $county = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/listingCounty/', [ $keywords, $county, $type, $role, $this->formatEncoding($encoding), ])); } public function referralListing( string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/referralListing/', [ $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function speculativeListing( string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/speculativeListing/', [ $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function openDayListing( string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/openDayListing/', [ $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function jobInformation(int|string $jobId, string $encoding = 'JSON'): array { return $this->getJson($this->buildPath('/get/job/information/', [ $jobId, $this->formatEncoding($encoding), ])); } public function jobAdvert(int|string $jobId): string { return $this->get($this->buildPath('/get/job/advert/', [$jobId])); } public function jobFileUrls(int|string $jobId, string $encoding = 'JSON'): array { return $this->getJson($this->buildPath('/get/job/fileurls/', [ $jobId, $this->formatEncoding($encoding), ])); } private function buildPath(string $prefix, array $segments): string { return $prefix . implode('/', array_map([$this, 'segment'], $segments)); } private function segment(null|int|string $value): string { if ($value === null || $value === '') { return '-'; } if ($value === '-') { return '-'; } return rawurlencode((string) $value); } private function formatEncoding(?string $encoding): string { return strtoupper($encoding ?? 'JSON'); } } $client = new ReachApiClient('a83d66d29041c407e8abf4187c533053'); $listing = $client->listing(); // Apply simple, client-side filtering beyond the API segments. $filters = [ 'division' => $_GET['division'] ?? null, 'officename' => $_GET['officename'] ?? null, 'hours' => $_GET['hours'] ?? null, 'role' => $_GET['role'] ?? null, ]; $filtered = array_filter($listing, static function (array $job) use ($filters): bool { foreach ($filters as $key => $value) { if ($value === null || $value === '') { continue; } if (!isset($job[$key]) || strcasecmp($job[$key], $value) !== 0) { return false; } } return true; }); $options = [ 'division' => ReachApiClient::renderOptions(array_column($listing, 'division'), $filters['division']), 'officename' => ReachApiClient::renderOptions(array_column($listing, 'officename'), $filters['officename']), 'hours' => ReachApiClient::renderOptions(array_column($listing, 'hours'), $filters['hours']), 'role' => ReachApiClient::renderOptions(array_column($listing, 'role'), $filters['role']), ]; ?> <form method="get" action=""> <label> Division <select name="division" onchange="this.form.submit()"> <option value="">All divisions</option> <?= $options['division']; ?> </select> </label> <label> Office <select name="officename" onchange="this.form.submit()"> <option value="">All offices</option> <?= $options['officename']; ?> </select> </label> <label> Role <select name="role" onchange="this.form.submit()"> <option value="">All roles</option> <?= $options['role']; ?> </select> </label> <label> Hours <select name="hours" onchange="this.form.submit()"> <option value="">Any hours</option> <?= $options['hours']; ?> </select> </label> <noscript><button type="submit">Apply filters</button></noscript> </form> <p>Showing <strong><?= count($filtered); ?></strong> of <strong><?= count($listing); ?></strong> vacancies.</p> <?php foreach ($filtered as $job): ?> <?php $jobId = $job['id']; $job = $client->jobInformation($jobId); $advertHtml = $client->jobAdvert($jobId); $attachments = $client->jobFileUrls($jobId); ?> <section class="vacancy-summary"> <h3><?= htmlspecialchars($job['title'], ENT_QUOTES, 'UTF-8'); ?></h3> <div class="vacancy-advert"><?= $advertHtml; ?></div> <?php if (!empty($attachments)): ?> <h4>Attachments</h4> <ul> <?php foreach ($attachments as $file): ?> <li> <a href="<?= htmlspecialchars($file['url'] ?? '#', ENT_QUOTES, 'UTF-8'); ?>" target="_blank"> <?= htmlspecialchars($file['type'] ?? 'Download', ENT_QUOTES, 'UTF-8'); ?> </a> </li> <?php endforeach; ?> </ul> <?php endif; ?> </section> <?php endforeach; ?> ?>
<?php try { $client = new ReachApiClient('a83d66d29041c407e8abf4187c533053'); $jobId = 129436; $information = $client->jobInformation($jobId); $advertHtml = $client->jobAdvert($jobId); $fileAttachments = $client->jobFileUrls($jobId); } catch (Throwable $exception) { error_log($exception->getMessage()); // Handle gracefully (show fallback message, etc.) } if (!empty($information)) { $vacancy = $information[0]; echo '<h3>' . htmlspecialchars($vacancy['title'] ?? 'Vacancy', ENT_QUOTES, 'UTF-8') . '</h3>'; echo $advertHtml; // Already HTML if (!empty($fileAttachments)) { echo '<h4>Supporting documents</h4><ul>'; foreach ($fileAttachments as $file) { $label = htmlspecialchars($file['type'] ?? 'Download', ENT_QUOTES, 'UTF-8'); $url = htmlspecialchars($file['url'] ?? '#', ENT_QUOTES, 'UTF-8'); echo '<li><a href="' . $url . '" target="_blank">' . $label . '</a></li>'; } echo '</ul>'; } }
Drop these helpers into your theme’s functions.php
file and register shortcodes for listings, vacancy detail pages
and alert signup URLs. The examples use strict typing, shared network helpers and defensive error handling.
<?php class Reach_Api_Helper { private string $host; public function __construct(private readonly string $apiKey) { $this->host = sprintf('https://%s.reach-ats.com', $apiKey); } public function get(string $path): string { $url = $this->host . $path; if (ini_get('allow_url_fopen')) { $response = @file_get_contents($url); } elseif (function_exists('curl_init')) { $curl = curl_init($url); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($curl); curl_close($curl); } else { throw new RuntimeException('Reach request requires allow_url_fopen or cURL support.'); } if ($response === false) { throw new RuntimeException("Reach request failed for {$url}"); } return $response; } public function getJson(string $path): array { return json_decode($this->get($path), true, 512, JSON_THROW_ON_ERROR); } public function listing( string $keywords = '-', string $location = '-', string $type = '-', string $role = '-', string $encoding = 'JSON' ): array { return $this->getJson($this->buildPath('/get/job/listing/', [ $keywords, $location, $type, $role, $this->formatEncoding($encoding), ])); } public function jobInformation(int|string $jobId, string $encoding = 'JSON'): array { return $this->getJson($this->buildPath('/get/job/information/', [ $jobId, $this->formatEncoding($encoding), ])); } public function jobAdvert(int|string $jobId): string { return $this->get($this->buildPath('/get/job/advert/', [$jobId])); } public function jobFileUrls(int|string $jobId, string $encoding = 'JSON'): array { return $this->getJson($this->buildPath('/get/job/fileurls/', [ $jobId, $this->formatEncoding($encoding), ])); } private function buildPath(string $prefix, array $segments): string { return $prefix . implode('/', array_map([$this, 'segment'], $segments)); } private function segment(null|int|string $value): string { if ($value === null || $value === '') { return '-'; } if ($value === '-') { return '-'; } return rawurlencode((string) $value); } private function formatEncoding(?string $encoding): string { return strtoupper($encoding ?? 'JSON'); } }
Usage: [reach-listing key="YOUR_KEY" detail_slug="/vacancies/detail" qs_param="vacancy_id"]
function reach_listing_shortcode(array $atts): string { $atts = shortcode_atts([ 'key' => 'a83d66d29041c407e8abf4187c533053', 'detail_slug' => '/vacancies/detail', 'qs_param' => 'vacancy_id', ], $atts, 'reach-listing'); $api = new Reach_Api_Helper($atts['key']); try { $jobs = $api->listing(); } catch (Throwable $exception) { error_log($exception->getMessage()); return '<p>Vacancies are unavailable right now.</p>'; } if (empty($jobs)) { return '<p>No live vacancies at the moment.</p>'; } $output = ''; foreach ($jobs as $job) { $advertExcerpt = !empty($job['shortdescription']) ? $job['shortdescription'] : substr(strip_tags($api->jobAdvert($job['id'])), 0, 200) . '...'; $detailUrl = esc_url(add_query_arg($atts['qs_param'], $job['id'], $atts['detail_slug'])); $applyUrl = esc_url($job['applyurl']); $output .= sprintf( '<article class="vacancy-card" data-group="%s"> <header> <h3><a href="%s">%s</a></h3> <p><strong>Location:</strong> %s</p> <p><strong>Function:</strong> %s</p> </header> <p>%s</p> <p> <a class="btn btn-primary" target="_blank" href="%s">Apply now</a> <a class="btn btn-outline" href="%s">More detail</a> </p> </article>', esc_attr($job['group'] ?? ''), esc_url($detailUrl), esc_html($job['title']), esc_html($job['location'] ?? 'Not specified'), esc_html($job['role'] ?? 'Not specified'), esc_html($advertExcerpt), $applyUrl, esc_url($detailUrl) ); } return $output; } add_shortcode('reach-listing', 'reach_listing_shortcode');
Usage: [reach-detail key="YOUR_KEY" qs_param="vacancy_id"]
function reach_detail_shortcode(array $atts): string { $atts = shortcode_atts([ 'key' => 'a83d66d29041c407e8abf4187c533053', 'qs_param' => 'vacancy_id', ], $atts, 'reach-detail'); $vacancyId = isset($_GET[$atts['qs_param']]) ? sanitize_text_field($_GET[$atts['qs_param']]) : null; if (!$vacancyId) { return '<p>Please select a vacancy.</p>'; } $api = new Reach_Api_Helper($atts['key']); try { $info = $api->jobInformation($vacancyId); $detail = $info[0] ?? []; $advert = $api->jobAdvert($vacancyId); $files = $api->jobFileUrls($vacancyId); } catch (Throwable $exception) { error_log($exception->getMessage()); return '<p>We can’t load that vacancy right now.</p>'; } if (empty($detail)) { return '<p>Sorry, we can’t find that vacancy in our system.</p>'; } ob_start(); ?> <article class="vacancy-detail"> <h2><?= esc_html($detail['title']); ?></h2> <p> <strong>Location:</strong> <?= esc_html($detail['location'] ?? ''); ?><br /> <strong>Category:</strong> <?= esc_html($detail['category'] ?? ''); ?><br /> <strong>Closing date:</strong> <?= esc_html($detail['closingdate'] ?? ''); ?> </p> <div class="vacancy-advert"><?= wp_kses_post($advert); ?></div> <p><a class="btn btn-primary" target="_blank" href="<?= esc_url($detail['applyurl'] ?? '#'); ?>">Apply now</a></p> <?php if (!empty($files)): ?> <h3>Supporting documents</h3> <ul> <?php foreach ($files as $file): ?> <li><a href="<?= esc_url($file['url'] ?? '#'); ?>" target="_blank"><?= esc_html($file['type'] ?? 'Download'); ?></a></li> <?php endforeach; ?> </ul> <?php endif; ?> </article> <?php return ob_get_clean(); } add_shortcode('reach-detail', 'reach_detail_shortcode');
Usage: [reach-jobalert-url key="YOUR_KEY" account_id="6" template_id="2"]
. Returns a ready-to-embed URL.
function reach_jobalert_url_shortcode(array $atts): string { $atts = shortcode_atts([ 'key' => 'a83d66d29041c407e8abf4187c533053', 'account_id' => '6', 'template_id' => '2', ], $atts, 'reach-jobalert-url'); return sprintf( 'https://%s.reach-ats.com/jobalert/load/%d/%d', rawurlencode($atts['key']), (int) $atts['account_id'], (int) $atts['template_id'] ); } add_shortcode('reach-jobalert-url', 'reach_jobalert_url_shortcode');