# 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:** ```json { "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):** ```json { "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):** ```json { "message": "Email atau username sudah terdaftar." } ``` **Error Response (400):** ```json { "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:** ```json { "login": "johndoe", "password": "securepass123" } ``` **Validation Rules:** - `login`: required (username or email) - `password`: required **Success Response (200):** ```json { "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):** ```json { "message": "Kredensial autentikasi tidak ada atau tidak valid." } ``` **Error Response (401) - Email not verified:** ```json { "message": "Email belum diverifikasi. Silakan verifikasi email terlebih dahulu." } ``` --- ### Verify Email **Endpoint:** `POST /auth/verify-email` **Description:** Verify user email with token **Request Body:** ```json { "token": "verification-token-here" } ``` **Success Response (200):** ```json { "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):** ```json { "message": "Token tidak valid atau sudah kadaluarsa" } ``` --- ### Resend Verification Email **Endpoint:** `POST /auth/resend-verification` **Description:** Resend verification email **Request Body:** ```json { "email": "john@example.com" } ``` **Success Response (200):** ```json { "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:** ```json { "email": "john@example.com" } ``` **Success Response (200):** ```json { "message": "Link reset password telah dikirim ke email Anda" } ``` --- ### Reset Password **Endpoint:** `POST /auth/reset-password` **Description:** Reset password with token **Request Body:** ```json { "token": "reset-token-here", "password": "newpassword123", "password_confirmation": "newpassword123" } ``` **Success Response (200):** ```json { "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):** ```json { "message": "Logout berhasil" } ``` --- ### Get Current User **Endpoint:** `GET /auth/me` **Description:** Get current authenticated user info **Headers:** ``` Authorization: Bearer {token} ``` **Success Response (200):** ```json { "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):** ```json [ { "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):** ```json { "id": "770e8400-e29b-41d4-a716-446655440000", "code": "RETAIL", "name": "Retail", "is_active": true, "created_at": "2026-01-01T00:00:00Z" } ``` **Error Response (404):** ```json { "message": "Business type not found" } ``` --- ## Subscription Plans ### List Plans **Endpoint:** `GET /plans` **Description:** Get all available subscription plans **Success Response (200):** ```json [ { "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):** ```json { "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:** ```json { "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):** ```json { "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):** ```json { "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:** ```json { "name": "Toko Maju Jaya Sejahtera", "phone": "0215551235", "email": "info@majujaya.co.id", "address": "Jl. Sudirman No. 125" } ``` **Success Response (200):** ```json { "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):** ```json { "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):** ```json { "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:** ```json { "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):** ```json { "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:** ```json { "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:** ```json { "name": "Product B Updated", "selling_price": 80000, "stock": 45 } ``` **Success Response (200):** ```json { "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:** ```json { "id": "uuid", "field": "value" } ``` **List Resources:** ```json { "data": [...], "pagination": { "page": 1, "limit": 20, "total": 100, "total_pages": 5 } } ``` **Operation Success:** ```json { "message": "Operation completed successfully" } ``` ### Error Response **400 Bad Request:** ```json { "message": "Validation errors", "errors": [ { "field": "email", "message": "Invalid email format", "code": 1001 } ] } ``` **401 Unauthorized:** ```json { "message": "Authentication credentials were missing or incorrect" } ``` **403 Forbidden:** ```json { "message": "You don't have permission to access this resource" } ``` **404 Not Found:** ```json { "message": "Resource not found" } ``` **409 Conflict:** ```json { "message": "Resource already exists" } ``` **429 Too Many Requests:** ```json { "message": "Rate limit exceeded. Try again later." } ``` **500 Internal Server Error:** ```json { "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