Brandfine Docs
WordPress

REST proxy reference

The WordPress plugin exposes a WP REST proxy at /wp-json/brandfine/v1/*. Reference for plugin developers and integrators.

For most customers, the shortcode and Gutenberg block are all you ever need. This page is for plugin developers or integrators who want to call Brandfine from frontend JavaScript on a WordPress page — without exposing the workspace API key in the browser.

Why a proxy exists

Brandfine's /external/* API expects an X-Api-Key header. The workspace API key is broad-scope and should never reach a browser bundle. The WP plugin holds the key server-side in wp_options and exposes a thin WP REST proxy that:

  1. Verifies a WordPress nonce (wp_rest action)
  2. Per-IP rate-limits requests
  3. Forwards to Brandfine with the X-Api-Key header attached
  4. Returns Brandfine's response verbatim (preserving status codes and body)

If you're integrating from outside the plugin's own widgets, this is your interface.

Endpoints

All routes live under /wp-json/brandfine/v1/.

GET /appointments/availability

Proxies GET /external/appointments/availability. Returns available slots within the workspace's configured booking window.

Query parameters (optional):

  • from — ISO8601 timestamp. Earliest slot start.
  • to — ISO8601 timestamp. Latest slot start.

Response (200):

{
  "enabled": true,
  "timezone": "Europe/Istanbul",
  "slotDurationMinutes": 30,
  "leadTimeHours": 0,
  "bookingWindowDays": 14,
  "policyText": null,
  "slots": [
    { "start": "2026-06-09T09:00:00.000Z", "end": "2026-06-09T09:30:00.000Z" },
    ...
  ],
  "windowStart": "2026-06-08T12:00:00.000Z",
  "windowEnd": "2026-06-22T12:00:00.000Z"
}

POST /appointments/requests

Proxies POST /external/appointments/requests. Creates a booking request in PENDING status.

Body (JSON):

{
  "visitorName": "Jane Doe",
  "visitorEmail": "jane@example.com",
  "visitorPhone": "+1 555 0123",
  "visitorMessage": "First consultation, ~30 min",
  "requestedAt": "2026-06-09T09:00:00.000Z"
}
  • visitorName, visitorEmail, requestedAt — required
  • visitorPhone, visitorMessage — optional

Response (201):

{
  "id": "cmq55qoc30001v1si40dduegn",
  "status": "PENDING",
  ...
}

Authentication

Every proxy route requires:

  1. Plugin connected. API key set in wp_options. If not → 503 brandfine_not_connected.
  2. WordPress nonce. Send as X-WP-Nonce header. Use the wp_rest action (WP's standard REST nonce action) so you don't fight WP's cookie-auth middleware:
    const nonce = wpApiSettings.nonce  // emitted by `wp_enqueue_script`
    fetch('/wp-json/brandfine/v1/appointments/availability', {
      headers: { 'X-WP-Nonce': nonce }
    })
    If your shortcode/block emits its own nonce as a data attribute, that works too — same action.
  3. Rate limit. Per-IP, scoped per HTTP method:
    • GET → 60 hits per minute
    • POST → 10 hits per 5 minutes

Limits hit → 429 brandfine_rate_limited.

Error format

The proxy forwards Brandfine's response body verbatim when Brandfine returns an error. Status codes preserved:

{
  "message": "That time was just taken. Pick another slot.",
  "statusCode": 404
}

Common upstream codes:

  • 400 — validation error (malformed body, missing fields, invalid timestamp format)
  • 404 — slot just got taken / outside business hours
  • 401 — API key invalid or revoked (rare — plugin would have shown an "endpoint not ready" warning already)
  • 429 — Brandfine's own per-workspace rate limit hit
  • 502/503 — Brandfine API unreachable

Example: calling from JavaScript

// Inside a script enqueued on a WordPress page
async function fetchAvailability() {
  const root = document.querySelector('[data-brandfine-widget]')
  const restRoot = root.dataset.restRoot
  const nonce = root.dataset.nonce

  const res = await fetch(`${restRoot}/availability`, {
    headers: { 'X-WP-Nonce': nonce },
    credentials: 'same-origin',
  })
  if (!res.ok) {
    const err = await res.json()
    throw new Error(err.message || `HTTP ${res.status}`)
  }
  return res.json()
}

The plugin's own widget JS at assets/js/appointments-widget.js is a reference implementation.

Extending — adding new routes

If you're building a custom shortcode that needs to hit a different Brandfine endpoint, the proxy is extensible from includes/class-brandfine-rest.php.

Pattern: add a route in register_routes(), use $this->forward($this->api->get('/external/...')) to relay upstream. The permission callback (nonce + rate limit + connection check) is shared across all routes.

On this page