Using the $bright PHP object to call the Bright API

Bright PHP API Reference

This document describes how to obtain the $bright object in WordPress, authenticate against the Bright server, and call the Bright API using callApi() and callV3Api().

It covers the WordPress connector layer (the $args model, access modes, response handling, and curl metadata). For field-by-field definitions of individual API controllers, see the Bright API documentation.


Prerequisites

Use this approach when you need server-side PHP access to Bright from:

  • Custom theme or plugin code
  • WooCommerce hooks
  • Bright template extensions
  • WP-CLI or admin scripts

Requirements:

Requirement Notes
PHP curl extension Without it, $bright->successfullyInitialized is false and API calls will not work
Bright plugin has been configured WP Site Options are created: bright_api_url, bright_realm_guid, bright_secret_key
Optional v3 URL bright_api_v3_url — if empty, v3 root is derived from the v2 URL (/v2/v3)
Optional anonymous key bright_anonymous_api_key — for unauthenticated (user ID 0) requests. Set in the WP Admin Console Bright settings

Obtaining $bright

$bright is a local variable name, not a WordPress global and is an instantiation of the Bright Singleton pattern (see PHP Singleton explanation). The standard pattern is:

$bright = \Bright\Wordpress::getInstance();

Singleton and class hierarchy

  • Bright\Wordpress extends Bright\Base extends Bright\Singleton
  • getInstance() creates one instance per request (lazy singleton)
  • Never instantiate Bright\Base directly

On first getInstance():

  1. Constructor runs getOptions() and loads apiRoot, realmGuid, realmSecretKey, SCORM Cloud keys from WordPress options
  2. If curl_init is missing, successfullyInitialized is set to false and initializationError describes the problem
$bright = \Bright\Wordpress::getInstance();
if (!$bright->successfullyInitialized) {
  error_log($bright->initializationError);
  return;
}

Other web stacks

If you maintain a non-WordPress Bright connector, brightClass() can return a different class name (for example \Bright\Drupal). The default is \Bright\Wordpress.


Authentication

Most callApi() calls use the default access mode accessToken, which requires $bright->accessToken to be set. If it is empty, callApi() returns null immediately (no HTTP request).

When authentication happens automatically

On a normal front-end page request, the plugin runs doHeader(), which:

  1. Sets the current WordPress user via setCurrentUser()
  2. Calls getAuthenticationCodeForUser() to obtain or restore an access token

In that context you can often call callApi() without extra setup, as long as the visitor is logged in (or anonymous access is configured).

The WordPress Action Workflow that Calls doHeader()

The Bright plugin integrates with WordPress using standard WP action hooks to ensure authentication and environment setup occur early in the page load cycle. The principal initialization occurs in the doHeader() method of the Bright connector class.

When is doHeader() called?

  • doHeader() is hooked into the wp action, which fires after the query variables are set and just before output starts.
  • This ensures that authentication and connection setup are ready before any content is rendered (including shortcodes, templates, etc).

Example from plugin bootstrap:

add_action('wp', array(\Bright\Wordpress::getInstance(), 'doHeader'));
Workflow Steps
  1. WordPress loads and triggers its lifecycle hooks.
  2. On every page request (unless in admin or REST context), the Bright plugin's bootstrap code hooks doHeader() to the wp action.
  3. When the wp action fires:
    • doHeader() sets up Bright\Wordpress:
      • Loads/caches WordPress user into the Bright OOP context.
      • Attempts to authenticate (sets accessToken for the user if needed).
      • Ensures all critical options (API URL, realm credentials, etc.) are loaded and validated.
    • After this, your code, theme files, or shortcodes may safely use $bright = \Bright\Wordpress::getInstance() and assume authentication is handled.
Where to find this in code
  • This hook is typically established in the main plugin file or an included bootstrap module within the plugin directory, for example:
// In bright-wordpress/bright.php or similar:
add_action('wp', function() {
  \Bright\Wordpress::getInstance()->doHeader();
});
  • See also the companion hooks for admin_init in the admin context, if relevant.
