MENU navbar-image

Introduction

Mintfit V2 LMS - A modular monolith learning management system with microservice-style boundaries.

This documentation provides comprehensive information about the Mintfit V2 API.

## Architecture

Mintfit V2 is built as a **modular monolith** with strict module boundaries:
- Each module is self-contained with Services as the public API
- Modules follow Domain-Driven Design principles (Domain, Application, Infrastructure layers)
- Inter-module communication is only allowed through Service classes in the Application layer

<aside>As you scroll, you'll see code examples in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>

Authenticating requests

This API is not authenticated.

Ad Management

Create a new ad

Example request:
curl --request POST \
    "http://localhost/v1/ads" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update ad by ID

Example request:
curl --request PUT \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/ads/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ad. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete ad by ID (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/ads/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ad. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Activate an ad

Example request:
curl --request POST \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/activate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/activate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/activate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads/{id}/activate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ad. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Pause an ad

Example request:
curl --request POST \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/pause" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/pause"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/pause';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads/{id}/pause

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ad. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Assign ad to placement

Example request:
curl --request POST \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads/{adId}/placements/{placementId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

adId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

placementId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Unassign ad from placement

Example request:
curl --request DELETE \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/ads/{adId}/placements/{placementId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

adId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

placementId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Ad Tracking

Track an ad impression (public endpoint) This endpoint is called when an ad is displayed to a user. The tracking is processed asynchronously via a queued job.

Example request:
curl --request POST \
    "http://localhost/v1/ads/track/impression" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"ad_id\": \"architecto\",
    \"placement_id\": \"architecto\",
    \"placement_key\": \"architecto\",
    \"frontend\": \"architecto\",
    \"tenant_id\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/ads/track/impression"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "ad_id": "architecto",
    "placement_id": "architecto",
    "placement_key": "architecto",
    "frontend": "architecto",
    "tenant_id": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/track/impression';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'ad_id' => 'architecto',
            'placement_id' => 'architecto',
            'placement_key' => 'architecto',
            'frontend' => 'architecto',
            'tenant_id' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads/track/impression

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

ad_id   string     

The id of an existing record in the ads table. Example: architecto

placement_id   string  optional    

This field is required when placement_key is not present. The id of an existing record in the placements table. Example: architecto

placement_key   string  optional    

This field is required when placement_id is not present. Example: architecto

frontend   string  optional    

This field is required when placement_key is present. Example: architecto

tenant_id   string  optional    

The id of an existing record in the tenants table. Example: architecto

Track an ad click (public endpoint) This endpoint is called when a user clicks on an ad. The tracking is processed asynchronously via a queued job.

Example request:
curl --request POST \
    "http://localhost/v1/ads/track/click" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"ad_id\": \"architecto\",
    \"placement_id\": \"architecto\",
    \"placement_key\": \"architecto\",
    \"frontend\": \"architecto\",
    \"tenant_id\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/ads/track/click"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "ad_id": "architecto",
    "placement_id": "architecto",
    "placement_key": "architecto",
    "frontend": "architecto",
    "tenant_id": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/track/click';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'ad_id' => 'architecto',
            'placement_id' => 'architecto',
            'placement_key' => 'architecto',
            'frontend' => 'architecto',
            'tenant_id' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads/track/click

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

ad_id   string     

The id of an existing record in the ads table. Example: architecto

placement_id   string  optional    

This field is required when placement_key is not present. The id of an existing record in the placements table. Example: architecto

placement_key   string  optional    

This field is required when placement_id is not present. Example: architecto

frontend   string  optional    

This field is required when placement_key is present. Example: architecto

tenant_id   string  optional    

The id of an existing record in the tenants table. Example: architecto

Attempt Management

Start a new attempt

Creates a new attempt for an attemptable entity (e.g., quiz_version). The attempt is created in 'in_progress' state. The identity_id is automatically set from the authenticated user.

Example request:
curl --request POST \
    "http://localhost/v1/attempts/start" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"attemptable_type\": \"quiz_version\",
    \"attemptable_id\": \"01JGQUIZVERSION0000001\",
    \"metadata\": []
}"
const url = new URL(
    "http://localhost/v1/attempts/start"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "attemptable_type": "quiz_version",
    "attemptable_id": "01JGQUIZVERSION0000001",
    "metadata": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/start';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'attemptable_type' => 'quiz_version',
            'attemptable_id' => '01JGQUIZVERSION0000001',
            'metadata' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "success": true,
    "message": "Attempt started successfully",
    "data": {
        "id": "01JGATTEMPT000000000001",
        "identity_id": "01JGUSER00000000000001",
        "attemptable_type": "quiz_version",
        "attemptable_id": "01JGQUIZVERSION0000001",
        "status": "in_progress",
        "started_at": "2024-01-15T10:30:00Z",
        "submitted_at": null,
        "abandoned_at": null,
        "total_score": null,
        "max_possible_score": null,
        "metadata": {},
        "slot_history": [],
        "responses": []
    }
}
 

Request      

POST v1/attempts/start

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

attemptable_type   string     

The type of attemptable entity. Example: quiz_version

attemptable_id   string     

The attemptable entity ID (ULID). Example: 01JGQUIZVERSION0000001

metadata   object  optional    

Optional metadata for the attempt.

Get attempts for attemptable

Retrieves paginated attempts for a specific identity and attemptable entity. Users can only view their own attempts unless they have the 'attempts.read_all' permission.

Example request:
curl --request GET \
    --get "http://localhost/v1/attempts/for-attemptable?identity_id=01JGUSER00000000000001&attemptable_type=quiz_version&attemptable_id=01JGQUIZVERSION0000001&per_page=10&page=1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"identity_id\": \"bngzmiyvdljnikhwaykcmyuwpw\",
    \"attemptable_type\": \"architecto\",
    \"attemptable_id\": \"ngzmiyvdljnikhwaykcmyuwpwl\",
    \"per_page\": 20,
    \"page\": 43
}"
const url = new URL(
    "http://localhost/v1/attempts/for-attemptable"
);

const params = {
    "identity_id": "01JGUSER00000000000001",
    "attemptable_type": "quiz_version",
    "attemptable_id": "01JGQUIZVERSION0000001",
    "per_page": "10",
    "page": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "identity_id": "bngzmiyvdljnikhwaykcmyuwpw",
    "attemptable_type": "architecto",
    "attemptable_id": "ngzmiyvdljnikhwaykcmyuwpwl",
    "per_page": 20,
    "page": 43
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/for-attemptable';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'identity_id' => '01JGUSER00000000000001',
            'attemptable_type' => 'quiz_version',
            'attemptable_id' => '01JGQUIZVERSION0000001',
            'per_page' => '10',
            'page' => '1',
        ],
        'json' => [
            'identity_id' => 'bngzmiyvdljnikhwaykcmyuwpw',
            'attemptable_type' => 'architecto',
            'attemptable_id' => 'ngzmiyvdljnikhwaykcmyuwpwl',
            'per_page' => 20,
            'page' => 43,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Attempts retrieved successfully",
    "data": {
        "current_page": 1,
        "data": [],
        "total": 0
    }
}
 

Request      

GET v1/attempts/for-attemptable

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

identity_id   string     

The identity ID. Example: 01JGUSER00000000000001

attemptable_type   string     

The attemptable type. Example: quiz_version

attemptable_id   string     

The attemptable ID. Example: 01JGQUIZVERSION0000001

per_page   integer  optional    

Optional items per page. Example: 10

page   integer  optional    

Optional page number. Example: 1

Body Parameters

identity_id   string  optional    

Must be 26 characters. Example: bngzmiyvdljnikhwaykcmyuwpw

attemptable_type   string     

Example: architecto

attemptable_id   string     

Must be 26 characters. Example: ngzmiyvdljnikhwaykcmyuwpwl

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 20

page   integer  optional    

Must be at least 1. Example: 43

Record an answer

Records or updates a user's answer to a question. Supports optimistic locking via current_version parameter.

Example request:
curl --request POST \
    "http://localhost/v1/attempts/record-answer" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"attempt_id\": \"01JGATTEMPT000000000001\",
    \"question_instance_id\": \"01JGQUESTION00000000001\",
    \"answer_data\": {
        \"selected_option_id\": \"b\"
    },
    \"time_spent_seconds\": 16,
    \"current_version\": 16,
    \"metadata\": []
}"
const url = new URL(
    "http://localhost/v1/attempts/record-answer"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "attempt_id": "01JGATTEMPT000000000001",
    "question_instance_id": "01JGQUESTION00000000001",
    "answer_data": {
        "selected_option_id": "b"
    },
    "time_spent_seconds": 16,
    "current_version": 16,
    "metadata": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/record-answer';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'attempt_id' => '01JGATTEMPT000000000001',
            'question_instance_id' => '01JGQUESTION00000000001',
            'answer_data' => [
                'selected_option_id' => 'b',
            ],
            'time_spent_seconds' => 16,
            'current_version' => 16,
            'metadata' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Answer recorded successfully",
    "data": {
        "id": "01JGRESPONSE0000000001",
        "attempt_id": "01JGATTEMPT000000000001",
        "question_instance_id": "01JGQUESTION00000000001",
        "answer_snapshot": {
            "selected_option_id": "b"
        },
        "submission_state": "answered",
        "points_awarded": null,
        "is_correct": null,
        "response_version": 1,
        "first_responded_at": "2024-01-15T10:31:00Z",
        "last_responded_at": "2024-01-15T10:31:00Z",
        "time_spent_seconds": 45,
        "metadata": null
    }
}
 

Request      

POST v1/attempts/record-answer

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

attempt_id   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

question_instance_id   string     

The question instance ID (ULID). Example: 01JGQUESTION00000000001

answer_data   object     

The answer data.

time_spent_seconds   integer  optional    

Optional time spent on question. Example: 16

current_version   integer  optional    

Optional current response version for optimistic locking. Example: 16

metadata   object  optional    

Optional response metadata.

Record slot display

Records that a slot/question was displayed to the user. Used for navigation tracking and scheduler context.

Example request:
curl --request POST \
    "http://localhost/v1/attempts/record-slot-display" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"attempt_id\": \"01JGATTEMPT000000000001\",
    \"slot_id\": \"01JGSLOT000000000000001\",
    \"question_instance_id\": \"architecto\",
    \"navigation_method\": \"sequential\",
    \"metadata\": []
}"
const url = new URL(
    "http://localhost/v1/attempts/record-slot-display"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "attempt_id": "01JGATTEMPT000000000001",
    "slot_id": "01JGSLOT000000000000001",
    "question_instance_id": "architecto",
    "navigation_method": "sequential",
    "metadata": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/record-slot-display';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'attempt_id' => '01JGATTEMPT000000000001',
            'slot_id' => '01JGSLOT000000000000001',
            'question_instance_id' => 'architecto',
            'navigation_method' => 'sequential',
            'metadata' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Slot display recorded successfully",
    "data": {
        "id": "01JGHISTORY00000000001",
        "attempt_id": "01JGATTEMPT000000000001",
        "slot_id": "01JGSLOT000000000000001",
        "question_instance_id": "01JGQUESTION00000000001",
        "displayed_at": "2024-01-15T10:30:30Z",
        "display_sequence": 1,
        "navigation_method": "sequential",
        "metadata": null
    }
}
 

Request      

POST v1/attempts/record-slot-display

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

attempt_id   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

slot_id   string     

The slot ID (ULID). Example: 01JGSLOT000000000000001

question_instance_id   string  optional    

Optional question instance ID if slot contains a question. Example: architecto

navigation_method   string  optional    

Optional navigation method. Example: sequential

metadata   object  optional    

Optional display metadata.

Get attempt by ID

Retrieves complete attempt details including all responses and slot history.

Example request:
curl --request GET \
    --get "http://localhost/v1/attempts/01JGATTEMPT000000000001" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/attempts/01JGATTEMPT000000000001"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/01JGATTEMPT000000000001';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Attempt retrieved successfully",
    "data": {
        "id": "01JGATTEMPT000000000001",
        "identity_id": "01JGUSER00000000000001",
        "attemptable_type": "quiz_version",
        "attemptable_id": "01JGQUIZVERSION0000001",
        "status": "in_progress",
        "started_at": "2024-01-15T10:30:00Z",
        "submitted_at": null,
        "abandoned_at": null,
        "total_score": null,
        "max_possible_score": null,
        "metadata": {},
        "slot_history": [],
        "responses": []
    }
}
 

Request      

GET v1/attempts/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

Submit attempt

