MENU navbar-image

Introduction

This documentation aims to provide all the information you need to work with our API.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

You can retrieve your token by visiting your dashboard and clicking Generate API token.

Authorization

Log in and issue a token

Authenticate a user by email or username and return an access token along with the user's profile, associated schools, primary role, and permissions.

Notes:

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"login\": \"user@example.com\",
    \"password\": \"secret-password\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/login"
);

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

let body = {
    "login": "user@example.com",
    "password": "secret-password"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "user": {
        "id": 123,
        "name": "Example User",
        "email": "user@example.com",
        "user_name": "example.user",
        "status": "active",
        "user_status_id": 10,
        "assignment_audit": null,
        "stripe_billing_portal_url": null,
        "created_at": "2025-01-01T00:00:00.000000Z",
        "updated_at": "2025-01-01T00:00:00.000000Z"
    },
    "profile": {
        "id": 456,
        "user_id": 123,
        "first_name": "Example",
        "last_name": "User",
        "image_url": "https://cdn.example.com/profiles/placeholder.jpg"
    },
    "schools": [
        {
            "id": 1,
            "nickname": "Main School"
        }
    ],
    "role": "administrator",
    "permissions": [
        "administrative.dashboard.index",
        "administrative.customer.group.index"
    ],
    "token": "<access_token>"
}
 

Example response (401):


{
    "message": "Invalid credentials"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "login": [
            "The login field is required."
        ],
        "password": [
            "The password field is required."
        ]
    }
}
 

Request      

POST api/login

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

login   string   

The user's email or username. Example: user@example.com

password   string   

The user's password. Example: secret-password

Send password reset email

Sends a password reset link to the user's email for the current tenant/school. This endpoint does not require authentication.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/password/forgot" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"johndoe@example.com\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/password/forgot"
);

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

let body = {
    "email": "johndoe@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Password reset link sent successfully. Please check your email."
}
 

Example response (404):


{
    "message": "We couldn't find a user with that email address."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email must be a valid email address."
        ]
    }
}
 

Request      

POST api/password/forgot

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

email   string   

The email of the user requesting the password reset. Example: johndoe@example.com

Response

Response Fields

message   string   

A short status message describing the result.

Reset password

Resets the user's password using a valid reset token for the current tenant/school. This endpoint does not require authentication.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/password/reset" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"johndoe@example.com\",
    \"token\": \"abc123\",
    \"password\": \"newpassword123\",
    \"password_confirmation\": \"newpassword123\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/password/reset"
);

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

let body = {
    "email": "johndoe@example.com",
    "token": "abc123",
    "password": "newpassword123",
    "password_confirmation": "newpassword123"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Password reset successfully."
}
 

Example response (400):


{
    "message": "Invalid token or email."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email must be a valid email address."
        ],
        "password": [
            "The password confirmation does not match."
        ]
    }
}
 

Request      

POST api/password/reset

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

email   string   

The email of the user. Example: johndoe@example.com

token   string   

The password reset token. Example: abc123

password   string   

The new password. Example: newpassword123

password_confirmation   string   

Confirmation of the new password. Example: newpassword123

Response

Response Fields

message   string   

A short status message describing the result.

Calendar

Calendar - Retrieve Calendar Events

requires authentication

This endpoint retrieves all sessions, evaluations, events, and bookings associated with a specific school. Sessions marked as evaluations (is_evaluation = true) are categorized as evaluations in the calendar. Requires permission: administrative.calendar.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar?month=march&start_date=2025-03-01&end_date=2025-03-31&player%5B%5D=0&group%5B%5D=0&coach%5B%5D=0&location%5B%5D=0&type=session" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar"
);

const params = {
    "month": "march",
    "start_date": "2025-03-01",
    "end_date": "2025-03-31",
    "player[]": "0",
    "group[]": "0",
    "coach[]": "0",
    "location[]": "0",
    "type": "session",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "calendar": [
        {
            "id": 1,
            "type": "session",
            "session_type": "private",
            "name": "Elite Training",
            "date": "2025-03-01",
            "duration": 90,
            "starting_time": "10:00",
            "ending_time": "11:30",
            "coach": "John Doe",
            "players": [
                {
                    "id": 12,
                    "name": "Carlos Lopez"
                },
                {
                    "id": 14,
                    "name": "Ana Gomez"
                }
            ],
            "group": {
                "id": 3,
                "name": "Grupo Avanzado",
                "description": "Jugadores avanzados mayores de 16",
                "coach_id": 8,
                "school_id": 1,
                "created_at": "2025-01-10T14:23:00.000000Z",
                "updated_at": "2025-02-15T11:45:00.000000Z",
                "players": [
                    {
                        "id": 21,
                        "name": "Pedro Márquez"
                    },
                    {
                        "id": 25,
                        "name": "Laura Núñez"
                    }
                ]
            },
            "location": "Court 1",
            "evaluation": null
        }
    ]
}
 

Example response (400):


{
    "message": "Invalid event type"
}
 

Example response (400):


{
    "message": "Invalid start_date format. Use YYYY-MM-DD"
}
 

Example response (400):


{
    "message": "Invalid end_date format. Use YYYY-MM-DD"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/calendar

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to retrieve calendar events for. Example: elite-tennis-academy

Query Parameters

month   string  optional  

optional The name of the month to filter events. Defaults to the current month if not provided. Example: march

start_date   string  optional  

optional Start date for filtering events (YYYY-MM-DD). Example: 2025-03-01

end_date   string  optional  

optional End date for filtering events (YYYY-MM-DD). Example: 2025-03-31

player[]   integer  optional  

optional One or more player IDs to filter sessions. Must be sent using repeated parameters. Example: 0

group[]   integer  optional  

optional One or more group IDs to filter sessions. Must be sent using repeated parameters. Example: 0

coach[]   integer  optional  

optional One or more coach IDs to filter sessions. Must be sent using repeated parameters. Example: 0

location[]   integer  optional  

optional One or more location IDs to filter sessions. Must be sent using repeated parameters. Example: 0

type   string  optional  

optional The type of event to filter. Must be one of: session, evaluation, event, booking. Example: session

Session - Create a New Training Session

requires authentication

This endpoint allows an authenticated administrative user to create one or more training sessions associated with a specific school. The school must belong to the authenticated user. If a recurring schedule is provided, multiple sessions will be created. Requires permission: administrative.calendar.session.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Advanced Training\",
    \"type\": \"group\",
    \"is_evaluation\": true,
    \"date\": \"2025-02-22\",
    \"starting_time\": \"13:00\",
    \"duration\": 60,
    \"observation\": \"Strength and agility training.\",
    \"coach_id\": 8,
    \"group_id\": 3,
    \"players\": [
        20,
        21,
        22
    ],
    \"location_id\": 5,
    \"school_site_id\": 1,
    \"status\": \"active\",
    \"repeat_weekly\": true,
    \"repeat_monthly\": true,
    \"repeat_times\": 4,
    \"recurring_schedule\": [
        \"hic\"
    ],
    \"extra_time_pre\": 16,
    \"extra_time_post\": 87,
    \"extra_time_pre_comment\": \"kfwjxbwuurxplxtaqkb\",
    \"extra_time_post_comment\": \"yfczdjepwljy\",
    \"evaluation_template_id\": 2,
    \"min_passing_score\": 75,
    \"items\": [
        \"et\"
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions"
);

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

let body = {
    "name": "Advanced Training",
    "type": "group",
    "is_evaluation": true,
    "date": "2025-02-22",
    "starting_time": "13:00",
    "duration": 60,
    "observation": "Strength and agility training.",
    "coach_id": 8,
    "group_id": 3,
    "players": [
        20,
        21,
        22
    ],
    "location_id": 5,
    "school_site_id": 1,
    "status": "active",
    "repeat_weekly": true,
    "repeat_monthly": true,
    "repeat_times": 4,
    "recurring_schedule": [
        "hic"
    ],
    "extra_time_pre": 16,
    "extra_time_post": 87,
    "extra_time_pre_comment": "kfwjxbwuurxplxtaqkb",
    "extra_time_post_comment": "yfczdjepwljy",
    "evaluation_template_id": 2,
    "min_passing_score": 75,
    "items": [
        "et"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Sessions created successfully",
    "sessions": [
        {
            "id": 1,
            "name": "Advanced Training",
            "type": "group",
            "date": "2025-02-22",
            "starting_time": "13:00",
            "ending_time": "14:00",
            "duration": 60,
            "coach_id": 8,
            "group_id": 3,
            "location_id": 5,
            "status": "active",
            "is_evaluation": true,
            "evaluation_id": 12
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "name": [
            "The name field is required."
        ],
        "evaluation_template_id": [
            "The evaluation template ID is required when the session is an evaluation."
        ],
        "min_passing_score": [
            "The minimum passing score is required when the session is an evaluation."
        ],
        "items": [
            "The items field is required when the session is an evaluation."
        ],
        "items.0.type": [
            "The type field must be either 'skill' or 'exercise'."
        ],
        "items.0.id": [
            "The id field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/calendar/sessions

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The school's slug. Example: elite-tennis-academy

Body Parameters

name   string   

The name of the session. Example: Advanced Training

type   string   

The type of the session. One of: semi-private, private, group. Example: group

is_evaluation   boolean  optional  

optional Indicates if the session is an evaluation. Default: false. Example: true

date   string   

Session date (YYYY-MM-DD). Example: 2025-02-22

starting_time   string   

Session start time (HH:MM, 24h). Example: 13:00

duration   integer   

Session duration in minutes. Example: 60

observation   string  optional  

optional Additional observations. Example: Strength and agility training.

coach_id   integer   

Coach ID. Example: 8

group_id   integer  optional  

optional Group ID (nullable). Example: 3

players   string[]  optional  

integer[] optional List of player IDs assigned to the session.

location_id   integer   

Location ID. Example: 5

school_site_id   integer  optional  

optional School site ID for multi-site schools. Example: 1

status   string   

One of: active, inactive, archived. Example: active

repeat_weekly   boolean  optional  

optional If true, repeats weekly; otherwise monthly by matching weekday. Default: false. Example: true

repeat_monthly   boolean  optional  

Example: true

repeat_times   integer  optional  

optional How many cycles to create. Default: 1. Example: 4

recurring_schedule   string[]  optional  

optional Recurrence rules to create multiple sessions.

day   string   

Day of week in English (e.g., monday, tuesday). Example: monday

starting_time   string   

Start time for that day (HH:MM). Example: 13:00

duration   integer   

Duration for that day (minutes). Example: 60

extra_time_pre   integer  optional  

Must be at least 0. Example: 16

extra_time_post   integer  optional  

Must be at least 0. Example: 87

extra_time_pre_comment   string  optional  

This field is required when extra_time_pre is > or 0. Must not be greater than 255 characters. Example: kfwjxbwuurxplxtaqkb

extra_time_post_comment   string  optional  

This field is required when extra_time_post is > or 0. Must not be greater than 255 characters. Example: yfczdjepwljy

evaluation_template_id   integer  optional  

required_if:is_evaluation,true The evaluation template ID. Example: 2

min_passing_score   integer  optional  

required_if:is_evaluation,true Minimum passing score (0-100). Example: 75

items   string[]  optional  

required_if:is_evaluation,true List of items (skills or exercises) to evaluate.

type   string   

Either skill or exercise. Example: skill

id   integer   

The skill or exercise ID. Example: 10

percentage   number   

Percentage weight (0-100). Example: 50

min_passing   integer   

Minimum passing score for this item. Example: 40

max_passing   integer   

Maximum score for this item. Example: 100

measurement_unit_id   integer  optional  

optional Measurement unit ID. Example: 1

order   integer   

Item order within evaluation. Example: 1

Response

Response Fields

message   string   

Status message.

sessions   string[]   

List of created sessions.

Session - Get Session Details

requires authentication

Retrieve a session with all related information (coach, players, group, location, creator, evaluation, and computed attendances). Requires permission: administrative.calendar.session.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Session retrieved successfully",
    "session": {
        "id": 10,
        "name": "Elite Training",
        "type": "group",
        "is_evaluation": false,
        "date": "2025-02-28",
        "starting_time": "10:00",
        "ending_time": "11:30",
        "duration": 90,
        "extra_time_pre": 10,
        "extra_time_pre_comment": "Coach requested extra warm-up time.",
        "extra_time_post": 15,
        "extra_time_post_comment": "Strategy review after training.",
        "observation": "High-intensity endurance drills.",
        "coach": {
            "id": 8,
            "name": "John Doe"
        },
        "players": [
            {
                "id": 20,
                "name": "Player One"
            }
        ],
        "group": {
            "id": 3,
            "name": "Advanced Juniors"
        },
        "location": {
            "id": 5,
            "name": "Central Court 1"
        },
        "status": "active",
        "creator": {
            "id": 1,
            "name": "Admin User"
        },
        "school": {
            "id": 2,
            "name": "Main Academy"
        },
        "schoolSite": null,
        "evaluation": {
            "id": 12,
            "scores": []
        },
        "attendances": [
            {
                "player_id": 20,
                "name": "Player One",
                "status": "present",
                "excuse_comment": null,
                "group_participant": true,
                "subscription_status": "active",
                "subscription_reason": "Valid",
                "remaining_sessions": 3,
                "extra_sessions": 0
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Session not found"
}
 

Request      

GET api/schools/{school_slug}/calendar/sessions/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The school's slug. Example: elite-tennis-academy

id   integer   

The session ID. Example: 10

Session - Update an Existing Training Session

requires authentication

Update a single session. Recurring sessions are not affected. Ending time is recalculated if starting_time or duration change. Requires permission: administrative.calendar.session.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Strength Training\",
    \"type\": \"group\",
    \"is_evaluation\": true,
    \"date\": \"2025-03-01\",
    \"starting_time\": \"10:30\",
    \"duration\": 90,
    \"extra_time_pre\": 10,
    \"extra_time_post\": 15,
    \"extra_time_pre_comment\": \"Additional warm-up\",
    \"extra_time_post_comment\": \"Review session\",
    \"observation\": \"Increased endurance drills\",
    \"coach_id\": 8,
    \"group_id\": 3,
    \"players\": [
        20,
        21,
        22
    ],
    \"location_id\": 5,
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions/10"
);

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

let body = {
    "name": "Strength Training",
    "type": "group",
    "is_evaluation": true,
    "date": "2025-03-01",
    "starting_time": "10:30",
    "duration": 90,
    "extra_time_pre": 10,
    "extra_time_post": 15,
    "extra_time_pre_comment": "Additional warm-up",
    "extra_time_post_comment": "Review session",
    "observation": "Increased endurance drills",
    "coach_id": 8,
    "group_id": 3,
    "players": [
        20,
        21,
        22
    ],
    "location_id": 5,
    "status": "active"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Session updated successfully",
    "session": {
        "id": 10,
        "name": "Strength Training",
        "type": "group",
        "is_evaluation": false,
        "date": "2025-03-01",
        "starting_time": "10:30",
        "ending_time": "12:00",
        "duration": 90,
        "extra_time_pre": 10,
        "extra_time_pre_comment": "Additional warm-up",
        "extra_time_post": 15,
        "extra_time_post_comment": "Review session",
        "observation": "Increased endurance drills",
        "coach": {
            "id": 8,
            "name": "John Doe"
        },
        "players": [
            {
                "id": 20,
                "name": "Player One"
            }
        ],
        "group": {
            "id": 3,
            "name": "Advanced Juniors"
        },
        "location": {
            "id": 5,
            "name": "Central Court 1"
        },
        "status": "active"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Session not found"
}
 

Request      

PUT api/schools/{school_slug}/calendar/sessions/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The school's slug. Example: elite-tennis-academy

id   integer   

The session ID. Example: 10

Body Parameters

name   string  optional  

optional Updated session name. Example: Strength Training

type   string  optional  

optional One of: semi-private, private, group. Example: group

is_evaluation   boolean  optional  

optional Whether it's an evaluation. Example: true

date   string  optional  

optional YYYY-MM-DD. Example: 2025-03-01

starting_time   string  optional  

optional HH:MM (24h). Example: 10:30

duration   integer  optional  

optional Minutes. Example: 90

extra_time_pre   integer  optional  

optional Minutes. Example: 10

extra_time_post   integer  optional  

optional Minutes. Example: 15

extra_time_pre_comment   string  optional  

required_if:extra_time_pre,>,0 Reason for extra pre time. Example: Additional warm-up

extra_time_post_comment   string  optional  

required_if:extra_time_post,>,0 Reason for extra post time. Example: Review session

observation   string  optional  

optional Additional observations. Example: Increased endurance drills

coach_id   integer  optional  

optional Coach ID. Example: 8

group_id   integer  optional  

optional Group ID (nullable). Example: 3

players   string[]  optional  

integer[] optional Player IDs; empty array removes all players.

location_id   integer  optional  

optional Location ID. Example: 5

status   string  optional  

optional One of: active, inactive, archived. Example: active

Session - Record Attendance for a Session

requires authentication

Record attendance for a session. Marks status as present/absent/excused (with optional comment), and records subscription state. On first attendance submission, the session status is set to completed. Optional extra time fields can also be updated. Requires permission: administrative.calendar.session.attendance

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions/1/attendance" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"attendances\": [
        \"ad\"
    ],
    \"extra_time_pre\": 10,
    \"extra_time_pre_comment\": \"Player arrived early for warm-up\",
    \"extra_time_post\": 15,
    \"extra_time_post_comment\": \"Extra drills and feedback\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/sessions/1/attendance"
);

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

let body = {
    "attendances": [
        "ad"
    ],
    "extra_time_pre": 10,
    "extra_time_pre_comment": "Player arrived early for warm-up",
    "extra_time_post": 15,
    "extra_time_post_comment": "Extra drills and feedback"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Attendance recorded successfully"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Session not found"
}
 

Example response (422):


{
    "message": "Player ID <PLAYER_ID> is not assigned to this session"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "attendances[0].player_id": [
            "The player_id field is required."
        ],
        "attendances[0].status": [
            "The status field must be 'present', 'absent', or 'excused'."
        ],
        "attendances[0].subscription_status": [
            "The subscription_status field is required and must be 'active' or 'inactive'."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/calendar/sessions/{id}/attendance

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The school's slug. Example: elite-tennis-academy

id   integer   

The ID of the session. Example: 1

session_id   integer   

The session ID. Example: 10

Body Parameters

attendances   string[]   

List of attendance rows.

player_id   integer   

Player ID to record. Example: 20

status   string   

One of: present, absent, excused. Example: present

excuse_comment   string  optional  

optional Required if status is excused. Example: Medical appointment

subscription_status   string   

One of: active, inactive. Example: active

subscription_reason   string  optional  

optional Reason for the subscription status. Example: Valid subscription

extra_time_pre   number  optional  

optional Minutes before session. Example: 10

extra_time_pre_comment   string  optional  

optional Comment for pre time. Example: Player arrived early for warm-up

extra_time_post   number  optional  

optional Minutes after session. Example: 15

extra_time_post_comment   string  optional  

optional Comment for post time. Example: Extra drills and feedback

Evaluation - List All Evaluations

requires authentication

This endpoint returns a paginated list of evaluations for a given school, including related sessions, templates, and scores. Supports filtering and sorting. Requires permission: administrative.calendar.evaluation.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/evaluations?per_page=15&search=advanced&filters[]=iste&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=contains&filters%5B0%5D%5Bvalue%5D=pending&sort_by=graded_at&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/evaluations"
);

const params = {
    "per_page": "15",
    "search": "advanced",
    "filters[0]": "iste",
    "filters[0][column]": "status",
    "filters[0][operator]": "contains",
    "filters[0][value]": "pending",
    "sort_by": "graded_at",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "evaluations": [
        {
            "id": 1,
            "status": "pending",
            "min_passing_score": 75,
            "graded_at": null,
            "template": {
                "id": 2,
                "nickname": "Technical evaluation"
            },
            "session": {
                "id": 10,
                "date": "2025-03-01",
                "name": "Advanced evaluation",
                "coach": {
                    "id": 5,
                    "name": "Sample Coach"
                },
                "group": {
                    "id": 3,
                    "name": "Elite Group"
                },
                "location": {
                    "id": 2,
                    "name": "Central Court"
                }
            },
            "scores": [
                {
                    "player_id": 20,
                    "score": 88,
                    "percentage": 50
                }
            ]
        }
    ],
    "metadata": {
        "columns": [
            "id",
            "status",
            "graded_at",
            "min_passing_score",
            "template",
            "session",
            "scores"
        ],
        "defaultColumns": [
            "id",
            "status",
            "template",
            "graded_at"
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/calendar/evaluations

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

per_page   integer  optional  

optional Records per page. Default: 10. Example: 15

search   string  optional  

optional Global search across all base columns. Example: advanced

filters   string[]  optional  

optional Filter conditions (column, operator, value). Supports relation dot-notation (e.g., session.coach.name).

filters[0][column]   string   

Column name or relation path. Example: status

filters[0][operator]   string   

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: contains

filters[0][value]   string   

mixed Value to compare. Use an array [min,max] for "between". Example: pending

sort_by   string  optional  

optional Column to sort by. Example: graded_at

direction   string  optional  

optional Sort direction (asc, desc). Default: asc. Example: desc

Evaluations - Qualify an evaluation in a school.

requires authentication

This endpoint allows an authorized user to grade an evaluation within a specific school. If all scores have been graded, the evaluation status will be updated to "completed." Requires permission: administrative.calendar.evaluation.qualify

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/evaluations/6/qualify" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"scores\": [
        \"aliquam\"
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/evaluations/6/qualify"
);

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

let body = {
    "scores": [
        "aliquam"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Evaluation graded successfully",
    "evaluation": {
        "id": 6,
        "evaluation_template_id": 1,
        "min_passing_score": 50,
        "status": "completed",
        "graded_at": "2025-03-14T16:30:00.000000Z",
        "created_at": "2025-03-14T15:52:31.000000Z",
        "updated_at": "2025-03-14T16:30:00.000000Z",
        "scores": [
            {
                "id": 61,
                "evaluation_id": 6,
                "player_id": 1689,
                "skill_id": 1,
                "exercise_id": null,
                "score": 85,
                "comment": "Good performance in the test.",
                "percentage": "25.00",
                "measurement_unit_id": 17,
                "zero_percent_score": 0,
                "min_passing": 1,
                "max_passing": 3,
                "order": 1,
                "created_at": "2025-03-14T15:52:31.000000Z",
                "updated_at": "2025-03-14T16:30:00.000000Z"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Evaluation not found."
}
 

Example response (422):


{
    "message": "The score ID 61 does not belong to the evaluation ID 6."
}
 

Example response (500):


{
    "message": "Error recording the grade",
    "error": "Error details"
}
 

Request      

PUT api/schools/{school_slug}/calendar/evaluations/{id}/qualify

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The evaluation ID. Example: 6

Body Parameters

scores   string[]   

List of scores to be recorded.

id   integer   

The ID of the score record. Example: 61

score   number  optional  

nullable The assigned score for the player. Example: 85

comment   string  optional  

nullable Additional comments regarding the evaluation. Example: "Good performance in the test."

Evaluations - Get evaluation results.

requires authentication

This endpoint retrieves the evaluation results for a specific school and evaluation. It calculates each student's final grade based on the evaluation's scoring criteria, including whether the student passed or failed.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/evaluations/6/results" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/calendar/evaluations/6/results"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "evaluation_id": 6,
    "min_passing_score": 50,
    "status": "completed",
    "results": [
        {
            "player_id": 1689,
            "final_score": 78.5,
            "passed": true,
            "scores": [
                {
                    "score_id": 61,
                    "exercise_id": null,
                    "skill_id": 1,
                    "raw_score": 2,
                    "final_score": 70,
                    "min_passing": 1,
                    "max_passing": 3,
                    "percentage": "25.00",
                    "passed": true
                },
                {
                    "score_id": 62,
                    "exercise_id": null,
                    "skill_id": 3,
                    "raw_score": 80,
                    "final_score": 85,
                    "min_passing": 30,
                    "max_passing": 90,
                    "percentage": "25.00",
                    "passed": true
                }
            ]
        },
        {
            "player_id": 1691,
            "final_score": 45.3,
            "passed": false,
            "scores": [
                {
                    "score_id": 65,
                    "exercise_id": null,
                    "skill_id": 1,
                    "raw_score": 1,
                    "final_score": 30,
                    "min_passing": 1,
                    "max_passing": 3,
                    "percentage": "25.00",
                    "passed": false
                },
                {
                    "score_id": 66,
                    "exercise_id": null,
                    "skill_id": 3,
                    "raw_score": 60,
                    "final_score": 45,
                    "min_passing": 30,
                    "max_passing": 90,
                    "percentage": "25.00",
                    "passed": false
                }
            ]
        }
    ],
    "general_exercises": [
        {
            "exercise_id": 1,
            "average_score": 80.25,
            "passing_rate": 66.67
        }
    ],
    "general_skills": [
        {
            "skill_id": 3,
            "average_score": 85,
            "passing_rate": 75
        }
    ]
}
 

Example response (404):


{
    "message": "Evaluation not found."
}
 

Request      

GET api/schools/{school_slug}/calendar/evaluations/{id}/results

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The evaluation ID. Example: 6

Customer

requires authentication

Search customers (users with roles lead, account, or player) associated with the given school. Supports filtering, global search, and sorting. Requires permission: administrative.customer.search

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/search?view=select&per_page=15&only_archived=1&filters%5Bsearch%5D=doe&filters%5B0%5D%5Bcolumn%5D=language&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=en&sort_by=first_name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/search"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "filters[search]": "doe",
    "filters[0][column]": "language",
    "filters[0][operator]": "equals",
    "filters[0][value]": "en",
    "sort_by": "first_name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "customers": {
        "data": [
            {
                "user_id": 101,
                "first_name": "John",
                "last_name": "Doe",
                "gender": "male",
                "language": "en",
                "role": "account"
            }
        ],
        "current_page": 1,
        "per_page": 10,
        "total": 42
    },
    "metadata": {
        "columns": [
            "user_id",
            "first_name",
            "last_name",
            "gender",
            "language",
            "role"
        ],
        "defaultColumns": [
            "user_id",
            "first_name",
            "last_name",
            "gender",
            "language",
            "role"
        ]
    }
}
 

Example response (200):


{"customers": [{"user_id": 101, "first_name": "John", "last_name": "Doe"}]}
When view=select, returns a flat list of customers with minimal fields.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Leads - List leads in a school

requires authentication

Returns the leads (users with role "lead") associated with the given school. Supports filters, sort, pagination, and a compact select view. Requires permission: administrative.customer.lead.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead?view=select&per_page=15&only_archived=1&filters[]=cumque&filters%5Bsearch%5D=emma&filters%5B0%5D%5Bcolumn%5D=language&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=en&sort_by=first_name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "filters[0]": "cumque",
    "filters[search]": "emma",
    "filters[0][column]": "language",
    "filters[0][operator]": "equals",
    "filters[0][value]": "en",
    "sort_by": "first_name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "leads": {
        "data": [
            {
                "user_id": 15,
                "first_name": "Emma",
                "last_name": "Johnson",
                "gender": "female",
                "language": "en",
                "lead_status": "active"
            }
        ],
        "current_page": 1,
        "per_page": 10,
        "total": 50
    },
    "metadata": {
        "columns": [
            "user_id",
            "first_name",
            "last_name",
            "gender",
            "language",
            "lead_status"
        ],
        "defaultColumns": [
            "user_id",
            "first_name",
            "last_name",
            "lead_status",
            "gender",
            "language"
        ]
    }
}
 

Example response (200):


{"leads": [{"user_id": 15, "first_name": "Emma", "last_name": "Johnson", "lead_status": "active"}]}
When view=select, a flat list of leads is returned.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/customer/lead

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (only for table). Default: 10. Example: 15

only_archived   boolean  optional  

optional If true, returns only archived leads. Default: false. Example: true

filters   string[]  optional  

optional Filter set. Supports relation dot-notation in column (e.g. user.userStatus.name) and global search via filters[search].

filters[search]   string  optional  

optional Global search over base profile columns. Example: emma

filters[0][column]   string   

Column to filter (base or relation). Example: language

filters[0][operator]   string   

Operator: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string   

mixed Value to compare. Use [min,max] for between. Example: en

sort_by   string  optional  

optional Base column to sort by (relations not supported). Example: first_name

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

Leads - Create or update

requires authentication

Create a new lead or update an existing one in the given school. If a new user is created, a unique user_name is generated and a password is set. The user is assigned the "lead" role and associated with the school. Supports profile image upload, custom fields, and optional weekly schedules. Requires permission: administrative.customer.lead.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "id=10"\
    --form "first_name=Emma"\
    --form "last_name=Johnson"\
    --form "gender=female"\
    --form "date_birth=2010-06-01"\
    --form "id_number=123456789"\
    --form "language=en"\
    --form "phone_code=1"\
    --form "billing_phone_number=+17865551234"\
    --form "secondary_phone_number=yhnbisb"\
    --form "billing_email=lead@example.com"\
    --form "secondary_email=stanton.klein@example.org"\
    --form "billing_country=United States"\
    --form "billing_state=Florida"\
    --form "billing_city=Miami"\
    --form "billing_address=1234 Ocean Dr"\
    --form "billing_zip_code=33101"\
    --form "has_medical_conditions="\
    --form "medical_details=iqkcba"\
    --form "schedules[][day_of_week]=monday"\
    --form "schedules[][start_time]=04:17"\
    --form "schedules[][end_time]=2109-08-04"\
    --form "custom_fields=[{"slug":"whatsapp","value":"+15550001111"}]"\
    --form "image=@/tmp/phpZ0DzaJ" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead"
);

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

const body = new FormData();
body.append('id', '10');
body.append('first_name', 'Emma');
body.append('last_name', 'Johnson');
body.append('gender', 'female');
body.append('date_birth', '2010-06-01');
body.append('id_number', '123456789');
body.append('language', 'en');
body.append('phone_code', '1');
body.append('billing_phone_number', '+17865551234');
body.append('secondary_phone_number', 'yhnbisb');
body.append('billing_email', 'lead@example.com');
body.append('secondary_email', 'stanton.klein@example.org');
body.append('billing_country', 'United States');
body.append('billing_state', 'Florida');
body.append('billing_city', 'Miami');
body.append('billing_address', '1234 Ocean Dr');
body.append('billing_zip_code', '33101');
body.append('has_medical_conditions', '');
body.append('medical_details', 'iqkcba');
body.append('schedules[][day_of_week]', 'monday');
body.append('schedules[][start_time]', '04:17');
body.append('schedules[][end_time]', '2109-08-04');
body.append('custom_fields', '[{"slug":"whatsapp","value":"+15550001111"}]');
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{
    "message": "Profile updated successfully",
    "lead": {}
}
 

Example response (201):


{
    "message": "Profile created successfully",
    "lead": {}
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "This email is already registered for another user."
}
 

Request      

POST api/schools/{school_slug}/customer/lead

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

Body Parameters

id   integer  optional  

optional Lead user ID to update. If omitted, a new lead is created. Example: 10

first_name   string   

First name. Example: Emma

last_name   string   

Last name. Example: Johnson

gender   string  optional  

optional One of: male, female, other. Example: female

date_birth   date  optional  

optional Date of birth (YYYY-MM-DD). Example: 2010-06-01

id_number   string  optional  

optional Identification number. Example: 123456789

language   string  optional  

optional Language code. Example: en

phone_code   string  optional  

optional Numeric country phone code. Example: 1

billing_phone_number   string  optional  

optional Primary phone number. Example: +17865551234

secondary_phone_number   string  optional  

Must not be greater than 40 characters. Example: yhnbisb

billing_email   string  optional  

optional Email address (also used as user email). Example: lead@example.com

secondary_email   string  optional  

Must be a valid email address. Must not be greater than 120 characters. Example: stanton.klein@example.org

billing_country   string  optional  

optional Country. Example: United States

billing_state   string  optional  

optional State/Region. Example: Florida

billing_city   string  optional  

optional City. Example: Miami

billing_address   string  optional  

optional Address. Example: 1234 Ocean Dr

billing_zip_code   string  optional  

optional Zip code. Example: 33101

has_medical_conditions   boolean  optional  

Example: false

medical_details   string  optional  

This field is required when has_medical_conditions is 1. Must not be greater than 1000 characters. Example: iqkcba

experience_level   string  optional  
schedules   object[]  optional  
day_of_week   string  optional  

This field is required when schedules is present. Example: monday

Must be one of:
  • monday
  • tuesday
  • wednesday
  • thursday
  • friday
  • saturday
  • sunday
start_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Example: 04:17

end_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Must be a date after schedules.*.start_time. Example: 2109-08-04

image   file  optional  

optional Profile image file. Example: /tmp/phpZ0DzaJ

custom_fields   string[]  optional  

optional Custom fields as objects with slug and value. When sending multipart/form-data, provide as a JSON string. Example: [{"slug":"whatsapp","value":"+15550001111"}]

slug   string  optional  

optional Custom field identifier. Example: whatsapp

value   mixed  optional  

optional Custom field value. Example: +15550001111

Leads - Show

requires authentication

Get a specific lead with its profile and associated schools, restricted to the given school. Requires permission: administrative.customer.lead.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "lead": {}
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "lead not found or not associated with this school"
}
 

Request      

GET api/schools/{school_slug}/customer/lead/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Lead user ID. Example: 10

Leads - Update

requires authentication

Updates a lead. Uses the same validation as store and accepts partial updates. Requires permission: administrative.customer.lead.store

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "first_name=vprulxuzcttchutgqkhhj"\
    --form "last_name=nkjsalue"\
    --form "gender=other"\
    --form "date_birth=2026-02-26T04:17:45"\
    --form "id_number=dicta"\
    --form "language=nuahdsedv"\
    --form "phone_code=2730"\
    --form "billing_phone_number=onddmlmyiyozlkwxa"\
    --form "secondary_phone_number=ejtdmfb"\
    --form "billing_email=isadore.koss@example.com"\
    --form "secondary_email=odooley@example.net"\
    --form "billing_country=sfqyhdtggzwlxuxdeaqz"\
    --form "billing_state=hfponakptfkmplgbk"\
    --form "billing_city=pvbzohacqnmjv"\
    --form "billing_address=evfcmuijymmmxbdjshfrkrh"\
    --form "billing_zip_code=nogqiqc"\
    --form "has_medical_conditions="\
    --form "medical_details=dzykf"\
    --form "schedules[][day_of_week]=monday"\
    --form "schedules[][start_time]=04:17"\
    --form "schedules[][end_time]=2114-03-18"\
    --form "image=@/tmp/phpj38fRA" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead/10"
);

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

const body = new FormData();
body.append('first_name', 'vprulxuzcttchutgqkhhj');
body.append('last_name', 'nkjsalue');
body.append('gender', 'other');
body.append('date_birth', '2026-02-26T04:17:45');
body.append('id_number', 'dicta');
body.append('language', 'nuahdsedv');
body.append('phone_code', '2730');
body.append('billing_phone_number', 'onddmlmyiyozlkwxa');
body.append('secondary_phone_number', 'ejtdmfb');
body.append('billing_email', 'isadore.koss@example.com');
body.append('secondary_email', 'odooley@example.net');
body.append('billing_country', 'sfqyhdtggzwlxuxdeaqz');
body.append('billing_state', 'hfponakptfkmplgbk');
body.append('billing_city', 'pvbzohacqnmjv');
body.append('billing_address', 'evfcmuijymmmxbdjshfrkrh');
body.append('billing_zip_code', 'nogqiqc');
body.append('has_medical_conditions', '');
body.append('medical_details', 'dzykf');
body.append('schedules[][day_of_week]', 'monday');
body.append('schedules[][start_time]', '04:17');
body.append('schedules[][end_time]', '2114-03-18');
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "PUT",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{
    "message": "Profile updated successfully",
    "lead": {}
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "lead not found or unauthorized."
}
 

Example response (422):


{
    "message": "This email is already registered for another user."
}
 

Request      

PUT api/schools/{school_slug}/customer/lead/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Lead user ID to update. Example: 10

Body Parameters

id   string  optional  
first_name   string  optional  

Must not be greater than 255 characters. Example: vprulxuzcttchutgqkhhj

last_name   string  optional  

Must not be greater than 255 characters. Example: nkjsalue

gender   string  optional  

Example: other

Must be one of:
  • male
  • female
  • other
date_birth   string  optional  

Must be a valid date. Example: 2026-02-26T04:17:45

id_number   string  optional  

Example: dicta

language   string  optional  

Must not be greater than 10 characters. Example: nuahdsedv

phone_code   string  optional  

This field is required when billing_phone_number is present. Must match the regex /^\d{1,4}$/. Example: 2730

billing_phone_number   string  optional  

Must not be greater than 40 characters. Example: onddmlmyiyozlkwxa

secondary_phone_number   string  optional  

Must not be greater than 40 characters. Example: ejtdmfb

billing_email   string  optional  

Must be a valid email address. Example: isadore.koss@example.com

secondary_email   string  optional  

Must be a valid email address. Must not be greater than 120 characters. Example: odooley@example.net

billing_country   string  optional  

Must not be greater than 100 characters. Example: sfqyhdtggzwlxuxdeaqz

billing_state   string  optional  

Must not be greater than 100 characters. Example: hfponakptfkmplgbk

billing_city   string  optional  

Must not be greater than 80 characters. Example: pvbzohacqnmjv

billing_address   string  optional  

Must not be greater than 255 characters. Example: evfcmuijymmmxbdjshfrkrh

billing_zip_code   string  optional  

Must not be greater than 20 characters. Example: nogqiqc

has_medical_conditions   boolean  optional  

Example: false

medical_details   string  optional  

This field is required when has_medical_conditions is 1. Must not be greater than 1000 characters. Example: dzykf

experience_level   string  optional  
schedules   object[]  optional  
day_of_week   string  optional  

This field is required when schedules is present. Example: monday

Must be one of:
  • monday
  • tuesday
  • wednesday
  • thursday
  • friday
  • saturday
  • sunday
start_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Example: 04:17

end_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Must be a date after schedules.*.start_time. Example: 2114-03-18

image   file  optional  

Must be an image. Must not be greater than 5120 kilobytes. Example: /tmp/phpj38fRA

Leads - Archive

requires authentication

Archive (soft-deactivate) a lead in the given school. Record is not deleted. Also archives the profile if present. Requires permission: administrative.customer.lead.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/lead/10"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Lead archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "lead not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/customer/lead/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Lead user ID to archive. Example: 10

Accounts - List accounts in a school

requires authentication

Obtiene las cuentas (usuarios con rol "account") asociadas a la escuela dada. Soporta filtros, búsqueda y ordenamiento. Requiere permiso: administrative.customer.account.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts?view=select&per_page=15&only_archived=1&filters[]=eveniet&filters%5Bsearch%5D=doe&filters%5B0%5D%5Bcolumn%5D=language&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=en&sort_by=first_name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "filters[0]": "eveniet",
    "filters[search]": "doe",
    "filters[0][column]": "language",
    "filters[0][operator]": "equals",
    "filters[0][value]": "en",
    "sort_by": "first_name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "accounts": {
        "data": [
            {
                "user_id": 101,
                "first_name": "John",
                "last_name": "Doe",
                "gender": "male",
                "language": "en",
                "account_status": "active"
            }
        ],
        "current_page": 1,
        "per_page": 10,
        "total": 42
    },
    "metadata": {
        "columns": [
            "user_id",
            "first_name",
            "last_name",
            "gender",
            "language",
            "account_status"
        ],
        "defaultColumns": [
            "user_id",
            "first_name",
            "last_name",
            "account_status",
            "gender",
            "language"
        ]
    }
}
 

Example response (200):


{"accounts": [{"user_id": 101, "first_name": "John", "last_name": "Doe", "account_status": "active"}]}
Cuando view=select, retorna una lista plana de cuentas.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/customer/accounts

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

Slug de la escuela. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Modo de respuesta. Uno de: table, select. Default: table. Example: select

per_page   integer  optional  

optional Elementos por página (solo table). Default: 10. Example: 15

only_archived   boolean  optional  

optional Si es true, retorna solo cuentas archivadas. Default: false. Example: true

filters   string[]  optional  

optional Conjunto de filtros. Soporta notación de relación con punto en column (p.ej. user.userStatus.name) y búsqueda global vía filters[search].

filters[search]   string  optional  

optional Búsqueda global sobre columnas base del perfil. Example: doe

filters[0][column]   string   

Columna a filtrar (base o relación). Example: language

filters[0][operator]   string   

Operador: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string   

mixed Valor a comparar. Para between envía [min,max]. Example: en

sort_by   string  optional  

optional Columna base para ordenar (relaciones no soportadas). Example: first_name

direction   string  optional  

optional Dirección de orden. Uno de: asc, desc. Default: asc. Example: desc

Accounts - Create or update

requires authentication

Create a new account or update an existing one for the given school. If a new user is created, a unique user_name is generated and an email with the initial password is sent to the provided billing_email. Allows uploading a profile image, saving custom fields, and optional weekly schedules. Requires permission: administrative.customer.account.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "id=10"\
    --form "first_name=John"\
    --form "last_name=Doe"\
    --form "gender=male"\
    --form "date_birth=1985-09-15"\
    --form "id_number=A-1234567"\
    --form "language=en"\
    --form "phone_code=1"\
    --form "billing_phone_number=+17865551234"\
    --form "secondary_phone_number=+17865550000"\
    --form "billing_email=account@example.com"\
    --form "secondary_email=secondary@example.com"\
    --form "billing_country=Argentina"\
    --form "billing_state=Buenos Aires"\
    --form "billing_city=CABA"\
    --form "billing_address=1234 Corrientes Ave"\
    --form "billing_zip_code=C1043AAU"\
    --form "has_medical_conditions=1"\
    --form "medical_details=ndkcjlbqjhc"\
    --form "schedules[][day_of_week]=sunday"\
    --form "schedules[][start_time]=04:17"\
    --form "schedules[][end_time]=2120-11-07"\
    --form "custom_fields=[{"slug":"whatsapp","value":"+15550001111"}]"\
    --form "image=@/tmp/phplL2C6Q" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts"
);

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

