MENU navbar-image

Introduction

Backend API for the M2 Partner referral platform. Covers authentication, partner onboarding, referral management, property activity timeline, commission tracking, and admin operations.

This documentation covers all Phase 1 M2 Partner API endpoints for the mobile app and web platform.

**Authentication** uses JWT Bearer tokens. Obtain a token via `POST /api/auth/login` with your email and password. Pass the returned token as `Authorization: Bearer {token}` on all subsequent requests.

**User flow:** Register → verify email (6-digit code) → await admin approval → access app.

<aside>Code examples are shown in the dark panel on the right. Use the tabs at the top right to switch between languages.</aside>

Authenticating requests

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

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

Obtain a token via POST /api/auth/login with your email and password. Pass the returned token as Authorization: Bearer {token} on all subsequent requests.

Authentication

Login

Authenticate with email and password to receive a JWT token.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"admin@example.com\",
    \"password\": \"secret\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/login"
);

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

let body = {
    "email": "admin@example.com",
    "password": "secret"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'admin@example.com',
            'password' => 'secret',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's email address. Example: admin@example.com

password   string     

The user's password. Example: secret

Confirm email

Confirms a newly registered user's email address using the code sent by email.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/confirm/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/confirm/architecto"
);

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


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

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
access-control-allow-origin: *
 

{
    "message": "INVALID_USER_CONFIRMATION_CODE",
    "body": []
}
 

Request      

GET api/auth/confirm/{confirmationCode}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

confirmationCode   string     

Example: architecto

Request password reset

Sends a password reset link to the given email address.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/password/request_reset" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"user@example.com\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/password/request_reset"
);

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

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

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/password/request_reset';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'user@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/password/request_reset

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's registered email address. Example: user@example.com

Reset password

Sets a new password using the token received via email.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/password/reset" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"user@example.com\",
    \"token\": \"abc123xyz\",
    \"password\": \"newpass456\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/password/reset"
);

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

let body = {
    "email": "user@example.com",
    "token": "abc123xyz",
    "password": "newpass456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/password/reset';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'user@example.com',
            'token' => 'abc123xyz',
            'password' => 'newpass456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/password/reset

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's email address. Example: user@example.com

token   string     

The password reset token received by email. Example: abc123xyz

password   string     

The new password. Example: newpass456

Register

Creates a new user account. Requires a valid sponsor invite code. Sends a 6-digit verification code to the provided email.

Terms of Service are NOT accepted here — the user accepts them later via POST /api/auth/accept-tos, after email verification (see the registration flow).

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/register" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"invite_code\": \"AB12CD34\",
    \"first_name\": \"Jelena\",
    \"last_name\": \"Petrović\",
    \"email\": \"jelena@example.com\",
    \"password\": \"Secret123!\",
    \"phone\": \"+381641234567\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/register"
);

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

let body = {
    "invite_code": "AB12CD34",
    "first_name": "Jelena",
    "last_name": "Petrović",
    "email": "jelena@example.com",
    "password": "Secret123!",
    "phone": "+381641234567"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/register';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'invite_code' => 'AB12CD34',
            'first_name' => 'Jelena',
            'last_name' => 'Petrović',
            'email' => 'jelena@example.com',
            'password' => 'Secret123!',
            'phone' => '+381641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "message": "REGISTRATION_SUCCESS",
    "body": {
        "user_id": 42
    }
}
 

Example response (406):


{
    "message": "VALIDATION_ERROR",
    "body": {
        "invite_code": [
            "Invalid invite code."
        ]
    }
}
 

Request      

POST api/auth/register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

invite_code   string     

Referral code of the sponsoring partner. Example: AB12CD34

first_name   string     

Example: Jelena

last_name   string     

Example: Petrović

email   string     

Example: jelena@example.com

password   string     

Minimum 8 characters. Example: Secret123!

phone   string     

Example: +381641234567

Social login

Authenticates (or registers) a user via a social OAuth provider. Logs in an existing user or creates a new account.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/social_login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"user@example.com\",
    \"provider\": \"google\",
    \"provider_id\": \"1234567890\",
    \"first_name\": \"John\",
    \"last_name\": \"Doe\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/social_login"
);

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

let body = {
    "email": "user@example.com",
    "provider": "google",
    "provider_id": "1234567890",
    "first_name": "John",
    "last_name": "Doe"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/social_login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'user@example.com',
            'provider' => 'google',
            'provider_id' => '1234567890',
            'first_name' => 'John',
            'last_name' => 'Doe',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/auth/social_login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's email from the OAuth provider. Example: user@example.com

provider   string     

The OAuth provider name. Example: google

provider_id   string     

The user's ID from the OAuth provider. Example: 1234567890

first_name   string  optional    

optional First name (used when creating a new account). Example: John

last_name   string  optional    

optional Last name (used when creating a new account). Example: Doe

Register with invitation

Creates a new confirmed user account using an invitation code. The email is pre-filled from the invitation.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/register/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"first_name\": \"John\",
    \"last_name\": \"Doe\",
    \"password\": \"secret123\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/register/architecto"
);

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

let body = {
    "first_name": "John",
    "last_name": "Doe",
    "password": "secret123"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/register/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'first_name' => 'John',
            'last_name' => 'Doe',
            'password' => 'secret123',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/auth/register/{invitationCode}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invitationCode   string     

Example: architecto

Body Parameters

first_name   string     

First name. Example: John

last_name   string     

Last name. Example: Doe

password   string     

Password. Example: secret123

Change password

requires authentication

Changes the authenticated user's password. Requires the current password for verification.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/password/change" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"old_password\": \"oldpass123\",
    \"new_password\": \"newpass456\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/password/change"
);

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

