diff --git a/src/app/Http/Kernel.php b/src/app/Http/Kernel.php
index 3105c2f4..31eec9da 100644
--- a/src/app/Http/Kernel.php
+++ b/src/app/Http/Kernel.php
@@ -1,88 +1,89 @@
 <?php
 
 namespace App\Http;
 
 use Illuminate\Foundation\Http\Kernel as HttpKernel;
 
 class Kernel extends HttpKernel
 {
     /**
      * The application's global HTTP middleware stack.
      *
      * These middleware are run during every request to your application.
      *
      * @var array<int, class-string|string>
      */
     protected $middleware = [
         // \App\Http\Middleware\TrustHosts::class,
         \App\Http\Middleware\RequestLogger::class,
         \App\Http\Middleware\TrustProxies::class,
         \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
         \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
         \App\Http\Middleware\TrimStrings::class,
         \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
         \App\Http\Middleware\DevelConfig::class,
         \App\Http\Middleware\Locale::class,
         \App\Http\Middleware\ContentSecurityPolicy::class,
         // FIXME: CORS handling added here, I didn't find a nice way
         // to add this only to the API routes
         // \App\Http\Middleware\Cors::class,
     ];
 
     /**
      * The application's route middleware groups.
      *
      * @var array<string, array<int, class-string|string>>
      */
     protected $middlewareGroups = [
         'web' => [
             // \App\Http\Middleware\EncryptCookies::class,
             // \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
             // \Illuminate\Session\Middleware\StartSession::class,
             // \Illuminate\Session\Middleware\AuthenticateSession::class,
             // \Illuminate\View\Middleware\ShareErrorsFromSession::class,
             // \App\Http\Middleware\VerifyCsrfToken::class,
             // \Illuminate\Routing\Middleware\SubstituteBindings::class,
         ],
 
         'api' => [
             // 'throttle:api',
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
         ],
     ];
 
     /**
      * The application's route middleware.
      *
      * These middleware may be assigned to groups or used individually.
      *
      * @var array<string, class-string|string>
      */
     protected $middlewareAliases = [
         'admin' => \App\Http\Middleware\AuthenticateAdmin::class,
         'auth' => \App\Http\Middleware\Authenticate::class,
         'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
         'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
         'can' => \Illuminate\Auth\Middleware\Authorize::class,
         'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
         'reseller' => \App\Http\Middleware\AuthenticateReseller::class,
         'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
         'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
         'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
         'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
         'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
+        'allowedHosts' => \App\Http\Middleware\AllowedHosts::class,
     ];
 
     /**
      * Handle an incoming HTTP request.
      *
      * @param \Illuminate\Http\Request $request HTTP Request object
      *
      * @return \Illuminate\Http\Response
      */
     public function handle($request)
     {
         // Overwrite the http request object
         return parent::handle(Request::createFrom($request));
     }
 }
