Fetching Redemptions/Seats Available For A License Key or Order(s)
Fetching Redemptions/Seats Available For A License Key or Order(s)
Developer guide for seat usage on license keys: how many seats are available, how many are used, and per-course breakdown. This document is written for integrators using the Bright Platform for WordPress plugin (WooCommerce license flows).
For raw HTTP API details (endpoints, status codes, authorization on the Rails side), see Bright Scoped Stored Queries API Reference. For general $bright->callApi() usage, see the plugin’s md/bright-php-api-reference.md.
What you get
Each redemption row describes one license invitation × course combination:
| Field | Meaning |
|---|---|
license_key |
Invitation name (the license key string) |
order_id |
WooCommerce order ID stored on the invitation (custom.order_id) |
created |
Invitation created timestamp |
seats_available |
Seats purchased for that course on this invitation |
seats_used |
Active registrations against those seats |
title |
Course title |
Typical display: “Seats Used: 2 of 5” (seats_used of seats_available).
Three ways to look up redemptions
| Lookup | URL / param | WordPress helper route | Best for |
|---|---|---|---|
| License key | invitation_name |
v2 stored query redemptions_available_for |
License Key Report links, purchaser-facing pages |
| One order | order_id |
v3 invitation/redemptions |
Order confirmation, admin order screens |
| Many orders | order_ids (comma-separated) |
v3 invitation/redemptions |
Batch reporting, “my orders” dashboards |
At least one of these parameters is required. The plugin does not infer a license key or order ID when none are supplied.
Built-in License Key Report page
On WooCommerce setup, the plugin creates a License Key Report page with two shortcodes:
[bright class="none" type="generic" template="redemptions_available_for"/]
[bright class="none" type="generic" template="invitation_results_matrix" datatables="yadcf"/]
The first shortcode loads seat usage; the second loads the completion matrix for the same license key.
URL parameters
The redemptions_available_for template reads query string parameters and passes them to getRedemptionsAvailableFor():
| Query param | Example | Notes |
|---|---|---|
invitation_name |
?invitation_name=13708-14097-56871-91719-163f07ce-c3270a5d |
Primary path for license-key links |
order_id |
?order_id=670 |
Single WooCommerce order |
order_ids |
?order_ids=670,684 |
Comma-separated list |
If the page is opened with no parameters, visitors see: “Cannot derive license key from URL.”
Linking from WooCommerce
The plugin builds license-key report URLs with:
$url = \Bright\WC\get_license_key_report_url($license_key);
// → {license_key_report_permalink}?invitation_name={license_key}
Used on order detail pages (woocommerce/order-details.php). Customize with the bright_woocommerce_license_key_report_url filter.
For order-based report links, build the permalink yourself:
$report_url = \Bright\WC\bwc_page_permalink('license_key_report');
$report_url = add_query_arg('order_ids', '670,684', $report_url);
PHP API — getRedemptionsAvailableFor()
Obtain the connector singleton:
$bright = \Bright\Wordpress::getInstance();
Call the routing helper (defined in php-connect/base.php):
$raw = $bright->getRedemptionsAvailableFor(array(
'params' => array(
'invitation_name' => $license_key, // OR order_id / order_ids — see routing below
),
'raw' => true, // default for this helper; returns JSON string
));
Automatic v2 vs v3 routing
params provided |
Backend call |
|---|---|
invitation_name (non-empty) |
callApi('stored_query/run', …) with name=redemptions_available_for, query_scope=bright |
order_id and/or order_ids (and no invitation_name) |
callV3Api('invitation/redemptions', …) |
Important: If invitation_name is set, the helper always uses the v2 stored query. order_id / order_ids in the same call are ignored. To filter by both license key and order on v3, call callV3Api() directly (see below).
Default for this helper is 'raw' => true. Pass 'raw' => false if you want a decoded PHP value (v2 only returns usefully decodable JSON for the stored-query path).
Examples
1. By license key (logged-in purchaser)
Uses the current user’s Bright access token (set during doHeader() for front-end requests). The user must be the invitation owner (initiating_user or listed in reporting_users on the invitation custom JSON).
$bright = \Bright\Wordpress::getInstance();
$raw = $bright->getRedemptionsAvailableFor(array(
'params' => array(
'invitation_name' => '13708-14097-56871-91719-163f07ce-c3270a5d',
),
));
$rows = json_decode($raw, true);
// v2 shape: array of column-indexed rows — see “Response shapes” below
2. By one order ID (server-side / realm)
Use accessMode => 'realm' in admin hooks, cron, or other server-side code where no user token exists. Realm credentials come from WordPress options (bright_realm_guid, bright_secret_key).
$raw = $bright->getRedemptionsAvailableFor(array(
'accessMode' => 'realm',
'params' => array(
'order_id' => '670',
),
));
$body = json_decode($raw, true);
foreach ($body['results'] as $entry) {
if ($entry['status'] !== 200) {
continue;
}
foreach ($entry['data'] as $row) {
// $row['seats_used'], $row['seats_available'], $row['title'], …
}
}
3. By multiple order IDs (batch)
$raw = $bright->getRedemptionsAvailableFor(array(
'accessMode' => 'realm',
'params' => array(
'order_ids' => '670,684',
),
));
$body = json_decode($raw, true);
Each element in $body['results'] is one requested order:
| Field | Meaning |
|---|---|
order_id |
Requested WooCommerce order ID |
status |
Per-order HTTP-style status (200, 403, 404) |
message |
Human-readable outcome |
data |
Array of redemption rows on success; null on error |
Check every entry’s status before reading data. The overall HTTP response may be 207 when some orders fail authorization or are not found.
4. Direct v3 call (combine filters)
When you need invitation_name and order_id / order_ids together, bypass the helper:
$raw = $bright->callV3Api('invitation/redemptions', array(
'accessMode' => 'realm',
'params' => array(
'invitation_name' => $license_key,
'order_ids' => '670',
),
'raw' => true,
));
5. Custom shortcode or theme template
Mirror the built-in template pattern (woocommerce/bright/templates/redemptions_available_for.php):
add_filter('bright_extend_on_generic', function ($coursedata, $attr) {
if (($attr['template'] ?? '') !== 'my_redemptions_widget') {
return $coursedata;
}
$order_ids = isset($_GET['order_ids']) ? sanitize_text_field(wp_unslash($_GET['order_ids'])) : '';
if ($order_ids === '') {
return $coursedata;
}
$bright = \Bright\Wordpress::getInstance();
return $bright->getRedemptionsAvailableFor(array(
'accessMode' => 'realm',
'params' => array('order_ids' => $order_ids),
'raw' => true,
));
}, 10, 2);
Register a matching Handlebars template if you render through the Bright template engine, or decode JSON in PHP and return HTML.
Response shapes
v2 — invitation_name only (stored query)
Top-level JSON is a flat array of rows. Each row is an array of column values (not keyed objects), in this order:
| Index | Column |
|---|---|
| 0 | license_key |
| 1 | order_id |
| 2 | created |
| 3 | seats_available |
| 4 | seats_used |
| 5 | title |
The stock Handlebars template uses <code class="kb-btn">{this.[4]}</code> and <code class="kb-btn">{this.[3]}</code> for the “Seats Used: X of Y” line.
v3 — order_id / order_ids (batch mode)
Top-level JSON object with a results array only (no meta):
{
"results": [
{
"order_id": 670,
"status": 200,
"message": "Success",
"data": [
{
"license_key": "...",
"order_id": 670,
"created": "2024-06-01 10:15:00",
"seats_available": 5,
"seats_used": 2,
"title": "Example Course A"
}
]
}
]
}
v3 — invitation_name only (flat mode)
If you call v3 directly with only invitation_name, results is a flat array of keyed row objects (same fields as data[] above), not the batch wrapper.
Authentication and access
accessMode
| Mode | When to use | Credentials |
|---|---|---|
accessToken (default) |
Front-end pages for logged-in purchasers | User API key from getAuthenticationCodeForUser() |
realm |
Server-side PHP (hooks, CLI, batch jobs) | bright_realm_guid + bright_secret_key from options |
Never expose realm secret keys in browser JavaScript.
Who can see which data
invitation_name (v2 stored query or v3 flat mode)
The API key’s user email must match the invitation’s:
custom.initiating_user(purchaser), or- one of the emails in
custom.reporting_users(comma-separated)
Otherwise the call returns 401.
order_id / order_ids (v3 batch mode)
Any authenticated user API key may call. Each order is evaluated separately:
Per-order status |
Meaning |
|---|---|
| 200 | Rows returned, or order is accessible but has no license rows (data: []) |
| 403 | Order exists in the realm, but this user is not an invitation owner for it |
| 404 | No license invitation in the realm has that order_id in custom |
With realm credentials, all matching license invitations in the realm are returned; unknown order IDs appear as 404 entries.
Invitation custom fields used for access
{
"initiating_user": "buyer@example.com",
"license": true,
"order_id": 670,
"reporting_users": "manager@example.com,other@example.com"
}
V3 API root URL
callV3Api() resolves the v3 base URL from plugin options:
bright_api_v3_urlif set- Else replace
/v2with/v3onbright_api_url - Else append
/v3to the v2 root
Example: https://bright.example.com/bright/api/v2 → https://bright.example.com/bright/api/v3
Related plugin surfaces
| Location | Role |
|---|---|
woocommerce/bright/templates/redemptions_available_for.php |
Built-in seat-usage shortcode template |
php-connect/reports.php |
invitation_results_matrix template (completion detail; uses same invitation_name URL param) |
woocommerce/admin/create-pages.php |
Creates License Key Report page on setup |
woocommerce/redirection.php |
get_license_key_report_url() |
php-connect/base.php |
getRedemptionsAvailableFor(), callV3Api() |
tests/test-redemptions-v3-api.php |
Unit tests for routing and response shapes |
Quick decision guide
Need seat usage for one license key on a purchaser-facing page?
→ URL: ?invitation_name={key}
→ PHP: getRedemptionsAvailableFor(['params' => ['invitation_name' => $key]])
→ Default accessMode (user token) if the viewer is the purchaser
Need seat usage for WooCommerce order(s) in server-side code?
→ PHP: getRedemptionsAvailableFor(['accessMode' => 'realm', 'params' => ['order_ids' => '670,684']])
→ Loop results[]; check status per order
Need both license key and order filter?
→ callV3Api('invitation/redemptions', …) with both params
Need completion matrix (learners, scores) for the same key?
→ Same page already includes invitation_results_matrix; requires invitation_name in URL
Related documentation
- Bright Scoped Stored Queries API Reference — HTTP API, status codes, authorization
- Builtin Bright Scoped Stored Queries — server-side query definitions
- Bright WordPress plugin
md/bright-php-api-reference.md—callApi(),callV3Api(), access modes