let body = {
    "old_password": "oldpass123",
    "new_password": "newpass456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/password/change';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'old_password' => 'oldpass123',
            'new_password' => 'newpass456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/password/change

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

old_password   string     

The current password. Example: oldpass123

new_password   string     

The new password. Example: newpass456

Refresh token

requires authentication

Issues a new JWT token using the current valid token.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/refresh" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/refresh"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/refresh';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/auth/refresh

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Logout

requires authentication

Invalidates the current JWT token, ending the session.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/logout" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/logout"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/logout';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/auth/logout

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get authenticated user

requires authentication

Retrieves the currently authenticated user's profile and relations.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/me" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/me"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/auth/me

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Stop impersonating user

requires authentication

Invalidates the impersonation token and issues a fresh token for the original super admin user.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/impersonate/stop" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/impersonate/stop"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/impersonate/stop';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/auth/impersonate/stop

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Start impersonating user

requires authentication

Issues a new token with the target user's ID embedded as a custom claim, allowing the super admin to act as that user.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/impersonate/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/impersonate/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/impersonate/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/auth/impersonate/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the impersonate. Example: architecto

Clear user tokens

requires authentication

Invalidates all active JWT tokens for the specified user. Requires super admin role.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/clear/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/clear/architecto"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/clear/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/auth/clear/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the clear. Example: architecto

Clear all tokens

requires authentication

Invalidates all active JWT tokens for all users. Super admins are skipped unless force=1. Requires super admin role.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/clear-all/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/auth/clear-all/architecto"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/clear-all/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/auth/clear-all/{force?}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

force   string  optional    

Example: architecto

Verify email

Submits the 6-digit code sent to the user's email during registration. On success, status changes to pending_tos — the user must still accept the Terms of Service via POST /api/auth/accept-tos before admins are notified.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/verify-email" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jelena@example.com\",
    \"code\": \"123456\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/verify-email"
);

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

let body = {
    "email": "jelena@example.com",
    "code": "123456"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/verify-email';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jelena@example.com',
            'code' => '123456',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "EMAIL_VERIFIED",
    "body": {}
}
 

Example response (406):


{
    "message": "CODE_EXPIRED",
    "body": {}
}
 

Example response (406):


{
    "message": "CODE_INVALID",
    "body": {}
}
 

Request      

POST api/auth/verify-email

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Example: jelena@example.com

code   string     

6-digit numeric code. Example: 123456

Accept Terms of Service

Final registration step ("Završi registraciju"). The user accepts both the Terms of Service / Privacy Policy and the confidentiality agreement. Only valid once the email has been verified (status pending_tos). On success the status changes to pending_approval and admins are notified.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/accept-tos" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jelena@example.com\",
    \"tos_accepted\": true,
    \"confidentiality_accepted\": true
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/accept-tos"
);

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

let body = {
    "email": "jelena@example.com",
    "tos_accepted": true,
    "confidentiality_accepted": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/accept-tos';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jelena@example.com',
            'tos_accepted' => true,
            'confidentiality_accepted' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "TOS_ACCEPTED",
    "body": {}
}
 

Example response (406):


{
    "message": "TOS_INVALID_STATE",
    "body": {}
}
 

Example response (406):


{
    "message": "VALIDATION_ERROR",
    "body": {
        "tos_accepted": [
            "The tos accepted field must be accepted."
        ]
    }
}
 

Request      

POST api/auth/accept-tos

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Example: jelena@example.com

tos_accepted   boolean     

Acceptance of the Terms of Service & Privacy Policy. Must be true. Example: true

confidentiality_accepted   boolean     

Acceptance of the confidentiality agreement. Must be true. Example: true

Resend verification code

Resends the 6-digit email verification code. Rate-limited to 3 per hour.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/resend-verification" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jelena@example.com\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/resend-verification"
);

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

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

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/resend-verification';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jelena@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "VERIFICATION_CODE_SENT",
    "body": {}
}
 

Example response (429):


{
    "message": "RATE_LIMIT_EXCEEDED",
    "body": {}
}
 

Request      

POST api/auth/resend-verification

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Example: jelena@example.com

Registration status

Returns whether the pending user has been approved by an admin. Used by the "Check status" button on the pending approval screen.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/auth/registration-status" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jelena@example.com\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/registration-status"
);

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

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

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/registration-status';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jelena@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "REGISTRATION_STATUS",
    "body": {
        "approved": false,
        "status": "pending_approval"
    }
}
 

Request      

GET api/auth/registration-status

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Example: jelena@example.com

Forgot password

Sends a 6-digit password reset code to the user's email. This overrides the default PCMS link-based reset — the app uses a code input.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/forgot-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jelena@example.com\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/forgot-password"
);

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

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

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/forgot-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jelena@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "PASSWORD_RESET_CODE_SENT",
    "body": {}
}
 

Request      

POST api/auth/forgot-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Example: jelena@example.com

Reset password

Verifies the 6-digit code and sets a new password.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/auth/reset-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"jelena@example.com\",
    \"code\": \"654321\",
    \"password\": \"NewSecret123!\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/auth/reset-password"
);

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

let body = {
    "email": "jelena@example.com",
    "code": "654321",
    "password": "NewSecret123!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/auth/reset-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => 'jelena@example.com',
            'code' => '654321',
            'password' => 'NewSecret123!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "PASSWORD_RESET_SUCCESS",
    "body": {}
}
 

Example response (406):


{
    "message": "CODE_EXPIRED",
    "body": {}
}
 

Request      

POST api/auth/reset-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Example: jelena@example.com

code   string     

6-digit code from email. Example: 654321

password   string     

New password, minimum 8 characters. Example: NewSecret123!

Onboarding

Validate invite code

Checks whether a referral code is valid and returns the sponsor's name. Used on the first onboarding screen before registration.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/onboarding/invite/AB12CD34" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/onboarding/invite/AB12CD34"
);

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


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

Example response (200):


{
    "message": "INVITE_CODE_VALID",
    "body": {
        "sponsor_first_name": "Marko",
        "sponsor_last_name": "Marković"
    }
}
 

Example response (404):


{
    "message": "INVITE_CODE_INVALID",
    "body": {}
}
 

Request      

GET api/onboarding/invite/{code}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

code   string     

The referral code to validate. Example: AB12CD34

Me

Get profile

requires authentication

Returns the authenticated user's profile.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/me" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/me"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "ME_RETRIEVED",
    "body": {
        "id": 1,
        "first_name": "Jelena",
        "last_name": "Petrović",
        "email": "jelena@example.com",
        "phone": "+381641234567",
        "status": "active",
        "referral_code": "AB12CD34",
        "roles": [
            "partner"
        ]
    }
}
 

Request      

GET api/me

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update profile