diff --git a/src/app/Http/Middleware/AllowedHosts.php b/src/app/Http/Middleware/AllowedHosts.php
new file mode 100644
index 00000000..4ba6ffb0
--- /dev/null
+++ b/src/app/Http/Middleware/AllowedHosts.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+
+class AllowedHosts
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param \Illuminate\Http\Request $request
+     * @param \Closure                 $next
+     * @param array|string             $hosts
+     *
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        $allowedDomains = \config('app.services_allowed_domains');
+        if (!in_array(request()->getHost(), $allowedDomains)) {
+            return abort(404);
+        }
+        return $next($request);
+    }
+}
diff --git a/src/config/app.php b/src/config/app.php
index 5588cdf7..9939f4ea 100644
--- a/src/config/app.php
+++ b/src/config/app.php
@@ -1,271 +1,278 @@
 <?php
 
 use Illuminate\Support\Facades\Facade;
 use Illuminate\Support\ServiceProvider;
 
 return [
 
     /*
     |--------------------------------------------------------------------------
     | Application Name
     |--------------------------------------------------------------------------
     |
     | This value is the name of your application. This value is used when the
     | framework needs to place the application's name in a notification or
     | any other location as required by the application or its packages.
     |
     */
 
     'name' => env('APP_NAME', 'Laravel'),
 
     /*
     |--------------------------------------------------------------------------
     | Application Environment
     |--------------------------------------------------------------------------
     |
     | This value determines the "environment" your application is currently
     | running in. This may determine how you prefer to configure various
     | services the application utilizes. Set this in your ".env" file.
     |
     */
 
     'env' => env('APP_ENV', 'production'),
 
     /*
     |--------------------------------------------------------------------------
     | Application Debug Mode
     |--------------------------------------------------------------------------
     |
     | When your application is in debug mode, detailed error messages with
     | stack traces will be shown on every error that occurs within your
     | application. If disabled, a simple generic error page is shown.
     |
     */
 
     'debug' => (bool) env('APP_DEBUG', false),
 
     /*
     |--------------------------------------------------------------------------
     | Application URL
     |--------------------------------------------------------------------------
     |
     | This URL is used by the console to properly generate URLs when using
     | the Artisan command line tool. You should set this to the root of
     | your application so that it is used when running Artisan tasks.
     */
 
     'url' => env('APP_URL', 'http://localhost'),
 
     'passphrase' => env('APP_PASSPHRASE', null),
 
     'public_url' => env('APP_PUBLIC_URL', env('APP_URL', 'http://localhost')),
 
     'asset_url' => env('ASSET_URL'),
 
     'support_url' => env('SUPPORT_URL', null),
 
     'support_email' => env('SUPPORT_EMAIL', null),
 
     'webmail_url' => env('WEBMAIL_URL', null),
 
     'theme' => env('APP_THEME', 'default'),
 
     'tenant_id' => env('APP_TENANT_ID', null),
 
     'currency' => \strtoupper(env('APP_CURRENCY', 'CHF')),
 
     /*
     |--------------------------------------------------------------------------
     | Application Domain
     |--------------------------------------------------------------------------
     |
     | System domain used for user signup (kolab identity)
     */
     'domain' => env('APP_DOMAIN', 'domain.tld'),
 
     'website_domain' => env('APP_WEBSITE_DOMAIN', env('APP_DOMAIN', 'domain.tld')),
 
-    'services_domain' => env(
-        'APP_SERVICES_DOMAIN',
-        "services." . env('APP_WEBSITE_DOMAIN', env('APP_DOMAIN', 'domain.tld'))
-    ),
+    // Restrict over which domains the services paths can be accessed.
+    'services_allowed_domains' => explode(',', env(
+        'APP_SERVICES_ALLOWED_DOMAINS',
+        "webapp,kolab," . env(
+            'APP_SERVICES_DOMAIN',
+            "services." . env(
+                'APP_WEBSITE_DOMAIN',
+                env('APP_DOMAIN', 'domain.tld')
+            )
+        )
+    )),
 
     /*
     |--------------------------------------------------------------------------
     | Application Timezone
     |--------------------------------------------------------------------------
     |
     | Here you may specify the default timezone for your application, which
     | will be used by the PHP date and date-time functions. We have gone
     | ahead and set this to a sensible default for you out of the box.
     |
     */
 
     'timezone' => 'UTC',
 
     /*
     |--------------------------------------------------------------------------
     | Application Locale Configuration
     |--------------------------------------------------------------------------
     |
     | The application locale determines the default locale that will be used
     | by the translation service provider. You are free to set this value
     | to any of the locales which will be supported by the application.
     |
     */
 
     'locale' => env('APP_LOCALE', 'en'),
 
     /*
     |--------------------------------------------------------------------------
     | Application Fallback Locale
     |--------------------------------------------------------------------------
     |
     | The fallback locale determines the locale to use when the current one
     | is not available. You may change the value to correspond to any of
     | the language folders that are provided through your application.
     |
     */
 
     'fallback_locale' => 'en',
 
     /*
     |--------------------------------------------------------------------------
     | Faker Locale
     |--------------------------------------------------------------------------
     |
     | This locale will be used by the Faker PHP library when generating fake
     | data for your database seeds. For example, this will be used to get
     | localized telephone numbers, street address information and more.
     |
     */
 
     'faker_locale' => 'en_US',
 
     /*
     |--------------------------------------------------------------------------
     | Encryption Key
     |--------------------------------------------------------------------------
     |
     | This key is used by the Illuminate encrypter service and should be set
     | to a random, 32 character string, otherwise these encrypted strings
     | will not be safe. Please do this before deploying an application!
     |
     */
 
     'key' => env('APP_KEY'),
 
     'cipher' => 'AES-256-CBC',
 
     /*
     |--------------------------------------------------------------------------
     | Autoloaded Service Providers
     |--------------------------------------------------------------------------
     |
     | The service providers listed here will be automatically loaded on the
     | request to your application. Feel free to add your own services to
     | this array to grant expanded functionality to your applications.
     |
     */
 
     'providers' => ServiceProvider::defaultProviders()->merge([
         /*
          * Application Service Providers...
          */
         App\Providers\AppServiceProvider::class,
         App\Providers\AuthServiceProvider::class,
         // App\Providers\BroadcastServiceProvider::class,
         App\Providers\EventServiceProvider::class,
         App\Providers\HorizonServiceProvider::class,
         App\Providers\PassportServiceProvider::class,
         App\Providers\RouteServiceProvider::class,
     ])->toArray(),
 
     /*
     |--------------------------------------------------------------------------
     | Class Aliases
     |--------------------------------------------------------------------------
     |
     | This array of class aliases will be registered when this application
     | is started. However, feel free to register as many as you wish as
     | the aliases are "lazy" loaded so they don't hinder performance.
     |
     */
 
     'aliases' => Facade::defaultAliases()->toArray(),
 
     'headers' => [
         'csp' => env('APP_HEADER_CSP', ""),
         'xfo' => env('APP_HEADER_XFO', ""),
     ],
 
     // Locations of knowledge base articles
     'kb' => [
         // An article about suspended accounts
         'account_suspended' => env('KB_ACCOUNT_SUSPENDED'),
         // An article about a way to delete an owned account
         'account_delete' => env('KB_ACCOUNT_DELETE'),
         // An article about the payment system
         'payment_system' => env('KB_PAYMENT_SYSTEM'),
     ],
 
     'company' => [
         'name' => env('COMPANY_NAME'),
         'address' => env('COMPANY_ADDRESS'),
         'details' => env('COMPANY_DETAILS'),
         'email' => env('COMPANY_EMAIL'),
         'logo' => env('COMPANY_LOGO'),
         'footer' => env('COMPANY_FOOTER', env('COMPANY_DETAILS')),
         'copyright' => 'Apheleia IT AG',
     ],
 
     'storage' => [
         'min_qty' => (int) env('STORAGE_MIN_QTY', 5), // in GB
     ],
 
     'vat' => [
         'mode' => (int) env('VAT_MODE', 0),
     ],
 
     'password_policy' => env('PASSWORD_POLICY') ?: 'min:6,max:255',
 
     'payment' => [
         'methods_oneoff' => env('PAYMENT_METHODS_ONEOFF', 'creditcard,paypal,banktransfer,bitcoin'),
         'methods_recurring' => env('PAYMENT_METHODS_RECURRING', 'creditcard'),
     ],
 
     'with_ldap' => (bool) env('APP_LDAP', true),
     'with_imap' => (bool) env('APP_IMAP', false),
 
     'with_admin' => (bool) env('APP_WITH_ADMIN', false),
     'with_files' => (bool) env('APP_WITH_FILES', false),
     'with_reseller' => (bool) env('APP_WITH_RESELLER', false),
     'with_services' => (bool) env('APP_WITH_SERVICES', false),
     'with_signup' => (bool) env('APP_WITH_SIGNUP', true),
     'with_subscriptions' => (bool) env('APP_WITH_SUBSCRIPTIONS', true),
     'with_wallet' => (bool) env('APP_WITH_WALLET', true),
 
     'with_distlists' => (bool) env('APP_WITH_DISTLISTS', true),
     'with_shared_folders' => (bool) env('APP_WITH_SHARED_FOLDERS', true),
     'with_resources' => (bool) env('APP_WITH_RESOURCES', true),
     'with_meet' => (bool) env('APP_WITH_MEET', true),
     'with_companion_app' => (bool) env('APP_WITH_COMPANION_APP', true),
 
     'signup' => [
         'email_limit' => (int) env('SIGNUP_LIMIT_EMAIL', 0),
         'ip_limit' => (int) env('SIGNUP_LIMIT_IP', 0),
     ],
 
     'woat_ns1' => env('WOAT_NS1', 'ns01.' . env('APP_DOMAIN')),
     'woat_ns2' => env('WOAT_NS2', 'ns02.' . env('APP_DOMAIN')),
 
     'ratelimit_whitelist' => explode(',', env('RATELIMIT_WHITELIST', '')),
     'companion_download_link' => env(
         'COMPANION_DOWNLOAD_LINK',
         "https://mirror.apheleia-it.ch/pub/companion-app-beta.apk"
     ),
 
     'vpn' => [
         'token_signing_key' => env('VPN_TOKEN_SIGNING_KEY', 0),
     ],
 ];
