api/API_DOCUMENTATION.md

16 KiB

API Documentation

Profitra API Documentation

Base Information

Base URL: https://api.profitra.id/v1

Development URL: http://localhost:8000/v1

Protocol: HTTPS (Production), HTTP (Development)

Content Type: application/json

Authentication: JWT Bearer Token


API Structure

Endpoints Organization

/v1
├── /auth              # Authentication
├── /users             # User management
├── /tenants           # Tenant management
├── /plans             # Subscription plans
├── /subscriptions     # Subscription management
├── /business-types    # Business types
├── /products          # Product management (tenant)
├── /categories        # Product categories (tenant)
├── /customers         # Customer management (tenant)
├── /sales             # Sales transactions (tenant)
├── /inventory         # Inventory management (tenant)
├── /reports           # Reports (tenant)
└── /settings          # Settings (tenant)

Authentication

Register

Endpoint: POST /auth/register

Description: Create new user account

Request Body:

{
  "full_name": "John Doe",
  "email": "john@example.com",
  "username": "johndoe",
  "password": "securepass123",
  "phone": "081234567890",
  "timezone": "Asia/Jakarta"
}

Validation Rules:

  • full_name: required, max 200 chars
  • email: required, valid email, unique, max 100 chars
  • username: required, alphanumeric, unique, 3-20 chars
  • password: required, min 8 chars
  • phone: optional, valid phone number
  • timezone: optional, default "Asia/Jakarta"

Success Response (201):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "john@example.com",
  "username": "johndoe",
  "tenant_id": null,
  "status": "INACTIVE",
  "created_at": "2026-07-20T08:00:00Z"
}

Error Response (409):

{
  "message": "Email atau username sudah terdaftar."
}

Error Response (400):

{
  "message": "Validation errors in your request",
  "errors": [
    {
      "field": "email",
      "message": "Invalid email format",
      "code": 1001
    }
  ]
}

Login

Endpoint: POST /auth/login

Description: Authenticate user and get JWT token

Request Body:

{
  "login": "johndoe",
  "password": "securepass123"
}

Validation Rules:

  • login: required (username or email)
  • password: required

Success Response (200):

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john@example.com",
    "username": "johndoe",
    "tenant_id": "660e8400-e29b-41d4-a716-446655440000",
    "status": "ACTIVE",
    "created_at": "2026-07-20T08:00:00Z",
    "profile": {
      "full_name": "John Doe",
      "phone": "081234567890",
      "timezone": "Asia/Jakarta"
    }
  }
}

Error Response (401):

{
  "message": "Kredensial autentikasi tidak ada atau tidak valid."
}

Error Response (401) - Email not verified:

{
  "message": "Email belum diverifikasi. Silakan verifikasi email terlebih dahulu."
}

Verify Email

Endpoint: POST /auth/verify-email

Description: Verify user email with token

Request Body:

{
  "token": "verification-token-here"
}

Success Response (200):

{
  "message": "Email berhasil diverifikasi",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john@example.com",
    "status": "ACTIVE",
    "email_verified_at": "2026-07-20T08:30:00Z"
  }
}

Error Response (400):

{
  "message": "Token tidak valid atau sudah kadaluarsa"
}

Resend Verification Email

Endpoint: POST /auth/resend-verification

Description: Resend verification email

Request Body:

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

Success Response (200):

{
  "message": "Email verifikasi telah dikirim ulang"
}

Rate Limit: 3 requests per hour per email


Forgot Password

Endpoint: POST /auth/forgot-password

Description: Request password reset

Request Body:

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

Success Response (200):

{
  "message": "Link reset password telah dikirim ke email Anda"
}

Reset Password

Endpoint: POST /auth/reset-password

Description: Reset password with token

Request Body:

{
  "token": "reset-token-here",
  "password": "newpassword123",
  "password_confirmation": "newpassword123"
}

Success Response (200):

{
  "message": "Password berhasil diubah"
}

Logout

Endpoint: POST /auth/logout

Description: Logout user (invalidate token if using refresh token)

Headers:

Authorization: Bearer {token}

Success Response (200):