requires authentication

Updates the authenticated user's profile fields.

Example request:
curl --request PATCH \
    "http://m2-partner-api.test/api/me" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"first_name\": \"Jelena\",
    \"last_name\": \"Petrović\",
    \"phone\": \"+381641234567\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/me"
);

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

let body = {
    "first_name": "Jelena",
    "last_name": "Petrović",
    "phone": "+381641234567"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'first_name' => 'Jelena',
            'last_name' => 'Petrović',
            'phone' => '+381641234567',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "ME_UPDATED",
    "body": {
        "id": 1
    }
}
 

Request      

PATCH api/me

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

first_name   string  optional    

optional Example: Jelena

last_name   string  optional    

optional Example: Petrović

phone   string  optional    

optional Example: +381641234567

Dashboard

requires authentication

Returns KPI summary and latest activity feed for the home screen.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/me/dashboard" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/me/dashboard"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me/dashboard';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "DASHBOARD_RETRIEVED",
    "body": {
        "earnings_total": 1250,
        "earnings_pending": 250,
        "referrals_count": 5,
        "active_count": 3,
        "team_count": 2,
        "activity_feed": []
    }
}
 

Request      

GET api/me/dashboard

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Digital card

requires authentication

Returns data for the partner's digital business card and QR code.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/me/digital-card" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/me/digital-card"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me/digital-card';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "DIGITAL_CARD_RETRIEVED",
    "body": {
        "first_name": "Jelena",
        "last_name": "Petrović",
        "status": "candidate",
        "referral_code": "AB12CD34",
        "qr_payload": "https://m2-partner-api.test/join/AB12CD34"
    }
}
 

Request      

GET api/me/digital-card

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Earnings

requires authentication

Returns earnings summary and commission bracket information.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/me/earnings" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/me/earnings"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me/earnings';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "EARNINGS_RETRIEVED",
    "body": {
        "total": 1250,
        "pending": 250,
        "paid": 1000,
        "brackets": {
            "seller": [
                {
                    "min": 750,
                    "max": 5000,
                    "rate": 10
                },
                {
                    "min": 5001,
                    "max": 10000,
                    "rate": 13
                },
                {
                    "min": 10001,
                    "max": null,
                    "rate": 15
                }
            ],
            "buyer": [
                {
                    "min": 750,
                    "max": 5000,
                    "rate": 7
                },
                {
                    "min": 5001,
                    "max": 10000,
                    "rate": 8
                },
                {
                    "min": 10001,
                    "max": null,
                    "rate": 10
                }
            ]
        }
    }
}
 

Request      

GET api/me/earnings

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

My team

requires authentication

Returns a list of partners sponsored by the authenticated user, including the sponsor's earnings from each (10% of their commissions).

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/me/team" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/me/team"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me/team';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "TEAM_RETRIEVED",
    "body": {
        "team": [
            {
                "id": 2,
                "first_name": "Ana",
                "last_name": "Anić",
                "role": "candidate",
                "completed_deals": 1,
                "my_earnings": 50
            }
        ]
    }
}
 

Request      

GET api/me/team

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Activities (email log)

requires authentication

Returns the authenticated user's email notification history. Powers the Activity Center (bell icon) in the app.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/me/activities?filter=week" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/me/activities"
);

const params = {
    "filter": "week",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/me/activities';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'filter' => 'week',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "ACTIVITIES_RETRIEVED",
    "body": {
        "activities": [
            {
                "template": "referral_activity",
                "subject": "Update on your referral: Marko",
                "sent_at": "2026-06-01 10:00:00",
                "reference_type": "referral",
                "reference_id": 5
            }
        ]
    }
}
 

Request      

GET api/me/activities

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

filter   string  optional    

optional Filter by period: week or month. Example: week

Referrals

List referrals

requires authentication

Returns referrals filtered by role: partners see only their own, agents see assigned referrals, admins see all.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/referrals?status=qualified&search=Marko" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/referrals"
);

const params = {
    "status": "qualified",
    "search": "Marko",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'qualified',
            'search' => 'Marko',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "REFERRALS_RETRIEVED",
    "body": {
        "referrals": [],
        "total": 0
    }
}
 

Request      

GET api/referrals

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

optional Filter by status. Example: qualified

search   string  optional    

optional Search by client name. Example: Marko

Create referral

requires authentication

Submits a new client referral. Runs duplicate detection automatically.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/referrals" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"client_name\": \"Marko Marković\",
    \"client_phone\": \"+381641234567\",
    \"client_email\": \"marko@example.com\",
    \"transaction_type\": \"sale\",
    \"property_type\": \"apartment\",
    \"estimated_value\": 150000,
    \"monthly_rent\": 600,
    \"gdpr_consent\": true,
    \"property_description\": \"Three-bedroom apartment in Novi Beograd\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/referrals"
);

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

let body = {
    "client_name": "Marko Marković",
    "client_phone": "+381641234567",
    "client_email": "marko@example.com",
    "transaction_type": "sale",
    "property_type": "apartment",
    "estimated_value": 150000,
    "monthly_rent": 600,
    "gdpr_consent": true,
    "property_description": "Three-bedroom apartment in Novi Beograd"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'client_name' => 'Marko Marković',
            'client_phone' => '+381641234567',
            'client_email' => 'marko@example.com',
            'transaction_type' => 'sale',
            'property_type' => 'apartment',
            'estimated_value' => 150000.0,
            'monthly_rent' => 600.0,
            'gdpr_consent' => true,
            'property_description' => 'Three-bedroom apartment in Novi Beograd',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "message": "REFERRAL_CREATED",
    "body": {
        "id": 1,
        "duplicate_flag": false,
        "duplicate_score": 0
    }
}
 

Example response (406):


{
    "message": "VALIDATION_ERROR",
    "body": {
        "gdpr_consent": [
            "GDPR consent is required."
        ]
    }
}
 

Request      

POST api/referrals

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

client_name   string     

Example: Marko Marković

client_phone   string     

Example: +381641234567

client_email   string  optional    

optional Example: marko@example.com

transaction_type   string     

One of: sale, buy, rent_out, rent_in. Example: sale

property_type   string     

One of: apartment, house, commercial, land, other. Example: apartment

