Skip to content

API Contracts

FORGE generates a RESTful API that follows strict contracts for request/response format, authentication, error handling, and pagination. This page is the complete endpoint reference for every route in a generated application.

Base URL

All API endpoints are served under a versioned base path:

https://api.{app-name}.test/api/v1

Authentication

Protected endpoints require a valid JWT access token in the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

TIP

For browser-based applications, FORGE also supports HttpOnly cookie delivery. The token is automatically sent with every request when using cookies -- no Authorization header needed.

Response Format

Every API response follows a consistent envelope structure.

Success Response

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "John Doe",
    "email": "john@example.com"
  }
}

Paginated Response

json
{
  "success": true,
  "data": [
    { "id": "...", "name": "..." },
    { "id": "...", "name": "..." }
  ],
  "meta": {
    "page": 1,
    "per_page": 15,
    "total": 42,
    "last_page": 3
  }
}

Error Response

json
{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "email": ["The email field is required"],
    "password": ["Must be at least 8 characters"]
  }
}

HTTP Status Codes

CodeMeaningWhen
200OKSuccessful GET, PUT
201CreatedSuccessful POST (resource created)
204No ContentSuccessful DELETE
401UnauthorizedMissing or invalid token
403ForbiddenValid token but insufficient permissions
404Not FoundResource does not exist
422Unprocessable EntityValidation errors
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error

Pagination

All list endpoints accept pagination query parameters:

GET /api/v1/admin/users?page=2&per_page=25
ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger15Items per page (max 100)
searchstring--Search term (where supported)
sortstringcreated_atSort field
orderstringdescSort direction (asc or desc)

Auth Endpoints

Authentication endpoints are public (no token required) unless noted.

POST /auth/login

Authenticate a user and receive JWT tokens.

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

{
  "email": "user@example.com",
  "password": "securepassword"
}
json
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 900,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "John Doe",
      "email": "user@example.com",
      "avatar": null,
      "roles": ["user"],
      "permissions": []
    }
  }
}
json
{
  "success": false,
  "message": "Invalid credentials"
}

POST /auth/register

Create a new user account.

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

{
  "name": "Jane Doe",
  "email": "jane@example.com",
  "password": "securepassword",
  "password_confirmation": "securepassword"
}
json
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 900,
    "user": {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "name": "Jane Doe",
      "email": "jane@example.com",
      "roles": ["user"],
      "permissions": []
    }
  }
}

POST /auth/refresh

Exchange a valid refresh token for a new token pair.

bash
POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
json
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 900
  }
}

WARNING

Refresh tokens are single-use. Each call invalidates the previous token and issues a new pair. If a refresh token is reused, all tokens for that user are revoked as a security precaution (refresh token rotation).

POST /auth/logout

Invalidate the current session. Requires authentication.

bash
POST /api/v1/auth/logout
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response (200):

json
{
  "success": true,
  "message": "Logged out successfully"
}

POST /auth/password/forgot

Send a password reset link to the user's email.

bash
POST /api/v1/auth/password/forgot
Content-Type: application/json

{
  "email": "user@example.com"
}

Response (200):

json
{
  "success": true,
  "message": "Password reset link sent"
}

POST /auth/password/reset

Reset the password using a token from the email.

bash
POST /api/v1/auth/password/reset
Content-Type: application/json

{
  "token": "reset-token-from-email",
  "password": "newsecurepassword",
  "password_confirmation": "newsecurepassword"
}

POST /auth/otp/send

Request a one-time password for mobile authentication.

bash
POST /api/v1/auth/otp/send
Content-Type: application/json

{
  "mobile": "+966500000000"
}

Response (200):

json
{
  "success": true,
  "message": "OTP sent successfully"
}

POST /auth/otp/verify

Verify the OTP and authenticate the user.

bash
POST /api/v1/auth/otp/verify
Content-Type: application/json

{
  "mobile": "+966500000000",
  "otp": "123456"
}
json
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 900,
    "user": {
      "id": "...",
      "name": "...",
      "mobile": "+966500000000"
    }
  }
}

Profile Endpoints

All profile endpoints require authentication.

GET /profile

Retrieve the authenticated user's profile.

bash
GET /api/v1/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response (200):

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "John Doe",
    "email": "john@example.com",
    "mobile": null,
    "avatar": "/media/avatars/john.jpg",
    "roles": ["user"],
    "permissions": [],
    "email_verified_at": "2026-01-15T10:30:00Z",
    "created_at": "2026-01-01T00:00:00Z"
  }
}