Summary Table
WP Hook Method Purpose
wp doHeader() Authenticates user and initializes Bright for all front-end page loads
init Sometimes used Occasionally used for earlier setup of plugin text domains or supporting structures
shortcode processing after doHeader() Shortcodes and API calls can rely on $bright having access/authentication initialized

In short:
If you call plugin APIs on a normal page, authentication/setup has already occurred because doHeader() runs on wp. This is why in normal theme/template code you do not need to manually call setCurrentUser() and getAuthenticationCodeForUser().

For custom workflows—before wp fires (early/exotic scripts, AJAX, admin, CLI)—you must handle authentication manually as referenced above.

When you must authenticate manually

Use this path in custom code that runs before doHeader(), in admin/CLI contexts, or when switching users:

$bright = \Bright\Wordpress::getInstance();
$user = wp_get_current_user();
$bright->setCurrentUser($user);
$bright->getAuthenticationCodeForUser($user);

if (empty($bright->accessToken)) {
  // Cannot call accessToken-mode APIs
  return;
}

Or combine set + authenticate:

$bright->setAndAuthenticateCurrentUser($user);

or even

$bright->setAndAuthenticateCurrentUser(wp_get_current_user());

Token acquisition

getAuthenticationCodeForUser() POSTs to {apiRoot}/api_key.json with realm credentials and the user's email. On success it sets:

Property Source
$bright->accessToken access_token from JSON response
$bright->accessTokenExpiration expires_at
$bright->realmId realm_id

Tokens are cached in per-user meta: bright_cached_api_key, bright_cached_api_key_expiration, bright_cached_api_key_url, bright_cached_realm_id.

Anonymous users

If the current user has ID === 0 and bright_anonymous_api_key is configured, that static key is used as accessToken without a server round-trip.

Re-authenticate after settings change

$bright->reset(); // clears token, reloads options, re-authenticates current user

One-off token for an email (realm mode via callApi)

$token = $bright->getAuthenticationCodeForEmail('learner@example.com');
// Internally: callApi('api_key', method POST, realm params, success callback)

callApi() overview

$result = $bright->callApi($apiController, $args);
Parameter Description
$apiController API path segment before .json (e.g. 'course', 'stored_query/run', 'invitation/redemptions')
$args Associative array of options (see full model below)

