framework

A work-email validation rule for Laravel

A Laravel validation rule that checks emails with isBusinessEmail using the HTTP client, blocks personal and disposable addresses and fails open.

isBusinessEmail for Laravel

Laravel’s validation rules are the natural place for this: 'email' => ['required', 'email', new WorkEmail].

1. Config

// config/services.php
'isbusinessemail' => [
    'key' => env('IBE_API_KEY'),
],
# .env
IBE_API_KEY=ibe_live_…

2. The rule

php artisan make:rule WorkEmail
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;

class WorkEmail implements ValidationRule
{
    public ?array $verdict = null;

    public function __construct(private string $policy = 'b2b') {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        try {
            $response = Http::withToken(config('services.isbusinessemail.key'))
                ->acceptJson()
                ->connectTimeout(2)
                ->timeout(3)
                ->post('https://api.isbusinessemail.com/v1/check', [
                    'email' => $value,
                    'policy' => $this->policy,
                ]);
        } catch (Throwable $e) {
            Log::warning('isBusinessEmail unreachable', ['error' => $e->getMessage()]);
            return; // fail open
        }

        if (! $response->successful()) {
            Log::warning('isBusinessEmail error', ['status' => $response->status()]);
            return; // fail open
        }

        $this->verdict = $response->json();

        if (($this->verdict['recommendation'] ?? 'allow') === 'block') {
            $suggestion = $this->verdict['did_you_mean'] ?? null;
            $fail($suggestion
                ? "Please use your work email. Did you mean {$suggestion}?"
                : 'Please use your work email.');
        }
    }
}

3. Use it

use App\Rules\WorkEmail;

public function store(Request $request)
{
    $rule = new WorkEmail();

    $data = $request->validate([
        'name'  => ['required', 'string', 'max:255'],
        'email' => ['required', 'email', 'max:254', $rule],
    ]);

    $user = User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'email_category' => $rule->verdict['category'] ?? null,
        'needs_review' => ($rule->verdict['recommendation'] ?? 'review') === 'review',
    ]);

    // ...
}

Keeping a reference to the rule lets you store category and Workspace/Microsoft 365 detection without a second call.

Notes

  • Put the cheap email rule before WorkEmail so obviously malformed input never costs a check.
  • For policy: 'strict', use new WorkEmail('strict').
  • In tests, Http::fake() lets you simulate allow, block and timeouts; or call the real API with test addresses.

Official docs: Laravel documentation (Validation, HTTP Client). See also: Signup form guide.