const body = new FormData();
body.append('id', '10');
body.append('first_name', 'John');
body.append('last_name', 'Doe');
body.append('gender', 'male');
body.append('date_birth', '1985-09-15');
body.append('id_number', 'A-1234567');
body.append('language', 'en');
body.append('phone_code', '1');
body.append('billing_phone_number', '+17865551234');
body.append('secondary_phone_number', '+17865550000');
body.append('billing_email', 'account@example.com');
body.append('secondary_email', 'secondary@example.com');
body.append('billing_country', 'Argentina');
body.append('billing_state', 'Buenos Aires');
body.append('billing_city', 'CABA');
body.append('billing_address', '1234 Corrientes Ave');
body.append('billing_zip_code', 'C1043AAU');
body.append('has_medical_conditions', '1');
body.append('medical_details', 'ndkcjlbqjhc');
body.append('schedules[][day_of_week]', 'sunday');
body.append('schedules[][start_time]', '04:17');
body.append('schedules[][end_time]', '2120-11-07');
body.append('custom_fields', '[{"slug":"whatsapp","value":"+15550001111"}]');
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{
    "message": "Profile updated successfully",
    "account": {
        "id": 10,
        "user_name": "john.doe1"
    }
}
 

Example response (201):


{
    "message": "Profile created successfully",
    "account": {
        "id": 10,
        "user_name": "john.doe1",
        "name": "John Doe",
        "email": "account@example.com",
        "profile": {
            "user_id": 10,
            "first_name": "John",
            "last_name": "Doe",
            "gender": "male",
            "date_birth": "1985-09-15",
            "language": "en",
            "billing_phone_number": "+17865551234",
            "billing_country": "Argentina",
            "billing_email": "account@example.com",
            "billing_state": "Buenos Aires",
            "billing_address": "1234 Corrientes Ave",
            "billing_zip_code": "C1043AAU",
            "image_url": "https://example-bucket/profile123.jpg",
            "custom_fields": [
                {
                    "slug": "whatsapp",
                    "value": "+15550001111"
                }
            ]
        },
        "schools": [
            {
                "id": 5,
                "nickname": "Elite Academy"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "This email is already registered for another user."
}
 

Request      

POST api/schools/{school_slug}/customer/accounts

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

Body Parameters

id   integer  optional  

optional Account user ID to update. If omitted, a new account is created. Example: 10

first_name   string  optional  

optional First name. Example: John

last_name   string  optional  

optional Last name. Example: Doe

gender   string  optional  

optional One of: male, female, other. Example: male

date_birth   date  optional  

optional Date of birth (YYYY-MM-DD). Example: 1985-09-15

id_number   string  optional  

optional Identification number. Example: A-1234567

language   string  optional  

optional Language code. Example: en

phone_code   string  optional  

optional Numeric country phone code; required with billing_phone_number. Example: 1

billing_phone_number   string  optional  

optional Billing phone. E.164 format will be derived using phone_code. Example: +17865551234

secondary_phone_number   string  optional  

optional Secondary phone. Example: +17865550000

billing_email   string  optional  

optional Billing email (also used as the account email). Example: account@example.com

secondary_email   string  optional  

optional Secondary email. Example: secondary@example.com

billing_country   string  optional  

optional Billing country. Example: Argentina

billing_state   string  optional  

optional Billing state/region. Example: Buenos Aires

billing_city   string  optional  

optional Billing city. Example: CABA

billing_address   string  optional  

optional Billing address. Example: 1234 Corrientes Ave

billing_zip_code   string  optional  

optional Billing ZIP/postal code. Example: C1043AAU

has_medical_conditions   boolean  optional  

Example: true

medical_details   string  optional  

This field is required when has_medical_conditions is 1. Must not be greater than 1000 characters. Example: ndkcjlbqjhc

experience_level   string  optional  
schedules   object[]  optional  
day_of_week   string  optional  

This field is required when schedules is present. Example: sunday

Must be one of:
  • monday
  • tuesday
  • wednesday
  • thursday
  • friday
  • saturday
  • sunday
start_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Example: 04:17

end_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Must be a date after schedules.*.start_time. Example: 2120-11-07

image   file  optional  

optional Profile image file. Example: /tmp/phplL2C6Q

custom_fields   string[]  optional  

optional Custom fields as objects with slug and value. When sending as multipart/form-data, provide as JSON string. Example: [{"slug":"whatsapp","value":"+15550001111"}]

slug   string  optional  

optional Custom field identifier. Example: whatsapp

value   mixed  optional  

optional Custom field value. Example: +15550001111

Accounts - Show

requires authentication

Get a specific account with its profile and associated schools, restricted to the given school. Requires permission: administrative.customer.account.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "account": {
        "id": 10,
        "user_name": "john.doe1",
        "name": "John Doe",
        "email": "account@example.com",
        "profile": {
            "user_id": 10,
            "first_name": "John",
            "last_name": "Doe",
            "gender": "male",
            "date_birth": "1985-09-15",
            "language": "en",
            "billing_phone_number": "+17865551234",
            "billing_country": "Argentina",
            "billing_email": "account@example.com",
            "billing_state": "Buenos Aires",
            "billing_address": "1234 Corrientes Ave",
            "billing_zip_code": "C1043AAU",
            "image_url": "https://example-bucket/profile123.jpg",
            "custom_fields": [
                {
                    "slug": "whatsapp",
                    "value": "+15550001111"
                }
            ]
        },
        "schools": [
            {
                "id": 5,
                "nickname": "Elite Academy"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "account not found or not associated with this school"
}
 

Request      

GET api/schools/{school_slug}/customer/accounts/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Account user ID. Example: 10

Accounts - Archive

requires authentication

Archive (soft-deactivate) an account in the given school. The record is not deleted. Requires permission: administrative.customer.account.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts/10"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Account archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "account not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/customer/accounts/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Account user ID to archive. Example: 10

Accounts - Get my players in a school

requires authentication

Returns the players (children) that belong to the authenticated account within the given school. Requires permission: customer.account.players

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts/my/players" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/accounts/my/players"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "id": 33,
        "name": "Player One",
        "profile": {
            "date_birth": "2015-06-01",
            "gender": "female",
            "experience_level": null,
            "has_medical_conditions": false,
            "medical_details": null
        }
    }
]
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/customer/accounts/my/players

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

Players - List players in a school

requires authentication

Returns the players (users with role "player") associated with the given school. Supports filters, sort, pagination, optional group/account filters, and optional subscription validation. Requires permission: administrative.customer.player.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players?view=select&per_page=15&only_archived=1&group_id=5&parent_id=3&validate_subscriptions=1&school_site_id=1&type=private&filters[]=voluptas&filters%5Bsearch%5D=lucas&filters%5B0%5D%5Bcolumn%5D=language&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=en&sort_by=first_name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "group_id": "5",
    "parent_id": "3",
    "validate_subscriptions": "1",
    "school_site_id": "1",
    "type": "private",
    "filters[0]": "voluptas",
    "filters[search]": "lucas",
    "filters[0][column]": "language",
    "filters[0][operator]": "equals",
    "filters[0][value]": "en",
    "sort_by": "first_name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "players": {
        "data": [
            {
                "user_id": 15,
                "first_name": "Lucas",
                "last_name": "Alvarez",
                "gender": "male",
                "language": "en",
                "player_status": "active"
            }
        ],
        "current_page": 1,
        "per_page": 10,
        "total": 50
    },
    "metadata": {
        "columns": [
            "user_id",
            "first_name",
            "last_name",
            "gender",
            "language",
            "player_status"
        ],
        "defaultColumns": [
            "user_id",
            "first_name",
            "last_name",
            "player_status",
            "gender",
            "language"
        ]
    }
}
 

Example response (200):


{"players": [{"id": 42, "user_id": 101, "first_name": "Lucas", "last_name": "Alvarez", "player_status": "active"}]}
When view=select, returns a flat list of players.
 

Example response (400):


{
    "message": "Invalid group_id"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/customer/players

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (only for table). Default: 10. Example: 15

only_archived   boolean  optional  

optional If true, returns only archived players. Default: false. Example: true

group_id   integer  optional  

optional Filter players by group id. Example: 5

parent_id   integer  optional  

optional Filter players by account (parent) id. Example: 3

validate_subscriptions   boolean  optional  

optional If true, adds a validation_subscription object per player. Example: true

school_site_id   integer   

if validate_subscriptions=true. School site id used to validate subscriptions. Example: 1

type   string   

if validate_subscriptions=true. One of: private, semi-private, group. Example: private

filters   string[]  optional  

optional Filter set. Supports relation dot-notation in column and global search via filters[search].

filters[search]   string  optional  

optional Global search over base profile columns. Example: lucas

filters[0][column]   string   

Column to filter (base or relation). Example: language

filters[0][operator]   string   

Operator: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string   

mixed Value to compare. Use [min,max] for between. Example: en

sort_by   string  optional  

optional Base column to sort by (relations not supported). Example: first_name

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

Players - Create or update

requires authentication

Create or update a Player profile (role "player") linked only to the school in the URL. The parent_id must be an Account belonging to this same school. Assigns the "player" role if missing and associates the player only to this school. Requires permission: administrative.customer.player.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "id=42"\
    --form "first_name=Lucas"\
    --form "last_name=Alvarez"\
    --form "gender=male"\
    --form "date_birth=2012-05-14"\
    --form "id_number=1234567890"\
    --form "language=en"\
    --form "phone_code=57"\
    --form "billing_phone_number=+573001234567"\
    --form "secondary_phone_number=+573119876543"\
    --form "billing_email=billing@family.com"\
    --form "secondary_email=mother@example.com"\
    --form "billing_country=Colombia"\
    --form "billing_state=Valle del Cauca"\
    --form "billing_city=Cali"\
    --form "billing_address=Calle 5 #10-20"\
    --form "billing_zip_code=760001"\
    --form "has_medical_conditions=1"\
    --form "medical_details=Asthma (uses inhaler)."\
    --form "experience_level=intermediate"\
    --form "schedules[][day_of_week]=friday"\
    --form "schedules[][start_time]=04:17"\
    --form "schedules[][end_time]=2032-10-13"\
    --form "parent_id=10"\
    --form "image=@/tmp/phpuAA2QJ" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players"
);

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

const body = new FormData();
body.append('id', '42');
body.append('first_name', 'Lucas');
body.append('last_name', 'Alvarez');
body.append('gender', 'male');
body.append('date_birth', '2012-05-14');
body.append('id_number', '1234567890');
body.append('language', 'en');
body.append('phone_code', '57');
body.append('billing_phone_number', '+573001234567');
body.append('secondary_phone_number', '+573119876543');
body.append('billing_email', 'billing@family.com');
body.append('secondary_email', 'mother@example.com');
body.append('billing_country', 'Colombia');
body.append('billing_state', 'Valle del Cauca');
body.append('billing_city', 'Cali');
body.append('billing_address', 'Calle 5 #10-20');
body.append('billing_zip_code', '760001');
body.append('has_medical_conditions', '1');
body.append('medical_details', 'Asthma (uses inhaler).');
body.append('experience_level', 'intermediate');
body.append('schedules[][day_of_week]', 'friday');
body.append('schedules[][start_time]', '04:17');
body.append('schedules[][end_time]', '2032-10-13');
body.append('parent_id', '10');
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (200, updated):


{
    "message": "Player updated successfully",
    "player": {
        "id": 42,
        "name": "Lucas Alvarez"
    }
}
 

Example response (201, created):


{
    "message": "Player created successfully",
    "player": {
        "id": 42,
        "name": "Lucas Alvarez"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "This email is already registered for another player under this account."
}
 

Example response (422):


{
    "message": "This email is already registered for another user."
}
 

Request      

POST api/schools/{school_slug}/customer/players

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

Body Parameters

id   integer  optional  

optional Player user id to update. If omitted, a new player is created. Example: 42

first_name   string  optional  

optional Player's first name. Example: Lucas

last_name   string  optional  

optional Player's last name. Example: Alvarez

gender   string  optional  

optional One of: male, female, other. Example: male

date_birth   date  optional  

optional Date of birth (YYYY-MM-DD). Example: 2012-05-14

id_number   string  optional  

optional Identification/document number. Example: 1234567890

language   string  optional  

optional Preferred language code. Max 10 chars. Example: en

phone_code   string  optional  

optional Required when billing_phone_number is present. Digits only (1–4). Example: 57

billing_phone_number   string  optional  

optional Primary phone. Will be normalized to E.164 when phone_code is provided. Example: +573001234567

secondary_phone_number   string  optional  

optional Secondary phone. Will be normalized to E.164 when phone_code is provided. Example: +573119876543

billing_email   string  optional  

optional Billing email address (also used as player's email if provided). Example: billing@family.com

secondary_email   string  optional  

optional Secondary contact email. Example: mother@example.com

billing_country   string  optional  

optional Billing country. Example: Colombia

billing_state   string  optional  

optional Billing state/region. Example: Valle del Cauca

billing_city   string  optional  

optional Billing city. Example: Cali

billing_address   string  optional  

optional Billing address. Example: Calle 5 #10-20

billing_zip_code   string  optional  

optional Billing zip/postal code. Example: 760001

has_medical_conditions   boolean  optional  

optional Whether the player has medical conditions. Example: true

medical_details   string  optional  

optional Required if has_medical_conditions is true. Example: Asthma (uses inhaler).

experience_level   string  optional  

optional Free-form field for experience level. Example: intermediate

schedules   object[]  optional  
day_of_week   string  optional  

This field is required when schedules is present. Example: friday

Must be one of:
  • monday
  • tuesday
  • wednesday
  • thursday
  • friday
  • saturday
  • sunday
start_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Example: 04:17

end_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Must be a date after schedules.*.start_time. Example: 2032-10-13

image   file  optional  

Must be an image. Must not be greater than 5120 kilobytes. Example: /tmp/phpuAA2QJ

parent_id   integer   

Account owner user id. Must belong to this school. Example: 10

Players - Show

requires authentication

Get a specific player with profile and associated schools, restricted to the given school. Requires permission: administrative.customer.player.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "player": {}
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "player not found or not associated with this school"
}
 

Request      

GET api/schools/{school_slug}/customer/players/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Player user ID. Example: 10

Players - Archive

requires authentication

Archive (soft-deactivate) a player for the given school. Record is not deleted. Also archives the profile if present. Requires permission: administrative.customer.player.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/players/10"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Player archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Player not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/customer/players/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug. Example: elite-tennis-academy

id   integer   

Player user ID to archive. Example: 10

Groups - List groups for a school

requires authentication

Returns training groups belonging to the given school. Supports filtering, sorting, pagination, and a compact "select" view.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups?view=select&per_page=15&only_archived=1&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=active&filters%5Bsearch%5D=elite&sort_by=name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "filters[0][column]": "status",
    "filters[0][operator]": "equals",
    "filters[0][value]": "active",
    "filters[search]": "elite",
    "sort_by": "name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "groups": {
        "current_page": 1,
        "data": [
            {
                "id": 10,
                "name": "Elite Training Group",
                "school_id": 2,
                "level_id": 3,
                "school_site_id": null,
                "creator_id": 5,
                "coach_id": 8,
                "starting_time": "15:00",
                "duration": 90,
                "coach_work_time": 60,
                "player_capacity": 12,
                "group_type": "group",
                "created_at": "2025-02-01T12:00:00.000000Z",
                "updated_at": "2025-02-01T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 30
    },
    "metadata": {
        "columns": [
            "id",
            "name",
            "coach_id",
            "player_capacity",
            "starting_time",
            "duration",
            "players"
        ],
        "defaultColumns": [
            "name",
            "starting_time",
            "duration",
            "player_capacity"
        ]
    }
}
 

Example response (200):


{
    "groups": [
        {
            "id": 1,
            "name": "Group A",
            "group_type": "group"
        },
        {
            "id": 2,
            "name": "Group B",
            "group_type": "private"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/customer/groups

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school whose groups you want to list. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. "table" returns a paginated table payload; "select" returns a minimal list. Default: table. Example: select

per_page   integer  optional  

optional Items per page for table view. Default: 10. Example: 15

only_archived   boolean  optional  

optional When true, returns only archived/inactive groups. Default: false. Example: true

filters   string[]  optional  

optional Dynamic filters. Each item includes column, operator, and value. Columns may use relation dot-notation (e.g., coach.name).

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

The filter operator. One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string  optional  

mixed The comparison value. Use an array for the between operator. Example: active

filters[search]   string  optional  

optional Global search applied across searchable columns. Example: elite

sort_by   string  optional  

optional Column to sort by (base table columns only). Example: name

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

Groups - Create a group in a school

requires authentication

Creates a new training group associated with the given school. Requires the permission "administrative.customer.group.store" and that the user belongs to the school.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Group A\",
    \"level_id\": 2,
    \"school_site_id\": 3,
    \"location_id\": 4,
    \"coach_id\": 5,
    \"days_of_training\": [
        \"consectetur\"
    ],
    \"starting_time\": \"15:30\",
    \"duration\": 90,
    \"coach_work_time\": 120,
    \"player_capacity\": 25,
    \"group_type\": \"group\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups"
);

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

let body = {
    "name": "Group A",
    "level_id": 2,
    "school_site_id": 3,
    "location_id": 4,
    "coach_id": 5,
    "days_of_training": [
        "consectetur"
    ],
    "starting_time": "15:30",
    "duration": 90,
    "coach_work_time": 120,
    "player_capacity": 25,
    "group_type": "group"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Group created successfully",
    "group": {
        "id": 10,
        "name": "Group A",
        "school_id": 2,
        "level_id": 3,
        "school_site_id": 1,
        "location_id": 4,
        "coach_id": 5,
        "starting_time": "15:30",
        "duration": 90,
        "coach_work_time": 120,
        "player_capacity": 25,
        "group_type": "group",
        "created_at": "2025-02-01T12:00:00.000000Z",
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "coach_id": [
            "The selected user is not a valid coach."
        ],
        "days_of_training.0": [
            "Each item must be an object with a single day as key."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/customer/groups

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school where the group will be created. Example: elite-tennis-academy

Body Parameters

name   string   

Group name. Example: Group A

level_id   integer   

Level ID. Must exist in levels table. Example: 2

school_site_id   integer   

School site ID. Must exist in school_sites table. Example: 3

location_id   integer  optional  

nullable Location ID. Must exist in locations table. Example: 4

coach_id   integer  optional  

nullable Coach user ID. Must exist in users table and have the role "coach". Example: 5

days_of_training   string[]   

Training schedule per day. Each item must be an object with a single day key and an object value containing startingTime, duration, and endingTime.

<day>   object   

Object with fields startingTime (H:i), duration (minutes), endingTime (H:i).

starting_time   string   

Base starting time in H:i format. Example: 15:30

duration   integer   

Base duration in minutes. Minimum: 1. Example: 90

coach_work_time   integer   

Coach work time in minutes. Minimum: 1. Example: 120

player_capacity   integer   

Maximum number of players. Minimum: 1. Example: 25

group_type   string   

Group type. Example: group

Groups - Show group details

requires authentication

Returns detailed information for a specific training group within the given school, including relations and assigned players. Requires the permission "administrative.customer.group.show".

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 10,
    "name": "Elite Training Group",
    "school": {
        "id": 2,
        "nickname": "Elite Academy"
    },
    "level": {
        "id": 3,
        "name": "Advanced"
    },
    "location": {
        "id": 4,
        "name": "Court 1"
    },
    "school_site": {
        "id": 1,
        "name": "Main Site"
    },
    "creator": {
        "id": 5,
        "name": "Admin User"
    },
    "coach": {
        "id": 8,
        "name": "John Doe"
    },
    "days_of_training": [
        {
            "Wednesday": {
                "startingTime": "15:30",
                "duration": 60,
                "endingTime": "16:30"
            }
        }
    ],
    "starting_time": "15:00",
    "duration": 90,
    "coach_work_time": 60,
    "player_capacity": 12,
    "group_type": "group",
    "players": [
        {
            "id": 20,
            "name": "Player One"
        },
        {
            "id": 21,
            "name": "Player Two"
        }
    ],
    "created_at": "2025-02-01T12:00:00.000000Z",
    "updated_at": "2025-02-01T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Group not found in this school"
}
 

Request      

GET api/schools/{school_slug}/customer/groups/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the group to retrieve. Example: 10

Groups - Update an existing group

requires authentication

Updates one or more fields of a training group within the given school. Requires the permission "administrative.customer.group.update" and that the user belongs to the school.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Advanced Group B\",
    \"level_id\": 2,
    \"school_site_id\": 3,
    \"location_id\": 4,
    \"coach_id\": 5,
    \"days_of_training\": [
        {
            \"Wednesday\": {
                \"startingTime\": \"15:30\",
                \"duration\": 60,
                \"endingTime\": \"16:30\"
            }
        }
    ],
    \"starting_time\": \"16:00\",
    \"duration\": 60,
    \"coach_work_time\": 90,
    \"player_capacity\": 20,
    \"group_type\": \"private\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10"
);

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

let body = {
    "name": "Advanced Group B",
    "level_id": 2,
    "school_site_id": 3,
    "location_id": 4,
    "coach_id": 5,
    "days_of_training": [
        {
            "Wednesday": {
                "startingTime": "15:30",
                "duration": 60,
                "endingTime": "16:30"
            }
        }
    ],
    "starting_time": "16:00",
    "duration": 60,
    "coach_work_time": 90,
    "player_capacity": 20,
    "group_type": "private"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Group updated successfully",
    "group": {
        "id": 10,
        "name": "Advanced Group B",
        "school_id": 2,
        "level_id": 2,
        "school_site_id": 3,
        "location_id": 4,
        "coach_id": 5,
        "starting_time": "16:00",
        "duration": 60,
        "coach_work_time": 90,
        "player_capacity": 20,
        "group_type": "private",
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (403):


{
    "message": "Group does not belong to this school"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "coach_id": [
            "The selected user is not a valid coach."
        ],
        "days_of_training.0": [
            "Each item must be an object with a single day as key."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/customer/groups/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the group to update. Example: 10

Body Parameters

name   string  optional  

optional Group name. Example: Advanced Group B

level_id   integer  optional  

optional Level ID. Must exist in levels table. Example: 2

school_site_id   integer  optional  

optional School site ID. Must exist in school_sites table. Example: 3

location_id   integer  optional  

optional Location ID. Must exist in locations table. Example: 4

coach_id   integer  optional  

optional Coach user ID. Must exist in users table and have the role "coach". Example: 5

days_of_training   string[]  optional  

optional Training schedule per day. Each item must be an object with a single day key and an object value containing startingTime, duration, and endingTime.

starting_time   string  optional  

optional Base starting time in H:i format. Example: 16:00

duration   integer  optional  

optional Base duration in minutes. Minimum: 1. Example: 60

coach_work_time   integer  optional  

optional Coach work time in minutes. Minimum: 1. Example: 90

player_capacity   integer  optional  

optional Maximum number of players. Minimum: 1. Example: 20

group_type   string  optional  

optional Group type. Example: private

Groups - Archive (delete) a group

requires authentication

Marks the specified group as archived (soft delete) within the given school. Archived groups are excluded from default queries.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Group archived successfully"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Group not found in this school"
}
 

Request      

DELETE api/schools/{school_slug}/customer/groups/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the group to archive. Example: 10

Groups - Assign players to a group

requires authentication

Assigns one or more players to a training group in the specified school. Requires the permission "administrative.customer.group.assign". Validates that the group belongs to the school and that each ID belongs to a user with the "player" role. Existing assignments are preserved.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10/assign-players" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"player_ids\": [
        20,
        21,
        22
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10/assign-players"
);

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

let body = {
    "player_ids": [
        20,
        21,
        22
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Players assigned successfully",
    "assigned_players": [
        20,
        21
    ]
}
 

Example response (400):


{
    "message": "Invalid or missing player IDs"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School or Group not found"
}
 

Request      

POST api/schools/{school_slug}/customer/groups/{id}/assign-players

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the group to assign players to. Example: 10

Body Parameters

player_ids   string[]   

List of player IDs to assign. Must be a non-empty array of integers.

Groups - Remove players from a group

requires authentication

Removes one or more players from a training group in the specified school. Requires the permission "administrative.customer.group.assign". If no player IDs are provided or the array is empty, all players will be removed from the group. Only users with the role "player" are considered; invalid IDs are ignored. If none are valid, a 400 error is returned.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10/remove-players" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"player_ids\": [
        20,
        21
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/customer/groups/10/remove-players"
);

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

let body = {
    "player_ids": [
        20,
        21
    ]
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Players removed successfully",
    "removed_players": [
        20,
        21
    ]
}
 

Example response (200):


{
    "message": "All players removed from the group"
}
 

Example response (400):


{
    "message": "Invalid input format"
}
 

Example response (400):


{
    "message": "No valid players found"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School or Group not found"
}
 

Request      

DELETE api/schools/{school_slug}/customer/groups/{id}/remove-players

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the group to remove players from. Example: 10

Body Parameters

player_ids   string[]  optional  

optional List of player IDs to remove. If omitted or an empty array, the group will be cleared.

Ecommerce

Form - Get picklist options for the subscription registration form.

Returns a list of selectable options for a specific form field used in the user subscription flow. Options are loaded from a centralized JSON file and mapped directly by field name. No authentication required.

Supported field names include:

Each field returns an array of options with label and value keys.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/picklist/gender" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/picklist/gender"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "label": "Male",
        "value": "Male"
    },
    {
        "label": "Female",
        "value": "Female"
    }
]
 

Example response (200, unknown field):


[]
Returned if the field key is not found in the picklist JSON.
 

Example response (404):


{
    "error": "Picklist file not found."
}
 

Request      

GET api/schools/{school_slug}/ecommerce/user-subscription-form/picklist/{field}

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

field   string   

The field to retrieve options for. Example: gender

Form - Check contact status (email and/or phone)

Checks whether a contact exists by email and/or phone and guides the user on next steps. If both are provided and they belong to different users, returns a mismatch. If only the phone matches, an email hint is returned (e.g., jz@do.c*).

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/check-email-status?email=user%40example.com&phone=%28786%29+555-1234&phone_code=57" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/check-email-status"
);

const params = {
    "email": "user@example.com",
    "phone": "(786) 555-1234",
    "phone_code": "57",
};
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());