estimated_value   number  optional    

optional Estimated property value in EUR. Example: 150000

monthly_rent   number  optional    

optional Required for rent_out/rent_in, min 500. Example: 600

gdpr_consent   boolean     

Must be true. Example: true

property_description   string  optional    

optional Example: Three-bedroom apartment in Novi Beograd

Check for duplicates

requires authentication

Pre-submission duplicate check. Returns a score and list of potential matches.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/referrals/check-duplicate" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"client_name\": \"Marko Marković\",
    \"client_phone\": \"+381641234567\",
    \"client_email\": \"marko@example.com\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/referrals/check-duplicate"
);

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

let body = {
    "client_name": "Marko Marković",
    "client_phone": "+381641234567",
    "client_email": "marko@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals/check-duplicate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'client_name' => 'Marko Marković',
            'client_phone' => '+381641234567',
            'client_email' => 'marko@example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "DUPLICATE_CHECK_COMPLETE",
    "body": {
        "score": 80,
        "flagged": true,
        "matches": [
            {
                "referral_id": 3,
                "score": 80
            }
        ]
    }
}
 

Request      

POST api/referrals/check-duplicate

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

client_name   string     

Example: Marko Marković

client_phone   string     

Example: +381641234567

client_email   string  optional    

optional Example: marko@example.com

Get referral

requires authentication

Returns a single referral with all fields.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/referrals/1" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/referrals/1"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "REFERRAL_RETRIEVED",
    "body": {
        "referral": {}
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

GET api/referrals/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

The referral ID. Example: 1

Update referral status

requires authentication

Updates the status of a referral. Admin/agent only.

Example request:
curl --request PATCH \
    "http://m2-partner-api.test/api/referrals/1/status" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"qualified\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/referrals/1/status"
);

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