{
  "message": "Logout berhasil"
}

Get Current User

Endpoint: GET /auth/me

Description: Get current authenticated user info

Headers:

Authorization: Bearer {token}

Success Response (200):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "john@example.com",
  "username": "johndoe",
  "tenant_id": "660e8400-e29b-41d4-a716-446655440000",
  "status": "ACTIVE",
  "profile": {
    "full_name": "John Doe",
    "phone": "081234567890",
    "timezone": "Asia/Jakarta"
  },
  "tenant": {
    "id": "660e8400-e29b-41d4-a716-446655440000",
    "name": "Toko Maju Jaya",
    "slug": "toko-maju-jaya",
    "business_type": "RETAIL"
  }
}

Business Types

List Business Types

Endpoint: GET /business-types

Description: Get all available business types

Success Response (200):

[
  {
    "id": "770e8400-e29b-41d4-a716-446655440000",
    "code": "RETAIL",
    "name": "Retail",
    "is_active": true,
    "created_at": "2026-01-01T00:00:00Z"
  },
  {
    "id": "880e8400-e29b-41d4-a716-446655440000",
    "code": "FNB",
    "name": "Food & Beverage",
    "is_active": true,
    "created_at": "2026-01-01T00:00:00Z"
  }
]

Get Business Type

Endpoint: GET /business-types/{id}

Description: Get business type by ID

Success Response (200):

{
  "id": "770e8400-e29b-41d4-a716-446655440000",
  "code": "RETAIL",
  "name": "Retail",
  "is_active": true,
  "created_at": "2026-01-01T00:00:00Z"
}

Error Response (404):

{
  "message": "Business type not found"
}

Subscription Plans

List Plans

Endpoint: GET /plans

Description: Get all available subscription plans

Success Response (200):

[
  {
    "id": "990e8400-e29b-41d4-a716-446655440000",
    "code": "FREE",
    "name": "Free Plan",
    "description": "30-day trial with basic features",
    "price": 0,
    "limits": {
      "products": 100,
      "customers": 100,
      "transactions_per_month": 500,
      "users": 1,
      "locations": 1
    },
    "is_active": true,
    "created_at": "2026-01-01T00:00:00Z"
  },
  {
    "id": "aa0e8400-e29b-41d4-a716-446655440000",
    "code": "BASIC",
    "name": "Basic Plan",
    "description": "For small to medium business",
    "price": 99000,
    "limits": {
      "products": 5000,
      "customers": -1,
      "transactions_per_month": 5000,
      "users": 5,
      "locations": 3
    },
    "is_active": true,
    "created_at": "2026-01-01T00:00:00Z"
  },
  {
    "id": "bb0e8400-e29b-41d4-a716-446655440000",
    "code": "PRO",
    "name": "Pro Plan",
    "description": "For medium to large business",
    "price": 299000,
    "limits": {
      "products": -1,
      "customers": -1,
      "transactions_per_month": -1,
      "users": -1,
      "locations": -1
    },
    "is_active": true,
    "created_at": "2026-01-01T00:00:00Z"
  }
]

Note: -1 means unlimited


Get Plan

Endpoint: GET /plans/{id}

Description: Get plan by ID

Success Response (200):

{
  "id": "990e8400-e29b-41d4-a716-446655440000",
  "code": "FREE",
  "name": "Free Plan",
  "description": "30-day trial with basic features",
  "price": 0,
  "limits": {
    "products": 100,
    "customers": 100,
    "transactions_per_month": 500,
    "users": 1,
    "locations": 1
  },
  "is_active": true
}

Tenants

Create Tenant (Onboarding)

Endpoint: POST /tenants

Description: Create new tenant (business onboarding)

Headers:

Authorization: Bearer {token}

Request Body:

{
  "name": "Toko Maju Jaya",
  "business_type_id": "770e8400-e29b-41d4-a716-446655440000",
  "phone": "0215551234",
  "email": "info@majujaya.com",
  "address": "Jl. Sudirman No. 123",
  "city": "Jakarta Pusat",
  "province": "DKI Jakarta",
  "postal_code": "10110",
  "plan_id": "990e8400-e29b-41d4-a716-446655440000",
  "settings": {
    "currency": "IDR",
    "timezone": "Asia/Jakarta",
    "tax_rate": 11,
    "fiscal_year_start": 1
  }
}