Submits the attempt for grading. Makes the attempt immutable.

Example request:
curl --request POST \
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/submit" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/submit"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/01JGATTEMPT000000000001/submit';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Attempt submitted successfully",
    "data": {
        "id": "01JGATTEMPT000000000001",
        "status": "submitted",
        "submitted_at": "2024-01-15T10:45:00Z"
    }
}
 

Request      

POST v1/attempts/{id}/submit

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

Abandon attempt

Abandons the attempt. Makes the attempt immutable.

Example request:
curl --request POST \
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/abandon" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/abandon"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/01JGATTEMPT000000000001/abandon';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Attempt abandoned successfully",
    "data": {
        "id": "01JGATTEMPT000000000001",
        "status": "abandoned",
        "abandoned_at": "2024-01-15T10:45:00Z"
    }
}
 

Request      

POST v1/attempts/{id}/abandon

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

Get scheduler runtime context

Returns context data needed by Quiz scheduler drivers.

Example request:
curl --request GET \
    --get "http://localhost/v1/attempts/01JGATTEMPT000000000001/scheduler-context" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/scheduler-context"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/01JGATTEMPT000000000001/scheduler-context';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Scheduler context retrieved successfully",
    "data": {
        "used_question_ids": [
            "01JGQUESTION00000000001"
        ],
        "current_score": 0,
        "max_score": 0,
        "answered_count": 1,
        "skipped_count": 0,
        "elapsed_time_seconds": 120,
        "metadata": {}
    }
}
 

Request      

GET v1/attempts/{id}/scheduler-context

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

Skip a question

Marks a question as explicitly skipped.

Example request:
curl --request POST \
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/skip/01JGQUESTION00000000001" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/attempts/01JGATTEMPT000000000001/skip/01JGQUESTION00000000001"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/01JGATTEMPT000000000001/skip/01JGQUESTION00000000001';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Question skipped successfully",
    "data": {
        "id": "01JGRESPONSE0000000001",
        "attempt_id": "01JGATTEMPT000000000001",
        "question_instance_id": "01JGQUESTION00000000001",
        "answer_snapshot": {},
        "submission_state": "skipped",
        "points_awarded": null,
        "is_correct": null,
        "response_version": 1,
        "first_responded_at": "2024-01-15T10:31:00Z",
        "last_responded_at": "2024-01-15T10:31:00Z",
        "time_spent_seconds": null,
        "metadata": null
    }
}
 

Request      

POST v1/attempts/{attemptId}/skip/{questionInstanceId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

attemptId   string     

The attempt ID (ULID). Example: 01JGATTEMPT000000000001

questionInstanceId   string     

The question instance ID (ULID). Example: 01JGQUESTION00000000001

Block Management

Get all block schemas Returns JSON schemas for all supported block types. Schemas define the structure and validation rules for each block type. Public endpoint - no authentication required.

Example request:
curl --request GET \
    --get "http://localhost/v1/blocks/schemas" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/blocks/schemas"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/schemas';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Block schemas retrieved successfully",
    "data": [
        {
            "type": "paragraph",
            "schema": {
                "type": "object",
                "properties": {
                    "content": {
                        "type": "string"
                    }
                }
            }
        },
        {
            "type": "headline",
            "schema": {
                "type": "object",
                "properties": {
                    "content": {
                        "type": "string"
                    },
                    "level": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 6
                    }
                }
            }
        }
    ]
}
 

Request      

GET v1/blocks/schemas

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Get schema for specific block type Returns the JSON schema for a specific block type. Supported types: paragraph, headline, image, table. Public endpoint - no authentication required.

Example request:
curl --request GET \
    --get "http://localhost/v1/blocks/schemas/paragraph" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/blocks/schemas/paragraph"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/schemas/paragraph';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Schema retrieved successfully for type: paragraph",
    "data": {
        "type": "paragraph",
        "schema": {
            "type": "object",
            "properties": {
                "content": {
                    "type": "string"
                }
            },
            "required": [
                "content"
            ]
        }
    }
}
 

Example response (404):


{
    "success": false,
    "message": "Block type not found: unknown"
}
 

Request      

GET v1/blocks/schemas/{type}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

type   string     

Block type. Example: paragraph

Validate array of blocks Validates an array of blocks against their JSON schemas. Optionally restrict validation to specific block types. Returns validation result with detailed error messages if validation fails. Public endpoint - no authentication required.

Example request:
curl --request POST \
    "http://localhost/v1/blocks/validate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"blocks\": [
        {
            \"type\": \"paragraph\",
            \"content\": \"Hello\"
        }
    ],
    \"allowed_types\": [
        \"paragraph\",
        \"headline\"
    ]
}"
const url = new URL(
    "http://localhost/v1/blocks/validate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "blocks": [
        {
            "type": "paragraph",
            "content": "Hello"
        }
    ],
    "allowed_types": [
        "paragraph",
        "headline"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/validate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
            $o = [
                clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
            ],
            null,
            [
                'stdClass' => [
                    'type' => [
                        'paragraph',
                    ],
                    'content' => [
                        'Hello',
                    ],
                ],
            ],
            [
                'blocks' => [
                    $o[0],
                ],
                'allowed_types' => [
                    'paragraph',
                    'headline',
                ],
            ],
            []
        ),
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "All blocks are valid",
    "data": {
        "is_valid": true,
        "errors": []
    }
}
 

Example response (400):


{
    "success": false,
    "message": "Validation failed",
    "errors": {
        "is_valid": false,
        "errors": [
            {
                "block_index": 0,
                "message": "Block type 'unknown' is not supported"
            },
            {
                "block_index": 1,
                "field": "content",
                "message": "The content field is required"
            }
        ]
    }
}
 

Request      

POST v1/blocks/validate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

blocks   string[]     

Array of block objects to validate.

allowed_types   string[]  optional    

Optional array of allowed block types. If provided, blocks with other types will fail validation.

Transform blocks to HTML Transforms an array of structured blocks into HTML markup. Supports paragraph, headline, image, and table block types. Preserves block structure and formatting. Public endpoint - no authentication required.

Example request:
curl --request POST \
    "http://localhost/v1/blocks/transform/html" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"blocks\": [
        {
            \"type\": \"paragraph\",
            \"content\": \"Hello world\"
        }
    ],
    \"language\": \"en\"
}"
const url = new URL(
    "http://localhost/v1/blocks/transform/html"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "blocks": [
        {
            "type": "paragraph",
            "content": "Hello world"
        }
    ],
    "language": "en"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/transform/html';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
            $o = [
                clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
            ],
            null,
            [
                'stdClass' => [
                    'type' => [
                        'paragraph',
                    ],
                    'content' => [
                        'Hello world',
                    ],
                ],
            ],
            [
                'blocks' => [
                    $o[0],
                ],
                'language' => 'en',
            ],
            []
        ),
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Blocks transformed to HTML successfully",
    "data": {
        "html": "<p>Hello world</p>"
    }
}
 

Example response (400):


{
    "success": false,
    "message": "No blocks provided"
}
 

Request      

POST v1/blocks/transform/html

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

blocks   string[]     

Array of block objects to transform.

language   string  optional    

Optional language code for HTML lang attribute. Example: en

Transform blocks to Markdown Transforms an array of structured blocks into Markdown format. Supports paragraph, headline, image, and table block types. Preserves block structure and converts formatting to Markdown syntax. Public endpoint - no authentication required.

Example request:
curl --request POST \
    "http://localhost/v1/blocks/transform/markdown" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"blocks\": [
        {
            \"type\": \"headline\",
            \"content\": \"Title\",
            \"level\": 1
        }
    ],
    \"language\": \"en\"
}"
const url = new URL(
    "http://localhost/v1/blocks/transform/markdown"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "blocks": [
        {
            "type": "headline",
            "content": "Title",
            "level": 1
        }
    ],
    "language": "en"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/transform/markdown';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
            $o = [
                clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
            ],
            null,
            [
                'stdClass' => [
                    'type' => [
                        'headline',
                    ],
                    'content' => [
                        'Title',
                    ],
                    'level' => [
                        1,
                    ],
                ],
            ],
            [
                'blocks' => [
                    $o[0],
                ],
                'language' => 'en',
            ],
            []
        ),
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Blocks transformed to Markdown successfully",
    "data": {
        "markdown": "# Title\n\nParagraph text here."
    }
}
 

Example response (400):


{
    "success": false,
    "message": "No blocks provided"
}
 

Request      

POST v1/blocks/transform/markdown

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

blocks   string[]     

Array of block objects to transform.

language   string  optional    

Optional language code for metadata. Example: en

Transform blocks to plain text Transforms an array of structured blocks into plain text without formatting. Supports paragraph, headline, image, and table block types. Strips all formatting and markup, preserving only text content. Public endpoint - no authentication required.

Example request:
curl --request POST \
    "http://localhost/v1/blocks/transform/plaintext" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"blocks\": [
        {
            \"type\": \"paragraph\",
            \"content\": \"Hello\"
        }
    ],
    \"language\": \"en\"
}"
const url = new URL(
    "http://localhost/v1/blocks/transform/plaintext"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "blocks": [
        {
            "type": "paragraph",
            "content": "Hello"
        }
    ],
    "language": "en"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/transform/plaintext';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
            $o = [
                clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
            ],
            null,
            [
                'stdClass' => [
                    'type' => [
                        'paragraph',
                    ],
                    'content' => [
                        'Hello',
                    ],
                ],
            ],
            [
                'blocks' => [
                    $o[0],
                ],
                'language' => 'en',
            ],
            []
        ),
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Blocks transformed to plain text successfully",
    "data": {
        "text": "Hello world"
    }
}
 

Example response (400):


{
    "success": false,
    "message": "No blocks provided"
}
 

Request      

POST v1/blocks/transform/plaintext

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

blocks   string[]     

Array of block objects to transform.

language   string  optional    

Optional language code for metadata. Example: en

Parse HTML to blocks Parses HTML content into structured block objects. Converts common HTML tags (p, h1-h6, img, table) into corresponding block types. Returns an array of validated block objects. Public endpoint - no authentication required.

Example request:
curl --request POST \
    "http://localhost/v1/blocks/parse/html" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"html\": \"<p>Hello world<\\/p><h1>Title<\\/h1>\"
}"
const url = new URL(
    "http://localhost/v1/blocks/parse/html"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "html": "<p>Hello world<\/p><h1>Title<\/h1>"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/parse/html';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'html' => '<p>Hello world</p><h1>Title</h1>',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "HTML parsed to blocks successfully",
    "data": {
        "blocks": [
            {
                "type": "paragraph",
                "content": "Hello world"
            },
            {
                "type": "headline",
                "content": "Title",
                "level": 1
            }
        ]
    }
}
 

Example response (400):


{
    "success": false,
    "message": "No HTML content provided"
}
 

Request      

POST v1/blocks/parse/html

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

html   string     

HTML content to parse. Example: <p>Hello world</p><h1>Title</h1>

Parse Markdown to blocks Parses Markdown content into structured block objects. Converts Markdown syntax (paragraphs, headings, images, tables) into corresponding block types. Returns an array of validated block objects. Public endpoint - no authentication required.

Example request:
curl --request POST \
    "http://localhost/v1/blocks/parse/markdown" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"markdown\": \"# Title\\\\n\\\\nParagraph text here.\"
}"
const url = new URL(
    "http://localhost/v1/blocks/parse/markdown"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "markdown": "# Title\\n\\nParagraph text here."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/blocks/parse/markdown';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'markdown' => '# Title\\n\\nParagraph text here.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Markdown parsed to blocks successfully",
    "data": {
        "blocks": [
            {
                "type": "headline",
                "content": "Title",
                "level": 1
            },
            {
                "type": "paragraph",
                "content": "Paragraph text here."
            }
        ]
    }
}
 

Example response (400):


{
    "success": false,
    "message": "No Markdown content provided"
}
 

Request      

POST v1/blocks/parse/markdown

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

markdown   string     

Markdown content to parse. Example: # Title\n\nParagraph text here.

Content Hierarchy

