Skip to content

API ​

Overview ​

Thesky9 CMS includes a REST API package that lets you interact with your application data from mobile apps, third-party services, or any HTTP client. It uses Laravel Sanctum for token-based authentication and supports optional API key protection, push notifications via Firebase Cloud Messaging (FCM), and user settings management.

The API is managed at Admin > Settings > API (/admin/settings/api).

API Settings ​

Enabling the API ​

Go to Admin > Settings > API and toggle Enable API. When disabled, all /api/v1/* endpoints return 503 Service Unavailable.

API Key Protection ​

API key protection adds a second layer of security on top of Sanctum tokens. When enabled, every request must include an X-API-KEY header.

  1. In Admin > Settings > API, enable API Key Protection.
  2. Copy the generated key (or click Generate to create a new one).
  3. Add the header to all requests:
bash
curl -X GET https://your-domain.com/api/v1/posts \
  -H "Authorization: Bearer your-sanctum-token" \
  -H "X-API-KEY: your-api-key" \
  -H "Content-Type: application/json"

Without a valid key, the API returns 401 Unauthorized:

json
{
  "message": "Invalid or missing API key. Please provide a valid X-API-KEY header.",
  "error": "Unauthorized"
}

TIP

Think of the API key as a gate key for your entire app, while the Sanctum token identifies individual users. The API key prevents unauthorized clients from accessing the API at all; the Sanctum token controls what each user can do.

Common use cases:

  • Restrict API access to only your official mobile app
  • Revoke all access instantly by rotating the key, without invalidating user sessions
  • Add a layer of protection against scrapers or unauthorized integrations

Sanctum Token Management ​

Admins can create and manage server-side API tokens at Admin > Settings > API > API Tokens. These tokens are useful for server-to-server integration (e.g., syncing data from an ERP).

  • Click Create to generate a named token. The plain-text token is shown only once — copy it immediately.
  • Tokens can be deleted individually from the table.

Authentication ​

Login ​

bash
POST /api/v1/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "your-password"
}

Response:

json
{
  "error": false,
  "data": {
    "token": "1|abc123...",
    "user": {
      "id": 1,
      "name": "John Doe",
      "email": "user@example.com"
    }
  },
  "message": null
}

Register ​

bash
POST /api/v1/register
Content-Type: application/json

{
  "first_name": "John",
  "last_name": "Doe",
  "email": "user@example.com",
  "password": "secret123",
  "password_confirmation": "secret123",
  "phone": "+1234567890"
}

Using the Token ​

Include the token in the Authorization header for all authenticated requests:

bash
curl -X GET https://your-domain.com/api/v1/me \
  -H "Authorization: Bearer 1|abc123..." \
  -H "Content-Type: application/json"

Logout ​

bash
GET /api/v1/logout
Authorization: Bearer your-token

This revokes all tokens for the authenticated user.

Password Reset ​

bash
# Request reset link
POST /api/v1/password/forgot
{ "email": "user@example.com" }

# Reset password (using token from email)
POST /api/v1/password/reset
{ "email": "user@example.com", "token": "reset-token", "password": "new-password", "password_confirmation": "new-password" }

Email Verification ​

If email verification is enabled in the API config, unverified users can request a new verification email:

bash
POST /api/v1/resend-verify-account-email
{ "email": "user@example.com" }

Available Endpoints ​

Authentication (Public) ​

MethodEndpointDescription
POST/api/v1/registerRegister new user
POST/api/v1/loginLogin and get token
POST/api/v1/email/checkCheck if email exists
POST/api/v1/password/forgotSend password reset link
POST/api/v1/resend-verify-account-emailResend verification email
POST/api/v1/device-tokensRegister device token

Profile (Authenticated) ​

MethodEndpointDescription
GET/api/v1/meGet user profile
PUT/api/v1/meUpdate profile (name, email, phone, DOB, gender)
POST/api/v1/update/avatarUpload user avatar
PUT/api/v1/update/passwordChange password (requires current password)
GET/api/v1/logoutLogout and revoke tokens

User Settings (Authenticated) ​

MethodEndpointDescription
GET/api/v1/settingsGet user settings
PUT/api/v1/settingsUpdate user settings

Available settings:

KeyTypeDefaultDescription
biometric_enabledbooleanfalseEnable biometric authentication
notification_enabledbooleantrueEnable push notifications
languagestringenPreferred language
currencystringUSDPreferred currency
themestringlightTheme preference (light, dark, auto)
timezonestringUTCUser timezone

Device Tokens (Authenticated) ​

MethodEndpointDescription
POST/api/v1/device-tokensRegister or update a device token
GET/api/v1/device-tokensList user's active device tokens
PUT/api/v1/device-tokens/{id}Update device token metadata
DELETE/api/v1/device-tokens/{id}Delete device token
DELETE/api/v1/device-tokens/by-tokenDelete by token value
POST/api/v1/device-tokens/{id}/deactivateDeactivate without deleting

Notifications (Authenticated) ​

MethodEndpointDescription
GET/api/v1/notificationsList notifications (paginated)
GET/api/v1/notifications/statsGet notification statistics
POST/api/v1/notifications/mark-all-readMark all as read
POST/api/v1/notifications/{id}/readMark one as read
POST/api/v1/notifications/{id}/clickedMark as clicked
DELETE/api/v1/notifications/{id}Delete notification

Query parameters for listing:

ParameterDescription
pagePage number
per_pageItems per page (max 50)
unread_onlyOnly return unread notifications
typeFilter by type (general, order, promotion, system)

Blog Endpoints ​

MethodEndpointDescription
GET/api/v1/postsList blog posts
GET/api/v1/posts/{id}Get a blog post
GET/api/v1/categoriesList blog categories
GET/api/v1/categories/{id}Get a blog category
GET/api/v1/tagsList blog tags
GET/api/v1/tags/{id}Get a blog tag

Page Endpoints ​

MethodEndpointDescription
GET/api/v1/pagesList pages
GET/api/v1/pages/{id}Get a page

Plugin Endpoints ​

Depending on installed plugins, additional endpoints may be available for:

  • E-commerce (products, orders, customers, carts, wishlists)
  • Galleries
  • Contact forms
  • And more

Request Parameters ​

Pagination ​

GET /api/v1/posts?page=2&per_page=15
  • page — Page number (default: 1)
  • per_page — Items per page (default: 10, max: 100)

Filtering ​

GET /api/v1/posts?status=published&category_id=5

Sorting ​

GET /api/v1/posts?sort_by=created_at&order=desc

Including Relations ​

GET /api/v1/posts?include=categories,tags,author

Response Format ​

Success (single resource) ​

json
{
  "error": false,
  "data": {
    "id": 1,
    "name": "Example"
  },
  "message": null
}

Success (paginated) ​

json
{
  "data": [...],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "per_page": 10,
    "to": 10,
    "total": 50
  },
  "links": {
    "first": "https://your-domain.com/api/v1/posts?page=1",
    "last": "https://your-domain.com/api/v1/posts?page=5",
    "prev": null,
    "next": "https://your-domain.com/api/v1/posts?page=2"
  }
}

Error ​

json
{
  "error": true,
  "data": null,
  "message": "Error description"
}

Validation Error (422) ​

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

Push Notifications (FCM) ​

Thesky9 CMS integrates with Firebase Cloud Messaging (FCM v1 API) to send push notifications to mobile app users.

Firebase Setup ​

  1. Create a project at Firebase Console.
  2. Go to Project Settings > Service Accounts.
  3. Click Generate new private key to download the service account JSON file.
  4. In Admin > Settings > API, under Push Notifications:
    • Enable Push Notifications
    • Enter your Firebase Project ID
    • Upload the service account JSON file

The file is stored in storage/app/firebase/ and validated for required fields (project_id, private_key, client_email).

Device Token Registration ​

Your mobile app must register its FCM token with the API after obtaining it from Firebase SDK:

bash
POST /api/v1/device-tokens
Content-Type: application/json

{
  "token": "fcm-device-token-from-firebase-sdk",
  "platform": "android",
  "device_id": "unique-device-identifier",
  "app_version": "1.0.0"
}
  • token (required) — The FCM registration token
  • platform — android or ios
  • device_id — Unique device identifier (for deduplication)
  • app_version — Your app version

If the token already exists, it updates the existing record instead of creating a duplicate.

"No active device tokens found" ​

This message in the admin panel means no mobile app has registered a device token yet. This is normal if:

  • The mobile app hasn't been built or released yet
  • No users have opened the app since FCM integration was added
  • The app isn't calling the /api/v1/device-tokens endpoint

Device tokens appear automatically once users open the mobile app and the app registers its FCM token.

Sending Notifications from Admin ​

Use the Send Custom Notification form at Admin > Settings > API:

FieldRequiredDescription
TitleYesNotification title (max 100 characters)
MessageYesNotification body (max 500 characters)
Target Devices—All Devices, Android only, iOS only, or Customers only
Action URLNoURL to open when notification is tapped
Image URLNoImage to display in the notification

Sending Notifications via Command Line ​

bash
php artisan api:send-notification --title="Sale!" --message="50% off all items today"

Notification Tracking ​

Each notification tracks:

  • Sent count — How many devices received the message
  • Failed count — How many deliveries failed
  • Delivered count — Confirmed deliveries
  • Read count — How many users read the notification

Users can retrieve their notifications via the /api/v1/notifications endpoints and mark them as read or clicked.

Scheduled Notifications ​

Notifications can be scheduled for future delivery. A command processes due notifications:

bash
php artisan schedule:run

Ensure your cronjob is configured for scheduled notifications to work.

Custom API Endpoints ​

You can add custom endpoints in your plugin:

1. Create an API Controller ​

php
<?php

namespace Botble\YourPlugin\Http\Controllers\API;

use Botble\Api\Http\Controllers\ApiController;
use Botble\YourPlugin\Models\YourModel;
use Botble\YourPlugin\Http\Resources\YourModelResource;
use Illuminate\Http\Request;

class YourModelController extends ApiController
{
    public function index(Request $request)
    {
        $data = YourModel::query()
            ->paginate($request->input('per_page', 10));

        return $this->respondWithData(YourModelResource::collection($data));
    }

    public function show(int|string $id)
    {
        $item = YourModel::query()->findOrFail($id);

        return $this->respondWithData(new YourModelResource($item));
    }
}

2. Create a Resource ​

php
<?php

namespace Botble\YourPlugin\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class YourModelResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'description' => $this->description,
            'created_at' => $this->created_at->format('Y-m-d H:i:s'),
            'updated_at' => $this->updated_at->format('Y-m-d H:i:s'),
        ];
    }
}

3. Register Routes ​

php
<?php

namespace Botble\YourPlugin\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;

class YourPluginServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Route::group([
            'prefix' => 'api/v1',
            'namespace' => 'Botble\YourPlugin\Http\Controllers\API',
            'middleware' => ['api', 'auth:sanctum'],
        ], function () {
            Route::apiResource('your-models', 'YourModelController');
        });
    }
}

API Documentation Generation ​

Generate browsable API documentation using Scribe:

bash
composer require knuckleswtf/scribe
php artisan scribe:generate

Access documentation at https://your-domain.com/docs.

API Versioning ​

All endpoints use the /api/v1/ prefix. To create a new API version, add a separate set of controllers and routes with a /api/v2/ prefix.