Success Response (201):

{
  "id": "660e8400-e29b-41d4-a716-446655440000",
  "name": "Toko Maju Jaya",
  "slug": "toko-maju-jaya",
  "business_type_id": "770e8400-e29b-41d4-a716-446655440000",
  "phone": "0215551234",
  "email": "info@majujaya.com",
  "address": "Jl. Sudirman No. 123",
  "status": "ACTIVE",
  "created_at": "2026-07-20T08:00:00Z",
  "subscription": {
    "id": "cc0e8400-e29b-41d4-a716-446655440000",
    "plan": "FREE",
    "status": "TRIAL",
    "started_at": "2026-07-20T08:00:00Z",
    "ended_at": "2026-08-19T23:59:59Z"
  }
}

Process:

  1. Validate request data
  2. Generate unique slug
  3. Create tenant record in central DB
  4. Create tenant database
  5. Run migrations on tenant DB
  6. Seed default data
  7. Create subscription
  8. Link user to tenant
  9. Assign OWNER role to user in tenant DB
  10. Send welcome email

Get Tenant

Endpoint: GET /tenants/{id}

Description: Get tenant details

Headers:

Authorization: Bearer {token}

Success Response (200):

{
  "id": "660e8400-e29b-41d4-a716-446655440000",
  "name": "Toko Maju Jaya",
  "slug": "toko-maju-jaya",
  "business_type": {
    "code": "RETAIL",
    "name": "Retail"
  },
  "phone": "0215551234",
  "email": "info@majujaya.com",
  "address": "Jl. Sudirman No. 123",
  "status": "ACTIVE",
  "created_at": "2026-07-20T08:00:00Z",
  "subscription": {
    "plan": "FREE",
    "status": "TRIAL",
    "expires_at": "2026-08-19T23:59:59Z"
  }
}

Update Tenant

Endpoint: PUT /tenants/{id}

Description: Update tenant information

Headers:

Authorization: Bearer {token}

Permission Required: tenants.edit or OWNER role

Request Body:

{
  "name": "Toko Maju Jaya Sejahtera",
  "phone": "0215551235",
  "email": "info@majujaya.co.id",
  "address": "Jl. Sudirman No. 125"
}

Success Response (200):

{
  "id": "660e8400-e29b-41d4-a716-446655440000",
  "name": "Toko Maju Jaya Sejahtera",
  "slug": "toko-maju-jaya",
  "phone": "0215551235",
  "email": "info@majujaya.co.id",
  "address": "Jl. Sudirman No. 125",
  "updated_at": "2026-07-20T09:00:00Z"
}

Products (Tenant-specific)

Note: All product endpoints require tenant context from JWT token

List Products

Endpoint: GET /products

Description: Get all products for current tenant

Headers:

Authorization: Bearer {token}

Query Parameters:

  • page: integer, default 1
  • limit: integer, default 20, max 100
  • search: string, search in name/sku
  • category_id: uuid, filter by category
  • status: string, filter by status (active/inactive)
  • sort: string, sort field (name, price, stock)
  • order: string, sort order (asc/desc)

Success Response (200):