let body = {
    "status": "qualified"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals/1/status';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'qualified',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "REFERRAL_STATUS_UPDATED",
    "body": {
        "id": 1,
        "status": "qualified"
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

PATCH api/referrals/{id}/status

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

Example: 1

Body Parameters

status   string     

One of: qualified, in_progress, price_agreed, completed, rejected. Example: qualified

Reject referral

requires authentication

Rejects a referral with a mandatory reason. Notifies the referrer by email. Admin/agent only.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/referrals/1/reject" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"rejection_reason\": \"Klijent je već u kontaktu sa drugom agencijom.\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/referrals/1/reject"
);

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

let body = {
    "rejection_reason": "Klijent je već u kontaktu sa drugom agencijom."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals/1/reject';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'rejection_reason' => 'Klijent je već u kontaktu sa drugom agencijom.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "REFERRAL_REJECTED",
    "body": {
        "id": 1
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/referrals/{id}/reject

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

Example: 1

Body Parameters

rejection_reason   string     

Example: Klijent je već u kontaktu sa drugom agencijom.

Property Timeline

List activities

requires authentication

Returns the property timeline for a referral. Partners only see activities with visibility = referrer; admins/agents see all.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/referrals/1/activities" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/referrals/1/activities"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals/1/activities';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "ACTIVITIES_RETRIEVED",
    "body": {
        "activities": []
    }
}
 

Request      

GET api/referrals/{referralId}/activities

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

referralId   integer     

Example: 1

Add activity

requires authentication

Adds a new timeline activity to a referral. Admin/agent only.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/referrals/1/activities" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"activity_type_id\": 3,
    \"description\": \"Client meeting scheduled for Monday.\",
    \"agent_notes\": \"Client seems motivated.\",
    \"visibility\": \"referrer\",
    \"amount\": 175000,
    \"next_action\": \"Send offer\",
    \"due_date\": \"2026-06-15\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/referrals/1/activities"
);

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

let body = {
    "activity_type_id": 3,
    "description": "Client meeting scheduled for Monday.",
    "agent_notes": "Client seems motivated.",
    "visibility": "referrer",
    "amount": 175000,
    "next_action": "Send offer",
    "due_date": "2026-06-15"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/referrals/1/activities';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'activity_type_id' => 3,
            'description' => 'Client meeting scheduled for Monday.',
            'agent_notes' => 'Client seems motivated.',
            'visibility' => 'referrer',
            'amount' => 175000.0,
            'next_action' => 'Send offer',
            'due_date' => '2026-06-15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "message": "ACTIVITY_CREATED",
    "body": {
        "id": 1
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/referrals/{referralId}/activities

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

referralId   integer     

Example: 1

Body Parameters

activity_type_id   integer     

ID of the activity type. Example: 3

description   string     

Visible to the referrer. Example: Client meeting scheduled for Monday.

agent_notes   string  optional    

optional Internal notes, not visible to referrer. Example: Client seems motivated.

visibility   string  optional    

optional referrer or admin_only. Defaults to the activity type's default. Example: referrer

amount   number  optional    

optional Deal amount relevant to this step. Example: 175000

next_action   string  optional    

optional What happens next. Example: Send offer

due_date   string  optional    

optional Expected date (Y-m-d). Example: 2026-06-15

Admin: Users

List users

requires authentication

Returns all users with filtering. Includes pending approval queue.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/admin/users?status=pending_approval&search=Jelena" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/admin/users"
);

const params = {
    "status": "pending_approval",
    "search": "Jelena",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/users';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'pending_approval',
            'search' => 'Jelena',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "USERS_RETRIEVED",
    "body": {
        "users": [],
        "total": 0
    }
}
 

Request      

GET api/admin/users

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

optional Filter by status. Example: pending_approval

search   string  optional    

optional Search by name or email. Example: Jelena

Approve user

requires authentication

Sets user status to active, generates a referral code, and sends a welcome email.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/admin/users/42/approve" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/admin/users/42/approve"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/users/42/approve';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "USER_APPROVED",
    "body": {
        "id": 42,
        "referral_code": "AB12CD34"
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/admin/users/{id}/approve

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

Example: 42

Block user

requires authentication

Sets user status to blocked, preventing login.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/admin/users/42/block" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/admin/users/42/block"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/users/42/block';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "USER_BLOCKED",
    "body": {
        "id": 42
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/admin/users/{id}/block

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

Example: 42

Promote to partner

requires authentication

Force-promotes a candidate to partner role regardless of deal count.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/admin/users/42/promote" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/admin/users/42/promote"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/users/42/promote';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "USER_PROMOTED",
    "body": {
        "id": 42
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/admin/users/{id}/promote

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

Example: 42

Admin: Commissions

Preview commission

requires authentication

Calculates the commission breakdown for a referral before confirming. Includes referral bonus if the referrer has a sponsor.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/admin/commissions/preview?referral_id=5&agency_commission=3500" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"referral_id\": 16,
    \"agency_commission\": 662
}"
const url = new URL(
    "http://m2-partner-api.test/api/admin/commissions/preview"
);

const params = {
    "referral_id": "5",
    "agency_commission": "3500",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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

let body = {
    "referral_id": 16,
    "agency_commission": 662
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/commissions/preview';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'referral_id' => '5',
            'agency_commission' => '3500',
        ],
        'json' => [
            'referral_id' => 16,
            'agency_commission' => 662,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "COMMISSION_PREVIEW",
    "body": {
        "rate": 0.1,
        "partner_amount": 350,
        "sponsor_bonus": 35,
        "sub_partner_net": 315,
        "bonus_eligible": true
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

GET api/admin/commissions/preview

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

referral_id   integer     

The referral to calculate for. Example: 5

agency_commission   number     

Agency commission amount in EUR (NOT property price). Example: 3500

Body Parameters

referral_id   integer     

Must match an existing stored value. Example: 16

agency_commission   number     

Must be at least 750. Example: 662

Create commission

requires authentication

Confirms and persists the direct commission for a referral, and (if the referrer has an eligible sponsor) auto-creates the sponsor's referral bonus — 10% of the direct amount, deducted from the sub-partner per the bruto=neto model. Admin only. This is the reset-proof home for commission creation (PCMS module extensions are regenerated away by hard-reset/sync).

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/admin/commissions" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"referral_id\": 5,
    \"agency_commission\": 5000
}"
const url = new URL(
    "http://m2-partner-api.test/api/admin/commissions"
);

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

let body = {
    "referral_id": 5,
    "agency_commission": 5000
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/commissions';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'referral_id' => 5,
            'agency_commission' => 5000.0,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "message": "COMMISSION_CREATED",
    "body": {
        "direct_id": 1,
        "direct_amount": 500,
        "bonus_id": 2,
        "sponsor_bonus": 50
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/admin/commissions

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

referral_id   integer     

Example: 5

agency_commission   number     

Agency commission in EUR (min 750). Example: 5000

Admin: Payouts

Initiate monthly payout

requires authentication

Creates payout records for all partners with pending commissions and notifies them.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/admin/payouts" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"period_month\": 6,
    \"period_year\": 2026
}"
const url = new URL(
    "http://m2-partner-api.test/api/admin/payouts"
);

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

let body = {
    "period_month": 6,
    "period_year": 2026
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/payouts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'period_month' => 6,
            'period_year' => 2026,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "PAYOUT_INITIATED",
    "body": {
        "payouts_created": 5,
        "total_disbursed": 4250
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

POST api/admin/payouts

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

period_month   integer     

Month (1–12). Example: 6

period_year   integer     

Year. Example: 2026

General

System-level endpoints for CMS management, AI helpers and API overdraft status.

Refresh API overdraft status

Refreshes and returns the current API overdraft flag. Used by the frontend to check if the API call limit is exceeded.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/refresh-api-overdraft-status" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/refresh-api-overdraft-status"
);

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


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

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
access-control-allow-origin: *
 

{
    "message": "API_OVERDRAFT_STATUS_REFRESH_SUCCESS",
    "body": {
        "api_overdraft_status": false
    }
}
 

Request      

GET api/refresh-api-overdraft-status

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Check export status

requires authentication

Returns the current database export status and a list of available export files.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/database-export" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/database-export"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/database-export';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (500):

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

{
    "message": "GENERAL_EXCEPTION",
    "body": {
        "message": "There's been an error!",
        "code": "disabled",
        "file": "disabled",
        "line": "disabled",
        "trace": "disabled"
    }
}
 

Request      

GET api/database-export

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Check import status

requires authentication

Returns the current database import status.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/database-import" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/database-import"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/database-import';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (500):

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

{
    "message": "GENERAL_EXCEPTION",
    "body": {
        "message": "There's been an error!",
        "code": "disabled",
        "file": "disabled",
        "line": "disabled",
        "trace": "disabled"
    }
}
 

Request      

GET api/database-import

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Export database

requires authentication

Dispatches an async job to export the database to a .tar archive. Check export status to poll for completion.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/database-export" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/database-export"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/database-export';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/database-export

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Import database

requires authentication

Uploads a .tar database backup file and dispatches an async import job.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/database-import" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "file=@/private/var/folders/kt/2yvy5kfx3g9d63g5xcj534z00000gn/T/phpH6qb7X" 
const url = new URL(
    "http://m2-partner-api.test/api/database-import"
);

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

const body = new FormData();
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/database-import';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'file',
                'contents' => fopen('/private/var/folders/kt/2yvy5kfx3g9d63g5xcj534z00000gn/T/phpH6qb7X', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/database-import

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

file   file     

The .tar database backup file to import. Example: /private/var/folders/kt/2yvy5kfx3g9d63g5xcj534z00000gn/T/phpH6qb7X

Hard reset CMS

requires authentication

Clears all dynamic models, migrations and DB tables, then rebuilds and re-seeds from scratch. Destructive — use with extreme caution.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/photon/hard-reset?clean=" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/photon/hard-reset"
);

const params = {
    "clean": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/photon/hard-reset';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'clean' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):

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

{
    "message": "PHOTON_HARD_RESET_SUCCESS",
    "body": []
}
 

Request      

GET api/photon/hard-reset

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

clean   boolean  optional    

optional If true, also removes generated model and migration files. Example: false

Soft reset CMS

requires authentication

Resets the CMS to its default state while preserving module extenders. Useful when a project-specific seed is configured.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/photon/soft-reset?clean=" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/photon/soft-reset"
);

const params = {
    "clean": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/photon/soft-reset';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'clean' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):

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

{
    "message": "PHOTON_SOFT_RESET_SUCCESS",
    "body": []
}
 

Request      

GET api/photon/soft-reset

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

clean   boolean  optional    

optional If true, also removes generated files. Example: false

Translate text (AI)

requires authentication

Translates the given text to the target language using OpenAI. Requires OPENAI_API_KEY to be configured.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/ai/translate" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"string\": \"Hello world\",
    \"target_language_code\": \"de\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/ai/translate"
);

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

let body = {
    "string": "Hello world",
    "target_language_code": "de"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/ai/translate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'string' => 'Hello world',
            'target_language_code' => 'de',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/ai/translate

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

string   string     

The text to translate. Example: Hello world

target_language_code   string     

ISO 639-1 language code of the target language. Example: de

Edit text (AI)

requires authentication

Edits or rewrites text according to the given system prompt, using OpenAI. Requires OPENAI_API_KEY to be configured.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/ai/edit" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"text\": \"This is a rough draft.\",
    \"system_prompt\": \"Fix grammar and improve clarity.\",
    \"target_language_code\": \"en\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/ai/edit"
);

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

let body = {
    "text": "This is a rough draft.",
    "system_prompt": "Fix grammar and improve clarity.",
    "target_language_code": "en"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/ai/edit';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'text' => 'This is a rough draft.',
            'system_prompt' => 'Fix grammar and improve clarity.',
            'target_language_code' => 'en',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/ai/edit

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

text   string     

The text to edit or rewrite. Example: This is a rough draft.

system_prompt   string     

Instructions for the AI on how to edit the text. Example: Fix grammar and improve clarity.

target_language_code   string  optional    

optional Target language code if translation is also needed. Example: en

Admin: Referrals

Remove referral from system

requires authentication

Fully deletes a referral from the system (e.g. deal closed through another agency). Different from rejection — the record is permanently removed.

Example request:
curl --request DELETE \
    "http://m2-partner-api.test/api/admin/referrals/5" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"Deal closed through another agency.\"
}"
const url = new URL(
    "http://m2-partner-api.test/api/admin/referrals/5"
);

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

let body = {
    "reason": "Deal closed through another agency."
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/admin/referrals/5';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'Deal closed through another agency.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "REFERRAL_REMOVED",
    "body": {
        "id": 5
    }
}
 

Example response (403):


{
    "message": "FORBIDDEN",
    "body": {}
}
 

Request      

DELETE api/admin/referrals/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   integer     

Example: 5

Body Parameters

reason   string     

Reason for removal. Example: Deal closed through another agency.

Entries

Endpoints for creating, reading, updating and deleting dynamic module entries.

Retrieves a single dynamic module entry.

requires authentication

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/architecto/564" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/architecto/564"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/architecto/564';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/{tableName}/{entryId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: 564

Filter entries

requires authentication

Returns paginated entries from one or more modules matching the given filter, sorting and pagination criteria.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/filter" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"modules\": [
        \"news\"
    ],
    \"filter\": {
        \"system\": [],
        \"fields\": []
    },
    \"sorting\": {
        \"field\": \"created_at\",
        \"direction\": \"desc\"
    },
    \"pagination\": {
        \"items_per_page\": 15,
        \"current_page\": 1
    },
    \"include\": [
        \"architecto\"
    ]
}"
const url = new URL(
    "http://m2-partner-api.test/api/filter"
);

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

let body = {
    "modules": [
        "news"
    ],
    "filter": {
        "system": [],
        "fields": []
    },
    "sorting": {
        "field": "created_at",
        "direction": "desc"
    },
    "pagination": {
        "items_per_page": 15,
        "current_page": 1
    },
    "include": [
        "architecto"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/filter';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'modules' => [
                'news',
            ],
            'filter' => [
                'system' => [],
                'fields' => [],
            ],
            'sorting' => [
                'field' => 'created_at',
                'direction' => 'desc',
            ],
            'pagination' => [
                'items_per_page' => 15,
                'current_page' => 1,
            ],
            'include' => [
                'architecto',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/filter

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

modules   string[]     

Module table name(s) to query.

filter   object  optional    

optional Filter criteria split into system and fields sub-objects.

system   object  optional    

optional System-level filter conditions (e.g. published, created_at range).

fields   object  optional    

optional Field-level filter conditions.

sorting   object  optional    

optional Sorting configuration.

field   string  optional    

optional Field name to sort by. Example: created_at

direction   string  optional    

optional Sort direction (asc or desc). Example: desc

pagination   object  optional    

optional Pagination settings.

items_per_page   integer  optional    

optional Number of items per page (max defined in config). Example: 15

current_page   integer  optional    

optional Current page number. Example: 1

include   string[]  optional    

optional Related fields/relations to eager-load in the response.

Download export

Downloads a previously exported file by its file name. Use the file name returned by the export endpoint.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/export/download/architecto" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/export/download/architecto"
);

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


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

Example response (404):

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

{
    "message": "FILE_NOT_FOUND",
    "body": {
        "file": "/Users/milanandjelkovic/Sites/m2-partner-api/storage/app/database-exports/architecto"
    }
}
 

Request      

GET api/export/download/{fileName}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

fileName   string     

Example: architecto

Get node children

requires authentication

Returns the immediate children of the specified node, or root-level nodes if no ID is given.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/nodes/architecto/architecto?child_modules=architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/nodes/architecto/architecto"
);

const params = {
    "child_modules": "architecto",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/nodes/architecto/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'child_modules' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/nodes/{tableName}/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

id   string     

The ID of the {tableName}. Example: architecto

Query Parameters

child_modules   string  optional    

optional Comma-separated list of scoped child module table names to include. Example: architecto

Get node ancestors

requires authentication

Returns the ancestral tree path from the root down to the specified node.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/nodes/architecto/ancestors/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/nodes/architecto/ancestors/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/nodes/architecto/ancestors/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/nodes/{tableName}/ancestors/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

id   string     

The ID of the ancestor. Example: architecto

Get node children

requires authentication

Returns the immediate children of the specified node, or root-level nodes if no ID is given.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/nodes/architecto?child_modules=architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/nodes/architecto"
);

const params = {
    "child_modules": "architecto",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/nodes/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'child_modules' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/nodes/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Query Parameters

child_modules   string  optional    

optional Comma-separated list of scoped child module table names to include. Example: architecto

Reposition node

requires authentication

Repositions a node within the tree using a named action (e.g. moveLeft, makeChildOf).

Example request:
curl --request PUT \
    "http://m2-partner-api.test/api/nodes/reposition" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"action\": \"moveLeft\",
    \"affected\": [],
    \"target\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/nodes/reposition"
);

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

let body = {
    "action": "moveLeft",
    "affected": [],
    "target": []
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/nodes/reposition';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'action' => 'moveLeft',
            'affected' => [],
            'target' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT api/nodes/reposition

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

action   string     

The repositioning action to perform. Example: moveLeft

affected   object     

The node to reposition (must include id and table_name).

target   object  optional    

optional The target node for actions like makeChildOf or moveBefore.

Call extension action

requires authentication

Calls a custom action defined in the module's extension class.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/extension_call/architecto/architecto/architecto/{+-0pB" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/extension_call/architecto/architecto/architecto/{+-0pB"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/extension_call/architecto/architecto/architecto/{+-0pB';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/extension_call/{tableName}/{entryId}/{action}/{parameters?}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: architecto

action   string     

Example: architecto

parameters   string  optional    

Example: {+-0pB

Subscribe to entry

requires authentication

Subscribes the authenticated user to change notifications for the specified entry.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/subscribe/architecto/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/subscribe/architecto/architecto"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/subscribe/architecto/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/subscribe/{tableName}/{entryId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: architecto

Unsubscribe from entry

requires authentication

Unsubscribes the authenticated user from change notifications for the specified entry.

Example request:
curl --request DELETE \
    "http://m2-partner-api.test/api/subscribe/architecto/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/subscribe/architecto/architecto"
);

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


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/subscribe/architecto/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/subscribe/{tableName}/{entryId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: architecto

Create entry

requires authentication

Creates a new entry in the specified module. File uploads are supported for asset modules (multipart/form-data).

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"system\": [],
    \"fields\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/architecto"
);

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

let body = {
    "system": [],
    "fields": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'system' => [],
            'fields' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Body Parameters

system   object  optional    

optional System-level fields (e.g. permissions, status overrides).

fields   object     

The module's field values keyed by field column name.

Duplicate entry

requires authentication

Creates a copy of an existing module entry, preserving all field values.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/duplicate/architecto/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/duplicate/architecto/architecto"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/duplicate/architecto/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/duplicate/{tableName}/{entryId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: architecto

Update entry

requires authentication

Updates field values on an existing module entry.

Example request:
curl --request PUT \
    "http://m2-partner-api.test/api/architecto/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"system\": [],
    \"fields\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/architecto/architecto"
);

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

let body = {
    "system": [],
    "fields": []
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/architecto/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'system' => [],
            'fields' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT api/{tableName}/{id}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

id   string     

The ID of the {tableName}. Example: architecto

Body Parameters

system   object  optional    

optional System-level overrides.

fields   object     

The field values to update, keyed by column name.

Mass update entries

requires authentication

Applies a bulk update to all module entries matching the given filter.

Example request:
curl --request PUT \
    "http://m2-partner-api.test/api/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"filter\": [],
    \"fields\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/architecto"
);

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

let body = {
    "filter": [],
    "fields": []
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'filter' => [],
            'fields' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT api/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Body Parameters

filter   object  optional    

optional Filter to select which entries to update.

fields   object  optional    

optional Field values to set on matched entries.

Delete entry

requires authentication

Deletes a module entry. The entry must have no related entries in other modules unless force is passed.

Example request:
curl --request DELETE \
    "http://m2-partner-api.test/api/architecto/564" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"force\": false
}"
const url = new URL(
    "http://m2-partner-api.test/api/architecto/564"
);

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

let body = {
    "force": false
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/architecto/564';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'force' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/{tableName}/{entryId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: 564

Body Parameters

force   boolean  optional    

optional Set to true to force delete even if the entry has related entries. Example: false

Export entries

requires authentication

Exports module entries matching the given filter to a file (e.g. XLSX). Returns a file name to use with the download endpoint.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/export/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"file_type\": \"xlsx\",
    \"file_name\": \"news-export\",
    \"filter\": [],
    \"sorting\": [],
    \"parameters\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/export/architecto"
);

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

let body = {
    "file_type": "xlsx",
    "file_name": "news-export",
    "filter": [],
    "sorting": [],
    "parameters": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/export/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'file_type' => 'xlsx',
            'file_name' => 'news-export',
            'filter' => [],
            'sorting' => [],
            'parameters' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/export/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Body Parameters

file_type   string  optional    

optional Export format. Example: xlsx

file_name   string  optional    

optional Output file name (without extension). A UUID is used if omitted. Example: news-export

filter   object  optional    

optional Filter criteria for which entries to export.

sorting   object  optional    

optional Sorting configuration.

parameters   object  optional    

optional Additional export-specific parameters.

Export single entry

requires authentication

Exports a single module entry to a file.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/export/architecto/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"file_type\": \"xlsx\",
    \"file_name\": \"entry-export\",
    \"parameters\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/export/architecto/architecto"
);

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

let body = {
    "file_type": "xlsx",
    "file_name": "entry-export",
    "parameters": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/export/architecto/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'file_type' => 'xlsx',
            'file_name' => 'entry-export',
            'parameters' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/export/{tableName}/{entryId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

entryId   string     

Example: architecto

Body Parameters

file_type   string  optional    

optional Export format. Example: xlsx

file_name   string  optional    

optional Output file name (without extension). Example: entry-export

parameters   object  optional    

optional Additional export-specific parameters.

Count entries

requires authentication

Returns the total count of module entries matching the given filter.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/count/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"filter\": []
}"
const url = new URL(
    "http://m2-partner-api.test/api/count/architecto"
);

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

let body = {
    "filter": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/count/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'filter' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/count/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Body Parameters

filter   object  optional    

optional Filter criteria to apply before counting.

Get entry stats

requires authentication

Returns aggregated statistics (totals, breakdowns) for the specified module's entries.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/stats/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/stats/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/stats/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/stats/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Get weekly chart data

requires authentication

Returns entry counts grouped by week for charting purposes.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/weekly-chart/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"chart_period\": 4
}"
const url = new URL(
    "http://m2-partner-api.test/api/weekly-chart/architecto"
);

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

let body = {
    "chart_period": 4
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/weekly-chart/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'chart_period' => 4,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/weekly-chart/{tableName}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

tableName   string     

Example: architecto

Body Parameters

chart_period   integer  optional    

optional Number of weeks to include in the chart. Example: 4

Fields

Endpoints for retrieving available field type definitions.

Retrieves all field types.

requires authentication

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/field_types" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/field_types"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/field_types';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/field_types

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Modules

Endpoints for managing CMS module definitions (schema, fields, types).

List modules

requires authentication

Returns all registered CMS modules with their field definitions.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/modules" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/modules"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/modules';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/modules

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create module

requires authentication

Creates a new dynamic module with its database table and field definitions. Requires super admin + dev environment.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/modules" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"module\": {
        \"name\": \"News Articles\",
        \"table_name\": \"news_articles\",
        \"type\": 1
    },
    \"fields\": [
        \"architecto\"
    ],
    \"reporting\": false
}"
const url = new URL(
    "http://m2-partner-api.test/api/modules"
);

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

let body = {
    "module": {
        "name": "News Articles",
        "table_name": "news_articles",
        "type": 1
    },
    "fields": [
        "architecto"
    ],
    "reporting": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/modules';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'module' => [
                'name' => 'News Articles',
                'table_name' => 'news_articles',
                'type' => 1,
            ],
            'fields' => [
                'architecto',
            ],
            'reporting' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/modules

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

module   object     

Module definition object.

name   string     

Human-readable module name. Example: News Articles

table_name   string     

Database table name (snake_case). Example: news_articles

type   integer     

Module type ID (1=non-sortable, 2=sortable, 3=multilevel, etc.). Example: 1

fields   string[]  optional    

optional Array of field definition objects.

reporting   boolean  optional    

optional Enable reporting dashboard for this module. Example: false

Update module

requires authentication

Updates an existing module's definition and field structure. Requires super admin + dev environment.

Example request:
curl --request PUT \
    "http://m2-partner-api.test/api/modules/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"module\": [],
    \"fields\": [
        \"architecto\"
    ],
    \"reporting\": false
}"
const url = new URL(
    "http://m2-partner-api.test/api/modules/architecto"
);

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

