# REST API

## Overview

The Loxo plugin exposes a REST API for programmatic control of job synchronization, webhooks, job data, and the Google Indexing integration. All endpoints live under the `loxo/v1` namespace on your own site:

```
https://example.com/wp-json/loxo/v1/
```

Replace `example.com` with your site’s domain in every example below. Every endpoint requires an administrator account (the `manage_options` capability) except [Job View Tracking](https://loxo.w4dev.com/docs/rest-api/#job-view-tracking), which is public, and the [Webhook Event Listener](https://loxo.w4dev.com/docs/rest-api/#webhook-event-listener), which is called by Loxo itself.

## Authentication

- **Cookie authentication** — used automatically by the plugin’s own admin screens while you are logged in to WordPress.
- **Application passwords** — for external integrations (WordPress 5.6+). Create one under *Users → Profile → Application Passwords*, then send it with HTTP Basic auth.

```
curl -u "admin:abcd efgh ijkl mnop" \
  https://example.com/wp-json/loxo/v1/job-sync
```

All examples below assume the `-u` flag shown here.

## Response Conventions

The API answers in two shapes, and knowing which to expect saves confusion:

**Status envelope** — the job synchronization endpoints reply with WordPress’s `code` / `message` / `data.status` envelope *even on success*. Read the HTTP status code and the `code` field, not the presence of an error shape:

```
{
  "code": "sync_scheduled",
  "message": "Background synchronization scheduled.",
  "data": { "status": 202 }
}
```

**Plain object** — everything else replies with a flat JSON object, most often carrying a `message` key, plus endpoint-specific data:

```
{ "message": "API Cache cleared." }
```

## Job Synchronization

One resource, five methods. The sync runs in the background; these endpoints start it and control it while it runs.

### POST /wp-json/loxo/v1/job-sync

Start a background job synchronization. No parameters.

```
curl -X POST -u "admin:abcd efgh ijkl mnop" \
  https://example.com/wp-json/loxo/v1/job-sync
```

**Response — 202 Accepted**

```
{
  "code": "sync_scheduled",
  "message": "Background synchronization scheduled.",
  "data": { "status": 202 }
}
```

**Errors:** `409 already_running` — another sync is already in progress.

### GET /wp-json/loxo/v1/job-sync

Get the current synchronization status. `message` carries progress text while a sync is running.

**Response — 200 OK**

```
{
  "running": true,
  "paused": false,
  "cancelled": false,
  "process_running": true,
  "message": "Synchronizing jobs..."
}
```

### PATCH /wp-json/loxo/v1/job-sync

Pause the running synchronization. The pause takes effect at the next safe point, so poll `GET` until `paused` is `true`.

**Response — 200 OK**

```
{
  "code": "sync_paused",
  "message": "Pause requested, wait for response.",
  "data": { "status": 200 }
}
```

**Errors:** `409 not_running` — no sync in progress · `409 already_paused`

### PUT /wp-json/loxo/v1/job-sync

Resume a paused synchronization.

**Response — 202 Accepted**

```
{
  "code": "sync_resumed",
  "message": "Background synchronization resumed.",
  "data": { "status": 202 }
}
```

**Errors:** `409 not_paused` — nothing to resume · `409 already_running`

### DELETE /wp-json/loxo/v1/job-sync

Cancel the ongoing synchronization. Like pause, cancellation is picked up at the next safe point.

**Response — 200 OK**

```
{
  "code": "sync_cancelled",
  "message": "Cancellation requested, wait for response.",
  "data": { "status": 200 }
}
```

## Webhook Management

Loxo can push job changes to your site in real time through three [webhooks](https://loxo.w4dev.com/docs/webhooks/): `job.create`, `job.update`, and `job.destroy`. These endpoints register and manage them on the Loxo side. All three accept one optional parameter:

| Parameter | Type | Required | Description |
|---|---|---|---|
| `callback_url` | string | No | The URL Loxo should deliver events to. Defaults to this site’s own event listener endpoint. |

### POST /wp-json/loxo/v1/loxo-webhooks

Register the missing webhooks with Loxo. Already-registered webhooks are left alone, so calling this repeatedly is safe.

```
curl -X POST -u "admin:abcd efgh ijkl mnop" \
  https://example.com/wp-json/loxo/v1/loxo-webhooks
```

**Response — 200 OK**

```
{ "message": "3 Webhook Registered." }
```

### GET /wp-json/loxo/v1/loxo-webhooks

Check which of the three webhooks are registered.

**Response — 200 OK** (all present)

```
{ "message": "All Webhooks are present." }
```

**Response — 200 OK** (some missing)

```
{
  "message": "Missing Webhooks - job.create, job.update.",
  "missing": 2
}
```

### DELETE /wp-json/loxo/v1/loxo-webhooks

Remove all webhooks this site registered with Loxo.

**Response — 200 OK**

```
{ "message": "3 Webhook Deleted." }
```

## Webhook Event Listener

### POST /wp-json/loxo/v1/loxo-event

The receiving end of the webhooks above: this is the callback URL registered with Loxo, and Loxo calls it when a job is created, updated, or deleted. It is documented here so you know what the URL is for — **you never need to call it yourself**, and it is not a supported surface for pushing your own data in.

## Job Expiration

### POST /wp-json/loxo/v1/job-expiration

Set the expiration date of a job. `PUT` is accepted as an alias.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | integer | Yes | The WordPress post ID of the job (not the Loxo job ID). |
| `date_expires` | string | Yes | Expiration date in `Y-m-d` format, e.g. `2026-12-31`. |

```
curl -X POST -u "admin:abcd efgh ijkl mnop" \
  -d "id=123" -d "date_expires=2026-12-31" \
  https://example.com/wp-json/loxo/v1/job-expiration
```

**Response — 200 OK**

```
{
  "success": true,
  "data": { "date_expires": "2026-12-31" }
}
```

**Note:** unlike the other endpoints, a failed call (unknown ID, invalid date) answers `{ "success": false, "data": false }` with HTTP status `200` — check the `success` field, not the status code.

## Job View Tracking

### POST /wp-json/loxo/v1/job-view

Increment a job’s view counter. This is the endpoint the plugin’s front end calls on each job page view; it is **public** — no authentication — and only works while view tracking is enabled in the plugin settings (see [Job View Tracking](https://loxo.w4dev.com/docs/job-view-tracking/)).

| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | integer | Yes | The WordPress post ID of the job. |

**Response — 200 OK**

```
{ "success": true }
```

**Errors:** `403 disabled` — view tracking is turned off · `400 invalid_job` — the ID is not a job.

A third `403` can come from WordPress rather than the plugin. The route needs no authentication, but if the request sends an `X-WP-Nonce` header, WordPress checks it before the route runs and rejects a stale one with `rest_cookie_invalid_nonce`. The plugin’s own front-end script always sends that header, and the token it uses is baked into the page HTML, so on a page served from a cache older than about a day every view request fails this way. Calling the endpoint yourself, send no nonce header at all rather than an old one.

## Data Management

### DELETE /wp-json/loxo/v1/cache

Clear all cached Loxo API responses. Harmless — the cache rebuilds on demand.

```
curl -X DELETE -u "admin:abcd efgh ijkl mnop" \
  https://example.com/wp-json/loxo/v1/cache
```

**Response — 200 OK**

```
{ "message": "API Cache cleared." }
```

### DELETE /wp-json/loxo/v1/data

⚠️ **Destructive.** Deletes every imported job from WordPress — posts, taxonomies, and metadata. There is no undo; the jobs can only be restored by running a fresh synchronization from Loxo.

```
curl -X DELETE -u "admin:abcd efgh ijkl mnop" \
  https://example.com/wp-json/loxo/v1/data
```

**Response — 200 OK**

```
{ "message": "All data deleted." }
```

## Google Indexing

Programmatic control of the [Google Indexing API integration](https://loxo.w4dev.com/docs/google-indexing-api/) — that guide covers creating the service account and credentials; these endpoints drive the integration once it exists.

### GET /wp-json/loxo/v1/google-indexing/status

Current state of the integration: configuration, daily quota, submission queue, and recent activity log.

**Response — 200 OK**

```
{
  "enabled": true,
  "configured": true,
  "client_email": "indexer@my-project.iam.gserviceaccount.com",
  "project_id": "my-project",
  "quota": { "used": 12, "limit": 200, "date": "2026-08-09" },
  "queue_count": 3,
  "log": [ ... ]
}
```

### POST /wp-json/loxo/v1/google-indexing/settings

Save the integration settings.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `enabled` | string | No | `yes` to enable automatic submission, anything else disables it. |
| `credentials` | string | No | The Google service-account key, as its raw JSON text. |
| `remove_credentials` | boolean | No | `true` deletes the stored key (and wins over `credentials`). |

**Response — 200 OK**

```
{
  "message": "Settings saved.",
  "status": { ...same shape as the status endpoint... }
}
```

### POST /wp-json/loxo/v1/google-indexing/test

Test the connection using the stored credentials. No parameters.

**Response — 200 OK**

```
{
  "message": "Connected as indexer@my-project.iam.gserviceaccount.com. The Google Indexing API connection is working.",
  "client_email": "indexer@my-project.iam.gserviceaccount.com"
}
```

**Errors:** `400 not_configured` — no service-account key saved yet.

### POST /wp-json/loxo/v1/google-indexing/submit

Submit a single URL to Google.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `url` | string | Yes | The URL to submit. Must belong to this website. |
| `type` | string | No | `URL_UPDATED` (default) or `URL_DELETED` to request removal. |

```
curl -X POST -u "admin:abcd efgh ijkl mnop" \
  -d "url=https://example.com/job/senior-developer/" \
  https://example.com/wp-json/loxo/v1/google-indexing/submit
```

**Response — 200 OK**

```
{
  "message": "URL submitted to Google for indexing.",
  "status": { ...same shape as the status endpoint... }
}
```

**Errors:** `400 invalid_url` / `400 invalid_url_host` — missing or foreign URL · `429 quota_exhausted` — the daily quota is used up.

### POST /wp-json/loxo/v1/google-indexing/submit-all

Queue every published job for submission. URLs are submitted from the queue over time, respecting the daily quota. No parameters.

**Response — 200 OK**

```
{
  "message": "Queued 24 job URLs for submission to Google.",
  "queued": 24,
  "status": { ...same shape as the status endpoint... }
}
```

**Errors:** `400 not_configured` — enable the integration and save a key first.

### POST /wp-json/loxo/v1/google-indexing/url-status

Ask Google what it knows about a URL.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `url` | string | Yes | The URL to check. Must belong to this website. |

**Response — 200 OK**

```
{
  "message": "Last update notification received by Google on August 9, 2026 10:30 am.",
  "metadata": {
    "url": "https://example.com/job/senior-developer/",
    "latestUpdate": {
      "url": "https://example.com/job/senior-developer/",
      "type": "URL_UPDATED",
      "notifyTime": "2026-08-09T04:30:12.000Z"
    }
  }
}
```

If Google has never seen the URL, the response is `{ "message": "Google has no record of this URL." }`.

### DELETE /wp-json/loxo/v1/google-indexing/log

Clear the integration’s activity log. No parameters.

**Response — 200 OK**

```
{ "message": "Activity log cleared." }
```

## Related documentation

- [Synchronizing Jobs](https://loxo.w4dev.com/docs/synchronizing-jobs/) — the plugin’s built-in sync process that most of these endpoints control.
- [Webhooks](https://loxo.w4dev.com/docs/webhooks/) — how to set up and manage real-time job updates.
- [Job View Tracking](https://loxo.w4dev.com/docs/job-view-tracking/) — the plugin feature behind the job-view endpoint.

Source: https://loxo.w4dev.com/docs/rest-api/
