/java Temp Email API
Create disposable inboxes, receive verification emails, and extract OTP codes from Java — a reusable client built on java.net.http.HttpClient (Java 11+) plus a runnable JUnit 5 test.
Email verification is one of the hardest things to cover in a Java test suite — the confirmation message lands in a real mailbox JUnit can't reach. The OpenInbox API fixes that: create a disposable inbox over HTTP, drive your flow, then poll the inbox and pull the code out of the body. This guide uses the built-in HttpClient (Java 11+) and Jackson, with a JUnit 5 integration test you can run today against the real API.
How the flow works
- Create a disposable inbox: POST /api/inbox (no auth) → { id, email, expiresAt }.
- Use that address in your signup/reset flow so your app emails it.
- Poll GET /api/inbound/api/emails?inboxEmail=<email> with the X-API-KEY header until an email arrives.
- Match a 4–8 digit code (or URL) in the email textBody with a Pattern.
- Submit the code / visit the link and assert.
Setup
HttpClient is built into the JDK since Java 11, so the only dependency is a JSON library — Jackson here — plus JUnit 5 for the test. Read the premium key from the environment.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- then: export OPENINBOX_API_KEY="your-premium-api-key" -->A reusable OpenInbox client
Encapsulate the two calls you need. createInbox creates a fresh inbox; waitForOtp polls the inbox and returns the first 4–8 digit code found in textBody. The inbox address is URL-encoded before it goes into the query string.
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class OpenInbox {
private static final String BASE = "https://api.openinbox.io/api";
private static final Pattern OTP = Pattern.compile("\\b\\d{4,8}\\b");
private final HttpClient http = HttpClient.newHttpClient();
private final ObjectMapper json = new ObjectMapper();
private final String apiKey = System.getenv("OPENINBOX_API_KEY");
/** Create a disposable inbox (no auth required). */
public JsonNode createInbox() throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create(BASE + "/inbox"))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> res =
http.send(req, HttpResponse.BodyHandlers.ofString());
return json.readTree(res.body()); // { id, email, expiresAt, ... }
}
/** Poll until an email arrives, then return the OTP from textBody. */
public String waitForOtp(String inboxEmail, Duration timeout) throws Exception {
long deadline = System.currentTimeMillis() + timeout.toMillis();
String url = BASE + "/inbound/api/emails?inboxEmail="
+ URLEncoder.encode(inboxEmail, StandardCharsets.UTF_8);
while (System.currentTimeMillis() < deadline) {
HttpRequest req = HttpRequest.newBuilder(URI.create(url))
.header("X-API-KEY", apiKey)
.GET()
.build();
HttpResponse<String> res =
http.send(req, HttpResponse.BodyHandlers.ofString());
JsonNode emails = json.readTree(res.body()).get("emails");
if (emails != null && emails.size() > 0) {
String body = emails.get(0).path("textBody").asText("");
Matcher m = OTP.matcher(body);
if (m.find()) return m.group();
}
Thread.sleep(2000);
}
throw new IllegalStateException("No OTP email arrived in time");
}
}A JUnit 5 integration test
The test creates an inbox, triggers your signup (call your own endpoint where indicated), waits for the code, and asserts it looks like an OTP. Extend the assertions to drive your verify endpoint end to end.
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SignupVerificationTest {
@Test
void verifiesEmailWithOtp() throws Exception {
OpenInbox openInbox = new OpenInbox();
JsonNode inbox = openInbox.createInbox();
String email = inbox.get("email").asText();
// Trigger your app so it emails the verification code to `email`
// (call your registration endpoint here)
String otp = openInbox.waitForOtp(email, Duration.ofSeconds(30));
assertTrue(otp.matches("\\d{4,8}"));
// …submit otp to your verify endpoint and assert the success state
}
}Reuse the client across a test class
HttpClient is thread-safe and designed to be shared, so build one OpenInbox per test class (in @BeforeAll) rather than per test — but still create a fresh inbox inside each test so parallel cases stay isolated. For very large suites, OpenInbox.waitForOtp can be switched to HttpClient.sendAsync so a polling thread is not parked on the network.
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertTrue;
class EmailFlowsTest {
static OpenInbox openInbox;
@BeforeAll
static void setUp() {
openInbox = new OpenInbox(); // one client, reused across tests
}
@Test
void signupSendsOtp() throws Exception {
JsonNode inbox = openInbox.createInbox(); // fresh inbox per test
String email = inbox.get("email").asText();
// … trigger your signup so it emails `email` …
String otp = openInbox.waitForOtp(email, Duration.ofSeconds(30));
assertTrue(otp.matches("\\d{4,8}"));
}
}On Java 8
java.net.http.HttpClient requires Java 11+. On Java 8, swap it for OkHttp (or Apache HttpClient): the requests are identical — POST https://api.openinbox.io/api/inbox to create, and GET https://api.openinbox.io/api/inbound/api/emails?inboxEmail=... with an X-API-KEY header to poll — only the client API differs.
Common pitfalls
- •Bound the polling loop with a Duration deadline (as shown) — an unbounded loop will hang the build if the email never lands.
- •Creating an inbox needs no auth, but GET /api/inbound/api/emails is premium. A 401/403 means the X-API-KEY header is missing or the plan is not paid.
- •Read the textBody field — there is no pre-parsed code. The Pattern \b\d{4,8}\b matches any 4–8 digit run; tighten it if the body contains other numbers.
- •Always URLEncoder.encode the inbox address before adding it to the query string so a + or . in the local part survives.
- •HttpClient.send blocks the thread; for many parallel tests prefer sendAsync, or give each test its own client and inbox to keep them isolated.
Frequently Asked Questions
How do I create a temporary email in Java?
Send a POST to https://api.openinbox.io/api/inbox with java.net.http.HttpClient (no auth) and read the email field from the JSON response, which also contains id and expiresAt.
Do I need an API key?
Creating an inbox is unauthenticated. Polling received emails (GET /api/inbound/api/emails) requires a premium X-API-KEY, read from the OPENINBOX_API_KEY environment variable.
How do I extract the OTP code in Java?
Compile Pattern.compile("\\b\\d{4,8}\\b") and run it against the email textBody returned by the poll endpoint, then call matcher.group(). There is no separate parsed field.
Which Java version do I need?
HttpClient is built in since Java 11. On Java 8, use OkHttp or Apache HttpClient instead — the endpoints, headers, and textBody field are unchanged.
Can I run this in JUnit and CI?
Yes — the example is a JUnit 5 test that only makes HTTPS calls, so it runs on any CI runner. Store the key as a secret exposed via OPENINBOX_API_KEY.
How long does a disposable inbox last?
Free inboxes expire after one hour. Create a fresh inbox per test so parallel tests stay isolated.
Related Pages
Explore More
Your private inbox is one click away
Free, instant, no registration needed. Works with any service. Expires automatically after 1 hour.
