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
emailrule beforeWorkEmailso obviously malformed input never costs a check. - For
policy: 'strict', usenew WorkEmail('strict'). - In tests,
Http::fake()lets you simulateallow,blockand timeouts; or call the real API with test addresses.
Official docs: Laravel documentation (Validation, HTTP Client). See also: Signup form guide.