CheckDisposable Emailcheckdisposable.email
← All guidesSpring Boot guide · Java / Kotlin

Block disposable email signups in Spring Boot

Implement a custom Bean Validation constraint. It runs as part of `@Valid` request-body validation — disposable emails return a 400 with a standard error response.

The code

// src/main/java/com/example/NotDisposable.java
package com.example;

import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotDisposable.Validator.class)
public @interface NotDisposable {
    String message() default "Please use a real email address.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    class Validator implements ConstraintValidator<NotDisposable, String> {
        private static final HttpClient HTTP = HttpClient.newHttpClient();
        private static final String KEY = System.getenv("CDE_KEY");

        @Override
        public boolean isValid(String email, ConstraintValidatorContext ctx) {
            if (email == null || email.isBlank()) return true;
            try {
                var req = HttpRequest.newBuilder()
                    .uri(URI.create("https://api.checkdisposable.email/v1/check?email=" + email))
                    .header("Authorization", "Bearer " + KEY)
                    .timeout(Duration.ofSeconds(3))
                    .build();
                var res = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
                if (res.statusCode() != 200) return true; // fail open
                return !res.body().contains("\"is_disposable\":true");
            } catch (Exception e) {
                return true; // fail open
            }
        }
    }
}

// SignupRequest.java
public record SignupRequest(
    @jakarta.validation.constraints.Email @NotDisposable String email,
    String password
) {}

Notes

JSON body class
Annotate the email field in your request record/DTO with both `@Email` and `@NotDisposable`. Spring runs them in order; the cheap regex check fails first if the format is malformed.
Production parsing
The example uses a string contains check for brevity. In real code, use Jackson to parse the body — that's standard in Spring Boot apps and gives you full type safety.
Spring Security
If you use Spring Security with a custom UserDetailsService, the constraint runs at request validation, so disposable emails never reach your user-creation logic.

Get a free API key

500 checks/month, no credit card. No credit card. 30 seconds.

Sign up free →