Reorder child pages

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/reorder" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"ordered_ids\": [
        \"architecto\"
    ]
}"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/reorder"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "ordered_ids": [
        "architecto"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/reorder';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'ordered_ids' => [
                'architecto',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{parentId}/reorder

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

parentId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

ordered_ids   string[]     

The id of an existing record in the contents table.

Content History

Compare two versions

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/compare" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"version1\": \"architecto\",
    \"version2\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/compare"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "version1": "architecto",
    "version2": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/compare';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'version1' => 'architecto',
            'version2' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/history/compare

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

version1   string     

Example: architecto

version2   string     

Example: architecto

Restore a previous version

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/history/{historyId}/restore

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

historyId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Content Management

Create new content page

Example request:
curl --request POST \
    "http://localhost/v1/content" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update content by ID

Example request:
curl --request PUT \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/content/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete content by ID (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/content/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Update content by slug

Example request:
curl --request PUT \
    "http://localhost/v1/content/slug/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/slug/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/slug/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/content/slug/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the slug. Example: architecto

Delete content by slug (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/content/slug/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/slug/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/slug/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/content/slug/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the slug. Example: architecto

Publish content page

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/publish" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/publish"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/publish';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/publish

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Unpublish content page (revert to draft)

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/unpublish" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/unpublish"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/unpublish';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/unpublish

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Schedule content for future publication

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/schedule" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"published_at\": \"2052-03-01\"
}"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/schedule"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "published_at": "2052-03-01"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/schedule';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'published_at' => '2052-03-01',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/schedule

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

published_at   string     

Must be a valid date. Must be a date after now. Example: 2052-03-01

Content Progress

Mark content page as completed

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/progress/complete" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/progress/complete"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/progress/complete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/progress/complete

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Update progress percentage for a content page

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/progress" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"progress_percentage\": 1
}"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/progress"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "progress_percentage": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/progress';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'progress_percentage' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/progress

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

progress_percentage   integer     

Must be at least 0. Must not be greater than 100. Example: 1

Content Tagging

Assign tag(s) to content

Example request:
curl --request POST \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"tag_slugs\": [
        \"architecto\"
    ],
    \"context\": \"n\"
}"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag_slugs": [
        "architecto"
    ],
    "context": "n"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'tag_slugs' => [
                'architecto',
            ],
            'context' => 'n',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/{id}/tags

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

tag_slugs   string[]     
context   string  optional    

Must not be greater than 50 characters. Example: n

Remove tag(s) from content

Example request:
curl --request DELETE \
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"tag_slugs\": [
        \"architecto\"
    ],
    \"context\": \"n\"
}"
const url = new URL(
    "http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag_slugs": [
        "architecto"
    ],
    "context": "n"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'tag_slugs' => [
                'architecto',
            ],
            'context' => 'n',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/content/{id}/tags

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the content. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

tag_slugs   string[]     
context   string  optional    

Must not be greater than 50 characters. Example: n

Bulk assign tags to multiple contents

Example request:
curl --request POST \
    "http://localhost/v1/content/tags/bulk-assign" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"assignments\": [
        {
            \"resource_id\": \"ngzmiyvdljnikhwaykcmyuwpwl\",
            \"tag_slugs\": [
                \"architecto\"
            ]
        }
    ],
    \"context\": \"b\"
}"
const url = new URL(
    "http://localhost/v1/content/tags/bulk-assign"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "assignments": [
        {
            "resource_id": "ngzmiyvdljnikhwaykcmyuwpwl",
            "tag_slugs": [
                "architecto"
            ]
        }
    ],
    "context": "b"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/content/tags/bulk-assign';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'assignments' => [
                [
                    'resource_id' => 'ngzmiyvdljnikhwaykcmyuwpwl',
                    'tag_slugs' => [
                        'architecto',
                    ],
                ],
            ],
            'context' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/content/tags/bulk-assign

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

assignments   object[]     

Must have at least 1 items.

resource_id   string     

Must be 26 characters. Example: ngzmiyvdljnikhwaykcmyuwpwl

tag_slugs   string[]     
context   string  optional    

Must not be greater than 50 characters. Example: b

Endpoints

POST livewire/update

Example request:
curl --request POST \
    "http://localhost/livewire/update" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/livewire/update"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/livewire/update';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST livewire/update

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

POST livewire/upload-file

Example request:
curl --request POST \
    "http://localhost/livewire/upload-file" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/livewire/upload-file"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/livewire/upload-file';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST livewire/upload-file

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

POST v1/ads/{id}/restore/{historyId}

