List Pass Grants
GET /v1/nextexams/passes/grants
List passes
Granted passes, newest first. status is the effective state: a pass past
its end reads expired even before the expiry sweep has run. Scope
passes:read.
Request
GET https://your-site.example/api/index.php/v1/nextexams/passes/grants?limit=<integer>&offset=<integer>&search=<string>&status=<string>&plan_id=<integer>&user_id=<integer>&source_ref=<string>
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
limit |
integer |
query |
No | Page size. (min 1) Defaults to 20. |
offset |
integer |
query |
No | Rows to skip. (min 0) Defaults to 0. |
search |
string |
query |
No | Matches the holder's name, username or email, the plan title, or the reference. |
status |
string |
query |
No | Only passes in this state. One of active, expired, revoked. |
plan_id |
integer |
query |
No | Only passes of this plan. (min 1) |
user_id |
integer |
query |
No | Only passes of this user. (min 1) |
source_ref |
string |
query |
No | The pass granted with this reference (exact match). |
Responses
200 — A page of passes.
{
"data": [
{
"id": 1,
"user_id": 1,
"user_name": "string",
"user_email": "string",
"plan_id": 1,
"plan_title": "string",
"source": "string",
"source_ref": "string",
"starts_at": "2026-01-01T12:00:00Z",
"expires_at": "2026-01-01T12:00:00Z",
"status": "active",
"created": "2026-01-01T12:00:00Z"
}
],
"meta": {
"total": 1
}
}
| Field | Type | Meaning |
|---|---|---|
data[].id |
integer |
|
data[].user_id |
integer |
|
data[].user_name |
string|null |
|
data[].user_email |
string|null |
|
data[].plan_id |
integer |
|
data[].plan_title |
string|null |
|
data[].source |
string |
manual, api, or ext:<element> for a pass an extension granted. |
data[].source_ref |
string|null |
The caller's own reference, unique per source. |
data[].starts_at |
string|null |
ISO 8601 in UTC, or null when not set. (date-time) |
data[].expires_at |
string|null |
ISO 8601 in UTC, or null when not set. (date-time) |
data[].status |
string |
One of active, expired, revoked. |
data[].created |
string|null |
ISO 8601 in UTC, or null when not set. (date-time) |
meta.total |
integer |
Matching records in all pages. |
Errors
| Status | Code | When |
|---|---|---|
401 |
INVALID_TOKEN |
No token (MISSING_TOKEN), or one that is wrong, expired, revoked, or whose owner can no longer sign in (INVALID_TOKEN). |
403 |
MISSING_SCOPE |
The token is valid but this request is not allowed, and code says why: MISSING_SCOPE (the token lacks the scope), FORBIDDEN (the owner's role does not allow it), ACCESS_DENIED (the owner cannot manage Next Exams), API_DISABLED (the API Tokens app is off), APP_DISABLED (the feature's app is off — meta.app names it), ACCOUNT_UNUSABLE (the owner is blocked or must reset their password), DEMO_LOCKED (a demo site). |
Examples
curl
curl -s "https://your-site.example/api/index.php/v1/nextexams/passes/grants" \
-H "Authorization: Bearer $API_TOKEN"
PHP
<?php
$ch = curl_init('https://your-site.example/api/index.php/v1/nextexams/passes/grants?limit=<integer>&offset=<integer>&search=<string>&status=<string>&plan_id=<integer>&user_id=<integer>&source_ref=<string>');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JavaScript
const API_TOKEN = process.env.API_TOKEN;
const response = await fetch("https://your-site.example/api/index.php/v1/nextexams/passes/grants?limit=<integer>&offset=<integer>&search=<string>&status=<string>&plan_id=<integer>&user_id=<integer>&source_ref=<string>", {
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
},
});
const data = await response.json();