json([ 'success' => 'invoiceshelf-self-hosted', ]); })->name('ping'); // Version 1 endpoints // -------------------------------------- Route::prefix('/v1')->group(function () { // App version // ---------------------------------- Route::get('/app/version', AppVersionController::class); // Authentication & Password Reset // ---------------------------------- Route::prefix('auth')->group(function () { Route::post('login', [AuthController::class, 'login']); Route::post('logout', [AuthController::class, 'logout'])->middleware('auth:sanctum'); // Send reset password mail Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->middleware('throttle:10,2'); // handle reset password form process Route::post('reset/password', [ResetPasswordController::class, 'reset']); }); // Invitation Registration (public) // ---------------------------------- Route::get('/invitations/{token}/details', [InvitationRegistrationController::class, 'details']); Route::post('/auth/register-with-invitation', [InvitationRegistrationController::class, 'register']); // Countries // ---------------------------------- Route::get('/countries', CountriesController::class); // Onboarding // ---------------------------------- Route::middleware(['redirect-if-installed'])->prefix('installation')->group(function () { Route::get('/wizard-step', [OnboardingWizardController::class, 'getStep']); Route::post('/wizard-step', [OnboardingWizardController::class, 'updateStep']); Route::post('/wizard-language', [OnboardingWizardController::class, 'saveLanguage']); Route::get('/languages', [LanguagesController::class, 'languages']); Route::get('/requirements', [RequirementsController::class, 'requirements']); Route::get('/permissions', [FilePermissionsController::class, 'permissions']); Route::post('/database/config', [DatabaseConfigurationController::class, 'saveDatabaseEnvironment']); Route::get('/database/config', [DatabaseConfigurationController::class, 'getDatabaseEnvironment']); Route::put('/set-domain', AppDomainController::class); Route::post('/login', LoginController::class); Route::post('/finish', FinishController::class); }); // Super Admin // ---------------------------------- Route::middleware(['auth:sanctum', 'super-admin'])->prefix('super-admin')->group(function () { Route::get('companies', [CompaniesController::class, 'index']); Route::get('companies/{company}', [CompaniesController::class, 'show']); Route::put('companies/{company}', [CompaniesController::class, 'update']); Route::get('users', [UsersController::class, 'index']); Route::get('users/{user}', [UsersController::class, 'show']); Route::put('users/{user}', [UsersController::class, 'update']); Route::post('users/{user}/impersonate', [UsersController::class, 'impersonate']); }); // Stop impersonation - uses auth:sanctum only (the impersonated user's token, not super-admin) Route::middleware(['auth:sanctum'])->prefix('super-admin')->group(function () { Route::post('stop-impersonating', [UsersController::class, 'stopImpersonating']); }); Route::middleware(['auth:sanctum', 'company'])->group(function () { Route::middleware(['bouncer'])->group(function () { // Bootstrap // ---------------------------------- Route::get('/bootstrap', BootstrapController::class); // Invitations (user-scoped — respond to invitations) // ---------------------------------- Route::get('/invitations/pending', [InvitationResponseController::class, 'pending']); Route::post('/invitations/{invitation:token}/accept', [InvitationResponseController::class, 'accept']); Route::post('/invitations/{invitation:token}/decline', [InvitationResponseController::class, 'decline']); // Currencies // ---------------------------------- Route::prefix('/currencies')->group(function () { Route::get('/used', [ExchangeRateProviderController::class, 'usedCurrenciesWithoutRate']); Route::post('/bulk-update-exchange-rate', [ExchangeRateProviderController::class, 'bulkUpdate']); }); // Dashboard // ---------------------------------- Route::get('/dashboard', DashboardController::class); // Auth check // ---------------------------------- Route::get('/auth/check', [AuthController::class, 'check']); // Search users // ---------------------------------- Route::get('/search', SearchController::class); Route::get('/search/user', [SearchController::class, 'users']); // MISC // ---------------------------------- Route::get('/config', ConfigController::class); Route::get('/currencies', CurrenciesController::class); Route::get('/timezones', [FormatsController::class, 'timezones']); Route::get('/date/formats', [FormatsController::class, 'dateFormats']); Route::get('/time/formats', [FormatsController::class, 'timeFormats']); Route::get('/next-number', [SerialNumberController::class, 'nextNumber']); Route::get('/number-placeholders', [SerialNumberController::class, 'placeholders']); Route::get('/current-company', [BootstrapController::class, 'currentCompany']); // Company Invitations (company-scoped — send invitations) // ---------------------------------- Route::apiResource('company-invitations', InvitationController::class)->only(['index', 'store', 'destroy']); // Customers // ---------------------------------- Route::post('/customers/delete', [CustomersController::class, 'delete']); Route::get('customers/{customer}/stats', CustomerStatsController::class); Route::resource('customers', CustomersController::class); // Items // ---------------------------------- Route::post('/items/delete', [ItemsController::class, 'delete']); Route::resource('items', ItemsController::class); Route::resource('units', UnitsController::class); // Invoices // ------------------------------------------------- Route::get('/invoices/{invoice}/send/preview', [InvoicesController::class, 'sendPreview']); Route::post('/invoices/{invoice}/send', [InvoicesController::class, 'send']); Route::post('/invoices/{invoice}/clone', [InvoicesController::class, 'clone']); Route::post('/invoices/{invoice}/status', [InvoicesController::class, 'changeStatus']); Route::post('/invoices/delete', [InvoicesController::class, 'delete']); Route::get('/invoices/templates', InvoiceTemplatesController::class); Route::apiResource('invoices', InvoicesController::class); // Recurring Invoice // ------------------------------------------------- Route::get('/recurring-invoice-frequency', RecurringInvoiceFrequencyController::class); Route::post('/recurring-invoices/delete', [RecurringInvoiceController::class, 'delete']); Route::apiResource('recurring-invoices', RecurringInvoiceController::class); // Estimates // ------------------------------------------------- Route::get('/estimates/{estimate}/send/preview', [EstimatesController::class, 'sendPreview']); Route::post('/estimates/{estimate}/send', [EstimatesController::class, 'send']); Route::post('/estimates/{estimate}/clone', [EstimatesController::class, 'clone']); Route::post('/estimates/{estimate}/status', [EstimatesController::class, 'changeStatus']); Route::post('/estimates/{estimate}/convert-to-invoice', [EstimatesController::class, 'convertToInvoice']); Route::get('/estimates/templates', EstimateTemplatesController::class); Route::post('/estimates/delete', [EstimatesController::class, 'delete']); Route::apiResource('estimates', EstimatesController::class); // Expenses // ---------------------------------- Route::get('/expenses/{expense}/show/receipt', [ExpensesController::class, 'showReceipt']); Route::post('/expenses/{expense}/upload/receipts', [ExpensesController::class, 'uploadReceipt']); Route::post('/expenses/delete', [ExpensesController::class, 'delete']); Route::apiResource('expenses', ExpensesController::class); Route::apiResource('categories', ExpenseCategoriesController::class); // Payments // ---------------------------------- Route::get('/payments/{payment}/send/preview', [PaymentsController::class, 'sendPreview']); Route::post('/payments/{payment}/send', [PaymentsController::class, 'send']); Route::post('/payments/delete', [PaymentsController::class, 'delete']); Route::apiResource('payments', PaymentsController::class); Route::apiResource('payment-methods', PaymentMethodsController::class); // Custom fields // ---------------------------------- Route::resource('custom-fields', CustomFieldsController::class); // Backup & Disk // ---------------------------------- Route::apiResource('backups', BackupsController::class); Route::apiResource('/disks', DiskController::class); Route::get('download-backup', [BackupsController::class, 'download']); Route::get('/disk/drivers', [DiskController::class, 'getDiskDrivers']); // Exchange Rate // ---------------------------------- Route::get('/currencies/{currency}/exchange-rate', [ExchangeRateProviderController::class, 'getRate']); Route::get('/currencies/{currency}/active-provider', [ExchangeRateProviderController::class, 'activeProvider']); Route::get('/used-currencies', [ExchangeRateProviderController::class, 'usedCurrencies']); Route::get('/supported-currencies', [ExchangeRateProviderController::class, 'supportedCurrencies']); Route::apiResource('exchange-rate-providers', ExchangeRateProviderController::class); // Settings // ---------------------------------- Route::get('/me', [UserProfileController::class, 'show']); Route::put('/me', [UserProfileController::class, 'update']); Route::get('/me/settings', [UserProfileController::class, 'showSettings']); Route::put('/me/settings', [UserProfileController::class, 'updateSettings']); Route::post('/me/upload-avatar', [UserProfileController::class, 'uploadAvatar']); Route::put('/company', [CompanyController::class, 'updateCompany']); Route::post('/company/upload-logo', [CompanyController::class, 'uploadCompanyLogo']); Route::get('/company/settings', [CompanySettingsController::class, 'show']); Route::post('/company/settings', [CompanySettingsController::class, 'update']); Route::get('/settings', [SettingsController::class, 'show']); Route::post('/settings', [SettingsController::class, 'update']); Route::get('/company/has-transactions', [CompanySettingsController::class, 'checkTransactions']); // Mails // ---------------------------------- Route::get('/mail/drivers', [MailConfigurationController::class, 'getMailDrivers']); Route::get('/mail/config', [MailConfigurationController::class, 'getMailEnvironment']); Route::post('/mail/config', [MailConfigurationController::class, 'saveMailEnvironment']); Route::post('/mail/test', [MailConfigurationController::class, 'testEmailConfig']); Route::get('/company/mail/config', [CompanyMailConfigurationController::class, 'getDefaultConfig']); Route::get('/company/mail/company-config', [CompanyMailConfigurationController::class, 'getMailConfig']); Route::post('/company/mail/company-config', [CompanyMailConfigurationController::class, 'saveMailConfig']); Route::post('/company/mail/company-test', [CompanyMailConfigurationController::class, 'testMailConfig']); // PDF Generation // ---------------------------------- Route::get('/pdf/drivers', [PDFConfigurationController::class, 'getDrivers']); Route::get('/pdf/config', [PDFConfigurationController::class, 'getEnvironment']); Route::post('/pdf/config', [PDFConfigurationController::class, 'saveEnvironment']); Route::apiResource('notes', NotesController::class); // Tax Types // ---------------------------------- Route::apiResource('tax-types', TaxTypesController::class); // Roles // ---------------------------------- Route::get('abilities', AbilitiesController::class); Route::apiResource('roles', RolesController::class); }); // Self Update // ---------------------------------- Route::get('/check/update', [UpdateController::class, 'checkVersion']); Route::post('/update/download', [UpdateController::class, 'download']); Route::post('/update/unzip', [UpdateController::class, 'unzip']); Route::post('/update/copy', [UpdateController::class, 'copy']); Route::post('/update/delete', [UpdateController::class, 'delete']); Route::post('/update/migrate', [UpdateController::class, 'migrate']); Route::post('/update/finish', [UpdateController::class, 'finish']); // Companies // ------------------------------------------------- Route::post('companies', [CompaniesController::class, 'store']); Route::post('/transfer/ownership/{user}', [CompanySettingsController::class, 'transferOwnership']); Route::post('companies/delete', [CompaniesController::class, 'destroy']); Route::get('companies', [CompaniesController::class, 'userCompanies']); // Users // ---------------------------------- Route::post('/members/delete', [MembersController::class, 'delete']); Route::apiResource('/members', MembersController::class); // Modules // ---------------------------------- Route::prefix('/modules')->group(function () { Route::get('/', [ModulesController::class, 'index']); Route::get('/check', [ModulesController::class, 'checkToken']); Route::get('/{module}', [ModulesController::class, 'show']); Route::post('/{module}/enable', [ModulesController::class, 'enable']); Route::post('/{module}/disable', [ModulesController::class, 'disable']); Route::post('/download', [ModuleInstallationController::class, 'download']); Route::post('/upload', [ModuleInstallationController::class, 'upload']); Route::post('/unzip', [ModuleInstallationController::class, 'unzip']); Route::post('/copy', [ModuleInstallationController::class, 'copy']); Route::post('/complete', [ModuleInstallationController::class, 'complete']); }); }); Route::prefix('/{company:slug}/customer')->group(function () { // Authentication & Password Reset // ---------------------------------- Route::prefix('auth')->group(function () { // Send reset password mail Route::post('password/email', [AuthForgotPasswordController::class, 'sendResetLinkEmail']); // handle reset password form process Route::post('reset/password', [AuthResetPasswordController::class, 'reset'])->name('customer.password.reset'); }); // Invoices, Estimates, Payments and Expenses endpoints // ------------------------------------------------------- Route::middleware(['auth:customer', 'customer-portal'])->group(function () { Route::get('/bootstrap', CustomerBootstrapController::class); Route::get('/dashboard', CustomerDashboardController::class); Route::get('invoices', [CustomerInvoicesController::class, 'index']); Route::get('invoices/{id}', [CustomerInvoicesController::class, 'show']); Route::post('/estimate/{estimate}/status', CustomerAcceptEstimateController::class); Route::get('estimates', [CustomerEstimatesController::class, 'index']); Route::get('estimates/{id}', [CustomerEstimatesController::class, 'show']); Route::get('payments', [CustomerPaymentsController::class, 'index']); Route::get('payments/{id}', [CustomerPaymentsController::class, 'show']); Route::get('/payment-method', PaymentMethodController::class); Route::get('expenses', [CustomerExpensesController::class, 'index']); Route::get('expenses/{id}', [CustomerExpensesController::class, 'show']); Route::post('/profile', [CustomerProfileController::class, 'updateProfile']); Route::get('/me', [CustomerProfileController::class, 'getUser']); Route::get('/countries', CountriesController::class); }); }); }); Route::get('/cron', CronJobController::class)->middleware('cron-job');