Example request:
curl --request POST \
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/ads/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/ads/{id}/restore/{historyId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ad. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

historyId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

DELETE v1/attempts/{id}

Example request:
curl --request DELETE \
    "http://localhost/v1/attempts/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/attempts/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/attempts/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/attempts/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the attempt. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Create a new course

Example request:
curl --request POST \
    "http://localhost/v1/courses" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/courses"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/courses

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update a course

Example request:
curl --request PUT \
    "http://localhost/v1/courses/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/courses/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/courses/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the course. Example: architecto

Delete a course

Example request:
curl --request DELETE \
    "http://localhost/v1/courses/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/courses/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/courses/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the course. Example: architecto

Publish a course

Example request:
curl --request POST \
    "http://localhost/v1/courses/architecto/publish" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/courses/architecto/publish"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/architecto/publish';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/courses/{id}/publish

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the course. Example: architecto

Archive a course

Example request:
curl --request POST \
    "http://localhost/v1/courses/architecto/archive" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/courses/architecto/archive"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/architecto/archive';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/courses/{id}/archive

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the course. Example: architecto

Add activity to course

Example request:
curl --request POST \
    "http://localhost/v1/courses/architecto/activities" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"activity_type\": \"content\",
    \"activity_id\": \"bngzmiyvdljnikhwaykcmyuwpw\",
    \"sort_order\": 89,
    \"is_required\": true,
    \"is_graded\": true,
    \"weight\": 34
}"
const url = new URL(
    "http://localhost/v1/courses/architecto/activities"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "activity_type": "content",
    "activity_id": "bngzmiyvdljnikhwaykcmyuwpw",
    "sort_order": 89,
    "is_required": true,
    "is_graded": true,
    "weight": 34
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/architecto/activities';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'activity_type' => 'content',
            'activity_id' => 'bngzmiyvdljnikhwaykcmyuwpw',
            'sort_order' => 89,
            'is_required' => true,
            'is_graded' => true,
            'weight' => 34,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/courses/{id}/activities

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the course. Example: architecto

Body Parameters

activity_type   string     

Example: content

Must be one of:
  • content
  • quiz
activity_id   string     

Must be 26 characters. Example: bngzmiyvdljnikhwaykcmyuwpw

title   object  optional    
description   object  optional    
sort_order   integer  optional    

Must be at least 0. Example: 89

is_required   boolean  optional    

Example: true

is_graded   boolean  optional    

Example: true

weight   number  optional    

Must be at least 0. Example: 34

unlock_rules   object  optional    

Update activity

Example request:
curl --request PUT \
    "http://localhost/v1/courses/activities/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"sort_order\": 27,
    \"is_required\": true,
    \"is_graded\": true,
    \"weight\": 39
}"
const url = new URL(
    "http://localhost/v1/courses/activities/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "sort_order": 27,
    "is_required": true,
    "is_graded": true,
    "weight": 39
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/activities/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'sort_order' => 27,
            'is_required' => true,
            'is_graded' => true,
            'weight' => 39,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/courses/activities/{activityId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

activityId   string     

Example: architecto

Body Parameters

title   object  optional    
description   object  optional    
sort_order   integer  optional    

Must be at least 0. Example: 27

is_required   boolean  optional    

Example: true

is_graded   boolean  optional    

Example: true

weight   number  optional    

Must be at least 0. Example: 39

unlock_rules   object  optional    

Delete activity

Example request:
curl --request DELETE \
    "http://localhost/v1/courses/activities/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/courses/activities/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/activities/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/courses/activities/{activityId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

activityId   string     

Example: architecto

Reorder activities

Example request:
curl --request POST \
    "http://localhost/v1/courses/architecto/activities/reorder" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"activity_ids\": [
        \"bngzmiyvdljnikhwaykcmyuwpw\"
    ]
}"
const url = new URL(
    "http://localhost/v1/courses/architecto/activities/reorder"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "activity_ids": [
        "bngzmiyvdljnikhwaykcmyuwpw"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/courses/architecto/activities/reorder';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'activity_ids' => [
                'bngzmiyvdljnikhwaykcmyuwpw',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/courses/{id}/activities/reorder

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the course. Example: architecto

Body Parameters

activity_ids   string[]     

Must be 26 characters.

Start an activity

Example request:
curl --request POST \
    "http://localhost/v1/enrollments/architecto/activities/architecto/start" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/enrollments/architecto/activities/architecto/start"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/enrollments/architecto/activities/architecto/start';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/enrollments/{enrollmentId}/activities/{activityId}/start

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

enrollmentId   string     

Example: architecto

activityId   string     

Example: architecto

Complete an activity

Example request:
curl --request POST \
    "http://localhost/v1/enrollments/architecto/activities/architecto/complete" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"score\": 1
}"
const url = new URL(
    "http://localhost/v1/enrollments/architecto/activities/architecto/complete"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "score": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/enrollments/architecto/activities/architecto/complete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'score' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/enrollments/{enrollmentId}/activities/{activityId}/complete

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

enrollmentId   string     

Example: architecto

activityId   string     

Example: architecto

Body Parameters

score   number  optional    

Must be at least 0. Must not be greater than 100. Example: 1

Suspend enrollment (admin)

Example request:
curl --request POST \
    "http://localhost/v1/enrollments/architecto/suspend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/enrollments/architecto/suspend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/enrollments/architecto/suspend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/enrollments/{id}/suspend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the enrollment. Example: architecto

Reactivate enrollment (admin)

Example request:
curl --request POST \
    "http://localhost/v1/enrollments/architecto/reactivate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/enrollments/architecto/reactivate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/enrollments/architecto/reactivate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/enrollments/{id}/reactivate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the enrollment. Example: architecto

Issue certificate for an enrollment

Example request:
curl --request POST \
    "http://localhost/v1/certificates/enrollments/architecto/issue" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"signed_by\": \"bngzmiyvdljnikhwaykcmyuwpw\"
}"
const url = new URL(
    "http://localhost/v1/certificates/enrollments/architecto/issue"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "signed_by": "bngzmiyvdljnikhwaykcmyuwpw"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/certificates/enrollments/architecto/issue';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'signed_by' => 'bngzmiyvdljnikhwaykcmyuwpw',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/certificates/enrollments/{enrollmentId}/issue

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

enrollmentId   string     

Example: architecto

Body Parameters

signed_by   string  optional    

Must be 26 characters. Example: bngzmiyvdljnikhwaykcmyuwpw

Regenerate certificate PDF

Example request:
curl --request POST \
    "http://localhost/v1/certificates/architecto/regenerate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/certificates/architecto/regenerate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/certificates/architecto/regenerate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/certificates/{id}/regenerate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the certificate. Example: architecto

Handle password authentication.

Example request:
curl --request POST \
    "http://localhost/dashboard/auth" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"password\": \"|]|{+-\"
}"
const url = new URL(
    "http://localhost/dashboard/auth"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "password": "|]|{+-"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/dashboard/auth';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'password' => '|]|{+-',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST dashboard/auth

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

password   string     

Example: |]|{+-

Logout from dashboard.

Example request:
curl --request POST \
    "http://localhost/dashboard/logout" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/dashboard/logout"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/dashboard/logout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST dashboard/logout

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

POST v1/quizzes

Example request:
curl --request POST \
    "http://localhost/v1/quizzes" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

PUT v1/quizzes/{id}

Example request:
curl --request PUT \
    "http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/quizzes/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the quiz. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

DELETE v1/quizzes/{id}

Example request:
curl --request DELETE \
    "http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/quizzes/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the quiz. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

PUT v1/quizzes/{slug}

Example request:
curl --request PUT \
    "http://localhost/v1/quizzes/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/quizzes/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the quiz. Example: architecto

DELETE v1/quizzes/{slug}

Example request:
curl --request DELETE \
    "http://localhost/v1/quizzes/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/quizzes/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the quiz. Example: architecto

POST v1/quizzes/{quiz_id}/versions

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6/versions" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6/versions"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/58C6f7nnFCJ9M6rjyc6rTKMzn6/versions';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/{quiz_id}/versions

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

quiz_id   string     

The ID of the quiz. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

POST v1/quizzes/versions/{version_id}/publish

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/publish" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/publish"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/publish';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/versions/{version_id}/publish

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

version_id   string     

The ID of the version. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

POST v1/quizzes/versions/{version_id}/duplicate

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/duplicate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/duplicate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/duplicate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/versions/{version_id}/duplicate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

version_id   string     

The ID of the version. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

DELETE v1/quizzes/versions/{version_id}

Example request:
curl --request DELETE \
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/quizzes/versions/{version_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

version_id   string     

The ID of the version. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Compare two quiz versions.

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/versions/compare" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"old_version_id\": \"architecto\",
    \"new_version_id\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/quizzes/versions/compare"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "old_version_id": "architecto",
    "new_version_id": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/versions/compare';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'old_version_id' => 'architecto',
            'new_version_id' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/versions/compare

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

old_version_id   string     

Example: architecto

new_version_id   string     

Example: architecto

POST v1/quizzes/versions/{version_id}/sections

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/sections" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/sections"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/sections';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/versions/{version_id}/sections

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

version_id   string     

The ID of the version. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

PUT v1/quizzes/sections/{section_id}

Example request:
curl --request PUT \
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/quizzes/sections/{section_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

section_id   string     

The ID of the section. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

DELETE v1/quizzes/sections/{section_id}

Example request:
curl --request DELETE \
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/quizzes/sections/{section_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

section_id   string     

The ID of the section. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

POST v1/quizzes/versions/{version_id}/sections/reorder

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/sections/reorder" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"section_ids\": [
        \"bngzmiyvdljnikhwaykcmyuwpw\"
    ]
}"
const url = new URL(
    "http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/sections/reorder"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "section_ids": [
        "bngzmiyvdljnikhwaykcmyuwpw"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/sections/reorder';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'section_ids' => [
                'bngzmiyvdljnikhwaykcmyuwpw',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/versions/{version_id}/sections/reorder

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

version_id   string     

The ID of the version. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

section_ids   string[]     

Must be 26 characters.

POST v1/quizzes/sections/{section_id}/slots

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6/slots" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6/slots"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6/slots';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/sections/{section_id}/slots

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

section_id   string     

The ID of the section. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

PUT v1/quizzes/slots/{slot_id}

Example request:
curl --request PUT \
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/quizzes/slots/{slot_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slot_id   string     

The ID of the slot. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

DELETE v1/quizzes/slots/{slot_id}

Example request:
curl --request DELETE \
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/quizzes/slots/{slot_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slot_id   string     

The ID of the slot. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

POST v1/quizzes/sections/{section_id}/slots/reorder

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6/slots/reorder" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"slot_ids\": [
        \"bngzmiyvdljnikhwaykcmyuwpw\"
    ]
}"
const url = new URL(
    "http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6/slots/reorder"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "slot_ids": [
        "bngzmiyvdljnikhwaykcmyuwpw"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/sections/58C6f7nnFCJ9M6rjyc6rTKMzn6/slots/reorder';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'slot_ids' => [
                'bngzmiyvdljnikhwaykcmyuwpw',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/sections/{section_id}/slots/reorder

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

section_id   string     

The ID of the section. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

slot_ids   string[]     

Must be 26 characters.

Bulk create question instances for a slot.

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6/question-instances/bulk" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"instances\": [
        {
            \"question_id\": \"architecto\",
            \"points\": 39
        }
    ]
}"
const url = new URL(
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6/question-instances/bulk"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "instances": [
        {
            "question_id": "architecto",
            "points": 39
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6/question-instances/bulk';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'instances' => [
                [
                    'question_id' => 'architecto',
                    'points' => 39,
                ],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/slots/{slot_id}/question-instances/bulk

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slot_id   string     

The ID of the slot. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

instances   object[]     

Must have at least 1 items.

question_id   string     

Example: architecto

points   number  optional    

Must be at least 0. Example: 39

metadata   object  optional    

POST v1/quizzes/slots/{slot_id}/question-instances

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6/question-instances" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6/question-instances"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/slots/58C6f7nnFCJ9M6rjyc6rTKMzn6/question-instances';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/slots/{slot_id}/question-instances

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slot_id   string     

The ID of the slot. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

PUT v1/quizzes/question-instances/{instance_id}

Example request:
curl --request PUT \
    "http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/quizzes/question-instances/{instance_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

instance_id   string     

The ID of the instance. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

POST v1/quizzes/question-instances/{instance_id}/refresh

Example request:
curl --request POST \
    "http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6/refresh" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6/refresh"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6/refresh';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/quizzes/question-instances/{instance_id}/refresh

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

instance_id   string     

The ID of the instance. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

DELETE v1/quizzes/question-instances/{instance_id}

Example request:
curl --request DELETE \
    "http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/quizzes/question-instances/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/quizzes/question-instances/{instance_id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

instance_id   string     

The ID of the instance. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Evaluation

Submit manual grade for assignment.

requires authentication

Example request:
curl --request POST \
    "http://localhost/v1/evaluation/manual-grading/58C6f7nnFCJ9M6rjyc6rTKMzn6/grade" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"points_awarded\": 27,
    \"feedback\": \"n\",
    \"rubric_scores\": [
        84
    ]
}"
const url = new URL(
    "http://localhost/v1/evaluation/manual-grading/58C6f7nnFCJ9M6rjyc6rTKMzn6/grade"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "points_awarded": 27,
    "feedback": "n",
    "rubric_scores": [
        84
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluation/manual-grading/58C6f7nnFCJ9M6rjyc6rTKMzn6/grade';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'points_awarded' => 27,
            'feedback' => 'n',
            'rubric_scores' => [
                84,
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/evaluation/manual-grading/{assignmentId}/grade

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

assignmentId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

points_awarded   number     

Must be at least 0. Example: 27

feedback   string  optional    

Must not be greater than 10000 characters. Example: n

rubric_scores   number[]  optional    

Must be at least 0.

Assign grading assignment to instructor.

requires authentication

Example request:
curl --request PUT \
    "http://localhost/v1/evaluation/manual-grading/58C6f7nnFCJ9M6rjyc6rTKMzn6/assign" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"assigned_to\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/evaluation/manual-grading/58C6f7nnFCJ9M6rjyc6rTKMzn6/assign"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "assigned_to": "architecto"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluation/manual-grading/58C6f7nnFCJ9M6rjyc6rTKMzn6/assign';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'assigned_to' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/evaluation/manual-grading/{assignmentId}/assign

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

assignmentId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

assigned_to   string     

Example: architecto

Regrade a single attempt.

requires authentication

Resets scores and re-dispatches grading job. Useful for fixing grading errors or updating after config changes.

Example request:
curl --request POST \
    "http://localhost/v1/evaluation/attempts/58C6f7nnFCJ9M6rjyc6rTKMzn6/regrade" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/evaluation/attempts/58C6f7nnFCJ9M6rjyc6rTKMzn6/regrade"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluation/attempts/58C6f7nnFCJ9M6rjyc6rTKMzn6/regrade';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/evaluation/attempts/{attemptId}/regrade

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

attemptId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Regrade all attempts for a quiz version.

requires authentication

Batch regrading for all submitted attempts of a quiz version. Useful after updating grading configuration.

Example request:
curl --request POST \
    "http://localhost/v1/evaluation/quiz-versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/regrade" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/evaluation/quiz-versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/regrade"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluation/quiz-versions/58C6f7nnFCJ9M6rjyc6rTKMzn6/regrade';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/evaluation/quiz-versions/{versionId}/regrade

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

versionId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Create new evaluation

Example request:
curl --request POST \
    "http://localhost/v1/evaluations" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/evaluations"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluations';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/evaluations

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update evaluation

Example request:
curl --request PUT \
    "http://localhost/v1/evaluations/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/evaluations/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluations/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/evaluations/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the evaluation. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete evaluation

Example request:
curl --request DELETE \
    "http://localhost/v1/evaluations/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/evaluations/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/evaluations/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/evaluations/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the evaluation. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Global User Management

List all users globally (cross-tenant)

Example request:
curl --request GET \
    --get "http://localhost/v1/global/users?per_page=20&page=1&is_anonymous=&search=john&created_at_gte=2024-01-01&created_at_lte=2024-12-31&sort=-created_at" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/global/users"
);

const params = {
    "per_page": "20",
    "page": "1",
    "is_anonymous": "0",
    "search": "john",
    "created_at_gte": "2024-01-01",
    "created_at_lte": "2024-12-31",
    "sort": "-created_at",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/global/users';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
            'page' => '1',
            'is_anonymous' => '0',
            'search' => 'john',
            'created_at_gte' => '2024-01-01',
            'created_at_lte' => '2024-12-31',
            'sort' => '-created_at',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Users retrieved successfully", "data": [...], "meta": {"pagination": {"total": 50, "per_page": 10, "current_page": 1, "last_page": 5}}}
 

Request      

GET v1/global/users

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Number of items per page (1-100). Defaults to 10. Example: 20

page   integer  optional    

Current page number. Defaults to 1. Example: 1

is_anonymous   boolean  optional    

Filter by anonymous status. Example: false

search   string  optional    

Search in email, phone, and username. Example: john

created_at_gte   string  optional    

Filter users created on or after date. Example: 2024-01-01

created_at_lte   string  optional    

Filter users created on or before date. Example: 2024-12-31

sort   string  optional    

Sort field. Prefix with '-' for descending. Example: -created_at

Create new user with explicit tenant assignment

Example request:
curl --request POST \
    "http://localhost/v1/global/users" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/global/users"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/global/users';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/global/users

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update user globally (allows tenant transfer)

Example request:
curl --request PUT \
    "http://localhost/v1/global/users/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/global/users/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/global/users/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/global/users/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the user. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete user globally (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/global/users/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/global/users/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/global/users/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/global/users/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the user. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Health

Check API Health

Example request:
curl --request GET \
    --get "http://localhost/v1/health" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/health"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/health';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "status": "healthy"
}
 

Example response (503):


{
    "status": "unhealthy"
}
 

Request      

GET v1/health

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Identity

Register new user Creates a new user account with identity (password). Requires at least one identifier (email, phone, or username) plus a password.

Example request:
curl --request POST \
    "http://localhost/v1/identity/register" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/identity/register"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/identity/register';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "success": true,
    "message": "Registration successful",
    "data": {
        "user": {
            "id": "01kav3fcmme10dwbeexyqkfndn",
            "email": "user@example.com",
            "tenant_id": "01kav3fbpbpb3wnhtadsmrmt7m"
        },
        "tokens": {
            "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "token_type": "Bearer",
            "expires_in": 86400
        }
    }
}
 

Request      

POST v1/identity/register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Login with credentials Authenticate with email/phone/username and password. Returns JWT access token (24h) and refresh token (30d). Rate limited to 10 attempts per hour.

Example request:
curl --request POST \
    "http://localhost/v1/identity/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/identity/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/identity/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Login successful",
    "data": {
        "user": {
            "id": "01kav3fcmme10dwbeexyqkfndn",
            "email": "user@example.com"
        },
        "tokens": {
            "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "token_type": "Bearer",
            "expires_in": 86400
        }
    }
}
 

Request      

POST v1/identity/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Refresh access token Exchange refresh token for new access and refresh tokens. Implements OAuth2 token rotation - old refresh token is blacklisted.

Example request:
curl --request POST \
    "http://localhost/v1/identity/refresh" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/identity/refresh"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/identity/refresh';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Token refreshed successfully",
    "data": {
        "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
        "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
        "token_type": "Bearer",
        "expires_in": 86400
    }
}
 

Request      

POST v1/identity/refresh

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Anonymous login Generate anonymous session token without creating database records. Returns a tracking ID and tokens that can be used to link requests across modules. User can later "upgrade" to authenticated by registering with this ID.

Example request:
curl --request POST \
    "http://localhost/v1/identity/anonymous" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/identity/anonymous"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/identity/anonymous';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Anonymous session created",
    "data": {
        "anonymous_id": "01kav3fcmme10dwbeexyqkfndn",
        "tokens": {
            "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "token_type": "Bearer",
            "expires_in": 86400
        }
    }
}
 

Request      

POST v1/identity/anonymous

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Claim anonymous account Converts anonymous session to authenticated account by claiming it with credentials. Requires anonymous token in Authorization header. Creates User and Identity records with the anonymous ID. Preserves data linkage across modules via shared ID.

Example request:
curl --request POST \
    "http://localhost/v1/identity/anonymous/claim" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/identity/anonymous/claim"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/identity/anonymous/claim';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Account upgraded successfully",
    "data": {
        "user": {
            "id": "01kav3fcmme10dwbeexyqkfndn",
            "email": "user@example.com",
            "is_anonymous": false
        },
        "tokens": {
            "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
            "token_type": "Bearer",
            "expires_in": 86400
        }
    }
}
 

Example response (401):


{
    "success": false,
    "message": "Missing or invalid authorization token"
}
 

Example response (409):


{
    "success": false,
    "message": "Anonymous account has already been upgraded"
}
 

Request      

POST v1/identity/anonymous/claim

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

anonymous   string     

Anonymous identifier (from route: /identity/anonymous/claim) Example: architecto

Change password Change password for authenticated user. Requires current password for verification. Protected route - requires valid access token.

requires authentication

Example request:
curl --request POST \
    "http://localhost/v1/identity/change-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/identity/change-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/identity/change-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Password changed successfully",
    "data": null
}
 

Request      

POST v1/identity/change-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Media Management

Upload new media file

Example request:
curl --request POST \
    "http://localhost/v1/media" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/media"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/media

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Upload media from URL

Example request:
curl --request POST \
    "http://localhost/v1/media/from-url" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"url\": \"http:\\/\\/www.bailey.biz\\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html\",
    \"privacy\": \"identity\",
    \"collection_name\": \"i\",
    \"title\": \"k\",
    \"description\": \"Aut ab provident perspiciatis quo omnis nostrum aut.\",
    \"alt_text\": \"w\",
    \"expires_at\": \"2026-02-05T13:20:33\"
}"
const url = new URL(
    "http://localhost/v1/media/from-url"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html",
    "privacy": "identity",
    "collection_name": "i",
    "title": "k",
    "description": "Aut ab provident perspiciatis quo omnis nostrum aut.",
    "alt_text": "w",
    "expires_at": "2026-02-05T13:20:33"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media/from-url';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'url' => 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html',
            'privacy' => 'identity',
            'collection_name' => 'i',
            'title' => 'k',
            'description' => 'Aut ab provident perspiciatis quo omnis nostrum aut.',
            'alt_text' => 'w',
            'expires_at' => '2026-02-05T13:20:33',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/media/from-url

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

url   string     

Must be a valid URL. Must not be greater than 2048 characters. Example: http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html

privacy   string  optional    

Example: identity

Must be one of:
  • public
  • tenant
  • identity
collection_name   string  optional    

Must not be greater than 100 characters. Example: i

title   string  optional    

Must not be greater than 255 characters. Example: k

description   string  optional    

Must not be greater than 1000 characters. Example: Aut ab provident perspiciatis quo omnis nostrum aut.

alt_text   string  optional    

Must not be greater than 500 characters. Example: w

expires_at   string  optional    

Must be a valid date. Example: 2026-02-05T13:20:33

Batch upload multiple media files

Example request:
curl --request POST \
    "http://localhost/v1/media/batch" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"files\": null,
    \"privacy\": \"tenant\",
    \"collection_name\": \"question-block-images\",
    \"titles\": [
        \"Image 1\",
        \"Image 2\"
    ]
}"
const url = new URL(
    "http://localhost/v1/media/batch"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "files": null,
    "privacy": "tenant",
    "collection_name": "question-block-images",
    "titles": [
        "Image 1",
        "Image 2"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media/batch';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'files' => null,
            'privacy' => 'tenant',
            'collection_name' => 'question-block-images',
            'titles' => [
                'Image 1',
                'Image 2',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{"success": true, "message": "3 files uploaded successfully", "data": {"media": [{"id": "01...", ...}], "errors": []}}
 

Request      

POST v1/media/batch

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

files   file[]     

Array of files to upload.

privacy   string     

Privacy level for all files. Example: tenant

collection_name   string  optional    

nullable Collection name for all files. Example: question-block-images

titles   string[]  optional    

nullable Array of titles (one per file).

Update media metadata (file content is immutable)

Example request:
curl --request PATCH \
    "http://localhost/v1/media/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/media/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PATCH",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH v1/media/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the medium. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete media by ID (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/media/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/media/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/media/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the medium. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Bulk delete media files

Example request:
curl --request POST \
    "http://localhost/v1/media/bulk-delete" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"ids\": [
        \"architecto\"
    ]
}"
const url = new URL(
    "http://localhost/v1/media/bulk-delete"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "ids": [
        "architecto"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media/bulk-delete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'ids' => [
                'architecto',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/media/bulk-delete

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

ids   string[]     

Cleanup expired media files

Example request:
curl --request POST \
    "http://localhost/v1/media/cleanup" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/media/cleanup"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/media/cleanup';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/media/cleanup

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Notification Management

Send a notification (single or bulk) Single notification: { "user_id": "ulid", "category": "info", "template_name": "welcome", "data": {...} } Bulk notifications: { "notifications": [ {"user_id": "ulid1", "category": "info", ...}, {"user_id": "ulid2", "category": "urgent", ...} ] }

Example request:
curl --request POST \
    "http://localhost/v1/notifications" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/notifications"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/notifications';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/notifications

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Mark all notifications as read for current user

Example request:
curl --request POST \
    "http://localhost/v1/notifications/read-all" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/notifications/read-all"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/notifications/read-all';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/notifications/read-all

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Mark notification as read

Example request:
curl --request PATCH \
    "http://localhost/v1/notifications/58C6f7nnFCJ9M6rjyc6rTKMzn6/read" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/notifications/58C6f7nnFCJ9M6rjyc6rTKMzn6/read"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PATCH",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/notifications/58C6f7nnFCJ9M6rjyc6rTKMzn6/read';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH v1/notifications/{id}/read

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the notification. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Notification Preferences

Update notification preferences

Example request:
curl --request PUT \
    "http://localhost/v1/notifications/preferences" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/notifications/preferences"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/notifications/preferences';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/notifications/preferences

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Delete notification preference (reset to defaults)

Example request:
curl --request DELETE \
    "http://localhost/v1/notifications/preferences" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"category\": \"info\"
}"
const url = new URL(
    "http://localhost/v1/notifications/preferences"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "category": "info"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/notifications/preferences';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'category' => 'info',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/notifications/preferences

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

category   string     

Example: info

Must be one of:
  • critical
  • urgent
  • info
  • marketing

Placement Management

Create a new placement

Example request:
curl --request POST \
    "http://localhost/v1/placements" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/placements"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/placements';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/placements

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update placement by ID

Example request:
curl --request PUT \
    "http://localhost/v1/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/placements/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the placement. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete placement by ID (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/placements/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/placements/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the placement. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Public

Public tenant lookup by slug (for login flow) Returns minimal tenant info (id, slug, name) without requiring authentication. This endpoint is used during login to resolve a tenant slug to its ID.

Example request:
curl --request GET \
    --get "http://localhost/v1/tenants/lookup/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/lookup/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/lookup/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "id": "01ARZ3...",
        "slug": "acme-corp",
        "name": "Acme Corp"
    }
}
 

Example response (404):


{
    "success": false,
    "message": "Tenant not found"
}
 

Request      

GET v1/tenants/lookup/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the lookup. Example: architecto

Question History

Compare two versions

Example request:
curl --request GET \
    --get "http://localhost/v1/questions/history/compare?old=01ARZ3NDEKTSV4RRFFQ69G5FAV&new=01ARZ4NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"old\": \"bngzmiyvdljnikhwaykcmyuwpw\",
    \"new\": \"lvqwrsitcpscqldzsnrwtujwvl\"
}"
const url = new URL(
    "http://localhost/v1/questions/history/compare"
);

const params = {
    "old": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
    "new": "01ARZ4NDEKTSV4RRFFQ69G5FAV",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "old": "bngzmiyvdljnikhwaykcmyuwpw",
    "new": "lvqwrsitcpscqldzsnrwtujwvl"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/history/compare';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'old' => '01ARZ3NDEKTSV4RRFFQ69G5FAV',
            'new' => '01ARZ4NDEKTSV4RRFFQ69G5FAV',
        ],
        'json' => [
            'old' => 'bngzmiyvdljnikhwaykcmyuwpw',
            'new' => 'lvqwrsitcpscqldzsnrwtujwvl',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Version comparison completed", "data": {"old_version": {...}, "new_version": {...}, "differences": {...}}}
 

Example response (404):


{
    "success": false,
    "message": "History record not found"
}
 

Request      

GET v1/questions/history/compare

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

old   string     

ID of the old history record. Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

new   string     

ID of the new history record. Example: 01ARZ4NDEKTSV4RRFFQ69G5FAV

Body Parameters

old   string     

Must be 26 characters. Example: bngzmiyvdljnikhwaykcmyuwpw

new   string     

Must be 26 characters. Example: lvqwrsitcpscqldzsnrwtujwvl

Get version history for a question

Example request:
curl --request GET \
    --get "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/history?per_page=20&page=1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/history"
);

const params = {
    "per_page": "20",
    "page": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/history';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
            'page' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Question history retrieved successfully", "data": [...], "meta": {"pagination": {...}}}
 

Example response (404):


{
    "success": false,
    "message": "Question not found"
}
 

Request      

GET v1/questions/{id}/history

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Query Parameters

per_page   integer  optional    

Number of history records per page. Defaults to 20. Example: 20

page   integer  optional    

Current page number. Defaults to 1. Example: 1

Get specific version from history

Example request:
curl --request GET \
    --get "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/history/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Question version retrieved successfully", "data": {...}}
 

Example response (404):


{
    "success": false,
    "message": "History record not found"
}
 

Request      

GET v1/questions/{id}/history/{historyId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

historyId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Restore question to a previous version (owner only)

Example request:
curl --request POST \
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"version_note\": \"Restored from version before error\"
}"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "version_note": "Restored from version before error"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/restore/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'version_note' => 'Restored from version before error',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Question restored successfully", "data": {...}}
 

Example response (403):


{
    "success": false,
    "message": "Only the owner tenant can restore this question"
}
 

Example response (404):


{
    "success": false,
    "message": "Question or history record not found"
}
 

Request      

POST v1/questions/{id}/restore/{historyId}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

historyId   string     

Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

version_note   string  optional    

nullable Note describing this restoration. Example: Restored from version before error

Question Management

List all questions with filters

Example request:
curl --request GET \
    --get "http://localhost/v1/questions?per_page=20&page=1&type=multiple_choice&is_public=1&search=math&created_at_gte=2024-01-01&created_at_lte=2024-12-31&tags=difficulty%3Ahard%2Ctopic%3Amath&tag_mode=and&sort=-created_at&include_public=1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/questions"
);