let body = {
    "module": [],
    "fields": [
        "architecto"
    ],
    "reporting": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/modules/architecto';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'module' => [],
            'fields' => [
                'architecto',
            ],
            'reporting' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT api/modules/{table}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

table   string     

Example: architecto

Body Parameters

module   object     

Updated module definition object.

fields   string[]  optional    

optional Updated array of field definition objects.

reporting   boolean  optional    

optional Enable/disable reporting for this module. Example: false

Get module

requires authentication

Returns a single module's definition including all field configurations.

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/modules/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/modules/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/modules/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/modules/{table}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

table   string     

Example: architecto

Delete module

requires authentication

Permanently deletes a module and its database table. Requires super admin + dev environment.

Example request:
curl --request DELETE \
    "http://m2-partner-api.test/api/modules/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/modules/architecto"
);

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


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/modules/architecto';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/modules/{table}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

table   string     

Example: architecto

Notifications

Endpoints for managing and retrieving user notifications.

Sends the specified notification.

requires authentication

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/notify/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/notify/architecto"
);

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


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notify/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/notify/{notification_name}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

notification_name   string     

Example: architecto

Marks the specific notification as read.

requires authentication

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/notifications/read/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/notifications/read/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notifications/read/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/notifications/read/{notificationId}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

notificationId   string     