Example response (200, account in school):


{
    "status": "account",
    "message": "User already exist with that email, please choose one of the following options to access your account"
}
 

Example response (200, player in school):


{
    "status": "player",
    "message": "This contact belongs to a player in this school. Please contact the account owner.",
    "account_contact": "owner@example.com"
}
 

Example response (200, lead in school):


{
    "status": "lead",
    "message": "This contact is registered as a lead. Please continue your registration.",
    "step_status": "step_1"
}
 

Example response (200, available existing user (not in school)):


{
    "status": "available_existing_user",
    "message": "Phone number already exist, but with different email"
}
 

Example response (200, available existing user (phone-only match in school)):


{
    "status": "available_existing_user",
    "message": "Phone number already exist, but with different email",
    "email_hint": "j***z@d***o.c*",
    "can_login_with": "phone"
}
 

Example response (200, mismatch (email and phone belong to different users)):


{
    "status": "mismatch",
    "message": "The given email and phone belong to different accounts.",
    "email_hint": "j***z@d***o.c*"
}
 

Example response (200, available new user):


{
    "status": "available_new_user",
    "message": "This contact information is available for registration."
}
 

Example response (200, in use by other role in school):


{
    "status": "in_use_other_role",
    "message": "This contact is already in use in this school.",
    "roles": [
        "coach",
        "staff"
    ]
}
 

Example response (422):


{
    "status": "error",
    "message": "Provide at least one of: email or phone."
}
 

Request      

GET api/schools/{school_slug}/ecommerce/user-subscription-form/check-email-status

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

email   string  optional  

required-without:phone The email to check (case-insensitive). Example: user@example.com

phone   string  optional  

required-without:email The phone to check (will be normalized to E.164). Example: (786) 555-1234

phone_code   string  optional  

required-with:phone Country calling code (digits only, no +). Example: 57

Form - Step 1: Register a new lead with minimal data

Creates a new user with the "lead" role and an initial profile, then links it to the given school. No authentication required. The email must be unique across users.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"user@example.com\",
    \"first_name\": \"John\",
    \"last_name\": \"Doe\",
    \"registration_type\": \"Kid\",
    \"interested_in\": \"Camp\",
    \"how_did_you_hear_about_us\": \"Friend Referral\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-1"
);

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

let body = {
    "email": "user@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "registration_type": "Kid",
    "interested_in": "Camp",
    "how_did_you_hear_about_us": "Friend Referral"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Lead created successfully.",
    "user_id": 24
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email has already been taken."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/user-subscription-form/step-1

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

email   string   

Valid and unique email. Example: user@example.com

first_name   string   

First name of the lead. Example: John

last_name   string   

Last name of the lead. Example: Doe

registration_type   string  optional  

optional Registration type (e.g., Kid or Adult). Example: Kid

interested_in   string  optional  

optional Area of interest. Example: Camp

how_did_you_hear_about_us   string  optional  

optional Marketing source. Example: Friend Referral

Form - Step 2: Primary and secondary contact information + password

Updates the lead's contact and billing information, requires setting a password, logs the user in, sends a welcome notification, and returns an auth token. Phone is normalized to E.164 using the provided country code. If the user is not linked to the school yet, it will be attached. No authentication required.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-2" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"lead@example.com\",
    \"phone_code\": \"57\",
    \"billing_phone_number\": \"(786) 555-1234\",
    \"billing_address\": \"123 Main St\",
    \"billing_address_2\": \"Apt 4B\",
    \"billing_city\": \"Miami\",
    \"billing_state\": \"FL\",
    \"billing_zip_code\": \"33101\",
    \"billing_country\": \"USA\",
    \"secondary_contact_first_name\": \"Maria\",
    \"secondary_contact_last_name\": \"Lopez\",
    \"secondary_phone_number\": \"+1987654321\",
    \"secondary_email\": \"maria.lopez@example.com\",
    \"password\": \"secret123\",
    \"password_confirmation\": \"secret123\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-2"
);

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

let body = {
    "email": "lead@example.com",
    "phone_code": "57",
    "billing_phone_number": "(786) 555-1234",
    "billing_address": "123 Main St",
    "billing_address_2": "Apt 4B",
    "billing_city": "Miami",
    "billing_state": "FL",
    "billing_zip_code": "33101",
    "billing_country": "USA",
    "secondary_contact_first_name": "Maria",
    "secondary_contact_last_name": "Lopez",
    "secondary_phone_number": "+1987654321",
    "secondary_email": "maria.lopez@example.com",
    "password": "secret123",
    "password_confirmation": "secret123"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Step 2 information saved successfully.",
    "profile": {
        "id": 10,
        "billing_phone_number": "+577865551234",
        "billing_address": "123 Main St",
        "billing_city": "Miami",
        "billing_state": "FL",
        "billing_zip_code": "33101",
        "billing_country": "USA",
        "step_status": "step_2"
    },
    "token": "1|eyJ0eXAiOiJKV1QiLCJhbGciOi..."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The selected email is invalid."
        ],
        "password": [
            "The password confirmation does not match."
        ],
        "billing_phone_number": [
            "The billing phone number field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/user-subscription-form/step-2

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

email   string   

Existing user email (must already exist). Example: lead@example.com

phone_code   string   

Country calling code (digits only, no +). Example: 57

billing_phone_number   string   

Primary phone (any format; will be normalized). Example: (786) 555-1234

billing_address   string   

Billing address. Example: 123 Main St

billing_address_2   string  optional  

optional Additional address information. Example: Apt 4B

billing_city   string   

City. Example: Miami

billing_state   string   

State/Province. Example: FL

billing_zip_code   string   

Postal/ZIP code. Example: 33101

billing_country   string   

Country. Example: USA

secondary_contact_first_name   string  optional  

optional Secondary contact first name. Example: Maria

secondary_contact_last_name   string  optional  

optional Secondary contact last name. Example: Lopez

secondary_phone_number   string  optional  

optional Secondary contact phone. Example: +1987654321

secondary_email   string  optional  

optional Secondary contact email. Example: maria.lopez@example.com

password   string   

Account password (min 6 chars). Example: secret123

password_confirmation   string   

Must match password. Example: secret123

Form - Step 3: Register one or more players under the authenticated account

requires authentication

Allows an authenticated account to create one or more player profiles (e.g., children). Players are created without email, assigned the "player" role, linked to the authenticated account as parent, and attached to the given school.

Permissions: customer.ecommerce.player.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-3" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"players\": [
        \"fugit\"
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-3"
);

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

let body = {
    "players": [
        "fugit"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Players registered successfully.",
    "player_ids": [
        32,
        33
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "players": [
            "The players field is required."
        ],
        "players.0.first_name": [
            "The players.0.first name field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/user-subscription-form/step-3

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

Slug of the school. Example: elite-tennis-academy

Body Parameters

players   string[]   

List of players to register.

first_name   string   

First name. Example: Emma

last_name   string   

Last name. Example: Johnson

date_birth   date   

Birthdate (YYYY-MM-DD). Example: 2015-06-01

gender   string   

Gender. Example: Female

level_of_experience   string   

Experience level. Example: Beginner

has_medical_condition   boolean   

Whether the player has medical conditions. Example: false

medical_condition_details   string  optional  

optional Details if has_medical_condition is true. Example: Asthma

Form - Step 4: Finalize player registration and create subscriptions

requires authentication

Updates player profiles, creates a pending subscription per player for the selected service, optionally applies per-player coupons, and generates a single order for all items. If the grand total is zero, the order is marked as paid and related subscriptions are activated with auto_renew=false.

Permissions: customer.ecommerce.subscription.store. Players must belong to the authenticated account.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-4" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"players\": [
        \"deleniti\"
    ],
    \"accept_terms\": true
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/user-subscription-form/step-4"
);

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

let body = {
    "players": [
        "deleniti"
    ],
    "accept_terms": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Training information and subscriptions saved successfully.",
    "updated_players": [
        1,
        2
    ],
    "order_id": 25,
    "total_amount": 150,
    "details": [
        {
            "player_id": 1,
            "player_name": "John Doe",
            "main_service": "Soccer 2x Weekly",
            "main_service_price": 120,
            "coupon_applied": "WELCOME10",
            "discount": 12
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Not Found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "players": [
            "The players field is required."
        ],
        "players.0.service_id": [
            "The players.0.service id field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/user-subscription-form/step-4

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

Slug of the school. Example: elite-tennis-academy

Body Parameters

players   string[]   

List of player payloads to finalize.

player_id   integer   

ID of the player (must be a child of the authenticated account). Example: 32

school_grade   string  optional  

optional School grade. Example: 4th

school_classroom   string  optional  

optional Classroom. Example: A

dismissal_type   string  optional  

optional Dismissal type. Example: Standard

t_shirt_size   string  optional  

optional T-shirt size. Example: M

service_id   integer   

ID of the selected main service (must belong to the school). Example: 123

training_location   string  optional  

optional Training location. Example: Court 1

coupon_code   string  optional  

optional Coupon code to apply for this player. Example: WELCOME10

accept_terms   boolean   

Whether the user accepts the terms and conditions. Example: true

Service Categories - List

requires authentication

List service categories for a school. Supports dynamic filters, sorting, and two response modes (table and select). Requires permission: administrative.ecommerce.service_category.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories?view=select&per_page=15&sort_by=nickname&direction=desc&filters[]=inventore&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=active" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories"
);

const params = {
    "view": "select",
    "per_page": "15",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "inventore",
    "filters[0][column]": "status",
    "filters[0][operator]": "equals",
    "filters[0][value]": "active",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "categories": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Beginner Programs",
                "description": "Entry-level tennis training",
                "status": "active"
            }
        ],
        "per_page": 10,
        "total": 1
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "status"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "status"
        ]
    }
}
 

Example response (200):


{"categories": [{"id": 1, "nickname": "Beginner Programs"}]}
When view=select, returns a flat list of categories with id and nickname.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/service-categories

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (table only). Default: 10. Example: 15

sort_by   string  optional  

optional Sort column (applied when using dynamic filters). Example: nickname

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

Example: status

filters[0][operator]   string  optional  

Options: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string  optional  

mixed Use [min,max] for between; ignored for empty/not_empty. Example: active

Service Categories - Create

requires authentication

Create a new service category for a school. Requires permission: administrative.ecommerce.service_category.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Memberships\",
    \"description\": \"Yearly payment required for all users.\",
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories"
);

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

let body = {
    "nickname": "Memberships",
    "description": "Yearly payment required for all users.",
    "status": "active"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Category created successfully",
    "category": {
        "id": 5,
        "nickname": "Memberships",
        "description": "Yearly payment required for all users.",
        "status": "active",
        "school_id": 3,
        "creator_id": 7
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/service-categories

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

nickname   string   

The nickname of the category. Example: Memberships

description   string  optional  

optional A short description of the category. Example: Yearly payment required for all users.

status   string  optional  

optional Status of the category. One of: active, inactive, archived. Defaults to active. Example: active

Service Categories - Show

requires authentication

Retrieve a specific service category for a school, including its creator. Requires permission: administrative.ecommerce.service_category.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories/5"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 5,
    "nickname": "Memberships",
    "description": "Yearly payment required for all users.",
    "status": "active",
    "school_id": 3,
    "creator_id": 7,
    "creator": {
        "id": 7,
        "name": "Admin User"
    },
    "created_at": "2025-06-09T14:00:00.000000Z",
    "updated_at": "2025-06-09T14:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Service Category not found"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/service-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the service category to retrieve. Example: 5

Service Categories - Update

requires authentication

Update an existing service category for a school. Requires permission: administrative.ecommerce.service_category.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories/3" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Premium Services\",
    \"description\": \"Includes advanced and personalized plans.\",
    \"status\": \"inactive\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories/3"
);

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

let body = {
    "nickname": "Premium Services",
    "description": "Includes advanced and personalized plans.",
    "status": "inactive"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Service category updated successfully",
    "category": {
        "id": 3,
        "nickname": "Premium Services",
        "description": "Includes advanced and personalized plans.",
        "status": "inactive",
        "school_id": 5,
        "creator_id": 7
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Service category not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/service-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the service category. Example: 3

Body Parameters

nickname   string  optional  

optional The new name for the category. Example: Premium Services

description   string  optional  

optional A description for the category. Example: Includes advanced and personalized plans.

status   string  optional  

optional One of: active, inactive, archived. Example: inactive

Service Categories - Archive

requires authentication

Archive a service category (status set to "archived"). Does not delete the record. Requires permission: administrative.ecommerce.service_category.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories/7" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/service-categories/7"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Service category archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Service category not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/ecommerce/service-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the service category. Example: 7

Services - List

requires authentication

List services for a school. Supports dynamic filters, sorting, and two response modes (table and select). Requires permission: administrative.ecommerce.service.index OR customer.ecommerce.subscription.store

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services?view=select&per_page=15&sort_by=nickname&direction=desc&filters[]=ipsa&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=active" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services"
);

const params = {
    "view": "select",
    "per_page": "15",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "ipsa",
    "filters[0][column]": "status",
    "filters[0][operator]": "equals",
    "filters[0][value]": "active",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "services": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Basic Plan",
                "frequency": "weekly",
                "sessions": 8,
                "status": "active",
                "regular_fee": "300.00"
            }
        ],
        "per_page": 10,
        "total": 1
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "type",
            "frequency",
            "sessions",
            "status",
            "regular_fee"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "status"
        ]
    }
}
 

Example response (200):


{"services": [{"id": 1, "nickname": "Basic Plan", "frequency": "weekly"}]}
When view=select, returns a flat list of services with id, nickname, and frequency.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/services

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (table only). Default: 10. Example: 15

sort_by   string  optional  

optional Sort column (applied when using dynamic filters). Example: nickname

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

Example: status

filters[0][operator]   string  optional  

Options: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string  optional  

mixed Use [min,max] for between; ignored for empty/not_empty. Example: active

Services - Create

requires authentication

Create a new service for a school. Requires permission: administrative.ecommerce.service.store Services include frequency, billing, pricing options, and belong to an optional category/site.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Private Lessons\",
    \"description\": \"One-on-one personalized training.\",
    \"frequency\": \"weekly\",
    \"type\": \"private\",
    \"sessions\": 12,
    \"session_duration\": 60,
    \"billing_option\": \"perspiciatis\",
    \"billing_limit\": 3,
    \"group_membership_limit\": 2,
    \"regular_fee\": 300000,
    \"discounted_fee\": 250000,
    \"currency\": \"USD\",
    \"features\": [
        \"Racket included\",
        \"Gym access\"
    ],
    \"status\": \"active\",
    \"school_site_id\": 2,
    \"category_id\": 4
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services"
);

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

let body = {
    "nickname": "Private Lessons",
    "description": "One-on-one personalized training.",
    "frequency": "weekly",
    "type": "private",
    "sessions": 12,
    "session_duration": 60,
    "billing_option": "perspiciatis",
    "billing_limit": 3,
    "group_membership_limit": 2,
    "regular_fee": 300000,
    "discounted_fee": 250000,
    "currency": "USD",
    "features": [
        "Racket included",
        "Gym access"
    ],
    "status": "active",
    "school_site_id": 2,
    "category_id": 4
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Service created successfully",
    "service": {
        "id": 15,
        "nickname": "Private Lessons",
        "description": "One-on-one personalized training.",
        "frequency": "weekly",
        "type": "private",
        "sessions": 12,
        "session_duration": 60,
        "group_membership_limit": 2,
        "billing_limit": 3,
        "regular_fee": "300000.00",
        "discounted_fee": "250000.00",
        "currency": "USD",
        "features": [
            "Racket included",
            "Gym access"
        ],
        "status": "active",
        "school_id": 3,
        "school_site_id": 2,
        "category_id": 4,
        "creator_id": 7,
        "school": {
            "id": 3,
            "name": "Elite Tennis Academy"
        },
        "school_site": {
            "id": 2,
            "name": "North Site"
        },
        "category": {
            "id": 4,
            "nickname": "Private"
        },
        "creator": {
            "id": 7,
            "name": "Admin User"
        }
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "frequency": [
            "The frequency field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/services

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

nickname   string   

Short name of the service. Example: Private Lessons

description   string  optional  

optional Full description of the service. Example: One-on-one personalized training.

frequency   string   

Frequency type. One of: daily, yearly, monthly, weekly, one-time. Example: weekly

type   string   

Service type. One of: group, private, semi-private. Example: private

sessions   integer   

Number of sessions included. Example: 12

session_duration   integer  optional  

optional Duration of each session in minutes. Example: 60

billing_option   string  optional  

Example: perspiciatis

billing_limit   integer  optional  

optional Number of billing cycles allowed. Example: 3

group_membership_limit   integer  optional  

optional Max number of groups a player can belong to under this service. Example: 2

regular_fee   number   

Base price of the service. Example: 300000

discounted_fee   number  optional  

optional Discounted price. Example: 250000

currency   string   

Currency code (ISO). Example: USD

features   string[]  optional  

optional Additional features. Defaults to [].

status   string  optional  

optional Service status. One of: active, inactive, archived. Defaults to active. Example: active

school_site_id   integer  optional  

optional ID of the site where the service is offered. Example: 2

category_id   integer  optional  

optional ID of the associated service category. Example: 4

Services - Show

requires authentication

Retrieve a specific service for a school, including relations (school, school_site, category, creator). Requires permission: administrative.ecommerce.service.show OR customer.ecommerce.subscription.store

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services/5"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "service": {
        "id": 5,
        "nickname": "Private Lessons",
        "description": "One-on-one personalized training.",
        "frequency": "weekly",
        "type": "private",
        "sessions": 12,
        "billing_limit": 3,
        "regular_fee": "300000.00",
        "discounted_fee": "250000.00",
        "currency": "USD",
        "features": [
            "Racket included",
            "Gym access"
        ],
        "status": "active",
        "school": {
            "id": 3,
            "name": "Elite Tennis Academy"
        },
        "school_site": {
            "id": 2,
            "name": "North Site"
        },
        "category": {
            "id": 4,
            "nickname": "Private"
        },
        "creator": {
            "id": 7,
            "name": "Admin User"
        }
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Service not found or unauthorized."
}
 

Request      

GET api/schools/{school_slug}/ecommerce/services/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the service to retrieve. Example: 5

Services - Update

requires authentication

Update an existing service for a school. Requires permission: administrative.ecommerce.service.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services/15" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Private Lessons\",
    \"description\": \"One-on-one training with a professional coach.\",
    \"frequency\": \"weekly\",
    \"type\": \"private\",
    \"sessions\": 12,
    \"session_duration\": 60,
    \"billing_limit\": 3,
    \"group_membership_limit\": 2,
    \"regular_fee\": 300000,
    \"discounted_fee\": 250000,
    \"currency\": \"USD\",
    \"features\": [
        \"Pool access\",
        \"Gym access\"
    ],
    \"status\": \"active\",
    \"school_site_id\": 2,
    \"category_id\": 4
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services/15"
);

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

let body = {
    "nickname": "Private Lessons",
    "description": "One-on-one training with a professional coach.",
    "frequency": "weekly",
    "type": "private",
    "sessions": 12,
    "session_duration": 60,
    "billing_limit": 3,
    "group_membership_limit": 2,
    "regular_fee": 300000,
    "discounted_fee": 250000,
    "currency": "USD",
    "features": [
        "Pool access",
        "Gym access"
    ],
    "status": "active",
    "school_site_id": 2,
    "category_id": 4
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Service updated successfully",
    "service": {
        "id": 15,
        "nickname": "Private Lessons",
        "description": "One-on-one training with a professional coach.",
        "frequency": "weekly",
        "type": "private",
        "sessions": 12,
        "session_duration": 60,
        "group_membership_limit": 2,
        "billing_limit": 3,
        "regular_fee": "300000.00",
        "discounted_fee": "250000.00",
        "currency": "USD",
        "features": [
            "Pool access",
            "Gym access"
        ],
        "status": "active",
        "school_id": 3,
        "school_site_id": 2,
        "category_id": 4,
        "creator_id": 7,
        "school": {
            "id": 3,
            "name": "Elite Tennis Academy"
        },
        "school_site": {
            "id": 2,
            "name": "North Site"
        },
        "category": {
            "id": 4,
            "nickname": "Private"
        },
        "creator": {
            "id": 7,
            "name": "Admin User"
        }
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Service not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "frequency": [
            "The frequency field is required."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/services/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the service to update. Example: 15

Body Parameters

nickname   string   

Short name of the service. Example: Private Lessons

description   string  optional  

optional Full description of the service. Example: One-on-one training with a professional coach.

frequency   string   

Frequency type. One of: daily, yearly, monthly, weekly, one-time. Example: weekly

type   string   

Service type. One of: group, private, semi-private. Example: private

sessions   integer   

Number of sessions included. Example: 12

session_duration   integer  optional  

optional Duration of each session in minutes. Example: 60

billing_limit   integer  optional  

optional Number of billing cycles allowed. Example: 3

group_membership_limit   integer  optional  

optional Max number of groups a player can belong to under this service. Example: 2

regular_fee   number   

Base price of the service. Example: 300000

discounted_fee   number  optional  

optional Discounted price. Example: 250000

currency   string   

Currency code (ISO). Example: USD

features   string[]  optional  

optional Additional features. Defaults to [].

status   string  optional  

optional Service status. One of: active, inactive, archived. Defaults to active when omitted. Example: active

school_site_id   integer  optional  

optional ID of the site where the service is offered. Example: 2

category_id   integer  optional  

optional ID of the associated service category. Example: 4

Services - Archive

requires authentication

Archive a service (status set to "archived"). Does not delete the record. Requires permission: administrative.ecommerce.service.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services/9" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/services/9"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Service archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Service not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/ecommerce/services/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the service. Example: 9

Subscriptions - List

requires authentication

List subscriptions for a school. Supports dynamic filters, sorting, and two response modes (table and select). Requires permission: administrative.ecommerce.subscription.index OR customer.ecommerce.subscription.index

Notes:

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions?view=select&per_page=15&sort_by=start_date&direction=desc&account_id=42&filters[]=voluptas&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=active" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions"
);

const params = {
    "view": "select",
    "per_page": "15",
    "sort_by": "start_date",
    "direction": "desc",
    "account_id": "42",
    "filters[0]": "voluptas",
    "filters[0][column]": "status",
    "filters[0][operator]": "equals",
    "filters[0][value]": "active",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "subscriptions": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Monthly - Tennis",
                "start_date": "2025-07-01T00:00:00.000000Z",
                "next_renewal_date": "2025-08-01T00:00:00.000000Z",
                "status": "active",
                "auto_renew": true,
                "payment_method": "card",
                "player": {
                    "id": 4,
                    "name": "John Doe"
                },
                "service": {
                    "id": 2,
                    "name": "Tennis Classes"
                }
            }
        ],
        "per_page": 10,
        "total": 1
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "status",
            "start_date",
            "next_renewal_date",
            "auto_renew",
            "payment_method",
            "player",
            "service"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "status",
            "start_date"
        ]
    }
}
 

Example response (200):


{"subscriptions": [{"id": 1, "nickname": "Monthly - Tennis"}]}
When view=select, returns a flat list of subscriptions with id and nickname.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/subscriptions

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (table only). Default: 10. Example: 15

sort_by   string  optional  

optional Sort column (applied when using dynamic filters). Example: start_date

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

account_id   integer  optional  

optional (Admin only) Account owner (filters by player.parent_id). Example: 42

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

Example: status

filters[0][operator]   string  optional  

Options: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string  optional  

mixed Use [min,max] for between; ignored for empty/not_empty. Example: active

Subscriptions - Create

requires authentication

Create a new subscription for a school. Requires permission: administrative.ecommerce.subscription.create

Behavior:

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"service_id\": 5,
    \"player_id\": 12,
    \"start_date\": \"2025-08-22\",
    \"next_renewal_date\": \"2025-09-22\",
    \"last_payment_date\": \"2025-08-15\",
    \"auto_renew\": true,
    \"has_pending_balance\": false,
    \"is_paused\": false,
    \"paused_at\": \"2025-08-20\",
    \"resume_at\": \"2025-09-01\",
    \"pause_reason\": \"Vacation pause request\",
    \"metadata\": {
        \"notes\": \"Created via admin panel\"
    },
    \"status\": \"active\\n\\nOrder generation (when status==pending):\",
    \"account_user_id\": 99,
    \"coupon_code\": \"WELCOME10\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions"
);

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