const params = {
    "per_page": "20",
    "page": "1",
    "type": "multiple_choice",
    "is_public": "1",
    "search": "math",
    "created_at_gte": "2024-01-01",
    "created_at_lte": "2024-12-31",
    "tags": "difficulty:hard,topic:math",
    "tag_mode": "and",
    "sort": "-created_at",
    "include_public": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
            'page' => '1',
            'type' => 'multiple_choice',
            'is_public' => '1',
            'search' => 'math',
            'created_at_gte' => '2024-01-01',
            'created_at_lte' => '2024-12-31',
            'tags' => 'difficulty:hard,topic:math',
            'tag_mode' => 'and',
            'sort' => '-created_at',
            'include_public' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Questions retrieved successfully", "data": [...], "meta": {"pagination": {...}}}
 

Request      

GET v1/questions

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Number of items per page (1-100). Defaults to 15. Example: 20

page   integer  optional    

Current page number. Defaults to 1. Example: 1

type   string  optional    

Filter by question type. Example: multiple_choice

is_public   boolean  optional    

Filter by public status. Example: true

search   string  optional    

Search in title (multilingual). Example: math

created_at_gte   string  optional    

Filter questions created on or after date. Example: 2024-01-01

created_at_lte   string  optional    

Filter questions created on or before date. Example: 2024-12-31

tags   string  optional    

Comma-separated tag slugs. Example: difficulty:hard,topic:math

tag_mode   string  optional    

Tag matching mode: 'and' (all tags) or 'or' (any tag). Defaults to 'or'. Example: and

sort   string  optional    

Sort field. Prefix with '-' for descending. Example: -created_at

include_public   boolean  optional    

Include public questions from other tenants. Defaults to true. Example: true

Create new question

Example request:
curl --request POST \
    "http://localhost/v1/questions" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "title[]=architecto"\
    --form "prompt[]=architecto"\
    --form "type=multiple_choice"\
    --form "is_public=1"\
    --form "instructions[]=architecto"\
    --form "feedbacks[]=architecto"\
    --form "media_id=01ARZ3NDEKTSV4RRFFQ69G5FAV"\
    --form "media_url=https://example.com/image.jpg"\
    --form "logic[]=architecto"\
    --form "version_note=Initial version"\
    --form "media_file=@/tmp/phpriv6t8jkhrfh3gJYSOM" 
const url = new URL(
    "http://localhost/v1/questions"
);

const headers = {
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('title[]', 'architecto');
body.append('prompt[]', 'architecto');
body.append('type', 'multiple_choice');
body.append('is_public', '1');
body.append('instructions[]', 'architecto');
body.append('feedbacks[]', 'architecto');
body.append('media_id', '01ARZ3NDEKTSV4RRFFQ69G5FAV');
body.append('media_url', 'https://example.com/image.jpg');
body.append('logic[]', 'architecto');
body.append('version_note', 'Initial version');
body.append('media_file', document.querySelector('input[name="media_file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'title[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'prompt[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'type',
                'contents' => 'multiple_choice'
            ],
            [
                'name' => 'is_public',
                'contents' => '1'
            ],
            [
                'name' => 'instructions[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'feedbacks[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'media_id',
                'contents' => '01ARZ3NDEKTSV4RRFFQ69G5FAV'
            ],
            [
                'name' => 'media_url',
                'contents' => 'https://example.com/image.jpg'
            ],
            [
                'name' => 'logic[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'version_note',
                'contents' => 'Initial version'
            ],
            [
                'name' => 'media_file',
                'contents' => fopen('/tmp/phpriv6t8jkhrfh3gJYSOM', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{"success": true, "message": "Question created successfully", "data": {...}}
 

Request      

POST v1/questions

Headers

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

title   string[]     

Multilingual title (JSON object with language codes as keys).

prompt   string[]     

Multilingual prompt (JSON object with language codes as keys).

type   string     

Question type. Example: multiple_choice

is_public   boolean  optional    

nullable Make question public to all tenants. Defaults to false. Example: true

instructions   string[]  optional    

nullable Array of block objects.

feedbacks   string[]  optional    

nullable Array of block objects.

media_id   string  optional    

nullable Media attachment ID (mutually exclusive with media_file and media_url). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

media_file   file  optional    

nullable Direct file upload (mutually exclusive with media_id and media_url). Example: /tmp/phpriv6t8jkhrfh3gJYSOM

media_url   string  optional    

nullable URL to download image from (mutually exclusive with media_id and media_file). Example: https://example.com/image.jpg

logic   string[]  optional    

nullable Custom logic/scoring rules.

version_note   string  optional    

nullable Note describing this version. Example: Initial version

List all public questions (across all tenants)

Example request:
curl --request GET \
    --get "http://localhost/v1/questions/public?per_page=20&page=1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/questions/public"
);

const params = {
    "per_page": "20",
    "page": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/public';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
            'page' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Public questions retrieved successfully", "data": [...], "meta": {"pagination": {...}}}
 

Request      

GET v1/questions/public

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Number of items per page. Defaults to 15. Example: 20

page   integer  optional    

Current page number. Defaults to 1. Example: 1

Bulk delete questions (owner only)

Example request:
curl --request POST \
    "http://localhost/v1/questions/bulk-delete" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"ids\": [
        \"01ARZ3...\",
        \"01ARZ4...\"
    ]
}"
const url = new URL(
    "http://localhost/v1/questions/bulk-delete"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "ids": [
        "01ARZ3...",
        "01ARZ4..."
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/bulk-delete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'ids' => [
                '01ARZ3...',
                '01ARZ4...',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Bulk delete completed",
    "data": {
        "deleted_count": 5,
        "errors": []
    }
}
 

Request      

POST v1/questions/bulk-delete

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

ids   string[]     

Array of question IDs to delete.

Get question by ID

Example request:
curl --request GET \
    --get "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Question retrieved successfully", "data": {...}}
 

Example response (404):


{
    "success": false,
    "message": "Question not found"
}
 

Request      

GET v1/questions/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Update question by ID (owner only)

Example request:
curl --request PUT \
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "title[]=architecto"\
    --form "prompt[]=architecto"\
    --form "type=architecto"\
    --form "is_public="\
    --form "instructions[]=architecto"\
    --form "feedbacks[]=architecto"\
    --form "media_id=architecto"\
    --form "media_url=http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"\
    --form "logic[]=architecto"\
    --form "version_note=Fixed typo in title"\
    --form "media_file=@/tmp/phphn7kmput9fscd8y7qbz" 
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('title[]', 'architecto');
body.append('prompt[]', 'architecto');
body.append('type', 'architecto');
body.append('is_public', '');
body.append('instructions[]', 'architecto');
body.append('feedbacks[]', 'architecto');
body.append('media_id', 'architecto');
body.append('media_url', 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html');
body.append('logic[]', 'architecto');
body.append('version_note', 'Fixed typo in title');
body.append('media_file', document.querySelector('input[name="media_file"]').files[0]);

fetch(url, {
    method: "PUT",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'title[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'prompt[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'type',
                'contents' => 'architecto'
            ],
            [
                'name' => 'is_public',
                'contents' => ''
            ],
            [
                'name' => 'instructions[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'feedbacks[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'media_id',
                'contents' => 'architecto'
            ],
            [
                'name' => 'media_url',
                'contents' => 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html'
            ],
            [
                'name' => 'logic[]',
                'contents' => 'architecto'
            ],
            [
                'name' => 'version_note',
                'contents' => 'Fixed typo in title'
            ],
            [
                'name' => 'media_file',
                'contents' => fopen('/tmp/phphn7kmput9fscd8y7qbz', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Question updated successfully", "data": {...}}
 

Example response (403):


{
    "success": false,
    "message": "Only the owner tenant can modify this question"
}
 

Example response (404):


{
    "success": false,
    "message": "Question not found"
}
 

Request      

PUT v1/questions/{id}

Headers

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

title   string[]  optional    

nullable Multilingual title.

prompt   string[]  optional    

nullable Multilingual prompt.

type   string  optional    

nullable Question type. Example: architecto

is_public   boolean  optional    

nullable Public visibility flag. Example: false

instructions   string[]  optional    

nullable Array of block objects.

feedbacks   string[]  optional    

nullable Array of block objects.

media_id   string  optional    

nullable Media attachment ID (mutually exclusive with media_file and media_url). Example: architecto

media_file   file  optional    

nullable Direct file upload (mutually exclusive with media_id and media_url). Example: /tmp/phphn7kmput9fscd8y7qbz

media_url   string  optional    

nullable URL to download image from (mutually exclusive with media_id and media_file). Example: http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html

logic   string[]  optional    

nullable Custom logic/scoring rules.

version_note   string  optional    

nullable Note describing this version. Example: Fixed typo in title

Delete question by ID (soft delete, owner only)

Example request:
curl --request DELETE \
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"version_note\": \"Removed outdated content\"
}"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "version_note": "Removed outdated content"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'version_note' => 'Removed outdated content',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Question deleted successfully", "data": {...}}
 

Example response (403):


{
    "success": false,
    "message": "Only the owner tenant can delete this question"
}
 

Example response (404):


{
    "success": false,
    "message": "Question not found"
}
 

Request      

DELETE v1/questions/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

version_note   string  optional    

nullable Note describing this deletion. Example: Removed outdated content

Duplicate a question

Example request:
curl --request POST \
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/duplicate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"overrides\": [
        \"architecto\"
    ]
}"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/duplicate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "overrides": [
        "architecto"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/duplicate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'overrides' => [
                'architecto',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{"success": true, "message": "Question duplicated successfully", "data": {...}}
 

Example response (404):


{
    "success": false,
    "message": "Question not found"
}
 

Request      

POST v1/questions/{id}/duplicate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

overrides   string[]  optional    

nullable Fields to override in the duplicate.

Question Tagging

Assign tag(s) to question

Example request:
curl --request POST \
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"tag_slugs\": [
        \"architecto\"
    ],
    \"context\": \"n\"
}"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag_slugs": [
        "architecto"
    ],
    "context": "n"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'tag_slugs' => [
                'architecto',
            ],
            'context' => 'n',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/questions/{id}/tags

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

tag_slugs   string[]     
context   string  optional    

Must not be greater than 50 characters. Example: n

Remove tag(s) from question

Example request:
curl --request DELETE \
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"tag_slugs\": [
        \"architecto\"
    ],
    \"context\": \"n\"
}"
const url = new URL(
    "http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag_slugs": [
        "architecto"
    ],
    "context": "n"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/58C6f7nnFCJ9M6rjyc6rTKMzn6/tags';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'tag_slugs' => [
                'architecto',
            ],
            'context' => 'n',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/questions/{id}/tags

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the question. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Body Parameters

tag_slugs   string[]     
context   string  optional    

Must not be greater than 50 characters. Example: n

Bulk assign tags to multiple questions

Example request:
curl --request POST \
    "http://localhost/v1/questions/tags/bulk-assign" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"assignments\": [
        {
            \"resource_id\": \"ngzmiyvdljnikhwaykcmyuwpwl\",
            \"tag_slugs\": [
                \"architecto\"
            ]
        }
    ],
    \"context\": \"b\"
}"
const url = new URL(
    "http://localhost/v1/questions/tags/bulk-assign"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "assignments": [
        {
            "resource_id": "ngzmiyvdljnikhwaykcmyuwpwl",
            "tag_slugs": [
                "architecto"
            ]
        }
    ],
    "context": "b"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/questions/tags/bulk-assign';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'assignments' => [
                [
                    'resource_id' => 'ngzmiyvdljnikhwaykcmyuwpwl',
                    'tag_slugs' => [
                        'architecto',
                    ],
                ],
            ],
            'context' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/questions/tags/bulk-assign

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

assignments   object[]     

Must have at least 1 items.

resource_id   string     

Must be 26 characters. Example: ngzmiyvdljnikhwaykcmyuwpwl

tag_slugs   string[]     
context   string  optional    

Must not be greater than 50 characters. Example: b

RBAC Management

Get my effective permissions Returns the current authenticated user's effective permissions after grant/deny resolution. Public endpoint - no rbac.manage permission required. Superadmin users have all permissions automatically.

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/me/permissions" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/me/permissions"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/me/permissions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Effective permissions retrieved",
    "data": [
        "users.read",
        "users.create",
        "tenants.read",
        "rbac.manage"
    ]
}
 

Request      

GET v1/rbac/me/permissions

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Get my role Returns the current authenticated user's assigned role. If no role is explicitly assigned, returns the tenant's default role. Public endpoint - no rbac.manage permission required.

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/me/role" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/me/role"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/me/role';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Role retrieved",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Administrator",
        "slug": "admin",
        "description": "Full system access",
        "is_default": false,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Request      

GET v1/rbac/me/role

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

List all permissions Returns all permissions in the tenant's RBAC system. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/permissions" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/permissions"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/permissions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Permissions retrieved",
    "data": [
        {
            "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
            "slug": "users.read",
            "name": "View Users",
            "description": "View user list and details",
            "resource": "users",
            "action": "read",
            "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
        }
    ]
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Request      

GET v1/rbac/permissions

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Create new permission Creates a new permission in the tenant's RBAC system. The slug must be unique within the tenant. Requires rbac.manage permission.

requires authentication

Example request:
curl --request POST \
    "http://localhost/v1/rbac/permissions" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"slug\": \"courses.publish\",
    \"name\": \"Publish Courses\",
    \"description\": \"Allows publishing courses to students\",
    \"resource\": \"courses\",
    \"action\": \"publish\"
}"
const url = new URL(
    "http://localhost/v1/rbac/permissions"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "slug": "courses.publish",
    "name": "Publish Courses",
    "description": "Allows publishing courses to students",
    "resource": "courses",
    "action": "publish"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/permissions';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'slug' => 'courses.publish',
            'name' => 'Publish Courses',
            'description' => 'Allows publishing courses to students',
            'resource' => 'courses',
            'action' => 'publish',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "success": true,
    "message": "Permission created successfully",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "slug": "courses.publish",
        "name": "Publish Courses",
        "description": "Allows publishing courses to students",
        "resource": "courses",
        "action": "publish",
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (400):


{
    "success": false,
    "message": "Permission with this slug already exists"
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Request      

POST v1/rbac/permissions

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

slug   string     

Permission slug (lowercase, dots/underscores allowed). Example: courses.publish

name   string     

Permission display name. Example: Publish Courses

description   string  optional    

Description of what this permission allows. Example: Allows publishing courses to students

resource   string     

Resource this permission applies to. Example: courses

action   string     

Action this permission allows. Example: publish

Get permission by ID Returns a single permission by its ID. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Permission retrieved",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "slug": "users.create",
        "name": "Create Users",
        "description": "Create new user accounts",
        "resource": "users",
        "action": "create",
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Permission not found"
}
 

Request      

GET v1/rbac/permissions/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

Permission ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Update permission Updates an existing permission. The slug cannot be changed after creation. Requires rbac.manage permission.

requires authentication

Example request:
curl --request PUT \
    "http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Create Users\",
    \"description\": \"Create new user accounts\",
    \"resource\": \"users\",
    \"action\": \"create\"
}"
const url = new URL(
    "http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Create Users",
    "description": "Create new user accounts",
    "resource": "users",
    "action": "create"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Create Users',
            'description' => 'Create new user accounts',
            'resource' => 'users',
            'action' => 'create',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Permission updated successfully",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "slug": "users.create",
        "name": "Create Users",
        "description": "Create new user accounts",
        "resource": "users",
        "action": "create",
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Permission not found"
}
 