{
  "data": [
    {
      "id": "dd0e8400-e29b-41d4-a716-446655440000",
      "sku": "PRD-001",
      "name": "Product A",
      "description": "Description of product A",
      "category_id": "ee0e8400-e29b-41d4-a716-446655440000",
      "category_name": "Electronics",
      "unit": "pcs",
      "purchase_price": 40000,
      "selling_price": 50000,
      "stock": 100,
      "min_stock": 10,
      "status": "active",
      "image": "https://cdn.profitra.id/products/product-a.jpg",
      "created_at": "2026-07-20T08:00:00Z",
      "updated_at": "2026-07-20T08:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 50,
    "total_pages": 3
  }
}

Get Product

Endpoint: GET /products/{id}

Description: Get product by ID

Headers:

Authorization: Bearer {token}

Success Response (200):

{
  "id": "dd0e8400-e29b-41d4-a716-446655440000",
  "sku": "PRD-001",
  "barcode": "1234567890123",
  "name": "Product A",
  "description": "Description of product A",
  "category_id": "ee0e8400-e29b-41d4-a716-446655440000",
  "category": {
    "id": "ee0e8400-e29b-41d4-a716-446655440000",
    "name": "Electronics"
  },
  "unit": "pcs",
  "purchase_price": 40000,
  "selling_price": 50000,
  "stock": 100,
  "min_stock": 10,
  "max_stock": 500,
  "location": "Rak A-1",
  "status": "active",
  "images": [
    "https://cdn.profitra.id/products/product-a-1.jpg",
    "https://cdn.profitra.id/products/product-a-2.jpg"
  ],
  "variants": [
    {
      "id": "ff0e8400-e29b-41d4-a716-446655440000",
      "name": "Size M",
      "sku": "PRD-001-M",
      "price": 50000,
      "stock": 50
    }
  ],
  "created_at": "2026-07-20T08:00:00Z",
  "updated_at": "2026-07-20T08:00:00Z"
}

Create Product

Endpoint: POST /products

Description: Create new product

Headers:

Authorization: Bearer {token}

Permission Required: products.create

Request Body:

{
  "sku": "PRD-002",
  "barcode": "1234567890124",
  "name": "Product B",
  "description": "Description of product B",
  "category_id": "ee0e8400-e29b-41d4-a716-446655440000",
  "unit": "pcs",
  "purchase_price": 60000,
  "selling_price": 75000,
  "stock": 50,
  "min_stock": 5,
  "max_stock": 200,
  "location": "Rak B-1",
  "status": "active"
}

Success Response (201):

{
  "id": "110e8400-e29b-41d4-a716-446655440000",
  "sku": "PRD-002",
  "name": "Product B",
  "selling_price": 75000,
  "stock": 50,
  "created_at": "2026-07-20T09:00:00Z"
}

Error Response (403) - Limit Reached:

{
  "message": "Product limit reached (100). Please upgrade your plan."
}

Update Product

Endpoint: PUT /products/{id}

Description: Update product

Headers:

Authorization: Bearer {token}

Permission Required: products.edit

Request Body:

{
  "name": "Product B Updated",
  "selling_price": 80000,
  "stock": 45
}

Success Response (200):

{
  "id": "110e8400-e29b-41d4-a716-446655440000",
  "name": "Product B Updated",
  "selling_price": 80000,
  "stock": 45,
  "updated_at": "2026-07-20T10:00:00Z"
}

Delete Product

Endpoint: DELETE /products/{id}

Description: Soft delete product

Headers:

Authorization: Bearer {token}

Permission Required: products.delete

Success Response (204):

No Content

Response Format Standards

Success Response

Single Resource:

{
  "id": "uuid",
  "field": "value"
}

List Resources:

{
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 100,
    "total_pages": 5
  }
}

Operation Success:

{
  "message": "Operation completed successfully"
}

Error Response

400 Bad Request:

{
  "message": "Validation errors",
  "errors": [
    {
      "field": "email",
      "message": "Invalid email format",
      "code": 1001
    }
  ]
}

401 Unauthorized:

{
  "message": "Authentication credentials were missing or incorrect"
}

403 Forbidden:

{
  "message": "You don't have permission to access this resource"
}

404 Not Found:

{
  "message": "Resource not found"
}

409 Conflict:

{
  "message": "Resource already exists"
}

429 Too Many Requests:

{
  "message": "Rate limit exceeded. Try again later."
}

500 Internal Server Error:

{
  "message": "An unexpected error occurred"
}

Rate Limiting

Default Limits:

  • Anonymous: 100 requests/hour
  • Authenticated: 1000 requests/hour
  • API Key (Pro plan): 10000 requests/hour

Headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1626782400

Versioning

API menggunakan URL versioning: /v1, /v2, etc.

Current version: v1


Last Updated: 2026-07-20