let body = {
    "service_id": 5,
    "player_id": 12,
    "start_date": "2025-08-22",
    "next_renewal_date": "2025-09-22",
    "last_payment_date": "2025-08-15",
    "auto_renew": true,
    "has_pending_balance": false,
    "is_paused": false,
    "paused_at": "2025-08-20",
    "resume_at": "2025-09-01",
    "pause_reason": "Vacation pause request",
    "metadata": {
        "notes": "Created via admin panel"
    },
    "status": "active\n\nOrder generation (when status==pending):",
    "account_user_id": 99,
    "coupon_code": "WELCOME10"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Subscription created successfully (order generated)",
    "subscription": {
        "id": 45,
        "nickname": "John Doe - Monthly Tennis",
        "service_id": 5,
        "player_id": 12,
        "school_id": 1,
        "start_date": "2025-08-22T00:00:00.000000Z",
        "next_renewal_date": "2025-09-22T00:00:00.000000Z",
        "status": "pending",
        "auto_renew": true,
        "has_pending_balance": false,
        "is_paused": false,
        "player": {
            "id": 12,
            "name": "John Doe"
        },
        "service": {
            "id": 5,
            "nickname": "Monthly Tennis"
        }
    },
    "order": {
        "id": 102,
        "status": "pending_payment",
        "total_amount": 120
    },
    "redemption": {
        "discount_applied": 12,
        "currency": "USD",
        "redeemed_at": "2025-08-20T10:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "service_id": [
            "Please select a service for this subscription."
        ],
        "player_id": [
            "Please select a player for this subscription."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/subscriptions

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

service_id   integer   

Service ID. Example: 5

player_id   integer   

Player ID (must have the 'player' role). Example: 12

coupon_id   string  optional  

The id of an existing record in the coupons table.

start_date   date  optional  

optional Start date (cannot be in the past). Recommended for recurring services. Format: YYYY-MM-DD. Example: 2025-08-22

next_renewal_date   date  optional  

optional Next renewal date (must be after start_date). If omitted and the service is recurring, it will be auto-calculated from start_date. Format: YYYY-MM-DD. Example: 2025-09-22

last_payment_date   date  optional  

optional Last payment date (cannot be in the future). Format: YYYY-MM-DD. Example: 2025-08-15

auto_renew   boolean  optional  

optional Whether the subscription auto-renews. Default: true. Example: true

has_pending_balance   boolean  optional  

optional Whether there's a pending balance. Default: false. Example: false

is_paused   boolean  optional  

optional Whether the subscription is paused. Default: false. Example: false

paused_at   date  optional  

optional Required if is_paused is true. Format: YYYY-MM-DD. Example: 2025-08-20

resume_at   date  optional  

optional Required if is_paused is true (must be after paused_at). Format: YYYY-MM-DD. Example: 2025-09-01

pause_reason   string  optional  

optional Required if is_paused is true. Max 500 chars. Example: Vacation pause request

metadata   object  optional  

optional Additional metadata as JSON object.

status   string   

Subscription status. One of: active, inactive, paused, canceled, expired. Default: active. Example: `active

Order generation (when status==pending):`

account_user_id   integer  optional  

optional Payer account user ID. Defaults to player's parent_id; falls back to player_id when missing. Example: 99

coupon_code   string  optional  

optional Coupon code to apply to the first charge. Example: WELCOME10

Subscriptions - Show

requires authentication

Retrieve a specific subscription with related data (player, service, coupon, school, payments). Requires permission: administrative.ecommerce.subscription.show OR customer.ecommerce.subscription.show

Notes:

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/45" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/45"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "subscription": {
        "id": 45,
        "nickname": "John Doe - Monthly Tennis",
        "service_id": 5,
        "player_id": 12,
        "school_id": 1,
        "coupon_id": null,
        "start_date": "2025-08-22T00:00:00.000000Z",
        "next_renewal_date": "2025-09-22T00:00:00.000000Z",
        "last_payment_date": null,
        "auto_renew": true,
        "has_pending_balance": false,
        "is_paused": false,
        "paused_at": null,
        "resume_at": null,
        "pause_reason": null,
        "metadata": {
            "notes": "Subscription created via admin panel"
        },
        "status": "active",
        "player": {
            "id": 12,
            "name": "John Doe",
            "email": "john.doe@example.com"
        },
        "service": {
            "id": 5,
            "nickname": "Monthly Tennis",
            "description": "Tennis classes for all levels",
            "frequency": "monthly",
            "type": "group",
            "regular_fee": 150,
            "currency": "USD"
        },
        "coupon": null,
        "school": {
            "id": 1,
            "nickname": "Elite Tennis Academy",
            "slug": "elite-tennis-academy"
        },
        "payments": [
            {
                "id": 999,
                "amount": 150,
                "currency": "USD",
                "paid_at": "2025-08-01T12:00:00.000000Z"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Subscription not found"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/subscriptions/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

Subscription ID. Example: 45

Subscriptions - Update

requires authentication

Update a subscription's details. Requires permission: administrative.ecommerce.subscription.update

Notes:

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/45" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"service_id\": 8,
    \"player_id\": 12,
    \"coupon_id\": 3,
    \"start_date\": \"2025-08-22\",
    \"next_renewal_date\": \"2025-09-22\",
    \"last_payment_date\": \"2025-08-15\",
    \"auto_renew\": true,
    \"has_pending_balance\": false,
    \"is_paused\": false,
    \"paused_at\": \"2025-08-20\",
    \"resume_at\": \"2025-09-01\",
    \"pause_reason\": \"Vacation pause request\",
    \"metadata\": {
        \"notes\": \"Updated via admin panel\"
    },
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/45"
);

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

let body = {
    "service_id": 8,
    "player_id": 12,
    "coupon_id": 3,
    "start_date": "2025-08-22",
    "next_renewal_date": "2025-09-22",
    "last_payment_date": "2025-08-15",
    "auto_renew": true,
    "has_pending_balance": false,
    "is_paused": false,
    "paused_at": "2025-08-20",
    "resume_at": "2025-09-01",
    "pause_reason": "Vacation pause request",
    "metadata": {
        "notes": "Updated via admin panel"
    },
    "status": "active"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Subscription updated successfully",
    "subscription": {
        "id": 45,
        "nickname": "John Doe - Advanced Tennis",
        "service_id": 8,
        "player_id": 12,
        "school_id": 1,
        "start_date": "2025-08-22T00:00:00.000000Z",
        "next_renewal_date": "2025-09-22T00:00:00.000000Z",
        "status": "active",
        "auto_renew": true,
        "player": {
            "id": 12,
            "name": "John Doe"
        },
        "service": {
            "id": 8,
            "nickname": "Advanced Tennis"
        }
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Subscription not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "service_id": [
            "The selected service does not exist."
        ],
        "player_id": [
            "The selected user is not a valid player."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/subscriptions/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

Subscription ID. Example: 45

Body Parameters

service_id   integer  optional  

The service ID. Example: 8

player_id   integer  optional  

The player ID (must have the 'player' role). Example: 12

coupon_id   integer  optional  

optional Coupon to associate. Example: 3

start_date   date  optional  

optional Start date (cannot be in the past). Format: YYYY-MM-DD. Example: 2025-08-22

next_renewal_date   date  optional  

optional Next renewal date (must be after start_date). If omitted, may be auto-calculated. Format: YYYY-MM-DD. Example: 2025-09-22

last_payment_date   date  optional  

optional Last payment date (cannot be in the future). Format: YYYY-MM-DD. Example: 2025-08-15

auto_renew   boolean  optional  

optional Whether the subscription auto-renews. Example: true

has_pending_balance   boolean  optional  

optional Whether there's a pending balance. Example: false

is_paused   boolean  optional  

optional Whether the subscription is paused. Example: false

paused_at   date  optional  

optional Required if is_paused is true. Format: YYYY-MM-DD. Example: 2025-08-20

resume_at   date  optional  

optional Required if is_paused is true (after paused_at). Format: YYYY-MM-DD. Example: 2025-09-01

pause_reason   string  optional  

optional Required if is_paused is true. Max 500 chars. Example: Vacation pause request

metadata   object  optional  

optional Additional metadata as JSON object.

status   string  optional  

optional Subscription status. One of: active, inactive, paused, canceled, expired. Example: active

Subscriptions - Pause

requires authentication

Pause a subscription. Requires permission: administrative.ecommerce.subscription.update

Notes:

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/12/pause" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"resume_at\": \"2025-09-01\",
    \"pause_reason\": \"Vacation pause request\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/12/pause"
);

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

let body = {
    "resume_at": "2025-09-01",
    "pause_reason": "Vacation pause request"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Subscription paused successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Subscription not found"
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/subscriptions/{id}/pause

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

Subscription ID. Example: 12

Body Parameters

resume_at   date  optional  

optional Date when the subscription should automatically resume. Format: YYYY-MM-DD. Example: 2025-09-01

pause_reason   string  optional  

optional Reason for pausing the subscription. Example: Vacation pause request

Subscriptions - Resume

requires authentication

Resume a paused subscription. Requires permission: administrative.ecommerce.subscription.update

Notes:

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/5/resume" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/5/resume"
);

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

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Subscription resumed successfully.",
    "subscription": {
        "id": 5,
        "status": "active",
        "next_renewal_date": "2025-09-17"
    }
}
 

Example response (400):


{
    "message": "This subscription is not paused."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Subscription not found"
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/subscriptions/{id}/resume

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

Subscription ID. Example: 5

Subscriptions - Cancel

requires authentication

Cancel a subscription at the end of the current period. Requires permission: administrative.ecommerce.subscription.update

Notes:

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/12/cancel" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"cancel_reason\": \"Player won\'t continue next month\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/subscriptions/12/cancel"
);

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

let body = {
    "cancel_reason": "Player won't continue next month"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Subscription will remain active until the end of the paid period and will not renew.",
    "subscription": {
        "id": 12,
        "status": "active",
        "access_until": "2025-08-31T23:59:59.000000Z"
    }
}
 

Example response (400):


{
    "message": "Only active subscriptions can be canceled."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Subscription not found"
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/subscriptions/{id}/cancel

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

Subscription ID. Example: 12

Body Parameters

cancel_reason   string  optional  

optional Reason for cancellation. Example: Player won't continue next month

Orders - List

requires authentication

List orders for a school. If the user has role "account" or "lead", only their own orders are returned. Supports dynamic filters, sorting, and two response modes (table and select). Requires permission: administrative.ecommerce.order.index (admins) or customer.ecommerce.order.index (customers)

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/orders?view=select&per_page=15&sort_by=created_at&direction=desc&service_id=3&filters[]=nulla&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=paid" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/orders"
);

const params = {
    "view": "select",
    "per_page": "15",
    "sort_by": "created_at",
    "direction": "desc",
    "service_id": "3",
    "filters[0]": "nulla",
    "filters[0][column]": "status",
    "filters[0][operator]": "equals",
    "filters[0][value]": "paid",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "orders": {
        "data": [
            {
                "id": 1,
                "status": "paid",
                "total_amount": "180.00",
                "currency": "USD",
                "payment_method": "card",
                "paid_at": "2025-08-01T12:00:00Z",
                "user": {
                    "id": 4,
                    "user_name": "juan.perez"
                },
                "items": [
                    {
                        "id": 1,
                        "description": "Monthly Tennis Plan",
                        "amount": "90.00",
                        "itemable": {
                            "id": 7,
                            "nickname": "Juanito Plan",
                            "service": {
                                "id": 3,
                                "nickname": "Tennis Pro Monthly"
                            }
                        }
                    }
                ]
            }
        ],
        "current_page": 1,
        "per_page": 10,
        "total": 42,
        "links": {
            "first": "...",
            "last": "...",
            "next": "..."
        }
    },
    "metadata": {
        "columns": [
            "id",
            "status",
            "total_amount",
            "payment_method",
            "paid_at",
            "user",
            "items"
        ],
        "defaultColumns": [
            "id",
            "status",
            "total_amount",
            "paid_at"
        ]
    }
}
 

Example response (200):


{"orders": [{"id": 1, "status": "paid"}]}
When view=select, returns a flat list of orders with id and status.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/orders

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (table only). Default: 10. Example: 15

sort_by   string  optional  

optional Sort column (base table). Example: created_at

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

service_id   integer  optional  

optional Filter by related service (via order items -> subscription.service_id). Example: 3

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator, value.

filters[0][column]   string  optional  

Example: status

filters[0][operator]   string  optional  

Options: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string  optional  

mixed Use [min,max] for between; ignored for empty/not_empty. Example: paid

Orders - Create

requires authentication

Creates an order with optional items. Admins can create orders for any user in the school; accounts/leads can only create orders for themselves. The total_amount is computed from items when provided.

Required permissions: administrative.ecommerce.order.store (admins) or customer.ecommerce.order.store (customers)

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/orders" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"user_id\": 8,
    \"currency\": \"USD\",
    \"payment_method\": \"card\",
    \"status\": \"et\",
    \"paid_at\": \"2025-08-01T12:00:00Z\",
    \"metadata\": [],
    \"items\": [
        \"et\"
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/orders"
);

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

let body = {
    "user_id": 8,
    "currency": "USD",
    "payment_method": "card",
    "status": "et",
    "paid_at": "2025-08-01T12:00:00Z",
    "metadata": [],
    "items": [
        "et"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Order created",
    "order": {
        "id": 1,
        "status": "pending",
        "total_amount": "100.00"
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/orders

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

user_id   integer  optional  

optional The user placing the order (admin only). When omitted, current user is used. Example: 8

currency   string   

ISO currency code. Example: USD

payment_method   string  optional  

optional Payment method label. Example: card

status   string  optional  

optional Order status. One of: pending, paid, failed, canceled. Default: pending Example: et

paid_at   datetime  optional  

optional Payment timestamp (ISO8601). Example: 2025-08-01T12:00:00Z

metadata   object  optional  

optional Arbitrary metadata

items   string[]  optional  

optional Order items collection

itemable_type   string   

Tenant model name (e.g., Subscription) Example: necessitatibus

itemable_id   integer   

Target id for itemable Example: 8

description   string   

Item description Example: Corrupti ut recusandae vel at.

amount   number   

Unit amount (decimal) Example: 337424037

quantity   integer  optional  

optional Units (default 1) Example: 7

metadata   object  optional  

optional Additional data for the item

Orders - Show

requires authentication

Returns a single order with items and user info. Accounts/leads can only access their own orders.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/vero/ecommerce/orders/11" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/vero/ecommerce/orders/11"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/ecommerce/orders/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

School slug Example: vero

id   integer   

Order id Example: 11

Orders - Update

requires authentication

Updates order fields and optionally replaces items. Recomputes total when items are provided. Accounts/leads can only update their own orders.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/quis/ecommerce/orders/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"currency\": \"m\",
    \"payment_method\": \"steoiwwlcyldsfyummkbjjro\",
    \"payment_url\": \"http:\\/\\/www.heathcote.info\\/eveniet-totam-voluptas-velit-maxime-ipsa-consequuntur\",
    \"status\": \"paid\",
    \"paid_at\": \"2026-02-26T04:17:46\",
    \"items\": [
        {
            \"itemable_type\": \"omnis\",
            \"itemable_id\": 19,
            \"description\": \"Ut ipsa nostrum commodi.\",
            \"amount\": 729494.695099553,
            \"quantity\": 41
        }
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/quis/ecommerce/orders/1"
);

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

let body = {
    "currency": "m",
    "payment_method": "steoiwwlcyldsfyummkbjjro",
    "payment_url": "http:\/\/www.heathcote.info\/eveniet-totam-voluptas-velit-maxime-ipsa-consequuntur",
    "status": "paid",
    "paid_at": "2026-02-26T04:17:46",
    "items": [
        {
            "itemable_type": "omnis",
            "itemable_id": 19,
            "description": "Ut ipsa nostrum commodi.",
            "amount": 729494.695099553,
            "quantity": 41
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PUT api/schools/{school_slug}/ecommerce/orders/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: quis

id   integer   

The ID of the order. Example: 1

Body Parameters

currency   string  optional  

Must not be greater than 10 characters. Example: m

payment_method   string  optional  

Must not be greater than 50 characters. Example: steoiwwlcyldsfyummkbjjro

payment_url   string  optional  

Must be a valid URL. Example: http://www.heathcote.info/eveniet-totam-voluptas-velit-maxime-ipsa-consequuntur

status   string  optional  

Example: paid

Must be one of:
  • pending
  • paid
  • failed
  • canceled
paid_at   string  optional  

Must be a valid date. Example: 2026-02-26T04:17:46

metadata   object  optional  
items   object[]  optional  
itemable_type   string  optional  

This field is required when items is present. Example: omnis

itemable_id   integer  optional  

This field is required when items is present. Example: 19

description   string  optional  

This field is required when items is present. Example: Ut ipsa nostrum commodi.

amount   number  optional  

This field is required when items is present. Example: 729494.69509955

quantity   integer  optional  

Must be at least 1. Example: 41

metadata   object  optional  

Orders - Delete

requires authentication

Deletes an order and its items. Accounts/leads can only delete their own orders.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/suscipit/ecommerce/orders/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/suscipit/ecommerce/orders/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/schools/{school_slug}/ecommerce/orders/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: suscipit

id   integer   

The ID of the order. Example: 1

Invoices - List

requires authentication

List invoices for a school. If the current user has role "account" or "lead", only their own invoices are returned. Supports filters, sorting, and two response modes (table and select). Requires permission: administrative.ecommerce.invoice.index (admins) or customer.ecommerce.invoice.index (customers)

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices?view=select&per_page=15&sort_by=paid_at&direction=desc&status=paid&service_id=3&date_from=2025-08-01&date_to=2025-08-31&invoiceable_type=order&filters[]=tempora&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=paid" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices"
);

const params = {
    "view": "select",
    "per_page": "15",
    "sort_by": "paid_at",
    "direction": "desc",
    "status": "paid",
    "service_id": "3",
    "date_from": "2025-08-01",
    "date_to": "2025-08-31",
    "invoiceable_type": "order",
    "filters[0]": "tempora",
    "filters[0][column]": "status",
    "filters[0][operator]": "equals",
    "filters[0][value]": "paid",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "invoices": {
        "data": [
            {
                "id": 12,
                "status": "paid",
                "amount": "180.00",
                "currency": "USD",
                "payment_method": "stripe",
                "issued_at": "2025-08-01T12:00:00Z",
                "paid_at": "2025-08-01T12:00:00Z",
                "user": {
                    "id": 4,
                    "user_name": "juan.perez"
                },
                "invoiceable": {
                    "type": "order",
                    "items": [
                        {
                            "id": 1,
                            "description": "Monthly Tennis Plan",
                            "amount": "90.00",
                            "quantity": 1,
                            "itemable": {
                                "id": 7,
                                "nickname": "Juanito Plan",
                                "service": {
                                    "id": 3,
                                    "nickname": "Tennis Pro Monthly"
                                }
                            }
                        }
                    ]
                }
            }
        ],
        "meta": {
            "current_page": 1,
            "per_page": 10,
            "total": 42
        },
        "links": {
            "first": "...",
            "last": "...",
            "next": "..."
        }
    },
    "metadata": {
        "columns": [
            "id",
            "status",
            "amount",
            "currency",
            "payment_method",
            "issued_at",
            "paid_at",
            "user",
            "invoiceable"
        ],
        "defaultColumns": [
            "id",
            "status",
            "amount",
            "paid_at"
        ]
    }
}
 

Example response (200):


{"invoices": [{"id": 12, "status": "paid", "amount": "180.00", "currency": "USD"}]}
When view=select, returns a flat list of invoices with a compact subset of fields.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/invoices

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (table only). Default: 10. Example: 15

sort_by   string  optional  

optional Sort column. Allowed: id,status,amount,currency,payment_method,paid_at,issued_at,created_at. Example: paid_at

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

status   string  optional  

optional Filter by status. Example: paid

service_id   integer  optional  

optional Filter by related service (subscription or order item). Example: 3

date_from   string  optional  

date optional Inclusive start date for issued_at (YYYY-MM-DD). Example: 2025-08-01

date_to   string  optional  

date optional Inclusive end date for issued_at (YYYY-MM-DD). Example: 2025-08-31

invoiceable_type   string  optional  

optional Filter by invoiceable type. One of: order, subscription. Example: order

filters   string[]  optional  

optional Dynamic filters (applied if a scopeFilter exists on the model). Each filter must include column, operator, and value.

filters[0][column]   string  optional  

Example: status

filters[0][operator]   string  optional  

Example: equals

filters[0][value]   string  optional  

mixed Example: paid

Invoices - Checkout

requires authentication

Preview or create a Stripe Checkout Session for an invoice. When confirm=false, returns a preview (items + total). When confirm=true, creates a session and returns checkout_url. Requires permission: customer.ecommerce.invoice.checkout (customers) or administrative.ecommerce.invoice.checkout (admins)

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices/ratione/checkout" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"invoice_id\": 456,
    \"success_url\": \"https:\\/\\/app.com\\/checkout\\/success\",
    \"cancel_url\": \"https:\\/\\/app.com\\/checkout\\/cancel\",
    \"confirm\": true
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices/ratione/checkout"
);

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

let body = {
    "invoice_id": 456,
    "success_url": "https:\/\/app.com\/checkout\/success",
    "cancel_url": "https:\/\/app.com\/checkout\/cancel",
    "confirm": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "items": [
        {
            "name": "Monthly Tennis Plan",
            "description": "Monthly Tennis Plan",
            "amount": 90,
            "quantity": 1
        }
    ],
    "total": 180
}
 

Example response (201):


{
    "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
    "checkout_session_id": "cs_test_...",
    "expires_at": 1712345678
}
 

Example response (403):


{
    "message": "Forbidden."
}
 

Example response (404):


{
    "message": "Invoice not found or unauthorized."
}
 

Example response (422):


{
    "message": "Invoice is not payable."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "invoice_id": [
            "The invoice id field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/invoices/{invoice}/checkout

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

invoice   string   

The invoice. Example: ratione

Body Parameters

invoice_id   integer   

The ID of the invoice (must belong to the school and be payable: status "pending"). Example: 456

success_url   string   

Redirect after success. Example: https://app.com/checkout/success

cancel_url   string   

Redirect if cancelled. Example: https://app.com/checkout/cancel

confirm   boolean  optional  

optional Default: false. If true, creates the Stripe session. Example: true

Invoices - Download PDF

requires authentication

Returns a temporary signed URL to download an invoice PDF from S3. If the PDF does not exist, it will be generated and uploaded first. Requires permission: administrative.ecommerce.invoice.download (admins) or customer.ecommerce.invoice.download (customers)

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices/123/pdf/download" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices/123/pdf/download"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (302):


{
    "Location": "https://s3-url/invoice_1.pdf?AWSAccessKeyId=..."
}
 

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Invoice not found or PDF could not be created"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/invoices/{id}/pdf/download

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the invoice. Example: 123

Invoices - Generate PDF

requires authentication

Generate a PDF for an invoice, upload it to S3, store the path in invoice metadata, and return an accessible URL. Requires permission: administrative.ecommerce.invoice.generate_pdf (admins) or customer.ecommerce.invoice.generate_pdf (customers)

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices/123/pdf/generate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/invoices/123/pdf/generate"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "url": "https://your-bucket.s3.amazonaws.com/invoices/2/invoice_1.pdf"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Invoice not found"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/invoices/{id}/pdf/generate

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the invoice. Example: 123

Combined Records - List

requires authentication

Unified paginated list that merges orders, invoices, and subscriptions into a single feed. Designed for admin dashboards where a consolidated financial overview is needed.

Notes:

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/combined-records?type=order&status=paid&user_id=42&service_type=group&date_from=2025-08-01&date_to=2025-08-31&search=ORD-12&per_page=20&sort_by=amount&direction=asc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"order\",
    \"status\": \"ullam\",
    \"user_id\": 20,
    \"service_type\": \"asperiores\",
    \"date_from\": \"2026-02-26T04:17:46\",
    \"date_to\": \"2026-02-26T04:17:46\",
    \"search\": \"error\",
    \"per_page\": 3,
    \"sort_by\": \"amount\",
    \"direction\": \"desc\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/combined-records"
);

const params = {
    "type": "order",
    "status": "paid",
    "user_id": "42",
    "service_type": "group",
    "date_from": "2025-08-01",
    "date_to": "2025-08-31",
    "search": "ORD-12",
    "per_page": "20",
    "sort_by": "amount",
    "direction": "asc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

let body = {
    "type": "order",
    "status": "ullam",
    "user_id": 20,
    "service_type": "asperiores",
    "date_from": "2026-02-26T04:17:46",
    "date_to": "2026-02-26T04:17:46",
    "search": "error",
    "per_page": 3,
    "sort_by": "amount",
    "direction": "desc"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "records": {
        "data": [
            {
                "id": 34,
                "type": "order",
                "status": "paid",
                "amount": "250.00",
                "currency": "USD",
                "date": "2025-09-10T14:30:00.000000Z",
                "user_id": 8,
                "school_id": 1,
                "reference": "ORD-34",
                "account": {
                    "id": 8,
                    "name": "Maria Lopez",
                    "user_name": "maria.lopez"
                },
                "lead": null,
                "player": null
            },
            {
                "id": 21,
                "type": "invoice",
                "status": "pending",
                "amount": "120.00",
                "currency": "EUR",
                "date": "2025-09-15T00:00:00.000000Z",
                "user_id": 15,
                "school_id": 1,
                "reference": "INV-21",
                "account": null,
                "lead": {
                    "id": 15,
                    "name": "Carlos Ruiz",
                    "user_name": "carlos.ruiz"
                },
                "player": null
            },
            {
                "id": 7,
                "type": "subscription",
                "status": "active",
                "amount": null,
                "currency": null,
                "date": "2025-09-01T00:00:00.000000Z",
                "user_id": 22,
                "school_id": 1,
                "reference": "Lucas Garcia - Advanced Tennis",
                "service_id": 5,
                "service_type": "group",
                "account": null,
                "lead": null,
                "player": {
                    "id": 22,
                    "name": "Lucas Garcia"
                },
                "service": {
                    "id": 5,
                    "nickname": "Advanced Tennis",
                    "type": "group"
                }
            }
        ],
        "current_page": 1,
        "per_page": 15,
        "total": 86,
        "last_page": 6,
        "from": 1,
        "to": 15,
        "first_page_url": "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/combined-records?page=1",
        "last_page_url": "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/combined-records?page=6",
        "next_page_url": "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/combined-records?page=2",
        "prev_page_url": null,
        "path": "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/combined-records"
    },
    "metadata": {
        "columns": [
            "id",
            "reference",
            "type",
            "status",
            "amount",
            "currency",
            "date",
            "user_id",
            "school_id",
            "account",
            "lead",
            "player",
            "service"
        ],
        "defaultColumns": [
            "reference",
            "type",
            "status",
            "amount",
            "date",
            "account",
            "lead",
            "player"
        ]
    }
}
 

Example response (401):


{
    "message": "Unauthenticated."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "type": [
            "The selected type is invalid."
        ],
        "per_page": [
            "The per page must be at least 1."
        ]
    }
}
 

Request      

GET api/schools/{school_slug}/ecommerce/combined-records

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

type   string  optional  

optional Filter by record type. One of: order, invoice, subscription, all. Default: all. Example: order

status   string  optional  

optional Filter by status (applies to all types). Example: paid

user_id   integer  optional  

optional Filter by user ID (orders/invoices: user_id, subscriptions: player_id). Example: 42

service_type   string  optional  

optional Filter subscriptions by service type (ignored for orders/invoices). Example: group

date_from   string  optional  

date optional Inclusive start date (YYYY-MM-DD). Example: 2025-08-01

date_to   string  optional  

date optional Inclusive end date (YYYY-MM-DD). Example: 2025-08-31

search   string  optional  

optional Free-text search across reference, status, and id. Example: ORD-12

per_page   integer  optional  

optional Items per page (1-100). Default: 15. Example: 20

sort_by   string  optional  

optional Sort field. One of: date, amount, id, status. Default: date. Example: amount

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: desc. Example: asc

Body Parameters

type   string  optional  

Example: order

Must be one of:
  • order
  • invoice
  • subscription
  • all
status   string  optional  

Example: ullam

user_id   integer  optional  

Example: 20

service_type   string  optional  

Example: asperiores

date_from   string  optional  

Must be a valid date. Example: 2026-02-26T04:17:46

date_to   string  optional  

Must be a valid date. Example: 2026-02-26T04:17:46

search   string  optional  

Example: error

per_page   integer  optional  

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

sort_by   string  optional  

Example: amount

Must be one of:
  • date
  • amount
  • id
  • status
direction   string  optional  

Example: desc

Must be one of:
  • asc
  • desc

Coupons - List coupons in a school

requires authentication

Returns the coupons associated with the given school. Supports filters, sorting, pagination, and a compact select view. Requires permission: administrative.ecommerce.coupon.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons?view=select&per_page=15&filters[]=voluptatum&filters%5Bsearch%5D=WELCOME&filters%5B0%5D%5Bcolumn%5D=active&filters%5B0%5D%5Boperator%5D=equals&filters%5B0%5D%5Bvalue%5D=true&sort_by=name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons"
);

const params = {
    "view": "select",
    "per_page": "15",
    "filters[0]": "voluptatum",
    "filters[search]": "WELCOME",
    "filters[0][column]": "active",
    "filters[0][operator]": "equals",
    "filters[0][value]": "true",
    "sort_by": "name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "coupons": {
        "data": [
            {
                "id": 1,
                "name": "Welcome Discount",
                "code": "WELCOME10",
                "type": "percentage",
                "value": "10.00",
                "currency": null,
                "applies_to": "first_payment",
                "max_redemptions": 100,
                "one_per_user": true,
                "starts_at": "2025-06-01T00:00:00.000000Z",
                "expires_at": "2025-06-30T23:59:59.000000Z",
                "active": true
            }
        ],
        "current_page": 1,
        "per_page": 10,
        "total": 42
    },
    "metadata": {
        "columns": [
            "id",
            "name",
            "code",
            "type",
            "value",
            "currency",
            "applies_to",
            "max_redemptions",
            "one_per_user",
            "starts_at",
            "expires_at",
            "active",
            "created_at"
        ],
        "defaultColumns": [
            "id",
            "name",
            "code",
            "active"
        ]
    }
}
 

Example response (200):


{"coupons": [{"id": 1, "name": "Welcome Discount"}]}
When view=select, returns a flat list of coupons with id and name.
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/ecommerce/coupons

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

view   string  optional  

optional Response mode. One of: table, select. Default: table. Example: select

per_page   integer  optional  

optional Items per page (only for table). Default: 10. Example: 15

filters   string[]  optional  

optional Filter set. Each filter has column, operator, and value.

filters[search]   string  optional  

optional Global search across base coupon columns. Example: WELCOME

filters[0][column]   string   

Column to filter (base or relation via dot-notation, e.g., creator.name). Example: active

filters[0][operator]   string   

Operator: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: equals

filters[0][value]   string   

mixed Value to compare. Use [min,max] for between. For empty/not_empty, value is ignored. Example: true

sort_by   string  optional  

optional Column to sort by (base table only). Example: name

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

Coupons - Create

requires authentication

Create a discount coupon associated with the given school. Coupons can apply to first payments, recurring payments, or both, and can include dynamic restrictions (by Service, SchoolSite, etc.).

Restrictions schema (array-of-objects): [ {"model": "Service", "ids": [1, 2, 3]}, {"model": "SchoolSite", "ids": [4]}, {"model": "ServiceCategory", "ids": [5, 6]} ] Semantics: the coupon is valid when the redemption context matches at least one of the listed model/ids entries (OR). Within an entry, ids is an OR-list for that model. Note: Current runtime validation enforces restrictions for model "Service". If you plan to restrict by other models, extend the validation accordingly. When sending multipart/form-data, send restrictions as a JSON string. Requires permission: administrative.ecommerce.coupon.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Welcome Discount\",
    \"code\": \"WELCOME10\",
    \"type\": \"percentage\",
    \"value\": 15,
    \"currency\": \"USD\",
    \"applies_to\": \"both\",
    \"max_redemptions\": 100,
    \"one_per_user\": true,
    \"starts_at\": \"2025-06-15 00:00:00\",
    \"expires_at\": \"2025-07-15 23:59:59\",
    \"active\": true,
    \"restrictions\": [
        \"sed\"
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons"
);

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

let body = {
    "name": "Welcome Discount",
    "code": "WELCOME10",
    "type": "percentage",
    "value": 15,
    "currency": "USD",
    "applies_to": "both",
    "max_redemptions": 100,
    "one_per_user": true,
    "starts_at": "2025-06-15 00:00:00",
    "expires_at": "2025-07-15 23:59:59",
    "active": true,
    "restrictions": [
        "sed"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Coupon created successfully",
    "coupon": {
        "id": 1,
        "code": "WELCOME10",
        "type": "percentage",
        "value": 15,
        "applies_to": "first_payment",
        "active": true
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "code": [
            "The code has already been taken."
        ],
        "currency": [
            "The currency field is required when type is fixed."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/coupons

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Body Parameters

name   string   

Internal name of the coupon. Example: Welcome Discount

code   string   

Unique redemption code. Must be unique per school. Example: WELCOME10

type   string   

Discount type. One of: fixed, percentage. Example: percentage

value   number   

Discount value. Example: 15

currency   string   

if type=fixed Currency code (ISO 4217). Example: USD

applies_to   string  optional  

optional Application rule. One of: first_payment, recurring, both. Default: first_payment. Example: both

max_redemptions   integer  optional  

optional Maximum number of redemptions (overall). Example: 100

one_per_user   boolean  optional  

optional If true, each user can redeem only once. Default: true. Example: true

starts_at   datetime  optional  

optional Start datetime (YYYY-MM-DD HH:MM:SS or ISO 8601). Example: 2025-06-15 00:00:00

expires_at   datetime  optional  

optional End datetime; must be after starts_at when both present. Example: 2025-07-15 23:59:59

active   boolean  optional  

optional Whether the coupon is active. Default: true. Example: true

restrictions   string[]  optional  

optional Dynamic usage restrictions. Provide an array of objects.

model   string   

Target model for restriction. One of: Service, SchoolSite, ServiceCategory, User. Example: Service

ids   string[]   

IDs allowed for the specified model.

Coupons - Show

requires authentication

Returns full details of a coupon by its ID within the given school. Requires permission: administrative.ecommerce.coupon.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons/3" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons/3"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "coupon": {
        "id": 3,
        "name": "Monthly Discount",
        "code": "MENSUAL15",
        "type": "percentage",
        "value": 15,
        "currency": "USD",
        "applies_to": "recurring",
        "max_redemptions": 100,
        "one_per_user": true,
        "starts_at": "2025-06-01T00:00:00Z",
        "expires_at": "2025-12-31T23:59:59Z",
        "active": true,
        "restrictions": [
            {
                "model": "Service",
                "ids": [
                    5
                ]
            },
            {
                "model": "ServiceCategory",
                "ids": [
                    6
                ]
            }
        ],
        "created_at": "2025-06-11T20:00:00Z",
        "updated_at": "2025-06-11T20:00:00Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Coupon not found or unauthorized."
}
 

Request      

GET api/schools/{school_slug}/ecommerce/coupons/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the coupon. Example: 3

Coupons - Update

requires authentication

Update an existing coupon in the given school. Requires permission: administrative.ecommerce.coupon.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons/12" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Summer Discount\",
    \"code\": \"SUMMER25\",
    \"type\": \"percentage\",
    \"value\": 25,
    \"currency\": \"USD\",
    \"applies_to\": \"both\",
    \"max_redemptions\": 100,
    \"one_per_user\": true,
    \"starts_at\": \"2025-06-01T00:00:00\",
    \"expires_at\": \"2025-06-30T23:59:59\",
    \"active\": true,
    \"restrictions\": [
        \"voluptatem\"
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons/12"
);

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

let body = {
    "name": "Summer Discount",
    "code": "SUMMER25",
    "type": "percentage",
    "value": 25,
    "currency": "USD",
    "applies_to": "both",
    "max_redemptions": 100,
    "one_per_user": true,
    "starts_at": "2025-06-01T00:00:00",
    "expires_at": "2025-06-30T23:59:59",
    "active": true,
    "restrictions": [
        "voluptatem"
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Coupon updated successfully",
    "coupon": {
        "id": 12,
        "name": "Summer Discount",
        "code": "SUMMER25",
        "type": "percentage",
        "value": 25,
        "currency": null,
        "applies_to": "both",
        "max_redemptions": 100,
        "one_per_user": true,
        "starts_at": "2025-06-01T00:00:00Z",
        "expires_at": "2025-06-30T23:59:59Z",
        "active": true,
        "restrictions": [
            {
                "model": "Service",
                "ids": [
                    5
                ]
            },
            {
                "model": "ServiceCategory",
                "ids": [
                    6
                ]
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Coupon not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "code": [
            "The code has already been taken."
        ],
        "currency": [
            "The currency field is required when type is fixed."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/ecommerce/coupons/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the coupon to update. Example: 12

Body Parameters

name   string   

Coupon name. Example: Summer Discount

code   string   

Unique redemption code (per school). Example: SUMMER25

type   string   

Discount type. One of: fixed, percentage. Example: percentage

value   number   

Discount value. Example: 25

currency   string   

if type=fixed Currency code (ISO 4217). Example: USD

applies_to   string  optional  

optional Application rule. One of: first_payment, recurring, both. Example: both

max_redemptions   integer  optional  

optional Maximum number of overall redemptions. Example: 100

one_per_user   boolean  optional  

optional If true, each user can redeem only once. Example: true

starts_at   datetime  optional  

optional Start datetime (YYYY-MM-DD HH:MM:SS or ISO 8601). Example: 2025-06-01T00:00:00

expires_at   datetime  optional  

optional End datetime; must be after starts_at when both present. Example: 2025-06-30T23:59:59

active   boolean  optional  

optional Whether the coupon is active. Example: true

restrictions   string[]  optional  

optional Dynamic usage restrictions. Provide an array of objects.

model   string   

Target model for restriction. One of: Service, SchoolSite, ServiceCategory, User. Example: Service

ids   string[]   

IDs allowed for the specified model.

Coupons - Archive

requires authentication

Archive a coupon by setting its status to "archived". This does not delete the record. Requires permission: administrative.ecommerce.coupon.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons/7" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/ecommerce/coupons/7"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Coupon archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Coupon not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/ecommerce/coupons/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The ID of the coupon. Example: 7

Checkout - Create Stripe Checkout Session

requires authentication

This endpoint creates a Stripe Checkout session for a previously generated order. If the 'confirm' parameter is false, it only returns a preview of the items and total to be paid. If 'confirm' is true, it creates the Stripe session and returns the payment URL.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/ale-academy/ecommerce/stripe/checkout/create" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"order_id\": 123,
    \"success_url\": \"https:\\/\\/myapp.com\\/checkout\\/success\",
    \"cancel_url\": \"https:\\/\\/myapp.com\\/checkout\\/cancel\",
    \"confirm\": true
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/ale-academy/ecommerce/stripe/checkout/create"
);

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

let body = {
    "order_id": 123,
    "success_url": "https:\/\/myapp.com\/checkout\/success",
    "cancel_url": "https:\/\/myapp.com\/checkout\/cancel",
    "confirm": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_..."
}
 

Example response (200):


{
    "items": [
        {
            "name": "Gold Plan",
            "description": "Monthly training plan",
            "amount": 99,
            "quantity": 1,
            "original_amount": 99,
            "discount_applied": 0,
            "discount_currency": null,
            "has_discount": false,
            "coupon_code": null
        },
        {
            "name": "John Doe - Registration Fee",
            "description": "Registration Fee",
            "amount": 25,
            "quantity": 1,
            "original_amount": 25,
            "discount_applied": 0,
            "discount_currency": null,
            "has_discount": false,
            "coupon_code": null
        },
        {
            "name": "Platform Fee",
            "description": "Platform fee for this order",
            "amount": 3.5,
            "quantity": 1,
            "original_amount": 3.5,
            "discount_applied": 0,
            "discount_currency": null,
            "has_discount": false,
            "coupon_code": null
        }
    ],
    "total": 127.5
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Not Found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "order_id": [
            "The order id field is required."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/ecommerce/stripe/checkout/create

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The school slug used to scope platform fees and registration fee rules. Example: ale-academy

Body Parameters

order_id   integer   

The ID of the already created order. Example: 123

success_url   string   

The URL to redirect after successful payment. Example: https://myapp.com/checkout/success

cancel_url   string   

The URL to redirect if the user cancels payment. Example: https://myapp.com/checkout/cancel

confirm   boolean  optional  

optional Default: false. If false, only returns a preview of what will be paid. If true, creates the Stripe session. Example: true

Checkout - Create Stripe Checkout Session for Multiple Orders

requires authentication

This endpoint creates a single Stripe Checkout session that includes multiple pending orders from the authenticated user. If the 'confirm' parameter is false, it only returns a preview of the items and total to be paid. If 'confirm' is true, it creates the Stripe session and returns the payment URL.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/ale-academy/ecommerce/stripe/checkout/create/multi-order" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"order_ids\": [
        1,
        2,
        3
    ],
    \"success_url\": \"https:\\/\\/example.com\\/success\",
    \"cancel_url\": \"https:\\/\\/example.com\\/cancel\",
    \"confirm\": true
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/ale-academy/ecommerce/stripe/checkout/create/multi-order"
);

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

let body = {
    "order_ids": [
        1,
        2,
        3
    ],
    "success_url": "https:\/\/example.com\/success",
    "cancel_url": "https:\/\/example.com\/cancel",
    "confirm": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "checkout_url": "https://checkout.stripe.com/pay/cs_test_..."
}
 

Example response (200):


{
    "items": [
        {
            "name": "Gold Plan",
            "description": "Monthly training plan",
            "amount": 99,
            "quantity": 1,
            "original_amount": 99,
            "discount_applied": 0,
            "discount_currency": null,
            "has_discount": false,
            "coupon_code": null
        },
        {
            "name": "Silver Plan",
            "description": "Bi-weekly training plan",
            "amount": 59,
            "quantity": 1,
            "original_amount": 59,
            "discount_applied": 10,
            "discount_currency": "USD",
            "has_discount": true,
            "coupon_code": "WELCOME10"
        },
        {
            "name": "Jane Smith - Registration Fee",
            "description": "Registration Fee",
            "amount": 25,
            "quantity": 1,
            "original_amount": 25,
            "discount_applied": 0,
            "discount_currency": null,
            "has_discount": false,
            "coupon_code": null
        },
        {
            "name": "Platform Fee",
            "description": "Platform Fee",
            "amount": 6.5,
            "quantity": 1,
            "original_amount": 6.5,
            "discount_applied": 0,
            "discount_currency": null,
            "has_discount": false,
            "coupon_code": null
        }
    ],
    "total": 189.5
}
 

Example response (400):


{
    "message": "No line items to pay."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "No valid orders found."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "order_ids": [
            "The order ids field is required."
        ],
        "success_url": [
            "The success url field is required."
        ],
        "cancel_url": [
            "The cancel url field is required."
        ]
    }
}
 

Example response (422):


{
    "message": "Please reduce the number of orders.",
    "metadata_length": 812
}
 

Request      

POST api/schools/{school_slug}/ecommerce/stripe/checkout/create/multi-order

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The school slug used to scope platform fees and registration fee rules. Example: ale-academy

Body Parameters

order_ids   string[]   

List of order IDs to include in the payment.

*   integer   

ID of a single order. Must belong to the user and be pending payment. Example: 1

success_url   string   

The URL to redirect after successful payment. Example: https://example.com/success

cancel_url   string   

The URL to redirect if the payment is cancelled. Example: https://example.com/cancel

confirm   boolean  optional  

optional Default: false. If false, only returns a preview of what will be paid. If true, creates the Stripe session. Example: true

Endpoints

POST api/stripe/webhook

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/stripe/webhook" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/stripe/webhook"
);

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

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/stripe/webhook

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

Update the specified resource in storage.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/nihil/staff/coaches/ut" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/nihil/staff/coaches/ut"
);

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

fetch(url, {
    method: "PUT",
    headers,
}).then(response => response.json());

Request      

PUT api/schools/{school_slug}/staff/coaches/{id}

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: nihil

id   string   

The ID of the coach. Example: ut

Display a listing of the resource.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/culpa/calendar/sessions" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/culpa/calendar/sessions"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/calendar/sessions

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: culpa

Remove the specified resource from storage.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/dolorem/calendar/sessions/et" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/dolorem/calendar/sessions/et"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/schools/{school_slug}/calendar/sessions/{id}

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: dolorem

id   string   

The ID of the session. Example: et

Remove the specified resource from storage.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/quia/performance/evaluation-templates/1" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/quia/performance/evaluation-templates/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Request      

DELETE api/schools/{school_slug}/performance/evaluation-templates/{id}

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: quia

id   integer   

The ID of the evaluation template. Example: 1

General

File upload

requires authentication

Uploads a file for a given module/entity within the specified school scope and creates a polymorphic media record on the target entity. The authenticated user must belong to the school provided in the route.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/vel/general/upload" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "module=exercises"\
    --form "entity_id=10"\
    --form "file=@/tmp/phpmIHqKl" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/vel/general/upload"
);

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

const body = new FormData();
body.append('module', 'exercises');
body.append('entity_id', '10');
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{
    "message": "File uploaded successfully",
    "file_url": "https://s3-bucket-url.com/exercises/10/media/filename.jpg"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Entity not found"
}
 

Example response (422):


{
    "message": "Invalid module or validation failed"
}
 

Example response (500):


{
    "message": "File upload failed"
}
 

Request      

POST api/schools/{school_slug}/general/upload

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: vel

school   integer   

The school id used to scope the operation. Example: 5

Body Parameters

module   string   

Module where the file will be stored. Allowed examples: exercises, skills, skillCategories. Example: exercises

entity_id   integer   

ID of the target entity the file will be attached to. Example: 10

file   file   

File to upload (multipart). Max size: 5MB.

Notes:

  • The controller validates membership of the authenticated user in the given school; a 403 is returned otherwise.
  • The controller maps module to a model class and checks the entity exists before uploading.
  • The uploaded file is stored via the application's FileUploadService and a media row is created with path and mime_type. Example: /tmp/phpmIHqKl

General - List tenant models (select-friendly)

requires authentication

Scans the tenant models directory (app/Models/Tenant) for PHP classes, includes only those that exist and extend Illuminate\Database\Eloquent\Model, and returns them as select-friendly options. Discovery is non-recursive. Results are sorted alphabetically. Any authenticated user may call this endpoint.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/voluptas/general/models/list" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/voluptas/general/models/list"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "models": [
        {
            "label": "School",
            "value": "School"
        },
        {
            "label": "Subscription",
            "value": "Subscription"
        },
        {
            "label": "User",
            "value": "User"
        }
    ]
}
 

Example response (200, No models found):


{
    "models": []
}
 

Example response (401):


{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/general/models/list

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: voluptas

Response

Response Fields

models   string[]   

The list of available tenant models.

*   object   
label   string   

Human-readable model name (e.g., "User").

value   string   

Value to use in selects (same as label).

General - Model options (id, name)

requires authentication

Returns select-friendly options for a given model name. Special case: model=Coach returns Users with coach role scoped to the school. Other models are resolved under App\Models\Tenant and filtered by school when applicable.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/dolorum/general/models/options?model=Coach" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/dolorum/general/models/options"
);

const params = {
    "model": "Coach",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "options": [
        {
            "id": 1,
            "name": "John Doe"
        }
    ]
}
 

Example response (422):


{
    "message": "Invalid model"
}
 

Request      

GET api/schools/{school_slug}/general/models/options

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: dolorum

Query Parameters

model   string   

Model name e.g. Coach, Location, SchoolSite, User. Example: Coach

Notes - List

requires authentication

List paginated notes attached to a target model within the current school. Only the User model is currently supported. Notes with status = archived are excluded by default. Results are ordered with pinned notes first and newest notes first thereafter.

Permissions: administrative.general.notes.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/general/notes?model=User&model_id=42&per_page=10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/general/notes"
);

const params = {
    "model": "User",
    "model_id": "42",
    "per_page": "10",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "notes": {
        "current_page": 1,
        "data": [
            {
                "id": 12,
                "school_id": 3,
                "noteable_type": "App\\Models\\Tenant\\User",
                "noteable_id": 42,
                "author_id": 7,
                "actor_name": "Admin User",
                "actor_roles": [
                    "administrator"
                ],
                "actor_primary_role": "administrator",
                "title": "Seguimiento",
                "body": "Llamada reprogramada para el viernes.",
                "visibility": "internal",
                "is_pinned": false,
                "tags": [
                    "follow-up"
                ],
                "meta": {
                    "subject_role": "Player",
                    "subject_status_id": 3,
                    "subject_status_name": "Active"
                },
                "status": "active",
                "created_at": "2025-10-15T12:00:00Z",
                "updated_at": "2025-10-15T12:10:00Z"
            }
        ],
        "per_page": 10,
        "total": 1
    }
}
 

Example response (200, Empty result):


{
    "notes": {
        "current_page": 1,
        "data": [],
        "per_page": 10,
        "total": 0
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "Invalid model provided"
}
 

Example response (422, Unsupported model):


{
    "message": "Only User model is supported for notes"
}
 

Request      

GET api/schools/{school_slug}/general/notes

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

Query Parameters

model   string   

The target model name or FQCN. Only "User" is supported. If a simple name is provided, it's resolved under App\Models\Tenant. Example: User

model_id   integer   

The target model id. Example: 42

per_page   integer  optional  

optional Records per page. Default: 10. Example: 10

Notes - Create

requires authentication

Create a note attached to a target (currently only User) within the current school. The author's roles and the target user's role/status are snapshotted into note.meta. Permissions: administrative.general.notes.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/odio/general/notes" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"model\": \"User\",
    \"model_id\": 42,
    \"title\": \"Llamada de seguimiento\",
    \"body\": \"Se acordó llamada el viernes.\",
    \"visibility\": \"internal\",
    \"tags\": [
        \"follow-up\",
        \"billing\"
    ],
    \"is_pinned\": true,
    \"meta\": {
        \"context\": \"phone\"
    }
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/odio/general/notes"
);

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

let body = {
    "model": "User",
    "model_id": 42,
    "title": "Llamada de seguimiento",
    "body": "Se acordó llamada el viernes.",
    "visibility": "internal",
    "tags": [
        "follow-up",
        "billing"
    ],
    "is_pinned": true,
    "meta": {
        "context": "phone"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Note created",
    "note": {
        "id": 12,
        "title": "Llamada de seguimiento",
        "visibility": "internal",
        "is_pinned": false,
        "tags": [
            "follow-up"
        ],
        "meta": {
            "subject_role": "Player",
            "subject_status_id": 3,
            "subject_status_name": "Active"
        }
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Target model not found"
}
 

Example response (422):


{
    "message": "Only User model is supported for notes"
}
 

Request      

POST api/schools/{school_slug}/general/notes

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: odio

Body Parameters

model   string   

Target model. Only "User" supported. If simple, resolved under App\Models\Tenant. Example: User

model_id   integer   

Target model id. Example: 42

title   string  optional  

optional Note title (max 150). Example: Llamada de seguimiento

body   string   

Note content. Example: Se acordó llamada el viernes.

visibility   string  optional  

optional One of: internal, public, private. Default: internal. Example: internal

tags   string[]  optional  

optional Array of strings.

is_pinned   boolean  optional  

optional Pin to top. Default: false. Example: true

meta   object  optional  

optional Additional metadata (merged with snapshot fields).

Notes - Show

requires authentication

Retrieve a single note by id, scoped to the current school. Permissions: administrative.general.notes.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/general/notes/12" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/elite-tennis-academy/general/notes/12"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "note": {
        "id": 12,
        "title": "Seguimiento",
        "body": "Llamada reprogramada para el viernes.",
        "visibility": "internal",
        "is_pinned": false,
        "tags": [
            "follow-up"
        ],
        "meta": {
            "subject_role": "Player"
        }
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Note not found"
}
 

Request      

GET api/schools/{school_slug}/general/notes/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: elite-tennis-academy

id   integer   

The note id. Example: 12

Notes - Update

requires authentication

Update an existing note within the current school. Permissions: administrative.general.notes.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/eligendi/general/notes/12" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"title\": \"Seguimiento actualizado\",
    \"body\": \"Se reagendó al lunes.\",
    \"visibility\": \"internal\",
    \"tags\": [
        \"follow-up\"
    ],
    \"is_pinned\": true,
    \"meta\": {
        \"context\": \"phone\"
    }
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/eligendi/general/notes/12"
);

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

let body = {
    "title": "Seguimiento actualizado",
    "body": "Se reagendó al lunes.",
    "visibility": "internal",
    "tags": [
        "follow-up"
    ],
    "is_pinned": true,
    "meta": {
        "context": "phone"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Note updated",
    "note": {
        "id": 12,
        "title": "Seguimiento actualizado",
        "is_pinned": true
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Note not found"
}
 

Request      

PUT api/schools/{school_slug}/general/notes/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: eligendi

id   integer   

Note id. Example: 12

Body Parameters

title   string  optional  

optional Max 150. Example: Seguimiento actualizado

body   string  optional  

optional Note content. Example: Se reagendó al lunes.

visibility   string  optional  

optional internal|public|private. Example: internal

tags   string[]  optional  

optional Array of strings.

is_pinned   boolean  optional  

optional Example: true

meta   object  optional  

optional

Notes - Archive (soft delete)

requires authentication

Archive a note (status = archived) within the current school scope. The record is not removed and will be excluded from listings.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/et/general/notes/12" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/et/general/notes/12"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Note archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Note not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/general/notes/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: et

id   integer   

Note id. Example: 12

Colors - Show

requires authentication

Retrieves the color assigned to a target model (if any). Returns null in color when the target has no color assigned.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/general/colors?colorable_type=location&colorable_id=17" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"colorable_type\": \"quos\",
    \"colorable_id\": 1
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/general/colors"
);

const params = {
    "colorable_type": "location",
    "colorable_id": "17",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

let body = {
    "colorable_type": "quos",
    "colorable_id": 1
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{"color": {"id": 15, "hex": "#00AAFF", "name": null, "colorable_type": "App\\\Models\\\Tenant\\\Location", "colorable_id": 17, "school_id": 3}}
 

Example response (200, No color assigned):


{
    "color": null
}
 

Example response (404):


{
    "message": "Colorable not found"
}
 

Request      

GET api/schools/{school_slug}/general/colors

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school (tenant scope). Example: aleacademy

Query Parameters

colorable_type   string   

Target model type. One of: user, coach, location, school_site. Example: location

colorable_id   integer   

The ID of the target record. Example: 17

Body Parameters

colorable_type   string   

Example: quos

colorable_id   integer   

Example: 1

Colors - Set/Update

requires authentication

Creates or updates a color assignment for a target model using a polymorphic relation. Supports user/coach, location, and school_site. If a color already exists for the target, it will be updated; otherwise it will be created. One color per record is enforced.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/general/colors" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"colorable_type\": \"coach\",
    \"colorable_id\": 42,
    \"hex\": \"#FFAA00\",
    \"name\": \"Primary Coach Color\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/general/colors"
);

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

let body = {
    "colorable_type": "coach",
    "colorable_id": 42,
    "hex": "#FFAA00",
    "name": "Primary Coach Color"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
  "message": "Color saved",
  "color": {"id": 10, "name": "Primary Coach Color", "hex": "#FFAA00", "colorable_type": "App\\\Models\\\Tenant\\\User", "colorable_id": 42, "school_id": 3}
}
 

Example response (404):


{
    "message": "Colorable not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "hex": [
            "The hex format is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/general/colors

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school (tenant scope). Example: aleacademy

Body Parameters

colorable_type   string   

Target model type. One of: user, coach, location, school_site. Example: coach

colorable_id   integer   

The ID of the target record. Example: 42

hex   string   

Hexadecimal color code in the form #RRGGBB. Example: #FFAA00

name   string  optional  

optional Friendly name for the color. Example: Primary Coach Color

Colors - Delete

requires authentication

Removes the color assignment for a target model. If the target has no color, the operation is treated as successful (idempotent).

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/general/colors" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"colorable_type\": \"school_site\",
    \"colorable_id\": 9
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/general/colors"
);

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

let body = {
    "colorable_type": "school_site",
    "colorable_id": 9
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Color removed"
}
 

Example response (404):


{
    "message": "Colorable not found"
}
 

Request      

DELETE api/schools/{school_slug}/general/colors

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school (tenant scope). Example: aleacademy

Body Parameters

colorable_type   string   

Target model type. One of: user, coach, location, school_site. Example: school_site

colorable_id   integer   

The ID of the target record. Example: 9

Global Search

requires authentication

Performs a global search across permitted buckets and models, returning grouped results. The model_names parameter accepts simple class names (e.g. "School") which are mapped to fully-qualified class names defined in config('search.models') (case-insensitive).

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/sit/general/search?q=%22andres%22&buckets[]=players&buckets[]=coaches&model_names[]=School&model_names[]=Group&per_bucket=10&per_model=5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/sit/general/search"
);

const params = {
    "q": ""andres"",
    "buckets[0]": "players",
    "buckets[1]": "coaches",
    "model_names[0]": "School",
    "model_names[1]": "Group",
    "per_bucket": "10",
    "per_model": "5",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "players": [
        {
            "id": 1,
            "name": "Andres"
        }
    ],
    "coaches": [
        {
            "id": 2,
            "name": "Maria"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "Validation error"
}
 

Global Search - Metadata

requires authentication

Returns metadata describing which buckets and models are available for global search. Use this to populate filters/UI before calling the search endpoint.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/ratione/general/search/meta" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/ratione/general/search/meta"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "buckets": [
        "players",
        "coaches",
        "schools"
    ],
    "models": [
        "App\\Models\\Tenant\\School",
        "App\\Models\\Tenant\\User"
    ]
}
 

Request      

GET api/schools/{school_slug}/general/search/meta

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: ratione

Performance

Skill Categories - List

requires authentication

Returns skill categories scoped to the given school. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname list). Required permission: administrative.performance.skillCategory.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=quisquam&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=archived" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "quisquam",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "archived",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "categories": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Technical Skills",
                "description": "Technical abilities required for performance.",
                "school": {
                    "id": 2,
                    "nickname": "Wildcats Academy"
                },
                "level": {
                    "id": 3,
                    "nickname": "Advanced",
                    "learningStage": {
                        "id": 1,
                        "nickname": "U12"
                    }
                },
                "created_at": "2025-02-01T12:00:00.000000Z",
                "updated_at": "2025-02-01T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 30
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "school.nickname",
            "level.nickname"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description"
        ]
    }
}
 

Example response (200):


{
    "categories": [
        {
            "id": 1,
            "nickname": "Technical Skills"
        },
        {
            "id": 2,
            "nickname": "Tactical Skills"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/performance/skill-categories

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope categories. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only categories with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by when filters are applied. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction when filters are applied. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: archived

Skill Categories - Create

requires authentication

Creates a new skill category scoped to the given school. Required permission: administrative.performance.skillCategory.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Technical Skills\",
    \"description\": \"Skills related to technical aspects.\",
    \"level_id\": 3
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories"
);

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

let body = {
    "nickname": "Technical Skills",
    "description": "Skills related to technical aspects.",
    "level_id": 3
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Skill category created successfully",
    "category": {
        "id": 1,
        "nickname": "Technical Skills",
        "description": "Skills related to technical aspects.",
        "school": {
            "id": 2,
            "nickname": "Wildcats Academy"
        },
        "level": {
            "id": 3,
            "nickname": "Advanced",
            "learningStage": {
                "id": 1,
                "nickname": "U12"
            }
        },
        "creator": {
            "id": 5,
            "name": "Admin User"
        },
        "created_at": "2025-02-01T12:00:00.000000Z",
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname has already been taken."
        ],
        "level_id": [
            "The selected level id is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/performance/skill-categories

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school where the category will be created. Example: aleacademy

Body Parameters

nickname   string   

Unique nickname for the category. Example: Technical Skills

description   string   

Description of the category. Example: Skills related to technical aspects.

level_id   integer   

The ID of the level associated with this category. Must exist in levels. Example: 3

Skill Categories - Show

requires authentication

Returns a single skill category scoped to the given school, including school, level (with learningStage), and skills. Required permission: administrative.performance.skillCategory.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 1,
    "nickname": "Technical Skills",
    "description": "Skills related to technical aspects.",
    "school": {
        "id": 2,
        "nickname": "Wildcats Academy"
    },
    "level": {
        "id": 3,
        "nickname": "Advanced",
        "learningStage": {
            "id": 1,
            "nickname": "U12"
        }
    },
    "skills": [
        {
            "id": 10,
            "nickname": "Dribbling"
        },
        {
            "id": 11,
            "nickname": "Passing"
        }
    ],
    "created_at": "2025-02-01T12:00:00.000000Z",
    "updated_at": "2025-02-01T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Skill category not found"
}
 

Request      

GET api/schools/{school_slug}/performance/skill-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the category. Example: aleacademy

id   integer   

The ID of the skill category to retrieve. Example: 1

Skill Categories - Update

requires authentication

Updates a skill category scoped to the given school. Partial updates are supported. Required permission: administrative.performance.skillCategory.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Technical Skills\",
    \"description\": \"Updated description.\",
    \"level_id\": 3
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories/1"
);

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

let body = {
    "nickname": "Technical Skills",
    "description": "Updated description.",
    "level_id": 3
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Skill category updated successfully",
    "category": {
        "id": 1,
        "nickname": "Technical Skills",
        "description": "Updated description.",
        "school": {
            "id": 2,
            "nickname": "Wildcats Academy"
        },
        "level": {
            "id": 3,
            "nickname": "Advanced"
        },
        "created_at": "2025-02-01T12:00:00.000000Z",
        "updated_at": "2025-02-01T12:30:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Skill category not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname has already been taken."
        ],
        "level_id": [
            "The selected level id is invalid."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/performance/skill-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the category. Example: aleacademy

id   integer   

The ID of the skill category to update. Example: 1

Body Parameters

nickname   string  optional  

The unique nickname for the category. Only if provided. Example: Technical Skills

description   string  optional  

The description for the category. Only if provided. Example: Updated description.

level_id   integer  optional  

The ID of the level associated with this category. Must exist in levels. Only if provided. Example: 3

Skill Categories - Archive

requires authentication

Archives a skill category by setting status=archived. Data is not deleted. Required permission: administrative.performance.skillCategory.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skill-categories/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Skill category archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Skill category not found"
}
 

Request      

DELETE api/schools/{school_slug}/performance/skill-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the category. Example: aleacademy

id   integer   

The ID of the skill category to archive. Example: 1

Measurement Criteria - List criteria

requires authentication

Returns measurement criteria with optional relations. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname list). Required permission: administrative.performance.measurementCriteria.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/measurement-criteria?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=labore&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=archived" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/measurement-criteria"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "labore",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "archived",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "measurement_criteria": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Speed",
                "description": "Measurement based on velocity",
                "measurement_units": [
                    {
                        "id": 1,
                        "nickname": "km/h",
                        "description": "Kilometers per hour"
                    },
                    {
                        "id": 2,
                        "nickname": "mph",
                        "description": "Miles per hour"
                    }
                ],
                "created_at": "2025-02-01T12:00:00.000000Z",
                "updated_at": "2025-02-01T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 20
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "creator.name"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description"
        ]
    }
}
 