Request      

PUT v1/rbac/permissions/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

Permission ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Body Parameters

name   string     

Permission display name. Example: Create Users

description   string  optional    

Description of what this permission allows. Example: Create new user accounts

resource   string     

Resource this permission applies to. Example: users

action   string     

Action this permission allows. Example: create

Delete permission Deletes a permission from the tenant's RBAC system. Cannot delete permissions that are currently assigned to roles. Requires rbac.manage permission.

requires authentication

Example request:
curl --request DELETE \
    "http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/permissions/01ARZ3NDEKTSV4RRFFQ69G5FAV';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Permission deleted successfully",
    "data": null
}
 

Example response (400):


{
    "success": false,
    "message": "Cannot delete permission: currently assigned to roles"
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Permission not found"
}
 

Request      

DELETE v1/rbac/permissions/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

Permission ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

List all roles Returns all roles in the tenant's RBAC system. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/roles" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/roles"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Roles retrieved",
    "data": [
        {
            "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
            "name": "Administrator",
            "slug": "admin",
            "description": "Full system access",
            "is_default": false,
            "is_superadmin": false,
            "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
        }
    ]
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Request      

GET v1/rbac/roles

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Create new role Creates a new role in the tenant's RBAC system. The slug must be unique within the tenant. Only one role can be marked as default per tenant. Superadmin roles bypass all permission checks. Requires rbac.manage permission.