Example: architecto

Counts unread notifications of the currently logged in user.

requires authentication

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/notifications/unread/count" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/notifications/unread/count"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notifications/unread/count';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/notifications/unread/count

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get notifications

requires authentication

Retrieves paginated notifications for the authenticated user.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/notifications/all" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"pagination\": {
        \"items_per_page\": 10,
        \"current_page\": 1
    }
}"
const url = new URL(
    "http://m2-partner-api.test/api/notifications/all"
);

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

let body = {
    "pagination": {
        "items_per_page": 10,
        "current_page": 1
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notifications/all';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'pagination' => [
                'items_per_page' => 10,
                'current_page' => 1,
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/notifications/all

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

pagination   object  optional    

optional Pagination settings.

items_per_page   integer  optional    

optional Items per page. Example: 10

current_page   integer  optional    

optional Current page. Example: 1

Get unread notifications

requires authentication

Retrieves paginated unread notifications for the authenticated user.

Example request:
curl --request POST \
    "http://m2-partner-api.test/api/notifications/unread" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"pagination\": {
        \"items_per_page\": 10,
        \"current_page\": 1
    }
}"
const url = new URL(
    "http://m2-partner-api.test/api/notifications/unread"
);

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

let body = {
    "pagination": {
        "items_per_page": 10,
        "current_page": 1
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notifications/unread';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'pagination' => [
                'items_per_page' => 10,
                'current_page' => 1,
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/notifications/unread

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

pagination   object  optional    

optional Pagination settings.

items_per_page   integer  optional    

optional Items per page. Example: 10

current_page   integer  optional    

optional Current page. Example: 1

Assigns the specified FCM token to the currently logged in user.

requires authentication

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/notifications/fcm/assign_token/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/notifications/fcm/assign_token/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notifications/fcm/assign_token/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/notifications/fcm/assign_token/{token}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

token   string     

Example: architecto

Revokes the specified FCM token from the currently logged in user.

requires authentication

Example request:
curl --request GET \
    --get "http://m2-partner-api.test/api/notifications/fcm/revoke_token/architecto" \
    --header "Authorization: Bearer {YOUR_JWT_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://m2-partner-api.test/api/notifications/fcm/revoke_token/architecto"
);

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


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://m2-partner-api.test/api/notifications/fcm/revoke_token/architecto';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_JWT_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

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

{
    "message": "TOKEN_EXPIRED",
    "body": []
}
 

Request      

GET api/notifications/fcm/revoke_token/{token}

Headers

Authorization        

Example: Bearer {YOUR_JWT_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

token   string     

Example: architecto