Example response (200):


{
    "measurement_criteria": [
        {
            "id": 1,
            "nickname": "Speed"
        },
        {
            "id": 2,
            "nickname": "Endurance"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/performance/measurement-criteria

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school context. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only criteria with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by when filters are applied. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction when filters are applied. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: archived

Measurement Criteria - Units by nickname

requires authentication

Retrieve a measurement criteria by its nickname within the given school, including all associated measurement units. Required permission: administrative.performance.measurementCriteria.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/measurement-criteria/units/speed" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/measurement-criteria/units/speed"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "measurement_criteria": {
        "id": 3,
        "nickname": "Speed",
        "description": "Measurement based on velocity",
        "measurement_units": [
            {
                "id": 1,
                "nickname": "km/h",
                "description": "Kilometers per hour"
            },
            {
                "id": 2,
                "nickname": "mph",
                "description": "Miles per hour"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Measurement Criteria not found"
}
 

Request      

GET api/schools/{school_slug}/performance/measurement-criteria/units/{nickname}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school context. Example: aleacademy

nickname   string   

The nickname of the measurement criteria. Example: speed

Skills - List

requires authentication

Returns skills scoped to the given school. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname list). Required permission: administrative.performance.skill.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=aut&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=archived" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "aut",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "archived",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "skills": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Dribbling",
                "description": "A key skill in soccer.",
                "school": {
                    "id": 2,
                    "nickname": "Wildcats Academy"
                },
                "skill_category": {
                    "id": 1,
                    "nickname": "Technical Skills"
                },
                "measurement_criteria": {
                    "id": 1,
                    "nickname": "Speed"
                },
                "creator": {
                    "id": 5,
                    "name": "Admin User"
                },
                "status": "active",
                "created_at": "2025-02-01T12:00:00.000000Z",
                "updated_at": "2025-02-01T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 30
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "school.nickname",
            "skillCategory.nickname",
            "measurement_criteria.nickname",
            "creator.name",
            "status"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description",
            "status"
        ]
    }
}
 

Example response (200):


{
    "skills": [
        {
            "id": 1,
            "nickname": "Dribbling"
        },
        {
            "id": 2,
            "nickname": "Passing"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/performance/skills

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope skills. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only skills with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by when filters are applied. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction when filters are applied. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: archived

Skills - Create

requires authentication

Creates a new skill scoped to the given school. Required permission: administrative.performance.skill.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Dribbling\",
    \"description\": \"A key skill in soccer.\",
    \"measurement_criteria_id\": 1,
    \"skill_category_id\": 1,
    \"status\": \"active\",
    \"level_ids\": [
        2,
        3
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills"
);

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

let body = {
    "nickname": "Dribbling",
    "description": "A key skill in soccer.",
    "measurement_criteria_id": 1,
    "skill_category_id": 1,
    "status": "active",
    "level_ids": [
        2,
        3
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Skill created successfully",
    "skill": {
        "id": 1,
        "nickname": "Dribbling",
        "description": "A key skill in soccer.",
        "school": {
            "id": 2,
            "nickname": "Wildcats Academy"
        },
        "skill_category": {
            "id": 1,
            "nickname": "Technical Skills"
        },
        "measurement_criteria": {
            "id": 1,
            "nickname": "Speed"
        },
        "creator": {
            "id": 5,
            "name": "Admin User"
        },
        "status": "active",
        "created_at": "2025-02-01T12:00:00.000000Z",
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "This nickname already exists in the system."
        ],
        "measurement_criteria_id": [
            "The selected measurement_criteria_id does not exist."
        ],
        "skill_category_id": [
            "The selected skill_category_id does not exist."
        ],
        "status": [
            "The status must be one of the following: active, inactive, archived."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/performance/skills

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school where the skill will be created. Example: aleacademy

Body Parameters

nickname   string   

Unique nickname for the skill. Example: Dribbling

description   string  optional  

The description of the skill. Nullable. Example: A key skill in soccer.

measurement_criteria_id   integer   

The measurement criteria id. Must exist in measurement_criterias. Example: 1

skill_category_id   integer   

The skill category id. Must exist in skill_categories. Example: 1

status   string   

One of: active, inactive, archived. Example: active

level_ids   string[]  optional  

optional Level IDs to associate to this skill (polymorphic).

*   integer  optional  

Each level ID must exist. Example: 9

Skills - Show

requires authentication

Returns a single skill scoped to the given school, including school, skill category, creator, and measurement criteria. Required permission: administrative.performance.skill.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 1,
    "nickname": "Dribbling",
    "description": "A key skill in soccer.",
    "school": {
        "id": 2,
        "nickname": "Wildcats Academy"
    },
    "skill_category": {
        "id": 1,
        "nickname": "Technical Skills"
    },
    "measurement_criteria": {
        "id": 1,
        "nickname": "Speed"
    },
    "creator": {
        "id": 5,
        "name": "Admin User"
    },
    "status": "active",
    "created_at": "2025-02-01T12:00:00.000000Z",
    "updated_at": "2025-02-01T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Skill not found"
}
 

Request      

GET api/schools/{school_slug}/performance/skills/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the skill. Example: aleacademy

id   integer   

The ID of the skill to retrieve. Example: 1

Skills - Update

requires authentication

Updates a skill scoped to the given school. Partial updates are supported. Required permission: administrative.performance.skill.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Dribbling\",
    \"description\": \"Updated description.\",
    \"measurement_criteria_id\": 1,
    \"skill_category_id\": 1,
    \"status\": \"active\",
    \"level_ids\": [
        2,
        3
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills/1"
);

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

let body = {
    "nickname": "Dribbling",
    "description": "Updated description.",
    "measurement_criteria_id": 1,
    "skill_category_id": 1,
    "status": "active",
    "level_ids": [
        2,
        3
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Skill updated successfully",
    "skill": {
        "id": 1,
        "nickname": "Dribbling",
        "description": "Updated description.",
        "school": {
            "id": 2,
            "nickname": "Wildcats Academy"
        },
        "skill_category": {
            "id": 1,
            "nickname": "Technical Skills"
        },
        "creator": {
            "id": 5,
            "name": "Admin User"
        },
        "status": "active",
        "created_at": "2025-02-01T12:00:00.000000Z",
        "updated_at": "2025-02-01T12:30:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Skill not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The skill nickname must be unique."
        ],
        "skill_category_id": [
            "The selected skill category does not exist."
        ],
        "measurement_criteria_id": [
            "The selected measurement criteria does not exist."
        ],
        "status": [
            "The status must be either active, inactive, or archived."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/performance/skills/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the skill. Example: aleacademy

id   integer   

The ID of the skill to update. Example: 1

Body Parameters

nickname   string  optional  

The unique nickname for the skill. Only if provided. Example: Dribbling

description   string  optional  

The description of the skill. Nullable. Only if provided. Example: Updated description.

measurement_criteria_id   integer  optional  

The measurement criteria id. Must exist in measurement_criterias if provided; can be null. Example: 1

skill_category_id   integer  optional  

The skill category id. Must exist in skill_categories if provided. Example: 1

status   string  optional  

One of: active, inactive, archived. Only if provided. Example: active

level_ids   string[]  optional  

Level IDs to associate (replaces existing).

*   integer  optional  

Each level ID must exist. Example: 2

Skills - Archive

requires authentication

Archives a skill by setting status=archived. Data is not deleted. Required permission: administrative.performance.skill.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/skills/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Skill archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Skill not found"
}
 

Request      

DELETE api/schools/{school_slug}/performance/skills/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the skill. Example: aleacademy

id   integer   

The ID of the skill to archive. Example: 1

Exercises - List exercises for a school

requires authentication

Returns exercises scoped to the given school. Supports table view (paginated with metadata) or select view (lightweight id/nickname list). Required permission: administrative.performance.exercise.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=quos&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive&filters%5Bskill_id%5D=3" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "quos",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
    "filters[skill_id]": "3",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "exercises": {
        "current_page": 1,
        "data": [
            {
                "id": 10,
                "nickname": "Sprint Training",
                "description": "Speed and endurance training.",
                "level": {
                    "id": 2,
                    "nickname": "Advanced"
                },
                "measurement_criteria": {
                    "id": 1,
                    "nickname": "Speed"
                },
                "status": "active",
                "creator": {
                    "id": 5,
                    "name": "Admin User"
                },
                "skills": [
                    {
                        "id": 3,
                        "nickname": "Speed Control"
                    }
                ],
                "media": [
                    {
                        "path": "https://s3.../exercise123.jpg",
                        "mime_type": "image/jpeg"
                    }
                ],
                "created_at": "2025-02-01T12:00:00.000000Z",
                "updated_at": "2025-02-01T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 50
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "status",
            "level.nickname",
            "creator.name"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description",
            "status"
        ]
    }
}
 

Example response (200):


{
    "exercises": [
        {
            "id": 10,
            "nickname": "Sprint Training"
        },
        {
            "id": 11,
            "nickname": "Agility Ladder"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/performance/exercises

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope exercises. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only exercises with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by when filters are applied. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction when filters are applied. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: inactive

filters[skill_id]   integer  optional  

Filter exercises related to a specific skill ID. Example: 3

Exercises - Create

requires authentication

Create a new exercise for the given school. Attaches optional skills and uploads optional media files. Required permission: administrative.performance.exercise.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Sprint Training\",
    \"description\": \"Speed and endurance training.\",
    \"level_ids\": [
        2,
        3
    ],
    \"skills\": [
        3,
        5,
        8
    ],
    \"measurement_criteria_id\": 1,
    \"media\": null
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises"
);

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

let body = {
    "nickname": "Sprint Training",
    "description": "Speed and endurance training.",
    "level_ids": [
        2,
        3
    ],
    "skills": [
        3,
        5,
        8
    ],
    "measurement_criteria_id": 1,
    "media": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Exercise created successfully",
    "exercise": {
        "id": 10,
        "nickname": "Sprint Training",
        "description": "Speed and endurance training.",
        "level": {
            "id": 2,
            "nickname": "Advanced"
        },
        "measurement_criteria_id": 1,
        "creator_id": 5,
        "school_id": 3,
        "media": [
            {
                "path": "https://s3.../exercise123.jpg",
                "mime_type": "image/jpeg"
            }
        ],
        "skills": [
            {
                "id": 3,
                "nickname": "Speed Control"
            },
            {
                "id": 5,
                "nickname": "Endurance"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "level_id": [
            "The selected level_id is invalid."
        ],
        "measurement_criteria_id": [
            "The selected measurement_criteria_id is invalid."
        ],
        "skills.0": [
            "The selected skills.0 is invalid."
        ],
        "media.0": [
            "The media.0 must be a file."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/performance/exercises

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to associate the exercise with. Example: aleacademy

Body Parameters

nickname   string   

The exercise nickname. Example: Sprint Training

description   string  optional  

A brief description of the exercise. Example: Speed and endurance training.

level_ids   string[]  optional  

optional Level IDs to associate to this exercise (polymorphic).

*   integer  optional  

Each level ID must exist. Example: 3

skills   string[]  optional  

IDs of skills to attach to the exercise.

*   integer  optional  

Each skill ID must exist. Example: 11

measurement_criteria_id   integer   

The measurement criteria ID associated with this exercise. Example: 1

media   file[]  optional  

Media files (images, videos, PDFs). Use multipart/form-data.

Exercises - Show

requires authentication

Get a single exercise by ID within the given school, including level, skills, media, measurement criteria, and creator information. Required permission: administrative.performance.exercise.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 10,
    "nickname": "Sprint Training",
    "description": "Speed and endurance training.",
    "level": {
        "id": 2,
        "nickname": "Advanced"
    },
    "measurement_criteria": {
        "id": 1,
        "nickname": "Speed"
    },
    "status": "active",
    "creator": {
        "id": 5,
        "name": "Admin User"
    },
    "skills": [
        {
            "id": 3,
            "nickname": "Speed Control"
        },
        {
            "id": 5,
            "nickname": "Endurance"
        }
    ],
    "media": [
        {
            "path": "https://s3.../exercise123.jpg",
            "mime_type": "image/jpeg"
        }
    ],
    "created_at": "2025-02-01T12:00:00.000000Z",
    "updated_at": "2025-02-01T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Exercise not found"
}
 

Request      

GET api/schools/{school_slug}/performance/exercises/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aleacademy

id   integer   

The ID of the exercise to retrieve. Example: 10

Exercises - Update

requires authentication

Update an existing exercise within the given school, including details, skills, and media. If media_ids is provided, only those media records are kept; any others are deleted. If media_ids is omitted, all existing media are removed. Required permission: administrative.performance.exercise.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Sprint Training\",
    \"description\": \"Speed and endurance training.\",
    \"level_ids\": [
        2,
        3
    ],
    \"skills\": [
        3,
        5,
        8
    ],
    \"measurement_criteria_id\": 1,
    \"media_ids\": [
        1,
        2
    ],
    \"media\": null
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises/10"
);

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

let body = {
    "nickname": "Sprint Training",
    "description": "Speed and endurance training.",
    "level_ids": [
        2,
        3
    ],
    "skills": [
        3,
        5,
        8
    ],
    "measurement_criteria_id": 1,
    "media_ids": [
        1,
        2
    ],
    "media": null
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Exercise updated successfully",
    "exercise": {
        "id": 10,
        "nickname": "Sprint Training",
        "description": "Speed and endurance training.",
        "level": {
            "id": 2,
            "nickname": "Advanced"
        },
        "creator": {
            "id": 5,
            "name": "Admin User"
        },
        "media": [
            {
                "id": 1,
                "path": "https://s3.../exercise123.jpg",
                "mime_type": "image/jpeg"
            }
        ],
        "skills": [
            {
                "id": 3,
                "nickname": "Speed Control"
            },
            {
                "id": 5,
                "nickname": "Endurance"
            }
        ],
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Exercise not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname has already been taken."
        ],
        "level_id": [
            "The selected level_id is invalid."
        ],
        "measurement_criteria_id": [
            "The selected measurement_criteria_id is invalid."
        ],
        "skills.0": [
            "The selected skills.0 is invalid."
        ],
        "media.0": [
            "The media.0 must be a file."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/performance/exercises/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aleacademy

id   integer   

The ID of the exercise to update. Example: 10

Body Parameters

nickname   string  optional  

The updated exercise nickname. Example: Sprint Training

description   string  optional  

A brief description. Example: Speed and endurance training.

level_ids   string[]  optional  

Level IDs to associate (replaces existing).

*   integer  optional  

Each level ID must exist. Example: 10

skills   string[]  optional  

IDs of skills to set on the exercise (replaces existing).

*   integer  optional  

Each skill ID must exist. Example: 3

measurement_criteria_id   integer  optional  

The measurement criteria ID (nullable). Example: 1

media_ids   string[]  optional  

IDs of existing media to keep. Any others will be deleted. If omitted, all existing media are removed.

media   file[]  optional  

Additional media files to upload (images, videos, PDFs). Use multipart/form-data.

Exercises - Archive

requires authentication

Archive an exercise by setting its status to archived. This does not permanently delete the record. Required permission: administrative.performance.exercise.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/exercises/10"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Exercise archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Exercise not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/performance/exercises/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aleacademy

id   integer   

The ID of the exercise to archive. Example: 10

Evaluation Templates - List templates for a school

requires authentication

Returns evaluation templates scoped to the given school. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname list). Required permission: administrative.performance.evaluationTemplate.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=harum&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "harum",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "templates": {
        "current_page": 1,
        "data": [
            {
                "id": 5,
                "nickname": "Speed Training Plan",
                "description": "A structured plan for sprint performance improvement.",
                "level": {
                    "id": 2,
                    "name": "Advanced"
                },
                "status": "active",
                "creator": {
                    "id": 7,
                    "name": "Admin User"
                },
                "skills": [
                    {
                        "id": 3,
                        "nickname": "Acceleration"
                    }
                ],
                "exercises": [
                    {
                        "id": 10,
                        "nickname": "Sprint Drills"
                    }
                ],
                "created_at": "2025-02-01T12:00:00.000000Z",
                "updated_at": "2025-02-01T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 25
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "status",
            "level.name",
            "creator.name"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description",
            "status"
        ]
    }
}
 

Example response (200):


{
    "templates": [
        {
            "id": 5,
            "nickname": "Speed Training Plan"
        },
        {
            "id": 6,
            "nickname": "Endurance Block"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/performance/evaluation-templates

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope templates. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only templates with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by when filters are applied. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction when filters are applied. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: inactive

Evaluation Templates - Create

requires authentication

Create a new evaluation template for the given school. Required permission: administrative.performance.evaluationTemplate.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Speed Training\",
    \"description\": \"A training plan for sprint performance.\",
    \"level_id\": 2,
    \"status\": \"active\",
    \"skills\": [
        1,
        2,
        3
    ],
    \"exercises\": [
        10,
        15,
        22
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates"
);

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

let body = {
    "nickname": "Speed Training",
    "description": "A training plan for sprint performance.",
    "level_id": 2,
    "status": "active",
    "skills": [
        1,
        2,
        3
    ],
    "exercises": [
        10,
        15,
        22
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Evaluation template created successfully",
    "template": {
        "id": 5,
        "nickname": "Speed Training",
        "description": "A training plan for sprint performance.",
        "level": {
            "id": 2,
            "name": "Advanced"
        },
        "status": "active",
        "school_id": 3,
        "creator": {
            "id": 7,
            "name": "Admin User"
        },
        "skills": [
            {
                "id": 1,
                "nickname": "Acceleration"
            }
        ],
        "exercises": [
            {
                "id": 10,
                "nickname": "Sprint Drills"
            }
        ],
        "created_at": "2025-03-10T12:00:00.000000Z",
        "updated_at": "2025-03-10T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "level_id": [
            "The selected level_id is invalid."
        ],
        "status": [
            "The status must be one of active, inactive, archived."
        ],
        "skills.0": [
            "The selected skills.0 is invalid."
        ],
        "exercises.0": [
            "The selected exercises.0 is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/performance/evaluation-templates

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to associate the template with. Example: aleacademy

Body Parameters

nickname   string   

The template name. Example: Speed Training

description   string  optional  

A short description of the template. Example: A training plan for sprint performance.

level_id   integer   

The ID of the level associated with this template. Example: 2

status   string   

One of: active, inactive, archived. Example: active

skills   string[]  optional  

IDs of skills to attach to the template.

*   integer  optional  

Each skill ID must exist. Example: 4

exercises   string[]  optional  

IDs of exercises to attach to the template.

*   integer  optional  

Each exercise ID must exist. Example: 2

Evaluation Templates - Show

requires authentication

Get a single evaluation template by ID within the given school. Combines skills and exercises under items, including each item's calculated percentage and measurement criteria. Required permission: administrative.performance.evaluationTemplate.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates/5"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "template": {
        "id": 5,
        "nickname": "Speed Training Plan",
        "description": "A structured plan for sprint performance improvement.",
        "level": {
            "id": 2,
            "name": "Advanced"
        },
        "status": "active",
        "creator": {
            "id": 7,
            "name": "Admin User"
        },
        "items": [
            {
                "type": "skill",
                "id": 3,
                "nickname": "Acceleration",
                "percentage": 50,
                "measurementCriteria": [
                    {
                        "id": 1,
                        "nickname": "Max Speed",
                        "measurementUnits": [
                            {
                                "id": 1,
                                "nickname": "km/h"
                            }
                        ]
                    }
                ]
            },
            {
                "type": "exercise",
                "id": 10,
                "nickname": "Sprint Drills",
                "percentage": 50,
                "measurementCriteria": []
            }
        ],
        "created_at": "2025-02-01T12:00:00.000000Z",
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Evaluation template not found or not associated with this school"
}
 

Request      

GET api/schools/{school_slug}/performance/evaluation-templates/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aleacademy

id   integer   

The template ID. Example: 5

Evaluation Templates - Update

requires authentication

Update an existing template within the given school. Required permission: administrative.performance.evaluationTemplate.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Sprint Performance Plan\",
    \"description\": \"A structured plan for speed improvement.\",
    \"level_id\": 2,
    \"status\": \"active\",
    \"skills\": [
        1,
        2,
        3
    ],
    \"exercises\": [
        10,
        15,
        22
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/performance/evaluation-templates/5"
);

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

let body = {
    "nickname": "Sprint Performance Plan",
    "description": "A structured plan for speed improvement.",
    "level_id": 2,
    "status": "active",
    "skills": [
        1,
        2,
        3
    ],
    "exercises": [
        10,
        15,
        22
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Evaluation template updated successfully",
    "template": {
        "id": 5,
        "nickname": "Sprint Performance Plan",
        "description": "A structured plan for speed improvement.",
        "level": {
            "id": 2,
            "name": "Advanced"
        },
        "status": "active",
        "creator": {
            "id": 7,
            "name": "Admin User"
        },
        "skills": [
            {
                "id": 3,
                "nickname": "Acceleration"
            }
        ],
        "exercises": [
            {
                "id": 10,
                "nickname": "Sprint Drills"
            }
        ],
        "updated_at": "2025-02-01T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Evaluation template not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "level_id": [
            "The selected level_id is invalid."
        ],
        "status": [
            "The status must be one of active, inactive, archived."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/performance/evaluation-templates/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aleacademy

id   integer   

The template ID to update. Example: 5

Body Parameters

nickname   string  optional  

The new template name. Example: Sprint Performance Plan

description   string  optional  

A short description. Example: A structured plan for speed improvement.

level_id   integer  optional  

The associated level ID. Example: 2

status   string  optional  

One of: active, inactive, archived. Example: active

skills   string[]  optional  

IDs of skills to set on the template (replaces existing).

*   integer  optional  

Each skill ID must exist. Example: 12

exercises   string[]  optional  

IDs of exercises to set on the template (replaces existing).

*   integer  optional  

Each exercise ID must exist. Example: 12

Report

Generate report (sync)

requires authentication

Generates a report immediately and stores it in the reports registry. If a matching report (same type, reference, range and calculation version) already exists, the existing report can be returned unless force_refresh=true.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/at/reports/generate" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"coach_productivity\",
    \"reference_id\": \"all\",
    \"start_date\": \"2026-01-01\",
    \"end_date\": \"2026-01-31\",
    \"evaluation_type\": \"group\",
    \"force_refresh\": false
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/at/reports/generate"
);

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

let body = {
    "type": "coach_productivity",
    "reference_id": "all",
    "start_date": "2026-01-01",
    "end_date": "2026-01-31",
    "evaluation_type": "group",
    "force_refresh": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Report generated",
    "report": {
        "id": 184,
        "name": null,
        "report_type": "coach_productivity",
        "canonical_report_type": "coach_productivity",
        "reference_id": "all",
        "reference_type": "coach",
        "school_id": 1,
        "date_range_start": "2026-01-01T00:00:00.000000Z",
        "date_range_end": "2026-01-31T00:00:00.000000Z",
        "calculated_data": {
            "scope": {
                "school_id": 1,
                "reference_id": "all",
                "reference_type": "coach",
                "start_date": "2026-01-01",
                "end_date": "2026-01-31",
                "period_days": 31
            },
            "summary": {
                "coaches_count": 4,
                "sessions_total": 42,
                "sessions_operational": 39,
                "sessions_worked": 31,
                "sessions_pending_attendance": 8,
                "sessions_canceled": 3,
                "worked_hours": 58.5,
                "pending_hours": 12.5,
                "avg_players_per_hour": 2.67,
                "avg_active_players_per_hour": 2.24,
                "occupancy_rate_percent": 78.2,
                "schedule_utilization_percent": 64.1,
                "productivity_index_percent": 50.13
            },
            "schedule": {
                "range_expected_minutes": 5940,
                "range_expected_hours": 99,
                "weekly_slots": [
                    {
                        "day_of_week": "monday",
                        "start_time": "14:00",
                        "end_time": "17:00",
                        "duration_minutes": 180,
                        "expected_minutes_in_range": 720,
                        "coach_id": 11,
                        "coach_name": "Coach One"
                    }
                ]
            },
            "distributions": {
                "sessions_by_status": {
                    "completed": 39,
                    "canceled": 3
                },
                "sessions_by_type": {
                    "private": 10,
                    "semi-private": 14,
                    "group": 18
                },
                "attendance_by_status": {
                    "present": 156,
                    "absent": 22,
                    "excused": 8
                }
            },
            "breakdown_by_group": [
                {
                    "group_id": 19,
                    "group_name": "Group A",
                    "sessions_total": 16,
                    "sessions_worked": 12,
                    "sessions_pending_attendance": 3
                }
            ],
            "breakdown_by_location": [
                {
                    "location_id": 3,
                    "location_name": "Court A",
                    "sessions_total": 22,
                    "sessions_worked": 16,
                    "sessions_pending_attendance": 5
                }
            ],
            "session_details": [
                {
                    "id": 901,
                    "date": "2026-01-12",
                    "type": "group",
                    "status": "completed",
                    "attendance_taken": true,
                    "worked_minutes": 60,
                    "pending_minutes": 0
                }
            ],
            "coaches": [
                {
                    "coach_id": 11,
                    "coach_name": "Coach One",
                    "schedule": {
                        "range_expected_minutes": 2340,
                        "range_expected_hours": 39,
                        "weekly_slots": [
                            {
                                "day_of_week": "monday",
                                "start_time": "14:00",
                                "end_time": "17:00",
                                "duration_minutes": 180
                            }
                        ]
                    },
                    "summary": {
                        "sessions_total": 20,
                        "sessions_operational": 18,
                        "sessions_worked": 14,
                        "sessions_pending_attendance": 4,
                        "sessions_canceled": 2,
                        "worked_hours": 24.75,
                        "pending_hours": 5.5,
                        "avg_players_per_hour": 2.5,
                        "occupancy_rate_percent": 81.43,
                        "schedule_utilization_percent": 69.12,
                        "productivity_index_percent": 56.27
                    },
                    "breakdown_by_group": [
                        {
                            "group_id": 19,
                            "group_name": "Group A",
                            "sessions_total": 10,
                            "sessions_worked": 8
                        }
                    ],
                    "breakdown_by_location": [
                        {
                            "location_id": 3,
                            "location_name": "Court A",
                            "sessions_total": 12,
                            "sessions_worked": 9
                        }
                    ],
                    "session_details": [
                        {
                            "id": 901,
                            "date": "2026-01-12",
                            "type": "group",
                            "attendance_taken": true,
                            "worked_minutes": 60
                        }
                    ],
                    "insights": {
                        "top_players_by_presence": [
                            {
                                "player_id": 120,
                                "player_name": "Player One",
                                "present_count": 8
                            }
                        ],
                        "top_active_players_by_presence": [
                            {
                                "player_id": 120,
                                "player_name": "Player One",
                                "present_count": 8
                            }
                        ]
                    }
                }
            ]
        },
        "filters": {
            "type": "coach_productivity",
            "reference_id": "all",
            "reference_type": "coach",
            "start_date": "2026-01-01",
            "end_date": "2026-01-31",
            "evaluation_type": "group"
        },
        "request_hash": "fd4b4f4f5ca9f1f9f5b5a2f5b2eaf6d157f3f91c5a3ebebf3dbf6a0b03357f23",
        "status": "completed",
        "calculation_version": 1,
        "generated_by": 7,
        "queued_at": null,
        "started_at": null,
        "completed_at": "2026-02-25T17:21:14.000000Z",
        "error_message": null,
        "last_exported_at": null,
        "created_at": "2026-02-25T17:21:14.000000Z",
        "updated_at": "2026-02-25T17:21:14.000000Z"
    },
    "summary": {
        "type": "coach_productivity",
        "reference_id": "all",
        "reference_type": "coach",
        "date_range": {
            "start_date": "2026-01-01",
            "end_date": "2026-01-31"
        },
        "status": "completed"
    }
}
 

Example response (200, Location v2 example (school_site_productivity)):


{
    "message": "Report generated",
    "report": {
        "id": 211,
        "report_type": "school_site_productivity",
        "canonical_report_type": "school_site_productivity",
        "reference_id": "all",
        "reference_type": "location",
        "calculated_data": {
            "scope": {
                "school_id": 1,
                "reference_id": "all",
                "reference_type": "location",
                "date_range": {
                    "start_date": "2026-02-01",
                    "end_date": "2026-02-28"
                },
                "calculation_version": 1
            },
            "summary": {
                "school_sites_count": 3,
                "sessions_total": 140,
                "sessions_worked": 112,
                "sessions_pending_attendance": 18,
                "sessions_canceled": 10,
                "worked_hours": 196.5,
                "pending_hours": 32,
                "total_players_present": 674,
                "estimated_capacity_total": 860,
                "occupancy_rate_percent": 78.37,
                "total_revenue": 21450,
                "productivity_index_percent": 24.94
            },
            "distributions": {
                "sessions_by_status": {
                    "completed": 130,
                    "canceled": 10
                },
                "sessions_by_type": {
                    "group": 62,
                    "semi-private": 41,
                    "private": 37
                },
                "attendances_by_status": {
                    "present": 674,
                    "absent": 81,
                    "excused": 25
                }
            },
            "breakdown_by_location": [
                {
                    "school_site_id": 1,
                    "school_site_name": "North Campus",
                    "sessions_total": 58,
                    "sessions_worked": 47,
                    "sessions_pending_attendance": 7,
                    "total_revenue": 8200
                }
            ],
            "location_details": [],
            "session_details": []
        }
    }
}
 

Example response (200, Performance v2 example (player_performance)):


{
    "message": "Report generated",
    "report": {
        "id": 212,
        "report_type": "player_performance",
        "canonical_report_type": "player_performance",
        "reference_id": "all",
        "reference_type": "player",
        "calculated_data": {
            "scope": {
                "school_id": 1,
                "reference_id": "all",
                "reference_type": "player",
                "date_range": {
                    "start_date": "2026-02-01",
                    "end_date": "2026-02-28"
                },
                "calculation_version": 1
            },
            "summary": {
                "players_analyzed": 45,
                "players_improving": 31,
                "players_declining": 8,
                "players_stable": 6,
                "average_improvement_percent": 7.42,
                "best_improvement_percent": 28.12,
                "worst_improvement_percent": -11.4
            },
            "distributions": {
                "improvement_ranges": {
                    "below_minus_10": 1,
                    "minus_10_to_0": 7,
                    "0_to_10": 24,
                    "above_10": 13
                }
            },
            "breakdown_by_player": [
                {
                    "player_id": 55,
                    "player_name": "Player Example",
                    "first_evaluation_score": 63.4,
                    "last_evaluation_score": 71.8,
                    "improvement_percentage": 13.25,
                    "evaluations_count": 5,
                    "trend": "improving"
                }
            ],
            "player_details": []
        }
    }
}
 

Example response (200, Ecommerce v2 example (ecommerce_revenue_overview)):


{
    "message": "Report generated",
    "report": {
        "id": 213,
        "report_type": "ecommerce_revenue_overview",
        "canonical_report_type": "ecommerce_revenue_overview",
        "reference_id": "all",
        "reference_type": "location",
        "calculated_data": {
            "scope": {
                "school_id": 1,
                "reference_id": "all",
                "reference_type": "location",
                "date_range": {
                    "start_date": "2026-02-01",
                    "end_date": "2026-02-28"
                },
                "calculation_version": 1
            },
            "summary": {
                "orders_revenue": 2400,
                "invoices_revenue": 1960,
                "subscriptions_estimated_revenue": 13250,
                "total_revenue": 17610,
                "orders_count": 38,
                "invoices_count": 44,
                "subscriptions_count": 110
            },
            "distributions": {
                "orders_by_status": {
                    "paid": 33,
                    "pending": 5
                },
                "invoices_by_status": {
                    "paid": 36,
                    "overdue": 8
                },
                "subscriptions_by_status": {
                    "active": 110
                },
                "revenue_by_source": {
                    "orders": 2400,
                    "invoices": 1960,
                    "subscriptions_estimated": 13250
                }
            },
            "breakdown_by_source": [
                {
                    "source": "orders",
                    "records_count": 38,
                    "revenue": 2400,
                    "share_percent": 13.63
                }
            ],
            "source_details": []
        }
    }
}
 

Example response (200, Executive v2 example (executive_monthly_overview)):


{
    "message": "Report generated",
    "report": {
        "id": 214,
        "report_type": "executive_monthly_overview",
        "canonical_report_type": "executive_monthly_overview",
        "reference_id": "all",
        "reference_type": "location",
        "calculated_data": {
            "scope": {
                "school_id": 1,
                "reference_id": "all",
                "reference_type": "location",
                "date_range": {
                    "start_date": "2026-02-01",
                    "end_date": "2026-02-28"
                },
                "calculation_version": 1
            },
            "summary": {
                "total_sessions": 82,
                "present_attendances": 305,
                "unique_training_players": 96,
                "active_subscriptions": 110,
                "total_revenue": 17610
            },
            "distributions": {
                "sessions_by_status": {
                    "completed": 78,
                    "canceled": 4
                },
                "sessions_by_type": {
                    "private": 19,
                    "semi-private": 21,
                    "group": 42
                },
                "active_subscriptions_by_location": {
                    "1": 80,
                    "2": 30
                },
                "revenue_by_source": {
                    "orders": 2400,
                    "invoices": 1960,
                    "subscriptions_estimated": 13250
                }
            },
            "breakdown_by_source": [
                {
                    "source": "orders",
                    "revenue": 2400,
                    "share_percent": 13.63
                }
            ],
            "breakdown_by_location": [
                {
                    "school_site_id": 1,
                    "school_site_name": "North Campus",
                    "sessions_total": 52,
                    "active_subscriptions": 80,
                    "estimated_subscription_revenue": 9800
                }
            ],
            "location_details": []
        }
    }
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "type": [
            "The selected type is invalid."
        ],
        "reference_id": [
            "The selected reference_id is not valid for this school and report type."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/reports/generate

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: at

slug   string   

School slug. Example: "my-academy"

Body Parameters

type   string   

Report type. Use /reports/get/types for full catalog. Example: coach_productivity

reference_id   string   

Entity id or "all" (when allowed by type). Example: all

start_date   date   

Start date (Y-m-d). Example: 2026-01-01

end_date   date   

End date (Y-m-d). Example: 2026-01-31

evaluation_type   string  optional  

optional Only for evaluation_average. Example: group

force_refresh   boolean  optional  

optional Force recalculation. Example: false

Generate report (async)

requires authentication

Queues report generation and returns a run UUID for status tracking. Use GET /reports/runs/{runUuid} to poll status until completed or failed.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/ipsam/reports/generate/async" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"coach_productivity\",
    \"reference_id\": \"all\",
    \"start_date\": \"2026-01-01\",
    \"end_date\": \"2026-01-31\",
    \"evaluation_type\": \"group\",
    \"force_refresh\": false,
    \"requested_format\": \"pdf\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/ipsam/reports/generate/async"
);

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

let body = {
    "type": "coach_productivity",
    "reference_id": "all",
    "start_date": "2026-01-01",
    "end_date": "2026-01-31",
    "evaluation_type": "group",
    "force_refresh": false,
    "requested_format": "pdf"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (202):


{
    "message": "Report queued",
    "run": {
        "run_uuid": "22e5f40e-6611-4ad7-a438-10f5258998ec",
        "status": "queued",
        "report_type": "executive_monthly_overview",
        "requested_format": "pdf",
        "queued_at": "2026-02-25T17:25:48.000000Z"
    }
}
 

Request      

POST api/schools/{school_slug}/reports/generate/async

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: ipsam

slug   string   

School slug. Example: "my-academy"

Body Parameters

type   string   

Report type. Example: coach_productivity

reference_id   string   

Entity id or "all". Example: all

start_date   date   

Start date (Y-m-d). Example: 2026-01-01

end_date   date   

End date (Y-m-d). Example: 2026-01-31

evaluation_type   string  optional  

Only for evaluation_average. Allowed values: group, player. Ignored for other report types. Example: group

Must be one of:
  • group
  • player
force_refresh   boolean  optional  

When true, bypass cached reports and force recalculation. Example: false

requested_format   string  optional  

optional Preferred output format. Example: pdf

Report types

requires authentication

Returns the canonical report catalog used by backend and frontend. Includes whether reference_id=all is supported and any accepted aliases.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/autem/reports/get/types" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/autem/reports/get/types"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "report_types": [
        {
            "type": "school_site_productivity",
            "type_label": "School site productivity",
            "description": "Location productivity across sessions, players and revenue.",
            "module": "Location",
            "reference_type": "location",
            "allows_all": true,
            "aliases": [
                "location_productivity"
            ]
        },
        {
            "type": "executive_monthly_overview",
            "type_label": "Executive monthly overview",
            "description": "Executive KPI snapshot for operations and ecommerce.",
            "module": "Executive",
            "reference_type": "location",
            "allows_all": true,
            "aliases": []
        }
    ]
}
 

Request      

GET api/schools/{school_slug}/reports/get/types

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: autem

slug   string   

School slug. Example: "my-academy"

Get report run status

requires authentication

Returns async execution status. Possible statuses: queued, running, completed, failed. If completed, report_id can be used with /reports/{id} and /reports/{id}/export.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/ullam/reports/runs/22e5f40e-6611-4ad7-a438-10f5258998ec" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/ullam/reports/runs/22e5f40e-6611-4ad7-a438-10f5258998ec"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "run": {
        "run_uuid": "22e5f40e-6611-4ad7-a438-10f5258998ec",
        "status": "completed",
        "report_id": 190,
        "report_type": "executive_monthly_overview",
        "requested_format": "pdf",
        "error_message": null,
        "queued_at": "2026-02-25T17:25:48.000000Z",
        "started_at": "2026-02-25T17:25:49.000000Z",
        "finished_at": "2026-02-25T17:25:51.000000Z"
    }
}
 

Request      

GET api/schools/{school_slug}/reports/runs/{runUuid}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: ullam

runUuid   string   

Run UUID. Example: 22e5f40e-6611-4ad7-a438-10f5258998ec

slug   string   

School slug. Example: "my-academy"

List reports

requires authentication

Paginated report registry for the school. view=select returns a lightweight list.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/omnis/reports?type=coach_productivity&status=completed&view=table&per_page=20" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/omnis/reports"
);

const params = {
    "type": "coach_productivity",
    "status": "completed",
    "view": "table",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "reports": {
        "current_page": 1,
        "data": [
            {
                "id": 190,
                "report_type": "executive_monthly_overview",
                "canonical_report_type": "executive_monthly_overview",
                "reference_id": "all",
                "reference_type": "location",
                "status": "completed",
                "date_range_start": "2026-01-01T00:00:00.000000Z",
                "date_range_end": "2026-01-31T00:00:00.000000Z",
                "generated_by": 7,
                "created_at": "2026-02-25T17:26:12.000000Z"
            }
        ],
        "per_page": 20,
        "total": 1
    },
    "metadata": {
        "columns": [
            "id",
            "report_type",
            "canonical_report_type",
            "reference_id",
            "reference_type",
            "status",
            "date_range_start",
            "date_range_end",
            "generated_by",
            "created_at"
        ],
        "defaultColumns": [
            "report_type",
            "reference_id",
            "reference_type",
            "status",
            "created_at"
        ]
    }
}
 

Request      

GET api/schools/{school_slug}/reports

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: omnis

slug   string   

School slug. Example: "my-academy"

Query Parameters

type   string  optional  

optional Filter by report type. Example: coach_productivity

status   string  optional  

optional Filter by status. Example: completed

view   string  optional  

optional table|select. Example: table

per_page   integer  optional  

optional Page size. Example: 20

Get report

requires authentication

Returns full persisted report payload (calculated_data varies by report_type).

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/et/reports/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/et/reports/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "report": {
        "id": 190,
        "report_type": "executive_monthly_overview",
        "canonical_report_type": "executive_monthly_overview",
        "reference_id": "all",
        "reference_type": "location",
        "school_id": 1,
        "date_range_start": "2026-01-01T00:00:00.000000Z",
        "date_range_end": "2026-01-31T00:00:00.000000Z",
        "calculated_data": {
            "scope": {
                "school_id": 1,
                "reference_id": "all",
                "reference_type": "location",
                "date_range": {
                    "start_date": "2026-01-01",
                    "end_date": "2026-01-31"
                },
                "calculation_version": 1
            },
            "summary": {
                "total_sessions": 82,
                "present_attendances": 305,
                "unique_training_players": 96,
                "active_subscriptions": 110,
                "estimated_subscription_revenue": 13250,
                "orders_revenue": 2400,
                "invoices_revenue": 1960,
                "total_revenue": 17610,
                "average_revenue_per_session": 214.76
            },
            "distributions": {
                "sessions_by_status": {
                    "completed": 78,
                    "canceled": 4
                },
                "sessions_by_type": {
                    "private": 19,
                    "semi-private": 21,
                    "group": 42
                },
                "active_subscriptions_by_location": {
                    "1": 80,
                    "2": 30
                },
                "revenue_by_source": {
                    "orders": 2400,
                    "invoices": 1960,
                    "subscriptions_estimated": 13250
                }
            },
            "breakdown_by_source": [
                {
                    "source": "orders",
                    "revenue": 2400,
                    "share_percent": 13.63
                }
            ],
            "breakdown_by_location": [
                {
                    "school_site_id": 1,
                    "school_site_name": "North Campus",
                    "sessions_total": 52,
                    "active_subscriptions": 80,
                    "estimated_subscription_revenue": 9800
                }
            ],
            "location_details": []
        },
        "status": "completed",
        "calculation_version": 1,
        "generated_by": 7,
        "created_at": "2026-02-25T17:26:12.000000Z",
        "updated_at": "2026-02-25T17:26:12.000000Z"
    }
}
 

Request      

GET api/schools/{school_slug}/reports/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: et

id   integer   

Report id. Example: 1

slug   string   

School slug. Example: "my-academy"

Export report

requires authentication

Supported formats: json, csv, xlsx, pdf. Binary formats are streamed as downloadable attachments.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aut/reports/1/export?format=csv" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aut/reports/1/export"
);

const params = {
    "format": "csv",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, JSON format):


{
    "id": 190,
    "report_type": "executive_monthly_overview",
    "canonical_report_type": "executive_monthly_overview",
    "reference_id": "all",
    "reference_type": "location",
    "status": "completed",
    "calculated_data": {
        "scope": {
            "school_id": 1,
            "reference_id": "all",
            "reference_type": "location"
        },
        "summary": {
            "total_sessions": 82,
            "total_revenue": 17610
        },
        "distributions": {
            "sessions_by_status": {
                "completed": 78,
                "canceled": 4
            }
        },
        "breakdown_by_source": [
            {
                "source": "orders",
                "revenue": 2400,
                "share_percent": 13.63
            }
        ],
        "breakdown_by_location": [
            {
                "school_site_id": 1,
                "school_site_name": "North Campus",
                "sessions_total": 52
            }
        ]
    }
}
 

Example response (200, CSV/XLSX/PDF formats):


"Binary file download stream"
 

Example response (422):


{
    "message": "Invalid export format"
}
 

Request      

GET api/schools/{school_slug}/reports/{id}/export

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aut

id   integer   

Report id. Example: 1

slug   string   

School slug. Example: "my-academy"

Query Parameters

format   string  optional  

optional json|csv|xlsx|pdf. Example: csv

Player sessions by report

requires authentication

Resolves attended sessions for a player inside the report date range and groups them by session type.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/deserunt/reports/1/players/15/sessions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/deserunt/reports/1/players/15/sessions"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "player_id": 15,
    "report_id": 190,
    "date_range": {
        "start": "2026-01-01",
        "end": "2026-01-31"
    },
    "sessions": {
        "group": [
            {
                "id": 901,
                "name": "Group Session 901",
                "date": "2026-01-12",
                "starting_time": "15:00",
                "ending_time": "16:00",
                "coach": {
                    "id": 11,
                    "name": "Coach One"
                },
                "group": {
                    "id": 19,
                    "name": "Group A"
                },
                "location": {
                    "id": 3,
                    "name": "Court A"
                },
                "attendance_status": "present",
                "excuse_comment": null
            }
        ]
    }
}
 

Request      

GET api/schools/{school_slug}/reports/{id}/players/{playerId}/sessions

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: deserunt

id   integer   

Report id. Example: 1

playerId   integer   

Player id. Example: 15

slug   string   

School slug. Example: "my-academy"

Setting

Profile - Get my profile

requires authentication

Returns the authenticated user's profile with an appended image_url and any saved custom_fields. No specific permission is required beyond being authenticated.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/setting/profile" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/profile"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "profile": {
        "id": 1,
        "user_id": 1,
        "first_name": "John",
        "last_name": "Doe",
        "gender": "male",
        "date_birth": "1990-01-01",
        "id_number": "123456789",
        "language": "en",
        "billing_phone_number": "+1234567890",
        "billing_country": "United States",
        "billing_email": "john.doe@example.com",
        "billing_state": "California",
        "billing_address": "123 Main Street",
        "billing_zip_code": "90210",
        "image_url": "https://s3.amazonaws.com/bucket-name/profile-images/image.jpg",
        "created_at": "2025-01-16T00:00:00.000000Z",
        "updated_at": "2025-01-16T00:00:00.000000Z"
    },
    "custom_fields": [
        {
            "slug": "hobby",
            "value": "soccer"
        },
        {
            "slug": "favorite_food",
            "value": "pizza"
        }
    ]
}
 

Example response (401):


{
    "message": "Unauthenticated."
}
 

Example response (404):


{
    "message": "Profile not found"
}
 

Request      

GET api/setting/profile

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Profile - Store or Update profile

requires authentication

Creates or updates the authenticated user's profile. Required permission: administrative.setting.profile.store.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/setting/profile" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "first_name=John"\
    --form "last_name=Doe"\
    --form "gender=male"\
    --form "date_birth=1990-01-01"\
    --form "id_number=123456789"\
    --form "language=en"\
    --form "phone_code=1"\
    --form "billing_phone_number=+1234567890"\
    --form "secondary_phone_number=+1234000999"\
    --form "billing_email=john.doe@example.com"\
    --form "secondary_email=john.alt@example.com"\
    --form "billing_country=United States"\
    --form "billing_state=California"\
    --form "billing_city=Los Angeles"\
    --form "billing_address=123 Main Street"\
    --form "billing_zip_code=90210"\
    --form "has_medical_conditions=1"\
    --form "medical_details=dardnrp"\
    --form "schedules[][day_of_week]=wednesday"\
    --form "schedules[][start_time]=04:17"\
    --form "schedules[][end_time]=2092-06-26"\
    --form "image=@/tmp/phpDC9PxU" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/profile"
);

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

const body = new FormData();
body.append('first_name', 'John');
body.append('last_name', 'Doe');
body.append('gender', 'male');
body.append('date_birth', '1990-01-01');
body.append('id_number', '123456789');
body.append('language', 'en');
body.append('phone_code', '1');
body.append('billing_phone_number', '+1234567890');
body.append('secondary_phone_number', '+1234000999');
body.append('billing_email', 'john.doe@example.com');
body.append('secondary_email', 'john.alt@example.com');
body.append('billing_country', 'United States');
body.append('billing_state', 'California');
body.append('billing_city', 'Los Angeles');
body.append('billing_address', '123 Main Street');
body.append('billing_zip_code', '90210');
body.append('has_medical_conditions', '1');
body.append('medical_details', 'dardnrp');
body.append('schedules[][day_of_week]', 'wednesday');
body.append('schedules[][start_time]', '04:17');
body.append('schedules[][end_time]', '2092-06-26');
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{
    "message": "Profile updated successfully",
    "profile": {
        "id": 1,
        "user_id": 1,
        "first_name": "John",
        "last_name": "Doe",
        "gender": "male",
        "date_birth": "1990-01-01",
        "id_number": "123456789",
        "language": "en",
        "billing_phone_number": "+1234567890",
        "billing_country": "United States",
        "billing_email": "john.doe@example.com",
        "billing_state": "California",
        "billing_address": "123 Main Street",
        "billing_zip_code": "90210",
        "image_url": "https://s3.amazonaws.com/bucket-name/profile-images/image.jpg",
        "created_at": "2025-01-16T00:00:00.000000Z",
        "updated_at": "2025-01-16T00:00:00.000000Z"
    }
}
 

Example response (201):


{
    "message": "Profile created successfully",
    "profile": {
        "id": 1,
        "user_id": 1,
        "first_name": "John",
        "last_name": "Doe",
        "gender": "male",
        "date_birth": "1990-01-01",
        "id_number": "123456789",
        "language": "en",
        "billing_phone_number": "+1234567890",
        "billing_country": "United States",
        "billing_email": "john.doe@example.com",
        "billing_state": "California",
        "billing_address": "123 Main Street",
        "billing_zip_code": "90210",
        "image_url": "https://s3.amazonaws.com/bucket-name/profile-images/image.jpg",
        "created_at": "2025-01-16T00:00:00.000000Z",
        "updated_at": "2025-01-16T00:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "phone_code": [
            "The phone code field is required when billing phone number is present."
        ],
        "gender": [
            "The selected gender is invalid."
        ],
        "billing_email": [
            "The billing email must be a valid email address."
        ]
    }
}
 

Request      

POST api/setting/profile

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

Body Parameters

id   string  optional  
first_name   string  optional  

optional The user's first name. Example: John

last_name   string  optional  

optional The user's last name. Example: Doe

gender   string  optional  

optional The user's gender. One of: male, female, other. Example: male

date_birth   date  optional  

optional The user's date of birth (YYYY-MM-DD). Example: 1990-01-01

id_number   string  optional  

optional A unique identification number for the user. Example: 123456789

language   string  optional  

optional The user's preferred language (max 10 chars). Example: en

phone_code   string  optional  

optional Required when billing_phone_number is present; up to 4 digits. Example: 1

billing_phone_number   string  optional  

optional Phone used for billing. When phone_code is provided, it will be normalized to E.164. Example: +1234567890

secondary_phone_number   string  optional  

optional A secondary phone number. Example: +1234000999

billing_email   string  optional  

optional Billing email address. Example: john.doe@example.com

secondary_email   string  optional  

optional Secondary email address. Example: john.alt@example.com

billing_country   string  optional  

optional Billing country. Example: United States

billing_state   string  optional  

optional Billing state or province. Example: California

billing_city   string  optional  

optional Billing city. Example: Los Angeles

billing_address   string  optional  

optional Billing address. Example: 123 Main Street

billing_zip_code   string  optional  

optional Billing ZIP or postal code. Example: 90210

has_medical_conditions   boolean  optional  

Example: true

medical_details   string  optional  

This field is required when has_medical_conditions is 1. Must not be greater than 1000 characters. Example: dardnrp

experience_level   string  optional  
schedules   object[]  optional  
day_of_week   string  optional  

This field is required when schedules is present. Example: wednesday

Must be one of:
  • monday
  • tuesday
  • wednesday
  • thursday
  • friday
  • saturday
  • sunday
start_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Example: 04:17

end_time   string  optional  

This field is required when schedules is present. Must be a valid date in the format H:i. Must be a date after schedules.*.start_time. Example: 2092-06-26

image   file  optional  

optional A profile image to upload. Example: /tmp/phpDC9PxU

Profile - Change password

requires authentication

Change the authenticated user's password. Required permission: administrative.setting.profile.changePassword.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/setting/change-password" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"current_password\": \"currentPassword123\",
    \"new_password\": \"NewPassword123\",
    \"new_password_confirmation\": \"NewPassword123\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/change-password"
);

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

let body = {
    "current_password": "currentPassword123",
    "new_password": "NewPassword123",
    "new_password_confirmation": "NewPassword123"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Password changed successfully."
}
 

Example response (400):


{
    "message": "Current password does not match."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "new_password": [
            "The new password must be at least 8 characters."
        ],
        "new_password_confirmation": [
            "The new password confirmation does not match."
        ]
    }
}
 