PUT /profile

Update the authenticated user's profile information.

bash
PUT /api/v1/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "name": "John Smith",
  "email": "john.smith@example.com"
}

POST /profile/avatar

Upload or replace the user's avatar image.

bash
POST /api/v1/profile/avatar
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: multipart/form-data

avatar: (binary file)

PUT /profile/password

Change the authenticated user's password.

bash
PUT /api/v1/profile/password
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "current_password": "oldsecurepassword",
  "password": "newsecurepassword",
  "password_confirmation": "newsecurepassword"
}

Admin Endpoints

All admin endpoints require authentication and the appropriate permission. Requests without the required permission receive a 403 Forbidden response.

Users

MethodEndpointPermissionDescription
GET/admin/usersusers.viewList all users (paginated)
POST/admin/usersusers.createCreate a new user
GET/admin/users/:idusers.viewGet a single user
PUT/admin/users/:idusers.editUpdate a user
DELETE/admin/users/:idusers.deleteSoft-delete a user
bash
GET /api/v1/admin/users?page=1&per_page=15&search=john
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
bash
POST /api/v1/admin/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "name": "New User",
  "email": "new@example.com",
  "password": "securepassword",
  "is_active": true,
  "roles": ["user"]
}
bash
PUT /api/v1/admin/users/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "name": "Updated Name",
  "is_active": false
}

Roles

MethodEndpointPermissionDescription
GET/admin/rolesroles.viewList all roles (paginated)
POST/admin/rolesroles.createCreate a new role
GET/admin/roles/:idroles.viewGet a single role with permissions
PUT/admin/roles/:idroles.editUpdate role and permissions
DELETE/admin/roles/:idroles.deleteDelete a role

DANGER

System roles (admin, user) cannot be deleted. Attempting to delete a system role returns 403 Forbidden.

Permissions

MethodEndpointPermissionDescription
GET/admin/permissionspermissions.viewList all permissions (grouped)

Permissions are read-only. They are created by seeders and template manifests, not by admin users.

Contents

MethodEndpointPermissionDescription
GET/admin/contentscontents.viewList content pages (paginated)
POST/admin/contentscontents.createCreate a content page
GET/admin/contents/:idcontents.viewGet a single content page
PUT/admin/contents/:idcontents.editUpdate a content page
DELETE/admin/contents/:idcontents.deleteSoft-delete a content page
bash
POST /api/v1/admin/contents
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: multipart/form-data

title: About Us
slug: about-us
summary: Learn more about our company
details: <p>Full HTML content here...</p>
seo: {"title":"About Us","description":"Company info"}
translations: {"ar":{"title":"من نحن","summary":"تعرف على شركتنا"}}
is_active: true
publish_at: 2026-02-01T00:00:00Z
featured_image: (binary file, optional)
json
{
  "success": true,
  "data": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "title": "About Us",
    "slug": "about-us",
    "summary": "Learn more about our company",
    "details": "<p>Full HTML content here...</p>",
    "seo": { "title": "About Us", "description": "Company info" },
    "translations": { "ar": { "title": "من نحن" } },
    "is_active": true,
    "publish_at": "2026-02-01T00:00:00Z",
    "featured_image": {
      "id": "...",
      "url": "/media/contents/about-hero.jpg"
    },
    "created_at": "2026-01-27T12:00:00Z"
  }
}
MethodEndpointPermissionDescription
GET/admin/menusmenus.viewList all menu items (paginated)
POST/admin/menusmenus.createCreate a menu item
GET/admin/menus/:idmenus.viewGet a single menu item
PUT/admin/menus/:idmenus.editUpdate a menu item
DELETE/admin/menus/:idmenus.deleteSoft-delete a menu item
bash
POST /api/v1/admin/menus
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "title": "Home",
  "url": "/",
  "icon": "home",
  "target": "_self",
  "visibility": "public",
  "parent_id": null,
  "sort_order": 1,
  "is_active": true,
  "translations": {
    "ar": { "title": "الرئيسية" }
  }
}

Lookups

MethodEndpointPermissionDescription
GET/admin/lookupslookups.viewList lookups (filterable by type)
POST/admin/lookupslookups.createCreate a lookup entry
GET/admin/lookups/:idlookups.viewGet a single lookup
PUT/admin/lookups/:idlookups.editUpdate a lookup
DELETE/admin/lookups/:idlookups.deleteDelete a lookup
bash
GET /api/v1/admin/lookups?type=country&page=1