URL built: <code class="kb-btn">apiRoot}/{apiController}.json?{query</code>

  • GET (default): all params go in the query string
  • POST / PUT: params are sent as the request body; query string is empty

callApi() — full $args model

Key Default Behavior
accessMode 'accessToken' 'accessToken' merges accessToken into params; 'realm' merges realm_guid and realm_secret_key from plugin options. See Access modes.
params [] Query string (GET) or POST/PUT body. Auth fields are merged automatically per accessMode.
method (GET) Set to 'POST' or 'PUT' to change HTTP method.
raw unset → decode If the key exists (isset($args['raw'])), the response body is returned as a string. If unset, json_decode() is applied. raw => false still returns a string because only key presence is checked.
encode Array of param names to json_encode before send (e.g. nested structures for invitations).
success Callable function ($rsp, $curlInfo, $curlError). Return value becomes the callApi() return. Runs when body length > 1.
failure Same signature; runs when body is empty or length ≤ 1.
errorMsgs 401, 404 messages Map HTTP status code → message string. Passed to curl(); may write HTML error blocks via writeToPage().
suppressErrorBlock false If true, suppresses generic Bright HTML error blocks on curl/HTTP errors.
dontcheckcache false Skip reading from the per-request in-memory API cache.
dontcacheresult false Do not store this response in the cache.
removecacheresult false Delete the matching cache entry after the call (use before updates).
apiRoot $bright->apiRoot Override API base URL. Set automatically by callV3Api().

Return semantics

Condition Return
accessMode === 'accessToken' and empty accessToken null (no request)
Response body empty or length ≤ 1 null, or failure callback result
Success, no success callback Decoded JSON (stdClass / arrays) or raw string if raw key set
Success, with success callback Whatever the callback returns
Failure, with failure callback Whatever the callback returns

Important: json_decode() is called without the true flag, so JSON objects become stdClass, not associative arrays.


Access modes

Only two values are implemented in callApi():

accessToken (default)

Merges the current user's token into params:

'accessToken' => $bright->accessToken

Use for: learner-scoped data — courses, registrations, stored queries run as the logged-in user.

$bright = \Bright\Wordpress::getInstance();
$rows = $bright->callApi('stored_query/run', array(
  'params' => array(
    'name' => 'getLicenseKeysForUser',
    'host_url' => get_site_url(),
    'query_scope' => 'bright',
  ),
));

realm

Merges realm credentials from plugin settings:

'realm_guid' => $bright->realmGuid,
'realm_secret_key' => $bright->realmSecretKey

Use for: server-side / admin operations — creating invitations, realm custom data, fetching registration by GUID without a user token, WooCommerce license-key flows.

$bright->callApi('invitation/add_learners', array(
  'accessMode' => 'realm',
  'params' => array(
    'name' => $invitationName,
    'learners' => \Bright\Base::encodeJson(array($email)),
  ),
));

Pitfall: invalid accessMode

Any value other than 'accessToken' or 'realm' skips auth injection. Requests will likely return 401. Always pass exactly one of the two supported strings.


Raw vs non-raw responses

Non-raw (default)

JSON is decoded automatically:

$course = $bright->getCourseDataByGuid($guid);
// $course is a stdClass with properties like course_guid, title, ...

List endpoints return a JSON array, which becomes a PHP array of stdClass objects:

$courses = $bright->callApi('course');
// array of stdClass

Raw

Pass 'raw' => true (or any value — only key presence matters):

$json = $bright->callApi('stored_query/run', array(
  'params' => array(
    'name' => 'bright_completion_matrix',
    'host_url' => get_site_url(),
    'query_scope' => 'bright',
  ),
  'raw' => true,
));
// $json is a string — embed in a page or pass to JavaScript

For associative arrays after a raw call:

$data = json_decode($json, true);

callV3Api()

$result = $bright->callV3Api($apiController, $args);

This is a thin wrapper: it sets $args['apiRoot'] from getBrightV3ApiRoot() and calls callApi(). All $args keys behave the same.

V3 URL resolution

  1. If bright_api_v3_url is set in plugin options, use that (trimmed, no trailing slash)
  2. Else if v2 root ends with /v2, replace with /v3
  3. Else append /v3 to the v2 root

Example: http://localhost:3000/bright/api/v2http://localhost:3000/bright/api/v3

getRedemptionsAvailableFor() routing helper

This helper picks v2 vs v3 automatically:

Params API used
invitation_name set v2 stored_query/run with name=redemptions_available_for
order_id or order_ids v3 invitation/redemptions via callV3Api

Default for this helper is 'raw' => true unless you override in $args.

$json = $bright->getRedemptionsAvailableFor(array(
  'accessMode' => 'realm',
  'params' => array('order_ids' => '670,684'),
));
$body = json_decode($json, true);

Example response shapes

Controller responses are defined by the Bright server. Below are representative shapes as seen through the WordPress connector.

v2 — single course (course with course_guid)

Non-raw; decoded object (from integration tests):

{
  "course_guid": "PSOAS_SCORM_12-17318b2dc9-7128-4e7e-b7e3-fa591b7fc446",
  "title": "The Role of the Psoas in Yoga Asana and Postural Health"
}

In PHP: $course->course_guid, $course->title.

v2 — course list (course without filter)

Returns an array of course objects.

v2 — registration (registration)

Often an array; helper methods return the first element:

$reg = $bright->getRegistrationDataForCourse($courseGuid);
// stdClass or '{}' string on 404

Example registration fields: type, course_guid, registration_guid, registration_guids.

v2 — stored query (stored_query/run)

Shape depends on the query name. Reports typically use raw => true and return a JSON string for templates or DataTables. See also Bright Scoped Stored Queries API Reference and your team's Rails stored-query docs for query parameters.

v3 — invitation/redemptions

When decoded with json_decode($raw, true):

{
  "results": [
    {
      "license_key": "13708-14097-56871-91719-163f07ce-c3270a5d",
      "order_id": 670,
      "created": "2024-06-01 10:15:00",
      "seats_available": 5,
      "seats_used": 2,
      "title": "Example Course A"
    }
  ],
  "meta": {
    "requested_order_ids": [670, 684],
    "returned_order_ids": [670, 684],
    "skipped_order_ids": [],
    "skipped_reason": null
  }
}

Auth — api_key POST response

Used internally by getAuthenticationCodeForUser() (not typically called via callApi for page auth):

{
  "access_token": "...",
  "expires_at": "...",
  "realm_id": 123
}

Invitation POST response

Realm-mode calls such as addLearnerToInvitation may return an object with messages or error — inspect $bright->curlHttpCode alongside the decoded body.


Success and failure callbacks

Callbacks receive three arguments: $rsp, $curlInfo, $curlError.

Success callback — transform return value

$title = $bright->getCourseDataByGuid($guid, array(
  'success' => function ($rsp, $curlInfo, $curlError) {
    return $rsp->title;
  },
));
// $title is a string

Failure callback — branch on HTTP status

$bright->callApi('stored_query/run', array(
  'params' => array(
    'name' => 'invitation_completion_matrix',
    'invitation_name' => $name,
    'host_url' => get_site_url(),
    'query_scope' => 'bright',
  ),
  'suppressErrorBlock' => true,
  'raw' => true,
  'failure' => function ($rsp, $curlInfo, $curlError) {
    if ($curlInfo['http_code'] == 401) {
      _e('You do not have permission to view this report.');
    }
    return null;
  },
));

Curl metadata

callApi() does not return curl data. After every call (including cache hits), inspect properties on $bright:

Property Set from Typical use
$bright->curlHttpCode $bright->curlInfo['http_code'] Branch on 200, 404, 500
$bright->curlError curl_error() Transport-level failures
$bright->curlInfo curl_getinfo() Full metadata array

Example: HTTP status after invitation

$ret = $bright->addLearnerToInvitation($email, $licenseKey, array(
  'errorMsgs' => array(),
  'params' => array('nodelay' => true),
));

if ($bright->curlHttpCode == 404) {
  // license key not found
} elseif ($bright->curlHttpCode == 200) {
  // success — check $ret->messages
} else {
  // other error — may use $ret->error
}

Notable curlInfo keys

After a call, $bright->curlInfo may include:

Key Meaning
http_code HTTP status (mirrored in curlHttpCode)
url Request URL
total_time Total time in seconds
content_type Response Content-Type
size_download Bytes downloaded

curlErrorMsg()

Returns a human-readable message. Do not use this to detect errors — use $bright->curlHttpCode and $bright->curlError instead.

Per-request API cache

Identical callApi arguments (URL + sorted params) are cached in memory for the duration of the request. On cache hit, curlInfo and curlError are restored from cache.

The page footer may expose stats via JavaScript:

Bright.cacheData = { "num_cache_hits": N, "num_cache_misses": M, "total_time": T };

Cookbook

1. Stored query as logged-in user

$bright = \Bright\Wordpress::getInstance();
// Assumes doHeader() already ran, or authenticate manually
$rows = $bright->callApi('stored_query/run', array(
  'params' => array(
    'name' => 'getLicenseKeysForUser',
    'host_url' => get_site_url(),
    'query_scope' => 'bright',
  ),
));

2. Stored query JSON for a template (raw)

$data = $bright->callApi('stored_query/run', array(
  'params' => $attr,
  'raw' => true,
));

3. Realm-mode POST — add learner to invitation

Handled by addLearnerToInvitation(); equivalent direct call:

$bright->callApi('invitation/add_learners', array(
  'accessMode' => 'realm',
  'params' => array(
    'name' => $invitationName,
    'learners' => \Bright\Base::encodeJson(array($learnerEmail)),
  ),
));

4. Direct v3 redemptions

$raw = $bright->callV3Api('invitation/redemptions', array(
  'accessMode' => 'realm',
  'params' => array('order_ids' => '670,684'),
  'raw' => true,
));
$body = json_decode($raw, true);

5. getRedemptionsAvailableFor with order IDs

$raw = $bright->getRedemptionsAvailableFor(array(
  'accessMode' => 'realm',
  'params' => array('order_ids' => '670,684'),
));

6. POST with encoded params — create invitation

$bright->callApi('invitation', array(
  'accessMode' => 'realm',
  'method' => 'POST',
  'encode' => array('course_guids', 'license_data', 'custom'),
  'params' => array(/* invitation fields */),
));

Or use $bright->createInvitation($args) which sets method and encode for you.

7. Fetch API key for an email (POST)

$token = $bright->callApi('api_key', array(
  'method' => 'POST',
  'params' => array(
    'realm_guid' => $bright->realmGuid,
    'realm_secret_key' => $bright->realmSecretKey,
    'user_email' => $email,
  ),
  'success' => function ($rsp) {
    return $rsp->access_token;
  },
));

8. Inspect HTTP code after any call

$result = $bright->callApi('course', array('params' => array('course_guid' => $guid)));
$code = $bright->curlHttpCode;

9. Cache control before an update

$bright->callApi('realm/' . $bright->realmId . '/write_custom_key', array(
  'accessMode' => 'realm',
  'method' => 'POST',
  'removecacheresult' => true,
  'dontcheckcache' => true,
  'params' => array(/* ... */),
));

Appendix — deprecated globals and functions

Pre–Bright 8.0 code may use global wrappers from php-connect/wp-deprecation.php. Prefer the singleton pattern for new code.

Deprecated Replacement
bright_curl($url, $method, $data) $bright = \Bright\Wordpress::getInstance(); then $bright->curl($url, array('method' => $method, 'params' => $data))
Global $bright_curl_error $bright->curlError (set by bright_curl() for backward compatibility)
Global $bright_curl_info $bright->curlInfo
bright_curl_error() $bright->curlErrorMsg() — display only, not for error detection

See php-connect/deprecation-list.md for the full migration list.


In this plugin

Document Topic
bright-shortcode-reference.md [bright] shortcode attributes
php-connect/filter-reference.md WordPress filters and actions
php-connect/attribute-reference.md Shortcode attribute details
php-connect/deprecation-list.md Deprecated functions
DEVELOPMENT.md Local development and debugging

External

Resource Topic
bright-api-doc v2 API controllers, request/response fields
v2 API controller reference Realm endpoints (e.g. realm_user/gcustom)

Bright server (Rails)

If your team maintains scoped stored-query documentation on the Rails app, use that for query name, query_scope, and parameter definitions. The WordPress plugin only passes those through via params on stored_query/run.


Quick reference

// 1. Get instance
$bright = \Bright\Wordpress::getInstance();

// 2. Authenticate (if needed)
$bright->setCurrentUser(wp_get_current_user());
$bright->getAuthenticationCodeForUser();

// 3. Call API
$result = $bright->callApi('course', array(
  'params' => array('course_guid' => $guid),
));

// 4. Check HTTP status (not returned from callApi)
if ($bright->curlHttpCode !== 200) {
  error_log($bright->curlErrorMsg());
}

// v3
$raw = $bright->callV3Api('invitation/redemptions', array(
  'accessMode' => 'realm',
  'params' => array('order_ids' => '123'),
  'raw' => true,
));