requires authentication

Example request:
curl --request POST \
    "http://localhost/v1/rbac/roles" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Content Manager\",
    \"slug\": \"content-manager\",
    \"description\": \"Manages course content and media\",
    \"is_default\": false,
    \"is_superadmin\": false
}"
const url = new URL(
    "http://localhost/v1/rbac/roles"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Content Manager",
    "slug": "content-manager",
    "description": "Manages course content and media",
    "is_default": false,
    "is_superadmin": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Content Manager',
            'slug' => 'content-manager',
            'description' => 'Manages course content and media',
            'is_default' => false,
            'is_superadmin' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "success": true,
    "message": "Role created successfully",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Content Manager",
        "slug": "content-manager",
        "description": "Manages course content and media",
        "is_default": false,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (400):


{
    "success": false,
    "message": "Role with this slug already exists"
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Request      

POST v1/rbac/roles

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Role display name. Example: Content Manager

slug   string     

Role slug (lowercase, hyphens/underscores allowed). Example: content-manager

description   string  optional    

Description of this role. Example: Manages course content and media

is_default   boolean     

Whether this is the default role for new users. Example: false

is_superadmin   boolean     

Whether this role bypasses all permission checks. Example: false

Get role by ID Returns a single role by its ID, including granted and denied permissions. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Role retrieved",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Administrator",
        "slug": "admin",
        "description": "Full system access",
        "is_default": false,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "granted_permissions": [
            "users.read",
            "users.create"
        ],
        "denied_permissions": []
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Role not found"
}
 

Request      

GET v1/rbac/roles/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

Role ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Get default role Returns the tenant's default role (assigned to new users automatically). Returns null if no default role is set. Only one default role can exist per tenant. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/roles/default" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/roles/default"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles/default';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Default role retrieved",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Default User",
        "slug": "default",
        "description": "Default permissions for new users",
        "is_default": true,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Request      

GET v1/rbac/roles/default

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update role Updates an existing role. The slug cannot be changed after creation. Setting is_default to true will unset any other default role. Requires rbac.manage permission.

requires authentication

Example request:
curl --request PUT \
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Content Manager\",
    \"description\": \"Manages course content and media\",
    \"is_default\": false,
    \"is_superadmin\": false
}"
const url = new URL(
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Content Manager",
    "description": "Manages course content and media",
    "is_default": false,
    "is_superadmin": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Content Manager',
            'description' => 'Manages course content and media',
            'is_default' => false,
            'is_superadmin' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Role updated successfully",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Content Manager",
        "slug": "content-manager",
        "description": "Manages course content and media",
        "is_default": false,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Role not found"
}
 

Request      

PUT v1/rbac/roles/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

Role ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Body Parameters

name   string     

Role display name. Example: Content Manager

description   string  optional    

Description of this role. Example: Manages course content and media

is_default   boolean     

Whether this is the default role for new users. Example: false

is_superadmin   boolean     

Whether this role bypasses all permission checks. Example: false

Delete role Deletes a role from the tenant's RBAC system. Cannot delete the default role or roles currently assigned to users. Requires rbac.manage permission.

requires authentication

Example request:
curl --request DELETE \
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Role deleted successfully",
    "data": null
}
 

Example response (400):


{
    "success": false,
    "message": "Cannot delete default role or role assigned to users"
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Role not found"
}
 

Request      

DELETE v1/rbac/roles/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

Role ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Sync role permissions Synchronizes a role's granted and denied permissions. Permissions not in either list will be removed from the role. Explicit grants override denies (if a permission appears in both lists, it will be granted). Requires rbac.manage permission.

requires authentication

Example request:
curl --request PUT \
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV/permissions/sync" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"grant_permission_ids\": [
        \"01ARZ3NDEKTSV4RRFFQ69G5FAV\",
        \"01ARZ3NDEKTSV4RRFFQ69G5FAW\"
    ],
    \"deny_permission_ids\": [
        \"01ARZ3NDEKTSV4RRFFQ69G5FAX\"
    ]
}"
const url = new URL(
    "http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV/permissions/sync"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "grant_permission_ids": [
        "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "01ARZ3NDEKTSV4RRFFQ69G5FAW"
    ],
    "deny_permission_ids": [
        "01ARZ3NDEKTSV4RRFFQ69G5FAX"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/roles/01ARZ3NDEKTSV4RRFFQ69G5FAV/permissions/sync';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'grant_permission_ids' => [
                '01ARZ3NDEKTSV4RRFFQ69G5FAV',
                '01ARZ3NDEKTSV4RRFFQ69G5FAW',
            ],
            'deny_permission_ids' => [
                '01ARZ3NDEKTSV4RRFFQ69G5FAX',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Role permissions synced successfully",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Content Manager",
        "slug": "content-manager",
        "description": "Manages course content",
        "is_default": false,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "granted_permissions": [
            "courses.read",
            "courses.create"
        ],
        "denied_permissions": [
            "courses.delete"
        ]
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Role not found or permission not found"
}
 

Request      

PUT v1/rbac/roles/{roleId}/permissions/sync

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

roleId   string     

Role ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Body Parameters

grant_permission_ids   string[]  optional    

Array of permission IDs to grant.

deny_permission_ids   string[]  optional    

Array of permission IDs to explicitly deny.

Assign role to user Assigns a role to a user (identity) in the tenant. If the user already has a role assignment, it will be updated. Requires rbac.manage permission.

requires authentication

Example request:
curl --request POST \
    "http://localhost/v1/rbac/users/assign-role" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"identity_id\": \"01ARZ3NDEKTSV4RRFFQ69G5FAV\",
    \"role_id\": \"01ARZ3NDEKTSV4RRFFQ69G5FAW\"
}"
const url = new URL(
    "http://localhost/v1/rbac/users/assign-role"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "identity_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
    "role_id": "01ARZ3NDEKTSV4RRFFQ69G5FAW"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/users/assign-role';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'identity_id' => '01ARZ3NDEKTSV4RRFFQ69G5FAV',
            'role_id' => '01ARZ3NDEKTSV4RRFFQ69G5FAW',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "success": true,
    "message": "Role assigned successfully",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAY",
        "identity_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "role_id": "01ARZ3NDEKTSV4RRFFQ69G5FAW",
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAZ",
        "assigned_by": "01ARZ3NDEKTSV4RRFFQ69G5FAA",
        "assigned_at": "2024-01-15T10:30:00Z"
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Identity not found or role not found"
}
 

Request      

POST v1/rbac/users/assign-role

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

identity_id   string     

Identity ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

role_id   string     

Role ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAW

Revoke user's role Revokes (removes) the role assignment from a user. The user will fall back to the tenant's default role if one exists. Requires rbac.manage permission.

requires authentication

Example request:
curl --request DELETE \
    "http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/revoke-role" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/revoke-role"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/revoke-role';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Role revoked successfully",
    "data": null
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "User has no role assignment"
}
 

Request      

DELETE v1/rbac/users/{identityId}/revoke-role

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

identityId   string     

Identity ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Get user's role Returns the role assigned to a specific user. If no role is explicitly assigned, returns the tenant's default role. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/role" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/role"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/role';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "User role retrieved",
    "data": {
        "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
        "name": "Content Manager",
        "slug": "content-manager",
        "description": "Manages course content",
        "is_default": false,
        "is_superadmin": false,
        "tenant_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV"
    }
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Identity not found"
}
 

Request      

GET v1/rbac/users/{identityId}/role

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

identityId   string     

Identity ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Get user's effective permissions Returns the effective permissions for a specific user after grant/deny resolution. Takes into account the user's assigned role and its permission grants/denies. Superadmin users will have all permissions. Requires rbac.manage permission.

requires authentication

Example request:
curl --request GET \
    --get "http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/effective-permissions" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/effective-permissions"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/rbac/users/01ARZ3NDEKTSV4RRFFQ69G5FAV/effective-permissions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "User effective permissions retrieved",
    "data": [
        "courses.read",
        "courses.create",
        "courses.update",
        "media.read"
    ]
}
 

Example response (403):


{
    "success": false,
    "message": "Insufficient permissions"
}
 

Example response (404):


{
    "success": false,
    "message": "Identity not found"
}
 

Request      

GET v1/rbac/users/{identityId}/effective-permissions

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

identityId   string     

Identity ID (ULID, 26 characters). Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV

Statistics

Record a usage event directly Records a usage event. Events are queued for async processing.

Example request:
curl --request POST \
    "http://localhost/v1/statistics/record" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/statistics/record"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/statistics/record';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/statistics/record

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Tag Contexts

Create context

Example request:
curl --request POST \
    "http://localhost/v1/tags/contexts" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tags/contexts"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags/contexts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/tags/contexts

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update context

Example request:
curl --request PATCH \
    "http://localhost/v1/tags/contexts/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tags/contexts/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PATCH",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags/contexts/architecto';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH v1/tags/contexts/{context}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

context   string     

The context. Example: architecto

Delete context

Example request:
curl --request DELETE \
    "http://localhost/v1/tags/contexts/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tags/contexts/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags/contexts/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/tags/contexts/{context}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

context   string     

The context. Example: architecto

Tag Management

Create new tag

Example request:
curl --request POST \
    "http://localhost/v1/tags" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tags"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/tags

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Bulk delete tags

Example request:
curl --request POST \
    "http://localhost/v1/tags/bulk-delete" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"ids\": [
        \"bngzmiyvdljnikhwaykcmyuwpw\"
    ],
    \"force\": false
}"
const url = new URL(
    "http://localhost/v1/tags/bulk-delete"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "ids": [
        "bngzmiyvdljnikhwaykcmyuwpw"
    ],
    "force": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags/bulk-delete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'ids' => [
                'bngzmiyvdljnikhwaykcmyuwpw',
            ],
            'force' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/tags/bulk-delete

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

ids   string[]     

Must be 26 characters.

force   boolean  optional    

Example: false

Update tag

Example request:
curl --request PATCH \
    "http://localhost/v1/tags/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tags/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PATCH",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH v1/tags/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the tag. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete tag

Example request:
curl --request DELETE \
    "http://localhost/v1/tags/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tags/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tags/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/tags/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the tag. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Tenant Management

List all tenants (public - no auth required)

Example request:
curl --request GET \
    --get "http://localhost/v1/tenants?per_page=20&page=1&enabled=1&search=acme&created_at_gte=2024-01-01&created_at_lte=2024-12-31&sort=-created_at" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants"
);