Settings

MethodEndpointPermissionDescription
GET/admin/settingssettings.viewGet all settings (grouped)
PUT/admin/settingssettings.editUpdate settings (batch)
bash
PUT /api/v1/admin/settings
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "general.app_name": "My Application",
  "general.timezone": "Asia/Riyadh",
  "auth.allow_registration": "true"
}

Languages

MethodEndpointPermissionDescription
GET/admin/languagessettings.viewList all languages
POST/admin/languagessettings.editAdd a language
PUT/admin/languages/:idsettings.editUpdate a language
DELETE/admin/languages/:idsettings.editRemove a language

Translations

MethodEndpointPermissionDescription
GET/admin/translationstranslations.viewList translations (by group)
PUT/admin/translationstranslations.editUpdate translations (batch)
bash
GET /api/v1/admin/translations?group=auth

API Keys

MethodEndpointPermissionDescription
GET/admin/api-keysapi_keys.viewList API keys
POST/admin/api-keysapi_keys.createCreate an API key
DELETE/admin/api-keys/:idapi_keys.revokeRevoke an API key

WARNING

The plain-text API key is only returned once, in the creation response. It is never stored or retrievable afterward. Store it securely immediately after creation.

Audit Logs

MethodEndpointPermissionDescription
GET/admin/audit-logsaudit.viewList audit logs (paginated, filterable)
bash
GET /api/v1/admin/audit-logs?resource_type=user&action=updated&page=1

Public Endpoints

Public endpoints require no authentication.

GET /contents/:slug

Retrieve a published content page by its slug. Accepts an optional locale query parameter for translated content.

bash
GET /api/v1/contents/about-us?locale=ar

Response (200):

json
{
  "success": true,
  "data": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "title": "من نحن",
    "slug": "about-us",
    "summary": "تعرف على شركتنا",
    "details": "<p>محتوى الصفحة...</p>",
    "seo": { "title": "من نحن", "description": "..." },
    "featured_image": {
      "url": "/media/contents/about-hero.jpg"
    }
  }
}

TIP

When a locale parameter is provided, the API merges the requested language's translations over the default language fields. If a translation is missing for a specific field, the default language value is used as a fallback.

GET /menus

Retrieve active navigation menu items as a nested tree. Supports locale and authentication-aware visibility filtering.

bash
GET /api/v1/menus?locale=ar

Response (200):

json
{
  "success": true,
  "data": [
    {
      "id": "...",
      "title": "الرئيسية",
      "url": "/",
      "icon": "home",
      "target": "_self",
      "children": []
    },
    {
      "id": "...",
      "title": "من نحن",
      "url": "/about-us",
      "icon": null,
      "target": "_self",
      "children": [
        {
          "id": "...",
          "title": "فريقنا",
          "url": "/our-team",
          "icon": null,
          "target": "_self",
          "children": []
        }
      ]
    }
  ]
}

GET /lookups

Retrieve active lookup entries by type.

bash
GET /api/v1/lookups?type=country&locale=ar

GET /languages

Retrieve the list of active languages.

bash
GET /api/v1/languages

Response (200):

json
{
  "success": true,
  "data": [
    { "code": "en", "name": "English", "native_name": "English", "direction": "ltr", "is_default": true },
    { "code": "ar", "name": "Arabic", "native_name": "العربية", "direction": "rtl", "is_default": false }
  ]
}

GET /translations

Retrieve UI translations for the frontend.

bash
GET /api/v1/translations?locale=ar&groups=common,auth,web.home

GET /settings/public

Retrieve public-facing settings.

bash
GET /api/v1/settings/public

Response (200):

json
{
  "success": true,
  "data": {
    "app_name": "My Application",
    "default_language": "en",
    "date_format": "YYYY-MM-DD",
    "allow_registration": true
  }
}

Admin Dashboard

GET /admin/stats

Retrieve dashboard statistics. Requires admin.view permission.

bash
GET /api/v1/admin/stats
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response (200):

json
{
  "success": true,
  "data": {
    "users_count": 156,
    "roles_count": 5,
    "contents_count": 23,
    "recent_activity": [
      { "action": "created", "resource_type": "user", "created_at": "..." }
    ]
  }
}

Error Codes Reference

Error CodeHTTP StatusDescription
VALIDATION_ERROR422Request body failed validation
UNAUTHORIZED401Missing or expired token
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
RATE_LIMITED429Too many requests
INTERNAL_ERROR500Unexpected server error

See Also

Released under the MIT License.