diff --git a/src/routes/api.php b/src/routes/api.php
index 28dd401f..71089a09 100644
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -1,331 +1,331 @@
 <?php
 
 use App\Http\Controllers\API;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Route;
 
 /*
 |--------------------------------------------------------------------------
 | API Routes
 |--------------------------------------------------------------------------
 |
 | Here is where you can register API routes for your application. These
 | routes are loaded by the RouteServiceProvider within a group which
 | is assigned the "api" middleware group. Enjoy building your API!
 |
 */
 
 Route::group(
     [
         'middleware' => 'api',
         'prefix' => 'auth'
     ],
     function () {
         Route::post('login', [API\AuthController::class, 'login']);
 
         Route::group(
             ['middleware' => 'auth:api'],
             function () {
                 Route::get('info', [API\AuthController::class, 'info']);
                 Route::post('info', [API\AuthController::class, 'info']);
                 Route::get('location', [API\AuthController::class, 'location']);
                 Route::post('logout', [API\AuthController::class, 'logout']);
                 Route::post('refresh', [API\AuthController::class, 'refresh']);
             }
         );
     }
 );
 
 Route::group(
     [
         'domain' => \config('app.website_domain'),
         'middleware' => 'api',
         'prefix' => 'auth'
     ],
     function () {
         Route::post('password-policy/check', [API\PasswordPolicyController::class, 'check']);
 
         Route::post('password-reset/init', [API\PasswordResetController::class, 'init']);
         Route::post('password-reset/verify', [API\PasswordResetController::class, 'verify']);
         Route::post('password-reset', [API\PasswordResetController::class, 'reset']);
     }
 );
 
 if (\config('app.with_signup')) {
     Route::group(
         [
             'domain' => \config('app.website_domain'),
             'middleware' => 'api',
             'prefix' => 'auth'
         ],
         function () {
             Route::get('signup/domains', [API\SignupController::class, 'domains']);
             Route::post('signup/init', [API\SignupController::class, 'init']);
             Route::get('signup/invitations/{id}', [API\SignupController::class, 'invitation']);
             Route::get('signup/plans', [API\SignupController::class, 'plans']);
             Route::post('signup/validate', [API\SignupController::class, 'signupValidate']);
             Route::post('signup/verify', [API\SignupController::class, 'verify']);
             Route::post('signup', [API\SignupController::class, 'signup']);
         }
     );
 }
 
 Route::group(
     [
         'domain' => \config('app.website_domain'),
         'middleware' => ['auth:api', 'scope:mfa,api'],
         'prefix' => 'v4'
     ],
     function () {
         Route::post('auth-attempts/{id}/confirm', [API\V4\AuthAttemptsController::class, 'confirm']);
         Route::post('auth-attempts/{id}/deny', [API\V4\AuthAttemptsController::class, 'deny']);
         Route::get('auth-attempts/{id}/details', [API\V4\AuthAttemptsController::class, 'details']);
         Route::get('auth-attempts', [API\V4\AuthAttemptsController::class, 'index']);
 
         Route::post('companion/register', [API\V4\CompanionAppsController::class, 'register']);
     }
 );
 
 if (\config('app.with_files')) {
     Route::group(
         [
             'domain' => \config('app.website_domain'),
             'middleware' => ['auth:api', 'scope:fs,api'],
             'prefix' => 'v4'
         ],
         function () {
                 Route::apiResource('fs', API\V4\FsController::class);
                 Route::get('fs/{itemId}/permissions', [API\V4\FsController::class, 'getPermissions']);
                 Route::post('fs/{itemId}/permissions', [API\V4\FsController::class, 'createPermission']);
                 Route::put('fs/{itemId}/permissions/{id}', [API\V4\FsController::class, 'updatePermission']);
                 Route::delete('fs/{itemId}/permissions/{id}', [API\V4\FsController::class, 'deletePermission']);
         }
     );
     Route::group(
         [
             'domain' => \config('app.website_domain'),
             'middleware' => [],
             'prefix' => 'v4'
         ],
         function () {
                 Route::post('fs/uploads/{id}', [API\V4\FsController::class, 'upload'])
                     ->middleware(['api']);
                 Route::get('fs/downloads/{id}', [API\V4\FsController::class, 'download']);
         }
     );
 }
 
 Route::group(
     [
         'domain' => \config('app.website_domain'),
         'middleware' => ['auth:api', 'scope:api'],
         'prefix' => 'v4'
     ],
     function () {
         Route::apiResource('companions', API\V4\CompanionAppsController::class);
         // This must not be accessible with the 2fa token,
         // to prevent an attacker from pairing a new device with a stolen token.
         Route::get('companions/{id}/pairing', [API\V4\CompanionAppsController::class, 'pairing']);
 
         Route::apiResource('domains', API\V4\DomainsController::class);
         Route::get('domains/{id}/confirm', [API\V4\DomainsController::class, 'confirm']);
         Route::get('domains/{id}/skus', [API\V4\DomainsController::class, 'skus']);
         Route::get('domains/{id}/status', [API\V4\DomainsController::class, 'status']);
         Route::post('domains/{id}/config', [API\V4\DomainsController::class, 'setConfig']);
 
         Route::apiResource('groups', API\V4\GroupsController::class);
         Route::get('groups/{id}/skus', [API\V4\GroupsController::class, 'skus']);
         Route::get('groups/{id}/status', [API\V4\GroupsController::class, 'status']);
         Route::post('groups/{id}/config', [API\V4\GroupsController::class, 'setConfig']);
 
         Route::apiResource('packages', API\V4\PackagesController::class);
 
         Route::apiResource('rooms', API\V4\RoomsController::class);
         Route::post('rooms/{id}/config', [API\V4\RoomsController::class, 'setConfig']);
         Route::get('rooms/{id}/skus', [API\V4\RoomsController::class, 'skus']);
 
         Route::post('meet/rooms/{id}', [API\V4\MeetController::class, 'joinRoom'])
             ->withoutMiddleware(['auth:api', 'scope:api']);
 
         Route::apiResource('resources', API\V4\ResourcesController::class);
         Route::get('resources/{id}/skus', [API\V4\ResourcesController::class, 'skus']);
         Route::get('resources/{id}/status', [API\V4\ResourcesController::class, 'status']);
         Route::post('resources/{id}/config', [API\V4\ResourcesController::class, 'setConfig']);
 
         Route::apiResource('shared-folders', API\V4\SharedFoldersController::class);
         Route::get('shared-folders/{id}/skus', [API\V4\SharedFoldersController::class, 'skus']);
         Route::get('shared-folders/{id}/status', [API\V4\SharedFoldersController::class, 'status']);
         Route::post('shared-folders/{id}/config', [API\V4\SharedFoldersController::class, 'setConfig']);
 
         Route::apiResource('skus', API\V4\SkusController::class);
 
         Route::apiResource('users', API\V4\UsersController::class);
         Route::post('users/{id}/config', [API\V4\UsersController::class, 'setConfig']);
         Route::get('users/{id}/skus', [API\V4\UsersController::class, 'skus']);
         Route::get('users/{id}/status', [API\V4\UsersController::class, 'status']);
 
         Route::apiResource('wallets', API\V4\WalletsController::class);
         Route::get('wallets/{id}/transactions', [API\V4\WalletsController::class, 'transactions']);
         Route::get('wallets/{id}/receipts', [API\V4\WalletsController::class, 'receipts']);
         Route::get('wallets/{id}/receipts/{receipt}', [API\V4\WalletsController::class, 'receiptDownload']);
 
         Route::get('password-policy', [API\PasswordPolicyController::class, 'index']);
         Route::post('password-reset/code', [API\PasswordResetController::class, 'codeCreate']);
         Route::delete('password-reset/code/{id}', [API\PasswordResetController::class, 'codeDelete']);
 
         Route::post('payments', [API\V4\PaymentsController::class, 'store']);
         //Route::delete('payments', [API\V4\PaymentsController::class, 'cancel']);
         Route::get('payments/mandate', [API\V4\PaymentsController::class, 'mandate']);
         Route::post('payments/mandate', [API\V4\PaymentsController::class, 'mandateCreate']);
         Route::put('payments/mandate', [API\V4\PaymentsController::class, 'mandateUpdate']);
         Route::delete('payments/mandate', [API\V4\PaymentsController::class, 'mandateDelete']);
         Route::post('payments/mandate/reset', [API\V4\PaymentsController::class, 'mandateReset']);
         Route::get('payments/methods', [API\V4\PaymentsController::class, 'paymentMethods']);
         Route::get('payments/pending', [API\V4\PaymentsController::class, 'payments']);
         Route::get('payments/has-pending', [API\V4\PaymentsController::class, 'hasPayments']);
         Route::get('payments/status', [API\V4\PaymentsController::class, 'paymentStatus']);
 
         Route::post('support/request', [API\V4\SupportController::class, 'request'])
             ->withoutMiddleware(['auth:api', 'scope:api'])
             ->middleware(['api']);
 
         Route::get('vpn/token', [API\V4\VPNController::class, 'token']);
     }
 );
 
 Route::group(
     [
         'domain' => \config('app.website_domain'),
         'prefix' => 'webhooks'
     ],
     function () {
         Route::post('payment/{provider}', [API\V4\PaymentsController::class, 'webhook']);
         Route::post('meet', [API\V4\MeetController::class, 'webhook']);
     }
 );
 
 if (\config('app.with_services')) {
     Route::group(
         [
-            'domain' => \config('app.services_domain'),
+            'middleware' => ['allowedHosts'],
             'prefix' => 'webhooks'
         ],
         function () {
             Route::get('nginx', [API\V4\NGINXController::class, 'authenticate']);
             Route::get('nginx-roundcube', [API\V4\NGINXController::class, 'authenticateRoundcube']);
             Route::get('nginx-httpauth', [API\V4\NGINXController::class, 'httpauth']);
             Route::post('cyrus-sasl', [API\V4\NGINXController::class, 'cyrussasl']);
             Route::post('policy/greylist', [API\V4\PolicyController::class, 'greylist']);
             Route::post('policy/ratelimit', [API\V4\PolicyController::class, 'ratelimit']);
             Route::post('policy/spf', [API\V4\PolicyController::class, 'senderPolicyFramework']);
         }
     );
 }
 
 if (\config('app.with_admin')) {
     Route::group(
         [
             'domain' => 'admin.' . \config('app.website_domain'),
             'middleware' => ['auth:api', 'admin'],
             'prefix' => 'v4',
         ],
         function () {
             Route::apiResource('domains', API\V4\Admin\DomainsController::class);
             Route::get('domains/{id}/skus', [API\V4\Admin\DomainsController::class, 'skus']);
             Route::post('domains/{id}/suspend', [API\V4\Admin\DomainsController::class, 'suspend']);
             Route::post('domains/{id}/unsuspend', [API\V4\Admin\DomainsController::class, 'unsuspend']);
 
             Route::get('eventlog/{type}/{id}', [API\V4\Admin\EventLogController::class, 'index']);
 
             Route::apiResource('groups', API\V4\Admin\GroupsController::class);
             Route::post('groups/{id}/suspend', [API\V4\Admin\GroupsController::class, 'suspend']);
             Route::post('groups/{id}/unsuspend', [API\V4\Admin\GroupsController::class, 'unsuspend']);
 
             Route::apiResource('resources', API\V4\Admin\ResourcesController::class);
             Route::apiResource('shared-folders', API\V4\Admin\SharedFoldersController::class);
             Route::apiResource('skus', API\V4\Admin\SkusController::class);
 
             Route::apiResource('users', API\V4\Admin\UsersController::class);
             Route::get('users/{id}/discounts', [API\V4\Admin\DiscountsController::class, 'userDiscounts']);
             Route::post('users/{id}/reset2FA', [API\V4\Admin\UsersController::class, 'reset2FA']);
             Route::post('users/{id}/resetGeoLock', [API\V4\Admin\UsersController::class, 'resetGeoLock']);
             Route::post('users/{id}/resync', [API\V4\Admin\UsersController::class, 'resync']);
             Route::get('users/{id}/skus', [API\V4\Admin\UsersController::class, 'skus']);
             Route::post('users/{id}/skus/{sku}', [API\V4\Admin\UsersController::class, 'setSku']);
             Route::post('users/{id}/suspend', [API\V4\Admin\UsersController::class, 'suspend']);
             Route::post('users/{id}/unsuspend', [API\V4\Admin\UsersController::class, 'unsuspend']);
 
             Route::apiResource('wallets', API\V4\Admin\WalletsController::class);
             Route::post('wallets/{id}/one-off', [API\V4\Admin\WalletsController::class, 'oneOff']);
             Route::get('wallets/{id}/transactions', [API\V4\Admin\WalletsController::class, 'transactions']);
 
             Route::get('stats/chart/{chart}', [API\V4\Admin\StatsController::class, 'chart']);
         }
     );
 
     Route::group(
         [
             'domain' => 'admin.' . \config('app.website_domain'),
             'prefix' => 'v4',
         ],
         function () {
             Route::get('inspect-request', [API\V4\Admin\UsersController::class, 'inspectRequest']);
         }
     );
 }
 
 if (\config('app.with_reseller')) {
     Route::group(
         [
             'domain' => 'reseller.' . \config('app.website_domain'),
             'middleware' => ['auth:api', 'reseller'],
             'prefix' => 'v4',
         ],
         function () {
             Route::apiResource('domains', API\V4\Reseller\DomainsController::class);
             Route::get('domains/{id}/skus', [API\V4\Reseller\DomainsController::class, 'skus']);
             Route::post('domains/{id}/suspend', [API\V4\Reseller\DomainsController::class, 'suspend']);
             Route::post('domains/{id}/unsuspend', [API\V4\Reseller\DomainsController::class, 'unsuspend']);
 
             Route::get('eventlog/{type}/{id}', [API\V4\Reseller\EventLogController::class, 'index']);
 
             Route::apiResource('groups', API\V4\Reseller\GroupsController::class);
             Route::post('groups/{id}/suspend', [API\V4\Reseller\GroupsController::class, 'suspend']);
             Route::post('groups/{id}/unsuspend', [API\V4\Reseller\GroupsController::class, 'unsuspend']);
 
             Route::apiResource('invitations', API\V4\Reseller\InvitationsController::class);
             Route::post('invitations/{id}/resend', [API\V4\Reseller\InvitationsController::class, 'resend']);
 
             Route::post('payments', [API\V4\Reseller\PaymentsController::class, 'store']);
             Route::get('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandate']);
             Route::post('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandateCreate']);
             Route::put('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandateUpdate']);
             Route::delete('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandateDelete']);
             Route::get('payments/methods', [API\V4\Reseller\PaymentsController::class, 'paymentMethods']);
             Route::get('payments/pending', [API\V4\Reseller\PaymentsController::class, 'payments']);
             Route::get('payments/has-pending', [API\V4\Reseller\PaymentsController::class, 'hasPayments']);
 
             Route::apiResource('resources', API\V4\Reseller\ResourcesController::class);
             Route::apiResource('shared-folders', API\V4\Reseller\SharedFoldersController::class);
             Route::apiResource('skus', API\V4\Reseller\SkusController::class);
 
             Route::apiResource('users', API\V4\Reseller\UsersController::class);
             Route::get('users/{id}/discounts', [API\V4\Reseller\DiscountsController::class, 'userDiscounts']);
             Route::post('users/{id}/reset2FA', [API\V4\Reseller\UsersController::class, 'reset2FA']);
             Route::post('users/{id}/resetGeoLock', [API\V4\Reseller\UsersController::class, 'resetGeoLock']);
             Route::post('users/{id}/resync', [API\V4\Reseller\UsersController::class, 'resync']);
             Route::get('users/{id}/skus', [API\V4\Reseller\UsersController::class, 'skus']);
             Route::post('users/{id}/skus/{sku}', [API\V4\Reseller\UsersController::class, 'setSku']);
             Route::post('users/{id}/suspend', [API\V4\Reseller\UsersController::class, 'suspend']);
             Route::post('users/{id}/unsuspend', [API\V4\Reseller\UsersController::class, 'unsuspend']);
 
             Route::apiResource('wallets', API\V4\Reseller\WalletsController::class);
             Route::post('wallets/{id}/one-off', [API\V4\Reseller\WalletsController::class, 'oneOff']);
             Route::get('wallets/{id}/receipts', [API\V4\Reseller\WalletsController::class, 'receipts']);
             Route::get('wallets/{id}/receipts/{receipt}', [API\V4\Reseller\WalletsController::class, 'receiptDownload']);
             Route::get('wallets/{id}/transactions', [API\V4\Reseller\WalletsController::class, 'transactions']);
 
             Route::get('stats/chart/{chart}', [API\V4\Reseller\StatsController::class, 'chart']);
         }
     );
 }