const params = {
    "per_page": "20",
    "page": "1",
    "enabled": "1",
    "search": "acme",
    "created_at_gte": "2024-01-01",
    "created_at_lte": "2024-12-31",
    "sort": "-created_at",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
            'page' => '1',
            'enabled' => '1',
            'search' => 'acme',
            'created_at_gte' => '2024-01-01',
            'created_at_lte' => '2024-12-31',
            'sort' => '-created_at',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Tenants retrieved successfully", "data": [...], "meta": {"pagination": {"total": 50, "per_page": 10, "current_page": 1, "last_page": 5}}}
 

Request      

GET v1/tenants

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Number of items per page (1-100). Defaults to 10. Example: 20

page   integer  optional    

Current page number. Defaults to 1. Example: 1

enabled   boolean  optional    

Filter by enabled status. Example: true

search   string  optional    

Search in name, slug, and domain. Example: acme

created_at_gte   string  optional    

Filter tenants created on or after date. Example: 2024-01-01

created_at_lte   string  optional    

Filter tenants created on or before date. Example: 2024-12-31

sort   string  optional    

Sort field. Prefix with '-' for descending. Example: -created_at

Create new tenant with initial admin user.

Example request:
curl --request POST \
    "http://localhost/v1/tenants" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Acme Corp\",
    \"slug\": \"acme\",
    \"domain\": \"acme.com\",
    \"description\": \"Eius et animi quos velit et.\",
    \"settings\": [
        \"architecto\"
    ],
    \"enabled\": false,
    \"admin_email\": \"admin@acme.com\",
    \"admin_password\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/tenants"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Acme Corp",
    "slug": "acme",
    "domain": "acme.com",
    "description": "Eius et animi quos velit et.",
    "settings": [
        "architecto"
    ],
    "enabled": false,
    "admin_email": "admin@acme.com",
    "admin_password": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Acme Corp',
            'slug' => 'acme',
            'domain' => 'acme.com',
            'description' => 'Eius et animi quos velit et.',
            'settings' => [
                'architecto',
            ],
            'enabled' => false,
            'admin_email' => 'admin@acme.com',
            'admin_password' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{"success": true, "message": "Tenant and admin user created successfully", "data": {"tenant": {...}, "admin": {"email": "admin@acme.com", "generated_password": "kR8mP2vN9qT3wX7z"}}}
 

Request      

POST v1/tenants

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Tenant name. Example: Acme Corp

slug   string     

Tenant slug. Example: acme

domain   string  optional    

nullable Tenant domain. Example: acme.com

description   string  optional    

nullable Tenant description. Example: Eius et animi quos velit et.

settings   string[]  optional    

nullable Tenant settings.

enabled   boolean  optional    

nullable Tenant enabled status. Defaults to true. Example: false

admin_email   string     

Admin user email. Example: admin@acme.com

admin_password   string  optional    

nullable Admin password (min 8 chars). If omitted, a secure password is generated and returned. Example: architecto

Get tenant by ID (public - no auth required)

Example request:
curl --request GET \
    --get "http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"id": "01ARZ3...", "name": "Acme Corp", ...}
 

Example response (404):


{
    "message": "Tenant not found"
}
 

Request      

GET v1/tenants/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the tenant. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Update tenant by ID

Example request:
curl --request PUT \
    "http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"id": "01ARZ3...", "name": "Updated Corp", ...}
 

Example response (404):


{
    "message": "Tenant not found"
}
 

Request      

PUT v1/tenants/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the tenant. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete tenant by ID (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"id": "01ARZ3...", "deleted_at": "2025-12-03T10:30:00Z", ...}
 

Example response (404):


{
    "message": "Tenant not found"
}
 

Request      

DELETE v1/tenants/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the tenant. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Get tenant by slug (public - no auth required)

Example request:
curl --request GET \
    --get "http://localhost/v1/tenants/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"id": "01ARZ3...", "name": "Acme Corp", "slug": "acme-corp", ...}
 

Example response (404):


{
    "message": "Tenant not found"
}
 

Request      

GET v1/tenants/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the tenant. Example: architecto

Update tenant by slug

Example request:
curl --request PUT \
    "http://localhost/v1/tenants/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"id": "01ARZ3...", "name": "Updated Corp", ...}
 

Example response (404):


{
    "message": "Tenant not found"
}
 

Request      

PUT v1/tenants/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the tenant. Example: architecto

Delete tenant by slug (soft delete)

Example request:
curl --request DELETE \
    "http://localhost/v1/tenants/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/tenants/architecto"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/tenants/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"id": "01ARZ3...", "deleted_at": "2025-12-03T10:30:00Z", ...}
 

Example response (404):


{
    "message": "Tenant not found"
}
 

Request      

DELETE v1/tenants/{slug}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

slug   string     

The slug of the tenant. Example: architecto

Transfer - Import/Export

Preview export Returns counts of entities that would be exported without creating the file. Includes resolved dependencies.

Example request:
curl --request POST \
    "http://localhost/v1/transfer/export/preview" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"entity_types\": [
        \"questions\",
        \"quizzes\"
    ],
    \"format\": \"json\",
    \"filters\": [],
    \"include_media_files\": false
}"
const url = new URL(
    "http://localhost/v1/transfer/export/preview"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "entity_types": [
        "questions",
        "quizzes"
    ],
    "format": "json",
    "filters": [],
    "include_media_files": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/transfer/export/preview';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'entity_types' => [
                'questions',
                'quizzes',
            ],
            'format' => 'json',
            'filters' => [],
            'include_media_files' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/transfer/export/preview

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

entity_types   string[]     

Entity types to export.

format   string  optional    

Export format (default: json). Example: json

filters   object  optional    

Optional filters per entity type.

include_media_files   boolean  optional    

Include media files in export (default: true). Example: false

Execute export Creates a ZIP package with selected entities and returns download information.

Example request:
curl --request POST \
    "http://localhost/v1/transfer/export" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"entity_types\": [
        \"questions\",
        \"quizzes\"
    ],
    \"format\": \"json\",
    \"filters\": [],
    \"include_media_files\": false,
    \"export_name\": \"architecto\"
}"
const url = new URL(
    "http://localhost/v1/transfer/export"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "entity_types": [
        "questions",
        "quizzes"
    ],
    "format": "json",
    "filters": [],
    "include_media_files": false,
    "export_name": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/transfer/export';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'entity_types' => [
                'questions',
                'quizzes',
            ],
            'format' => 'json',
            'filters' => [],
            'include_media_files' => false,
            'export_name' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/transfer/export

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

entity_types   string[]     

Entity types to export.

format   string  optional    

Export format (default: json). Example: json

filters   object  optional    

Optional filters per entity type.

include_media_files   boolean  optional    

Include media files in export (default: true). Example: false

export_name   string  optional    

Optional name for the export. Example: architecto

Validate import file Validates the uploaded ZIP file without importing. Returns validation result with entity counts and any errors. In upsert/skip modes, shows breakdown of existing vs new entities.

Example request:
curl --request POST \
    "http://localhost/v1/transfer/import/validate" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "mode=architecto"\
    --form "file=@/tmp/php7b0iab6k6ulocVNzNuk" 
const url = new URL(
    "http://localhost/v1/transfer/import/validate"
);

const headers = {
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('mode', 'architecto');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/transfer/import/validate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'mode',
                'contents' => 'architecto'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/php7b0iab6k6ulocVNzNuk', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/transfer/import/validate

Headers

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

file   file     

The ZIP file to validate. Example: /tmp/php7b0iab6k6ulocVNzNuk

mode   string  optional    

Import mode: create, upsert, or skip (default: create). Example: architecto

Execute import Imports entities from the uploaded ZIP file.

Example request:
curl --request POST \
    "http://localhost/v1/transfer/import" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "mode=architecto"\
    --form "file=@/tmp/phpf21rqgcm8gtg9H1ThLZ" 
const url = new URL(
    "http://localhost/v1/transfer/import"
);

const headers = {
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('mode', 'architecto');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/transfer/import';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'mode',
                'contents' => 'architecto'
            ],
            [
                'name' => 'file',
                'contents' => fopen('/tmp/phpf21rqgcm8gtg9H1ThLZ', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/transfer/import

Headers

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

file   file     

The ZIP file to import. Example: /tmp/phpf21rqgcm8gtg9H1ThLZ

mode   string  optional    

Import mode: create, upsert, or skip (default: create). Example: architecto

User Management

List all users in tenant

Example request:
curl --request GET \
    --get "http://localhost/v1/users?per_page=20&page=1&is_anonymous=&search=john&created_at_gte=2024-01-01&created_at_lte=2024-12-31&sort=-created_at" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/users"
);

const params = {
    "per_page": "20",
    "page": "1",
    "is_anonymous": "0",
    "search": "john",
    "created_at_gte": "2024-01-01",
    "created_at_lte": "2024-12-31",
    "sort": "-created_at",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/users';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
            'page' => '1',
            'is_anonymous' => '0',
            'search' => 'john',
            'created_at_gte' => '2024-01-01',
            'created_at_lte' => '2024-12-31',
            'sort' => '-created_at',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{"success": true, "message": "Users retrieved successfully", "data": [...], "meta": {"pagination": {"total": 50, "per_page": 10, "current_page": 1, "last_page": 5}}}
 

Request      

GET v1/users

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Number of items per page (1-100). Defaults to 10. Example: 20

page   integer  optional    

Current page number. Defaults to 1. Example: 1

is_anonymous   boolean  optional    

Filter by anonymous status. Example: false

search   string  optional    

Search in email, phone, and username. Example: john

created_at_gte   string  optional    

Filter users created on or after date. Example: 2024-01-01

created_at_lte   string  optional    

Filter users created on or before date. Example: 2024-12-31

sort   string  optional    

Sort field. Prefix with '-' for descending. Example: -created_at

Create new user in tenant

Example request:
curl --request POST \
    "http://localhost/v1/users" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/users"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/users';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/users

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Update user by ID within tenant

Example request:
curl --request PUT \
    "http://localhost/v1/users/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/users/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/users/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT v1/users/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the user. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Delete user by ID (soft delete) within tenant

Example request:
curl --request DELETE \
    "http://localhost/v1/users/58C6f7nnFCJ9M6rjyc6rTKMzn6" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/users/58C6f7nnFCJ9M6rjyc6rTKMzn6"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/users/58C6f7nnFCJ9M6rjyc6rTKMzn6';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE v1/users/{id}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the user. Example: 58C6f7nnFCJ9M6rjyc6rTKMzn6

Verification

Initiate verification for any handle type Sends a 6-digit verification code to the authenticated user's handle (email, phone, etc.).

Example request:
curl --request POST \
    "http://localhost/v1/verification/email/initiate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/verification/email/initiate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/verification/email/initiate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/verification/{handleType}/initiate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

handleType   string     

The handle type to verify (email, phone). Example: email

Verify handle with code Verifies the user's handle (email, phone, etc.) using the provided 6-digit code.

Example request:
curl --request POST \
    "http://localhost/v1/verification/email/verify" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"code\": \"123456\"
}"
const url = new URL(
    "http://localhost/v1/verification/email/verify"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "123456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/verification/email/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'code' => '123456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/verification/{handleType}/verify

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

handleType   string     

The handle type to verify (email, phone). Example: email

Body Parameters

code   string     

The 6-digit verification code. Example: 123456

Resend verification code Resends a new verification code to the user's handle (email, phone, etc.).

Example request:
curl --request POST \
    "http://localhost/v1/verification/email/resend" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/verification/email/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/verification/email/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/verification/{handleType}/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

handleType   string     

The handle type to verify (email, phone). Example: email

Initiate password reset Sends a password reset link to the user's email. No authentication required. Always returns success to prevent user enumeration.

Example request:
curl --request POST \
    "http://localhost/v1/verification/password-reset/initiate" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/verification/password-reset/initiate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/verification/password-reset/initiate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/verification/password-reset/initiate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Verify password reset token Validates a password reset token without consuming it. Used by frontend to show password reset form.

Example request:
curl --request POST \
    "http://localhost/v1/verification/password-reset/verify" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/verification/password-reset/verify"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/verification/password-reset/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/verification/password-reset/verify

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Complete password reset Resets the user's password using the provided token and new password. Token is consumed and cannot be reused.

Example request:
curl --request POST \
    "http://localhost/v1/verification/password-reset/complete" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://localhost/v1/verification/password-reset/complete"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://localhost/v1/verification/password-reset/complete';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST v1/verification/password-reset/complete

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json