Request      

POST api/setting/change-password

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

current_password   string   

The user's current password. Example: currentPassword123

new_password   string   

The new password (min 8 chars). Must be confirmed. Example: NewPassword123

new_password_confirmation   string   

Must match new_password. Example: NewPassword123

School - List schools (admin)

requires authentication

Lists schools that belong to the authenticated administrative user. Required permission: administrative.setting.school.index.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/setting/schools?per_page=15&only_archived=1&filters[]=vel&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive&sort_by=nickname&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/schools"
);

const params = {
    "per_page": "15",
    "only_archived": "1",
    "filters[0]": "vel",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
    "sort_by": "nickname",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "schools": {
        "data": [
            {
                "id": 1,
                "nickname": "Wildcats",
                "address": "123 Main St",
                "status": "active",
                "created_at": "2023-01-15T00:00:00.000000Z",
                "updated_at": "2023-06-10T00:00:00.000000Z",
                "administrative": {
                    "id": 1,
                    "name": "John Doe"
                },
                "creator": {
                    "id": 2,
                    "name": "Jane Smith"
                },
                "school_sites": [
                    {
                        "id": 1,
                        "name": "Main Campus",
                        "locations": [
                            {
                                "id": 1,
                                "name": "Field A",
                                "description": "Main soccer field"
                            }
                        ]
                    }
                ]
            }
        ],
        "links": {
            "first": "http://example.com/api/setting/schools?page=1",
            "last": "http://example.com/api/setting/schools?page=10",
            "prev": null,
            "next": "http://example.com/api/setting/schools?page=2"
        },
        "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 10,
            "path": "http://example.com/api/setting/schools",
            "per_page": 10,
            "to": 10,
            "total": 100
        }
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "address",
            "status",
            "created_at",
            "updated_at",
            "administrative.name",
            "creator.name"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "address",
            "description"
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/setting/schools

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

per_page   integer  optional  

optional Number of records per page. Default: 10. Example: 15

only_archived   boolean  optional  

optional If true, returns only archived schools. Default: false. Example: true

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator and value.

filters[0][column]   string   

The column to filter by. Example: status

filters[0][operator]   string   

Filter operator. One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string   

mixed Value to compare. Use an array for the "between" operator. Example: inactive

sort_by   string  optional  

optional Column to sort results by. Example: nickname

direction   string  optional  

optional Sort direction. One of: asc, desc. Default: asc. Example: desc

School - Create

requires authentication

Creates a school owned by the authenticated administrative user, optionally with school sites and locations. The school's photo can be uploaded and is stored via a polymorphic media relation. Required permission: administrative.setting.school.store.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/setting/schools" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "nickname=Wildcats"\
    --form "address=123 Main St"\
    --form "sport=Soccer"\
    --form "description=A leading sports school."\
    --form "status=active"\
    --form "name_representative=John"\
    --form "last_name_representative=Doe"\
    --form "id_number_representative=123456789"\
    --form "phone_number_representative=+1234567890"\
    --form "email_representative=john.doe@example.com"\
    --form "school_sites[][locations][][name]=gcatimxvdzbktmuqqgmc"\
    --form "school_sites[][locations][][description]=Fugiat sed sed delectus corrupti quae."\
    --form "photo=@/tmp/phpIL2wwo" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/schools"
);

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

const body = new FormData();
body.append('nickname', 'Wildcats');
body.append('address', '123 Main St');
body.append('sport', 'Soccer');
body.append('description', 'A leading sports school.');
body.append('status', 'active');
body.append('name_representative', 'John');
body.append('last_name_representative', 'Doe');
body.append('id_number_representative', '123456789');
body.append('phone_number_representative', '+1234567890');
body.append('email_representative', 'john.doe@example.com');
body.append('school_sites[][locations][][name]', 'gcatimxvdzbktmuqqgmc');
body.append('school_sites[][locations][][description]', 'Fugiat sed sed delectus corrupti quae.');
body.append('photo', document.querySelector('input[name="photo"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (201):


{
    "message": "School created successfully",
    "school": {
        "id": 1,
        "nickname": "Wildcats",
        "address": "123 Main St",
        "photo_url": "https://s3.amazonaws.com/bucket/schools/images/abc123.jpg",
        "school_sites": [
            {
                "id": 1,
                "name": "Main Campus",
                "phone": "123456",
                "address": "123 Main St",
                "email": "campus@example.com",
                "locations": [
                    {
                        "id": 1,
                        "name": "Field A",
                        "description": "Main soccer field"
                    }
                ]
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "email_representative": [
            "The email representative must be a valid email address."
        ],
        "school_sites.0.name": [
            "The name field is required."
        ]
    }
}
 

Example response (500):


{
    "message": "An error occurred: <details>"
}
 

Request      

POST api/setting/schools

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

Body Parameters

nickname   string   

The school's nickname. Example: Wildcats

address   string   

The school's address. Example: 123 Main St

photo   file  optional  

optional The school's photo. Must be a valid image file (jpg, png, etc.). Example: /tmp/phpIL2wwo

sport   string  optional  

optional The school's main sport. Example: Soccer

description   string  optional  

optional The school's description. Example: A leading sports school.

status   string  optional  

optional The school's status. One of: active, inactive. Default: active. Example: active

name_representative   string   

The representative's first name. Example: John

last_name_representative   string   

The representative's last name. Example: Doe

id_number_representative   string   

The representative's ID number. Example: 123456789

phone_number_representative   string   

The representative's phone number. Example: +1234567890

email_representative   string   

The representative's email. Example: john.doe@example.com

school_sites   string[]  optional  

optional Array of school sites. Each site may include locations.

name   string   

Must not be greater than 255 characters. Example: szp

phone   string   

Must not be greater than 20 characters. Example: ocbycmxowyekqqbf

address   string   

Must not be greater than 255 characters. Example: fqpule

email   string   

Must be a valid email address. Must not be greater than 255 characters. Example: goodwin.elfrieda@example.com

locations   object[]  optional  
name   string   

Must not be greater than 255 characters. Example: gcatimxvdzbktmuqqgmc

description   string  optional  

Example: Fugiat sed sed delectus corrupti quae.

School - Show

requires authentication

Retrieves a school by ID, including its school sites, locations, and appended photo_url. Required permission: administrative.setting.school.show or customer.setting.school.show.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/setting/schools/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/schools/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "School retrieved successfully",
    "school": {
        "id": 1,
        "nickname": "Wildcats",
        "address": "123 Main St",
        "sport": "Soccer",
        "status": "active",
        "name_representative": "John",
        "last_name_representative": "Doe",
        "id_number_representative": "123456789",
        "phone_number_representative": "+1234567890",
        "email_representative": "john.doe@example.com",
        "photo_url": "https://s3.amazonaws.com/bucket-name/schools/photos/photo.jpg",
        "school_sites": [
            {
                "id": 1,
                "name": "Main Campus",
                "phone": "123456",
                "address": "123 Main St",
                "email": "campus@example.com",
                "locations": [
                    {
                        "id": 1,
                        "name": "Field A",
                        "description": "Main soccer field",
                        "state": "active"
                    }
                ]
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found or unauthorized."
}
 

Request      

GET api/setting/schools/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

id   integer   

The ID of the school. Example: 1

School - Update

requires authentication

Updates an existing school (must belong to the authenticated administrative user), including its school sites, locations, and photo. Required permission: administrative.setting.school.update.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/setting/schools/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "nickname=Wildcats"\
    --form "address=123 Main St"\
    --form "sport=Soccer"\
    --form "description=A leading sports school."\
    --form "status=active"\
    --form "name_representative=John"\
    --form "last_name_representative=Doe"\
    --form "id_number_representative=123456789"\
    --form "phone_number_representative=+1234567890"\
    --form "email_representative=john.doe@example.com"\
    --form "school_sites[][locations][][name]=fhqhppgffkngazdjlifv"\
    --form "school_sites[][locations][][description]=Voluptatem quo quasi alias accusantium."\
    --form "photo=@/tmp/php7AEazD" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/schools/1"
);

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

const body = new FormData();
body.append('nickname', 'Wildcats');
body.append('address', '123 Main St');
body.append('sport', 'Soccer');
body.append('description', 'A leading sports school.');
body.append('status', 'active');
body.append('name_representative', 'John');
body.append('last_name_representative', 'Doe');
body.append('id_number_representative', '123456789');
body.append('phone_number_representative', '+1234567890');
body.append('email_representative', 'john.doe@example.com');
body.append('school_sites[][locations][][name]', 'fhqhppgffkngazdjlifv');
body.append('school_sites[][locations][][description]', 'Voluptatem quo quasi alias accusantium.');
body.append('photo', document.querySelector('input[name="photo"]').files[0]);

fetch(url, {
    method: "PUT",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{
    "message": "School updated successfully",
    "school": {
        "id": 1,
        "nickname": "Wildcats",
        "address": "123 Main St",
        "sport": "Soccer",
        "status": "active",
        "name_representative": "John",
        "last_name_representative": "Doe",
        "id_number_representative": "123456789",
        "phone_number_representative": "+1234567890",
        "email_representative": "john.doe@example.com",
        "photo_url": "https://s3.amazonaws.com/bucket-name/schools/photos/photo.jpg",
        "school_sites": [
            {
                "id": 1,
                "name": "Main Campus",
                "phone": "123456",
                "address": "123 Main St",
                "email": "campus@example.com",
                "locations": [
                    {
                        "id": 1,
                        "name": "Field A",
                        "description": "Main soccer field",
                        "state": "active"
                    }
                ]
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field must be a string."
        ],
        "school_sites.0.name": [
            "The name field is required when updating a school site."
        ]
    }
}
 

Example response (500):


{
    "message": "An error occurred: <details>"
}
 

Request      

PUT api/setting/schools/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

id   integer   

The ID of the school to update. Example: 1

Body Parameters

nickname   string  optional  

optional The school's nickname. Example: Wildcats

address   string  optional  

optional The school's address. Example: 123 Main St

photo   file  optional  

optional The school's photo. Must be a valid image file (jpg, png, etc.). Example: /tmp/php7AEazD

sport   string  optional  

optional The school's main sport. Example: Soccer

description   string  optional  

optional The school's description. Example: A leading sports school.

status   string  optional  

optional The school's status. One of: active, inactive. Default: active. Example: active

name_representative   string  optional  

optional The representative's first name. Example: John

last_name_representative   string  optional  

optional The representative's last name. Example: Doe

id_number_representative   string  optional  

optional The representative's ID number. Example: 123456789

phone_number_representative   string  optional  

optional The representative's phone number. Example: +1234567890

email_representative   string  optional  

optional The representative's email. Example: john.doe@example.com

school_sites   string[]  optional  

optional Array of school sites. Each site may include locations.

id   string  optional  

The id of an existing record in the school_sites table.

name   string  optional  

This field is required when school_sites is present. Must not be greater than 255 characters. Example: spiizvzvuspvjkrtzkqoyfbx

phone   string  optional  

This field is required when school_sites is present. Must not be greater than 20 characters. Example: axvpnsntirjk

address   string  optional  

This field is required when school_sites is present. Must not be greater than 255 characters. Example: xdeuyqk

email   string  optional  

This field is required when school_sites is present. Must be a valid email address. Must not be greater than 255 characters. Example: rebeka.carter@example.org

locations   object[]  optional  
id   string  optional  

The id of an existing record in the locations table.

name   string  optional  

This field is required when school_sites.*.locations is present. Must not be greater than 255 characters. Example: fhqhppgffkngazdjlifv

description   string  optional  

Example: Voluptatem quo quasi alias accusantium.

School - Archive

requires authentication

Archive (soft) a school by setting its status to archived. The school remains in the database. Required permission: administrative.setting.school.delete.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/setting/schools/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/setting/schools/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "School archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found or unauthorized."
}
 

Example response (500):


{
    "message": "An error occurred: <details>"
}
 

Request      

DELETE api/setting/schools/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

id   integer   

The ID of the school to archive. Example: 1

Level - Division Categories - List

requires authentication

Returns division categories scoped to the given school. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname list). Required permission: administrative.setting.level.divisionCategory.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=ab&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "ab",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "categories": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Primary Division",
                "description": "This is the primary division category.",
                "status": "active",
                "school": {
                    "id": 1,
                    "nickname": "Wildcats"
                },
                "created_at": "2025-01-17T12:00:00.000000Z",
                "updated_at": "2025-01-17T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 50
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "status",
            "school.nickname"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description",
            "status"
        ]
    }
}
 

Example response (200):


{
    "categories": [
        {
            "id": 1,
            "nickname": "Primary Division"
        },
        {
            "id": 2,
            "nickname": "Secondary Division"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/setting/levels/division-categories

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope categories. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only categories with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: inactive

Level - Division Categories - Create

requires authentication

Creates a new division category scoped to the provided school. Required permission: administrative.setting.level.divisionCategory.store. The authenticated user must be authorized for the school.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Primary Division\",
    \"description\": \"This is the primary division category.\",
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories"
);

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

let body = {
    "nickname": "Primary Division",
    "description": "This is the primary division category.",
    "status": "active"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Division category created successfully",
    "category": {
        "id": 1,
        "nickname": "Primary Division",
        "description": "This is the primary division category.",
        "status": "active",
        "school": {
            "id": 1,
            "nickname": "Wildcats"
        },
        "creator_id": 5,
        "created_at": "2025-01-17T12:00:00.000000Z",
        "updated_at": "2025-01-17T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "description": [
            "The description field is required."
        ],
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/setting/levels/division-categories

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school where the category will be created. Example: aleacademy

Body Parameters

nickname   string   

The category nickname. Max 255 chars. Example: Primary Division

description   string   

A brief description. Max 500 chars. Example: This is the primary division category.

status   string   

Category status. One of: active, inactive. Example: active

Level - Division Categories - Show

requires authentication

Returns a single division category (with its school) scoped to the provided school. Required permission: administrative.setting.level.divisionCategory.show.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 1,
    "nickname": "Primary Division",
    "description": "This is the primary division category.",
    "status": "active",
    "school": {
        "id": 1,
        "nickname": "Wildcats"
    },
    "created_at": "2025-01-17T12:00:00.000000Z",
    "updated_at": "2025-01-17T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Division category not found or unauthorized."
}
 

Request      

GET api/schools/{school_slug}/setting/levels/division-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the request. Example: aleacademy

id   integer   

The ID of the division category to retrieve. Example: 1

Level - Division Categories - Update

requires authentication

Partially update a division category scoped to the provided school. Required permission (as implemented): administrative.setting.level.divisionCategory.show.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Primary Division\",
    \"description\": \"Updated description\",
    \"status\": \"inactive\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/division-categories/1"
);

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

let body = {
    "nickname": "Primary Division",
    "description": "Updated description",
    "status": "inactive"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Division category updated successfully",
    "category": {
        "id": 1,
        "nickname": "Primary Division",
        "description": "Updated description",
        "status": "inactive",
        "school": {
            "id": 1,
            "nickname": "Wildcats"
        },
        "creator_id": 5,
        "created_at": "2025-01-17T12:00:00.000000Z",
        "updated_at": "2025-01-17T12:30:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Division category not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/setting/levels/division-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the request. Example: aleacademy

id   integer   

The ID of the division category to update. Example: 1

Body Parameters

nickname   string  optional  

optional The category nickname. Max 255 chars. Example: Primary Division

description   string  optional  

optional A brief description. Max 500 chars. Example: Updated description

status   string  optional  

optional Category status. One of: active, inactive. Example: inactive

Level - Division Categories - Archive

requires authentication

This endpoint allows the authenticated administrator to archive a division category by changing its status to "archived". This action does not delete the category from the database.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/dolorum/setting/levels/division-categories/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/dolorum/setting/levels/division-categories/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Division category archived successfully."
}
 

Example response (404):


{
    "message": "Division category not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/setting/levels/division-categories/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: dolorum

id   integer   

The ID of the division category to archive. Example: 1

Level - Learning Stages - List

requires authentication

Returns learning stages scoped to the given school. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname list). Required permission: administrative.setting.level.learningStage.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=vel&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "vel",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "stages": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Kindergarten",
                "description": "Learning stage for children aged 3-5.",
                "status": "active",
                "school": {
                    "id": 1,
                    "nickname": "Wildcats"
                },
                "created_at": "2025-01-17T12:00:00.000000Z",
                "updated_at": "2025-01-17T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 50
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "description",
            "status",
            "school.nickname"
        ],
        "defaultColumns": [
            "id",
            "nickname",
            "description",
            "status"
        ]
    }
}
 

Example response (200):


{
    "stages": [
        {
            "id": 1,
            "nickname": "Kindergarten"
        },
        {
            "id": 2,
            "nickname": "Primary"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/setting/levels/learning-stages

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope stages. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only stages with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: inactive

Level - Learning Stages - Create

requires authentication

Creates a new learning stage scoped to the provided school. Required permission: administrative.setting.level.learningStage.store. The authenticated user must be authorized for the school.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Kindergarten\",
    \"description\": \"Stage for children aged 3-5.\",
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages"
);

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

let body = {
    "nickname": "Kindergarten",
    "description": "Stage for children aged 3-5.",
    "status": "active"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Learning stage created successfully",
    "learning_stage": {
        "id": 1,
        "nickname": "Kindergarten",
        "description": "Stage for children aged 3-5.",
        "status": "active",
        "school": {
            "id": 1,
            "nickname": "Wildcats"
        },
        "creator_id": 5,
        "created_at": "2025-01-17T12:00:00.000000Z",
        "updated_at": "2025-01-17T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "description": [
            "The description field is required."
        ],
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/setting/levels/learning-stages

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school where the stage will be created. Example: aleacademy

Body Parameters

nickname   string   

The stage nickname. Max 255 chars. Example: Kindergarten

description   string   

A brief description. Max 500 chars. Example: Stage for children aged 3-5.

status   string   

Stage status. One of: active, inactive. Example: active

Level - Learning Stages - Show

requires authentication

Returns a single learning stage (with its school) scoped to the provided school. Required permission: administrative.setting.level.learningStage.show.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 1,
    "nickname": "Kindergarten",
    "description": "Stage for children aged 3-5.",
    "status": "active",
    "school": {
        "id": 1,
        "nickname": "Wildcats"
    },
    "created_at": "2025-01-17T12:00:00.000000Z",
    "updated_at": "2025-01-17T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Learning stage not found or unauthorized."
}
 

Request      

GET api/schools/{school_slug}/setting/levels/learning-stages/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the request. Example: aleacademy

id   integer   

The ID of the learning stage to retrieve. Example: 1

Level - Learning Stages - Update

requires authentication

Partially update a learning stage scoped to the provided school. Required permission: administrative.setting.level.learningStage.update.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Kindergarten\",
    \"description\": \"Updated description\",
    \"status\": \"inactive\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages/1"
);

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

let body = {
    "nickname": "Kindergarten",
    "description": "Updated description",
    "status": "inactive"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Learning stage updated successfully",
    "learning_stage": {
        "id": 1,
        "nickname": "Updated Kindergarten",
        "description": "Updated description for the learning stage.",
        "status": "inactive",
        "school": {
            "id": 1,
            "nickname": "Wildcats"
        },
        "creator_id": 5,
        "created_at": "2025-01-17T12:00:00.000000Z",
        "updated_at": "2025-01-17T12:30:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Learning stage not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/setting/levels/learning-stages/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the request. Example: aleacademy

id   integer   

The ID of the learning stage to update. Example: 1

Body Parameters

nickname   string  optional  

optional The stage nickname. Max 255 chars. Example: Kindergarten

description   string  optional  

optional A brief description. Max 500 chars. Example: Updated description

status   string  optional  

optional Stage status. One of: active, inactive. Example: inactive

Level - Learning Stages - Archive

requires authentication

Archives a learning stage by setting status to "archived". This does not delete the record. Required permission: administrative.setting.level.learningStage.destroy.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/learning-stages/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Learning stage archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Learning stage not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/setting/levels/learning-stages/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the request. Example: aleacademy

id   integer   

The ID of the learning stage to archive. Example: 1

Level - Levels - List

requires authentication

Returns levels scoped to the given school. Supports a table view (paginated with metadata) or a select view (lightweight id/nickname/name list). Required permission: administrative.setting.level.level.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels?view=select&per_page=15&only_archived=1&sort_by=nickname&direction=desc&filters[]=eum&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "sort_by": "nickname",
    "direction": "desc",
    "filters[0]": "eum",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "levels": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "nickname": "Level A",
                "name": "First Level",
                "description": "The first level of the learning stage.",
                "status": "active",
                "learning_stage": {
                    "id": 1,
                    "nickname": "Kindergarten"
                },
                "division_category": {
                    "id": 1,
                    "nickname": "Primary Division"
                },
                "school": {
                    "id": 1,
                    "nickname": "Wildcats"
                },
                "created_at": "2025-01-17T12:00:00.000000Z",
                "updated_at": "2025-01-17T12:00:00.000000Z"
            }
        ],
        "per_page": 10,
        "total": 50
    },
    "metadata": {
        "columns": [
            "id",
            "nickname",
            "name",
            "description",
            "status",
            "school.nickname"
        ],
        "defaultColumns": [
            "name",
            "nickname",
            "description",
            "status"
        ]
    }
}
 

Example response (200):


{
    "levels": [
        {
            "id": 1,
            "nickname": "Level A",
            "name": "First Level"
        },
        {
            "id": 2,
            "nickname": "Level B",
            "name": "Second Level"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/setting/levels/levels

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope levels. Example: aleacademy

Query Parameters

view   string  optional  

Determines the response format. One of: table, select. Defaults to table. Example: select

per_page   integer  optional  

When view=table, number of records per page. Min: 1. Defaults to 10. Example: 15

only_archived   boolean  optional  

If true, returns only levels with status=archived. Default: false. Example: true

sort_by   string  optional  

Column to sort by. Must be a valid column. Example: nickname

direction   string  optional  

Sort direction. One of: asc, desc. Default: asc. Example: desc

filters   string[]  optional  

Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string  optional  

The column to filter by. Example: status

filters[0][operator]   string  optional  

One of: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string  optional  

mixed The value to compare. Use an array for the between operator. Example: inactive

Level - Levels - Create

requires authentication

Creates a new level in the given school. Required permission: administrative.setting.level.level.store. Only the school's creator can perform this action.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Beginner\",
    \"name\": \"Level 1\",
    \"description\": \"Entry-level stage for new students.\",
    \"learning_stage_id\": 1,
    \"division_category_id\": 1,
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels"
);

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

let body = {
    "nickname": "Beginner",
    "name": "Level 1",
    "description": "Entry-level stage for new students.",
    "learning_stage_id": 1,
    "division_category_id": 1,
    "status": "active"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Level created successfully.",
    "level": {
        "id": 10,
        "nickname": "Beginner",
        "name": "Level 1",
        "description": "Entry-level stage for new students.",
        "status": "active",
        "learning_stage": {
            "id": 1,
            "nickname": "Kindergarten"
        },
        "division_category": {
            "id": 2,
            "nickname": "Primary Division"
        },
        "school": {
            "id": 3,
            "nickname": "Wildcats"
        },
        "created_at": "2025-01-17T12:00:00.000000Z",
        "updated_at": "2025-01-17T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "learning_stage_id": [
            "The selected learning stage id is invalid."
        ],
        "division_category_id": [
            "The selected division category id is invalid."
        ],
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

POST api/schools/{school_slug}/setting/levels/levels

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to associate the level with. Example: aleacademy

Body Parameters

nickname   string   

The level's nickname. Example: Beginner

name   string   

The level's name. Example: Level 1

description   string  optional  

nullable A description of the level. Example: Entry-level stage for new students.

learning_stage_id   integer   

The ID of the associated learning stage. Must exist in learning_stages. Example: 1

division_category_id   integer   

The ID of the associated division category. Must exist in division_categories. Example: 1

status   string   

The status of the level. One of: active, inactive. Example: active

Level - Levels - Show

requires authentication

Returns the details of a specific level in the given school. Required permission: administrative.setting.level.level.show. Only the school's creator can perform this action.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels/1"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "id": 1,
    "nickname": "Beginner",
    "name": "Level 1",
    "description": "Entry-level stage for new students.",
    "status": "active",
    "learning_stage": {
        "id": 1,
        "nickname": "Kindergarten"
    },
    "division_category": {
        "id": 2,
        "nickname": "Primary Division"
    },
    "school": {
        "id": 3,
        "nickname": "Wildcats"
    },
    "created_at": "2025-01-17T12:00:00.000000Z",
    "updated_at": "2025-01-17T12:00:00.000000Z"
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (404):


{
    "message": "Level not found or unauthorized."
}
 

Request      

GET api/schools/{school_slug}/setting/levels/levels/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the level. Example: aleacademy

id   integer   

The ID of the level to retrieve. Example: 1

Level - Levels - Update

requires authentication

Updates an existing level in the given school. Required permission: administrative.setting.level.level.update. Only the school's creator can perform this action.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"nickname\": \"Advanced\",
    \"name\": \"Level 2\",
    \"description\": \"Intermediate-level stage for students.\",
    \"learning_stage_id\": 1,
    \"division_category_id\": 1,
    \"status\": \"active\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels/1"
);

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

let body = {
    "nickname": "Advanced",
    "name": "Level 2",
    "description": "Intermediate-level stage for students.",
    "learning_stage_id": 1,
    "division_category_id": 1,
    "status": "active"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Level updated successfully.",
    "level": {
        "id": 1,
        "nickname": "Advanced",
        "name": "Level 2",
        "description": "Intermediate-level stage for students.",
        "status": "active",
        "learning_stage": {
            "id": 1,
            "nickname": "Kindergarten"
        },
        "division_category": {
            "id": 2,
            "nickname": "Primary Division"
        },
        "school": {
            "id": 3,
            "nickname": "Wildcats"
        },
        "created_at": "2025-01-17T12:00:00.000000Z",
        "updated_at": "2025-01-17T12:00:00.000000Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (404):


{
    "message": "Level not found or unauthorized."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "nickname": [
            "The nickname field is required."
        ],
        "learning_stage_id": [
            "The selected learning stage id is invalid."
        ],
        "division_category_id": [
            "The selected division category id is invalid."
        ],
        "status": [
            "The selected status is invalid."
        ]
    }
}
 

Request      

PUT api/schools/{school_slug}/setting/levels/levels/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school that owns the level. Example: aleacademy

id   integer   

The ID of the level to update. Example: 1

Body Parameters

nickname   string   

The level's nickname. Example: Advanced

name   string   

The level's name. Example: Level 2

description   string  optional  

nullable A description of the level. Example: Intermediate-level stage for students.

learning_stage_id   integer   

The ID of the associated learning stage. Must exist in learning_stages. Example: 1

division_category_id   integer   

The ID of the associated division category. Must exist in division_categories. Example: 1

status   string   

The status of the level. One of: active, inactive. Example: active

Level - Levels - Archive

requires authentication

Archives a level by setting its status to "archived". Required permission: administrative.setting.level.level.delete. Only the school's creator can perform this action. This doesn't permanently delete the record.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels/1" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/levels/levels/1"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Level archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (404):


{
    "message": "Level not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/setting/levels/levels/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school that owns the level. Example: aleacademy

id   integer   

The ID of the level to archive. Example: 1

User Statuses - List

requires authentication

List user statuses for a school. Requires admin permission: administrative.setting.user-status.index

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/doloribus/setting/user-statuses?sort_by=name&direction=asc&per_page=10&view=select&only_archived=1&search=active&filters[]=corporis&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive%0ANote%3A+Relation+dot-notation+is+supported+for+columns+%28e.g.%2C+profile.first_name%29%3B+nested+paths+are+allowed." \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/doloribus/setting/user-statuses"
);

const params = {
    "sort_by": "name",
    "direction": "asc",
    "per_page": "10",
    "view": "select",
    "only_archived": "1",
    "search": "active",
    "filters[0]": "corporis",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive
Note: Relation dot-notation is supported for columns (e.g., profile.first_name); nested paths are allowed.",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "statuses": {
        "data": [
            {
                "id": 1,
                "name": "Active Player",
                "role": "player",
                "description": "Has an active paid service.",
                "created_at": "2025-10-15T12:00:00Z"
            }
        ],
        "current_page": 1,
        "last_page": 1,
        "per_page": 10,
        "total": 1
    },
    "metadata": {
        "columns": [
            "id",
            "name",
            "role",
            "description",
            "created_at"
        ],
        "defaultColumns": [
            "id",
            "name",
            "role"
        ]
    }
}
 

Example response (200, Select view):


{
    "statuses": [
        {
            "id": 1,
            "name": "Active Player",
            "role": "player"
        }
    ]
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

GET api/schools/{school_slug}/setting/user-statuses

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: doloribus

school   string   

The school's slug. Example: my-school

Query Parameters

sort_by   string  optional  

Column to sort by. Example: name

direction   string  optional  

Sort direction (asc|desc). Example: asc

per_page   integer  optional  

Records per page. Default 10. Example: 10

view   string  optional  

When "select", returns minimal fields (id, name, role). Default: table. Example: select

only_archived   boolean  optional  

If true, returns only archived statuses. Default: false. Example: true

search   string  optional  

optional Global search across all base columns. Example: active

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string   

The name of the column to filter by. Example: status

filters[0][operator]   string   

The filter operator. Options: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string   

mixed The value to compare. Use an array for "between" operator. Example: inactive Note: Relation dot-notation is supported for columns (e.g., profile.first_name); nested paths are allowed.

User Statuses - Create

requires authentication

Create a status for the school. Requires admin permission: administrative.setting.user-status.store

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/aperiam/setting/user-statuses" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Active Player\",
    \"role\": \"player\",
    \"description\": \"Has an active paid service.\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aperiam/setting/user-statuses"
);

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

let body = {
    "name": "Active Player",
    "role": "player",
    "description": "Has an active paid service."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Status created",
    "status": {
        "id": 12,
        "school_id": 1,
        "name": "Active Player",
        "role": "player",
        "description": "Has an active paid service.",
        "created_at": "2025-10-15T12:00:00Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Request      

POST api/schools/{school_slug}/setting/user-statuses

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aperiam

school   string   

The school's slug. Example: my-school

Body Parameters

name   string   

The status name. Example: Active Player

role   string   

The target role (player, lead, account). Example: player

description   string  optional  

A helpful description. Example: Has an active paid service.

User Statuses - Show

requires authentication

Get a status by id. Requires admin permission: administrative.setting.user-status.show

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/quas/setting/user-statuses/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/quas/setting/user-statuses/5"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "status": {
        "id": 5,
        "school_id": 1,
        "name": "Active Player",
        "role": "player",
        "description": "Has an active paid service.",
        "created_at": "2025-10-15T12:00:00Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Not found"
}
 

Request      

GET api/schools/{school_slug}/setting/user-statuses/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: quas

id   integer   

The status ID. Example: 5

school   string   

The school's slug. Example: my-school

User Statuses - Update

requires authentication

Update a status. Requires admin permission: administrative.setting.user-status.update

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/ut/setting/user-statuses/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Paused Player\",
    \"role\": \"player\",
    \"description\": \"Service is paused or canceled.\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/ut/setting/user-statuses/5"
);

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

let body = {
    "name": "Paused Player",
    "role": "player",
    "description": "Service is paused or canceled."
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Status updated",
    "status": {
        "id": 5,
        "school_id": 1,
        "name": "Paused Player",
        "role": "player",
        "description": "Service is paused or canceled.",
        "updated_at": "2025-10-15T12:00:00Z"
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Not found"
}
 

Request      

PUT api/schools/{school_slug}/setting/user-statuses/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: ut

id   integer   

The status ID. Example: 5

school   string   

The school's slug. Example: my-school

Body Parameters

name   string  optional  

The status name. Example: Paused Player

role   string  optional  

The target role (player, lead, account). Example: player

description   string  optional  

A helpful description. Example: Service is paused or canceled.

User Statuses - Archive (soft delete)

requires authentication

Archive a status if it is not currently assigned to any user. Requires admin permission: administrative.setting.user-status.delete

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/quod/setting/user-statuses/5" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/quod/setting/user-statuses/5"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Status archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Not found"
}
 

Example response (422):


{
    "message": "Cannot archive: status is assigned to one or more users."
}
 

Request      

DELETE api/schools/{school_slug}/setting/user-statuses/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: quod

id   integer   

The status ID. Example: 5

school   string   

The school's slug. Example: my-school

Assign a status to a user

requires authentication

Assign a school status to a user. Requires admin permission: administrative.setting.user-status.assign

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/eius/setting/user-statuses/assign-to-user" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"user_id\": 42,
    \"user_status_id\": 12,
    \"note\": \"qldtpjrtlmhzygdfqvuar\"
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/eius/setting/user-statuses/assign-to-user"
);

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

let body = {
    "user_id": 42,
    "user_status_id": 12,
    "note": "qldtpjrtlmhzygdfqvuar"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Status assigned to user",
    "user": {
        "id": 42,
        "name": "Jane Doe",
        "email": "jane@example.com",
        "user_status_id": 12
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Status not found in this school"
}
 

Example response (422):


{
    "message": "User does not belong to the specified school"
}
 

Example response (422):


{
    "message": "User role is not compatible with this status"
}
 

Request      

POST api/schools/{school_slug}/setting/user-statuses/assign-to-user

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: eius

school   string   

The school's slug. Example: my-school

Body Parameters

user_id   integer   

The user to assign the status to. Example: 42

user_status_id   integer   

The status to assign. Example: 12

note   string  optional  

Must not be greater than 1000 characters. Example: qldtpjrtlmhzygdfqvuar

Configuration - Settings - Show configuration by slug

requires authentication

Returns configuration data for the given school and slug. Required permission: administrative.setting.configuration.show. Slugs: "all" returns the entire settings JSON, "main" returns colors and main blocks, any other value returns a specific module. Only the school's creator can perform this action.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/configuration/users" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/configuration/users"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "settings": {
        "modules": [
            {
                "slug": "users",
                "plural_name": "Users",
                "sections": []
            }
        ],
        "version": "1.0.0"
    }
}
 

Example response (200):


{
    "colors": {
        "menus": "#242745",
        "header": "#FFFFFF",
        "background": "#f8f7fa",
        "typography": "#1F1F1F"
    },
    "main": {
        "site_name": "AleAcademy",
        "logo": "https://example.com/logo.png"
    }
}
 

Example response (200):


{
    "template": {
        "slug": "users",
        "plural_name": "Users",
        "singular_name": "User",
        "sections": [
            {
                "default_name": "general",
                "forms": [
                    {
                        "default_name": "profile_form",
                        "fields": [
                            {
                                "label_en": "First Name",
                                "label_es": "Nombre"
                            }
                        ]
                    }
                ]
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (404):


{
    "message": "Setting not found for the authenticated user."
}
 

Example response (404):


{
    "message": "Modules not found in the settings JSON."
}
 

Example response (404):


{
    "message": "Template not found for the provided slug."
}
 

Request      

GET api/schools/{school_slug}/setting/configuration/{slug}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the settings. Example: aleacademy

slug   string   

One of: all, main, or a module slug (e.g., users). Example: users

Configuration - Settings - Update colors

requires authentication

Updates the colors configuration for the given school or resets them to defaults. Required permission: administrative.setting.configuration.update. Only the school's creator can perform this action.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/configuration/colors" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reset\": true,
    \"changes\": [
        {
            \"name\": \"menus\",
            \"color\": \"#FF5733\"
        },
        {
            \"name\": \"header\",
            \"color\": \"#000000\"
        }
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/configuration/colors"
);

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

let body = {
    "reset": true,
    "changes": [
        {
            "name": "menus",
            "color": "#FF5733"
        },
        {
            "name": "header",
            "color": "#000000"
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "response": {
        "menus": "#FF5733",
        "header": "#000000",
        "background": "#f8f7fa",
        "typography": "#1F1F1F"
    },
    "status": "success",
    "message": "Colors updated successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "status": "error",
    "message": "Settings not found for the authenticated user."
}
 

Example response (404):


{
    "status": "error",
    "message": "Colors data not found in the settings."
}
 

Example response (500):


{
    "status": "error",
    "message": "Failed to update colors.",
    "error": "Validation or server error details."
}
 

Request      

PUT api/schools/{school_slug}/setting/configuration/colors

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the settings. Example: aleacademy

Body Parameters

reset   boolean  optional  

optional If true, resets colors to defaults and ignores changes. Example: true

changes   string[]  optional  

optional Required when reset is false. A list of color updates where each item includes a name and a hex color.

name   string   

The color key to update. One of: menus, header, background, typography. Example: menus

color   string   

The hexadecimal color value. Example: #FF5733

Configuration - Settings - Update module configuration

requires authentication

Updates a module's configuration for the given school. Required permission: administrative.setting.configuration.update. Only the school's creator can perform this action. You can update module names and, optionally, form fields and custom fields.

Example request:
curl --request PUT \
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/configuration" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"module\": \"users\",
    \"plural_name\": \"Users\",
    \"singular_name\": \"User\",
    \"plural_name_es\": \"Usuarios\",
    \"singular_name_es\": \"Usuario\",
    \"section\": \"general\",
    \"form\": \"profile_form\",
    \"subsection\": \"details\",
    \"form_section\": \"address_section\",
    \"fields\": [
        {
            \"label_en\": \"First Name\",
            \"label_es\": \"Nombre\"
        }
    ],
    \"custom_fields\": [
        {
            \"label_en\": \"Gender\",
            \"label_es\": \"Género\",
            \"type\": \"select\",
            \"options\": [
                {
                    \"name_en\": \"Male\",
                    \"name_es\": \"Hombre\"
                },
                {
                    \"name_en\": \"Female\",
                    \"name_es\": \"Mujer\"
                }
            ]
        }
    ]
}"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aleacademy/setting/configuration"
);

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

let body = {
    "module": "users",
    "plural_name": "Users",
    "singular_name": "User",
    "plural_name_es": "Usuarios",
    "singular_name_es": "Usuario",
    "section": "general",
    "form": "profile_form",
    "subsection": "details",
    "form_section": "address_section",
    "fields": [
        {
            "label_en": "First Name",
            "label_es": "Nombre"
        }
    ],
    "custom_fields": [
        {
            "label_en": "Gender",
            "label_es": "Género",
            "type": "select",
            "options": [
                {
                    "name_en": "Male",
                    "name_es": "Hombre"
                },
                {
                    "name_en": "Female",
                    "name_es": "Mujer"
                }
            ]
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "response": {
        "id": 1,
        "parent_id": 1,
        "language": "en",
        "colors": {
            "primary": "#000000",
            "secondary": "#FFFFFF"
        },
        "icons": "default",
        "url": "https://example.com",
        "setting": {
            "modules": [
                {
                    "id": "1",
                    "plural_name": "Users",
                    "singular_name": "User",
                    "plural_name_es": "Usuarios",
                    "singular_name_es": "Usuario",
                    "sections": [
                        {
                            "default_name": "general",
                            "forms": [
                                {
                                    "default_name": "profile_form",
                                    "fields": [
                                        {
                                            "label_en": "First Name",
                                            "label_es": "Nombre"
                                        }
                                    ],
                                    "custom_fields": [
                                        {
                                            "name": "gender",
                                            "type": "select",
                                            "label_en": "Gender",
                                            "label_es": "Género",
                                            "options": [
                                                {
                                                    "name_en": "Male",
                                                    "name_es": "Hombre",
                                                    "value": "Male"
                                                },
                                                {
                                                    "name_en": "Female",
                                                    "name_es": "Mujer",
                                                    "value": "Female"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        "created_at": "2025-01-10T12:00:00.000000Z",
        "updated_at": "2025-01-10T12:00:00.000000Z"
    },
    "status": "success",
    "message": "The configuration was updated correctly! To see the changes, please reload the page."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Example response (404):


{
    "message": "Module not found."
}
 

Request      

PUT api/schools/{school_slug}/setting/configuration

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school to scope the settings. Example: aleacademy

Body Parameters

module   string   

The module slug to update. Example: users

plural_name   string  optional  

optional The plural name of the module in English. Example: Users

singular_name   string  optional  

optional The singular name of the module in English. Example: User

plural_name_es   string  optional  

optional The plural name of the module in Spanish. Example: Usuarios

singular_name_es   string  optional  

optional The singular name of the module in Spanish. Example: Usuario

section   string   

When updating form fields, the default name of the section to update. Example: general

form   string  optional  

optional The default name of the form to update within the section. Example: profile_form

subsection   string  optional  

optional The default name of the subsection to update within the section. Example: details

form_section   string  optional  

optional When the form is nested in sections, the default name of the form section to update. Example: address_section

fields   string[]  optional  

optional When updating form fields, an array aligned to the existing form fields; each item may include label_en and/or label_es.

custom_fields   string[]  optional  

optional Custom fields to add to the form; each item must include label_en, label_es, type, and optional options for select/checkbox/radio.

Staff

Coach - List all coaches for a school

requires authentication

Returns a paginated list of users with the "Coach" role associated to the specified school. Supports view=table/select, filters, sorting and pagination. Required permission: administrative.staff.coach.index.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/id/staff/coaches?view=select&per_page=15&only_archived=1&filters[]=nam&filters%5B0%5D%5Bcolumn%5D=status&filters%5B0%5D%5Boperator%5D=not_equals&filters%5B0%5D%5Bvalue%5D=inactive&sort_by=first_name&direction=desc" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/id/staff/coaches"
);

const params = {
    "view": "select",
    "per_page": "15",
    "only_archived": "1",
    "filters[0]": "nam",
    "filters[0][column]": "status",
    "filters[0][operator]": "not_equals",
    "filters[0][value]": "inactive",
    "sort_by": "first_name",
    "direction": "desc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": 15,
            "name": "Carlos Lozano",
            "email": "carlos.lozano@example.com",
            "status": "active",
            "school": {
                "id": 2,
                "nickname": "Academia Fútbol"
            },
            "created_at": "2025-02-01T12:00:00.000000Z",
            "updated_at": "2025-02-01T12:00:00.000000Z"
        }
    ],
    "meta": {
        "current_page": 1,
        "per_page": 10,
        "total": 50
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "School not found"
}
 

Request      

GET api/schools/{school_slug}/staff/coaches

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: id

school   integer   

The ID of the school to list coaches for. Example: 5

Query Parameters

view   string  optional  

optional Determines if the response is for a table or a select dropdown. Options: "table", "select". Defaults to "table". Example: select

per_page   integer  optional  

optional Number of records per page. Defaults to 10. Example: 15

only_archived   boolean  optional  

optional If true, retrieves only inactive coaches. Default is false. Example: true

filters   string[]  optional  

optional Dynamic filters. Each filter must include column, operator, and value.

filters[0][column]   string   

The name of the column to filter by. Example: status

filters[0][operator]   string   

The filter operator. Options: contains, not_contains, equals, not_equals, less_than, greater_than, between, empty, not_empty. Example: not_equals

filters[0][value]   string   

mixed The value to compare. Use an array for "between" operator. Example: inactive

sort_by   string  optional  

optional Column to sort results by. Must be a valid column name. Example: first_name

direction   string  optional  

optional Sort direction. Options: asc, desc. Default is asc. Example: desc

Coach - Store or Update

requires authentication

Create or update a Coach user/profile for the specified school.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/voluptatem/staff/coaches" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "id=10"\
    --form "first_name=Andrew"\
    --form "last_name=Ramirez"\
    --form "gender=male"\
    --form "date_birth=1985-09-15"\
    --form "id_number=9876543210"\
    --form "language=en"\
    --form "phone_code=1"\
    --form "billing_phone_number=+17865551234"\
    --form "secondary_phone_number=dgsqdhntqwnyvboj"\
    --form "billing_email=billing@company.com"\
    --form "secondary_email=stamm.althea@example.net"\
    --form "billing_country=Argentina"\
    --form "billing_state=Buenos Aires"\
    --form "billing_city=lknwsqogrutzywztlojny"\
    --form "billing_address=1234 Corrientes Ave, CABA"\
    --form "billing_zip_code=C1043AAU"\
    --form "has_medical_conditions=1"\
    --form "medical_details=wuqedbuway"\
    --form "schedules[]=et"\
    --form "school_ids[]=5"\
    --form "custom_fields[]=nam"\
    --form "image=@/tmp/phpVD2EuW" 
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/voluptatem/staff/coaches"
);

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

const body = new FormData();
body.append('id', '10');
body.append('first_name', 'Andrew');
body.append('last_name', 'Ramirez');
body.append('gender', 'male');
body.append('date_birth', '1985-09-15');
body.append('id_number', '9876543210');
body.append('language', 'en');
body.append('phone_code', '1');
body.append('billing_phone_number', '+17865551234');
body.append('secondary_phone_number', 'dgsqdhntqwnyvboj');
body.append('billing_email', 'billing@company.com');
body.append('secondary_email', 'stamm.althea@example.net');
body.append('billing_country', 'Argentina');
body.append('billing_state', 'Buenos Aires');
body.append('billing_city', 'lknwsqogrutzywztlojny');
body.append('billing_address', '1234 Corrientes Ave, CABA');
body.append('billing_zip_code', 'C1043AAU');
body.append('has_medical_conditions', '1');
body.append('medical_details', 'wuqedbuway');
body.append('schedules[]', 'et');
body.append('school_ids[]', '5');
body.append('custom_fields[]', 'nam');
body.append('image', document.querySelector('input[name="image"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (200):


{ "message": "Profile updated successfully", "coach": { ... } }
 

Example response (201):


{ "message": "Profile created successfully", "coach": { ... } }
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (413):


{
    "message": "Request Entity Too Large. File exceeds maximum size limit."
}
 

Example response (422):


{
    "message": "This email is already registered for another user."
}
 

Request      

POST api/schools/{school_slug}/staff/coaches

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: multipart/form-data

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: voluptatem

school   integer   

The ID of the school the coach will belong to. Example: 5

Body Parameters

id   integer  optional  

optional Coach id to update. Example: 10

first_name   string   

First name. Example: Andrew

last_name   string   

Last name. Example: Ramirez

gender   string  optional  

optional Gender (male,female,other). Example: male

date_birth   date  optional  

optional Date of birth (YYYY-MM-DD). Example: 1985-09-15

id_number   string  optional  

optional Identification number. Example: 9876543210

language   string  optional  

optional Preferred language. Example: en

phone_code   string  optional  

optional Phone country code. Example: 1

billing_phone_number   string  optional  

optional Billing phone. Example: +17865551234

secondary_phone_number   string  optional  

Must not be greater than 40 characters. Example: dgsqdhntqwnyvboj

billing_email   string   

Billing email and user's email. Example: billing@company.com

secondary_email   string  optional  

Must be a valid email address. Must not be greater than 120 characters. Example: stamm.althea@example.net

billing_country   string  optional  

optional Billing country. Example: Argentina

billing_state   string  optional  

optional Billing state. Example: Buenos Aires

billing_city   string  optional  

Must not be greater than 80 characters. Example: lknwsqogrutzywztlojny

billing_address   string  optional  

optional Billing address. Example: 1234 Corrientes Ave, CABA

billing_zip_code   string  optional  

optional Billing zip code. Example: C1043AAU

has_medical_conditions   boolean  optional  

Example: true

medical_details   string  optional  

This field is required when has_medical_conditions is 1. Must not be greater than 1000 characters. Example: wuqedbuway

experience_level   string  optional  
schedules   string[]  optional  

optional Work schedules. Can be a JSON string in multipart requests.

day_of_week   string   

Day (monday-sunday). Example: monday

start_time   string   

Start time (HH:MM). Example: 14:00

end_time   string   

End time (HH:MM). Example: 20:00

image   file  optional  

optional Profile image (multipart file upload). Max size: 2MB. Formats: jpeg, png, jpg, gif, webp. Example: /tmp/phpVD2EuW

school_ids   string[]  optional  

optional Additional school IDs to associate.

custom_fields   string[]  optional  

optional Custom fields as objects: { "slug": "...", "value": "..." }

Coach - Show

requires authentication

Retrieve a Coach by id for the specified school. The response includes the coach's user data, profile (and its media), associated schools and schedules.

Notes:

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/praesentium/staff/coaches/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/praesentium/staff/coaches/10"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "coach": {
        "id": 10,
        "user_name": "andrew.ramirez1",
        "name": "Andrew Ramirez",
        "email": "andrew.ramirez@email.com",
        "profile": {
            "id": 4,
            "user_id": 10,
            "first_name": "Andrew",
            "last_name": "Ramirez",
            "gender": "male",
            "date_birth": "1985-09-15",
            "id_number": "9876543210",
            "language": "en",
            "billing_phone_number": "+17865551234",
            "billing_country": "Argentina",
            "billing_email": "billing@company.com",
            "billing_state": "Buenos Aires",
            "billing_address": "1234 Corrientes Ave, CABA",
            "billing_zip_code": "C1043AAU",
            "image_url": "https://s3-bucket-url.com/profile123.jpg",
            "custom_fields": [
                {
                    "slug": "whatsapp",
                    "value": "+17865559999"
                }
            ]
        },
        "schools": [
            {
                "id": 5,
                "nickname": "Elite Football Academy"
            },
            {
                "id": 8,
                "nickname": "National Soccer Academy"
            }
        ],
        "schedules": [
            {
                "day_of_week": "monday",
                "start_time": "14:00",
                "end_time": "20:00"
            }
        ]
    }
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Coach not found or not associated with this school"
}
 

Request      

GET api/schools/{school_slug}/staff/coaches/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: praesentium

id   integer   

The ID of the Coach to retrieve. Example: 10

school   integer   

The ID of the school to scope the coach lookup. Example: 5

Coach - Archive (soft) a Coach

requires authentication

Marks a coach (and its profile) as archived/inactive without deleting records.

Example request:
curl --request DELETE \
    "https://aleacademy.aleosapp.com/api/schools/laudantium/staff/coaches/10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/laudantium/staff/coaches/10"
);

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

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Coach archived successfully."
}
 

Example response (403):


{
    "message": "Unauthorized"
}
 

Example response (404):


{
    "message": "Coach not found or unauthorized."
}
 

Request      

DELETE api/schools/{school_slug}/staff/coaches/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: laudantium

id   integer   

The ID of the Coach to archive. Example: 10

school   integer   

The ID of the school used to scope the operation. Example: 5

Sync

Account - Sync whit zoho

requires authentication

Synchronizes accounts from Zoho and creates users linked to a specified school.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/quae/sync/zoho/accounts" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/quae/sync/zoho/accounts"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/accounts

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: quae

Leads - Sync with Zoho

requires authentication

Synchronizes leads from Zoho and stores them in the local database.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/aut/sync/zoho/leads" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/aut/sync/zoho/leads"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/leads

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: aut

Player - Sync whit zoho

requires authentication

Synchronizes players from Zoho and creates users linked to a specified school.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/consequuntur/sync/zoho/players" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/consequuntur/sync/zoho/players"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/players

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: consequuntur

Group - Sync whit zoho

requires authentication

Synchronizes groups from Zoho and creates groups linked to a specified school.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/id/sync/zoho/groups" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/id/sync/zoho/groups"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/groups

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: id

Player Vs Group - Sync whit zoho

requires authentication

Synchronizes Cont Vs Group from Zoho and creates users linked to a specified school.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/voluptatum/sync/zoho/player_goups" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/voluptatum/sync/zoho/player_goups"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/player_goups

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: voluptatum

Service Catalog - Sync with Zoho

requires authentication

Synchronizes service catalog from Zoho and stores the data in the database.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/velit/sync/zoho/service_catalogs" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/velit/sync/zoho/service_catalogs"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/service_catalogs

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: velit

Subscription - Sync with Zoho

requires authentication

Synchronizes subscriptions from Zoho and creates or updates subscriptions in the database.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/schools/omnis/sync/zoho/subscriptions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/omnis/sync/zoho/subscriptions"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/schools/{school_slug}/sync/zoho/subscriptions

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: omnis

QuickBooks - Sync Recurring Subscriptions

requires authentication

Triggers a synchronization of active RecurringTransaction templates from QuickBooks Online into the local subscriptions system for the specified school. The controller will use the configured QuickBooks credentials (and refresh the access token if necessary).

Note: the method currently runs synchronously and returns once the sync attempt completes.

Example request:
curl --request POST \
    "https://aleacademy.aleosapp.com/api/schools/magnam/sync/quickbooks/sync-subscriptions" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/schools/magnam/sync/quickbooks/sync-subscriptions"
);

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

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "message": "QuickBooks recurring templates synchronized successfully"
}
 

Example response (401, unauthorized):


{
    "message": "Unauthenticated."
}
 

Example response (500, error):


{
    "message": "Error synchronizing with QuickBooks"
}
 

Request      

POST api/schools/{school_slug}/sync/quickbooks/sync-subscriptions

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

school_slug   string   

The slug of the school. Example: magnam

school   integer   

The school id to sync subscriptions for. Example: 5

Utilities

This endpoint is not scoped to a specific school — it is available under /api/metadata/filter-operators and requires authentication (auth:sanctum).

Countries - List

Returns a flattened list of countries loaded from JSON files under storage/app/locations. Files named select_countries.json are intentionally excluded.

Source files are expected under storage/app/locations and may be split across multiple JSON files.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/locations/countries" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/locations/countries"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "id": "Afghanistan",
        "name": "Afghanistan"
    },
    {
        "id": "Albania",
        "name": "Albania"
    }
]
 

Example response (500):


{
    "error": "Location directory not found"
}
 

Request      

GET api/locations/countries

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

Phone codes - List

Returns a normalized list of phone/dial codes derived from storage/app/locations/countries.json. The response contains objects with these keys: country_name, country_short_name (ISO2) and country_phone_code (integer, dial code without the leading '+'). The method normalizes input keys from different sources (e.g. dial_code, country_phone_code, calling_code).

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/locations/phone-code" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/locations/phone-code"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "country_name": "Afghanistan",
        "country_short_name": "AF",
        "country_phone_code": 93
    },
    {
        "country_name": "Albania",
        "country_short_name": "AL",
        "country_phone_code": 355
    }
]
 

Example response (500):


{
    "error": "countries.json not found"
}
 

Example response (500):


{
    "error": "Invalid countries.json"
}
 

Request      

GET api/locations/phone-code

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

States - List by Country

Returns the list of states for a given country loaded from JSON files under storage/app/locations/states. The controller normalizes the provided country by replacing spaces with underscores to build the filename.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/locations/Afghanistan/states" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/locations/Afghanistan/states"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "state_name": "Badakhshan"
    },
    {
        "state_name": "Badgis"
    }
]
 

Example response (404):


{
    "error": "Country not found"
}
 

Example response (500):


{
    "error": "Invalid data file"
}
 

Request      

GET api/locations/{country}/states

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

country   string   

Country name or code. Spaces are replaced by underscores to match file names. Example: Afghanistan

Cities - List by Country and State

Returns all cities for a given state in a given country. Data is loaded from storage/app/locations/cities/{country}/{state}.json. Inputs are normalized by replacing spaces with underscores when building the filename.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/locations/Afghanistan/Badakhshan/cities" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/locations/Afghanistan/Badakhshan/cities"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "city_name": "Eshkashem"
    }
]
 

Example response (404):


{
    "error": "State not found"
}
 

Example response (500):


{
    "error": "Invalid data file"
}
 

Request      

GET api/locations/{country}/{state}/cities

Headers

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

country   string   

Country name or code. Spaces will be replaced with underscores to match filenames. Example: Afghanistan

state   string   

State name. Spaces will be replaced with underscores to match filenames. Example: Badakhshan

Get available filter operators

requires authentication

This endpoint returns a list of supported filter operators that can be used in dynamic search/filter features. Each operator includes labels in English and Spanish for internationalization purposes.

Example request:
curl --request GET \
    --get "https://aleacademy.aleosapp.com/api/metadata/filter-operators" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://aleacademy.aleosapp.com/api/metadata/filter-operators"
);

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

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
  "operators": [
    {
      "value": "contains",
      "label": {
        "es": "Contiene",
        "en": "Contains"
      }
    },
    {
      "value": "not_equals",
      "label": {
        "es": "Diferente de",
        "en": "Not equal to"
      }
    },
    {
      "value": "empty",
      "label": {
        "es": "Vacío",
        "en": "Empty"
      }
    }
    // ... other operators
  ]
}
 

Request      

GET api/metadata/filter-operators

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json