blog
Test email signup and verification APIs with Postman
Create a private inbox, wait for the signup email, extract its code and verify the authenticated account in a Postman collection.

An API signup test should continue after the server accepts the email address. Receive the confirmation message, submit the code it contains, then sign in and check that the session belongs to the verified account.
MailSlurp gives Postman a private inbox through ordinary HTTP requests. The collection can create an address for each run, wait for the message and extract its code without opening webmail. The example below tests signup email verification. For a true second-factor login, trigger the email challenge after the password step and apply the same receive-and-submit sequence.
Prepare the collection and application
Use the MailSlurp Postman workspace to explore the inbox and email requests. The existing signup collection is a starting point to fork; adapt its application requests to the contract below or your own API. Your application must send a real verification email to the address the collection creates.
This is an adaptable application example, not a public demo API. It assumes these endpoints on a staging application you control:
| Request | Example input | Assertion |
|---|---|---|
POST /sign-up |
JSON emailAddress, password |
Signup accepted; email sent asynchronously |
POST /confirm |
JSON emailAddress, code |
Confirmation accepted |
POST /login |
JSON emailAddress, password |
Response includes accessToken |
GET /me |
Bearer access token | Response includes the account's emailAddress and emailVerified: true |
Replace the paths, fields and status expectations with your documented API. Add collection variables appBaseUrl, apiKey, password, expectedSubject, inboxId, emailAddress, emailId, sendStartedAt, code and accessToken. Set appBaseUrl to your test application's HTTPS origin and expectedSubject to its verification subject. Keep credentials in private local values or your runner's secret store; do not publish populated environments.
MailSlurp requests use X-API-Key: {{apiKey}}. Application requests use your application's authentication, not the MailSlurp key. Set Postman's request timeout longer than the 60-second message wait, for example 75 seconds. A short HTTP timeout can end the request before the mailbox wait finishes.
Create an inbox for this run
Create an expiring inbox so an interrupted run also has a cleanup fallback:
POST https://api.mailslurp.com/inboxes/withOptions
X-API-Key: {{apiKey}}
Content-Type: application/json
{ "expiresIn": 600000 }
Before this creation request, clear resource variables in its pre-request script so a failed creation cannot leave a previous run's inbox selected:
["inboxId", "emailAddress", "emailId", "code", "accessToken"].forEach(name =>
pm.collectionVariables.unset(name),
);
In the request's post-response script, check and store the returned resource:
pm.test("created a private test inbox", () => {
pm.response.to.have.status(201);
const inbox = pm.response.json();
pm.expect(inbox.id).to.be.a("string").and.not.empty;
pm.expect(inbox.emailAddress).to.include("@");
pm.collectionVariables.set("inboxId", inbox.id);
pm.collectionVariables.set("emailAddress", inbox.emailAddress);
});
["emailId", "code", "accessToken"].forEach(name => pm.collectionVariables.unset(name));
Run each collection iteration with its own inbox. Do not let concurrent runs overwrite a shared exported environment containing resource IDs.
Trigger signup, then wait for its email
Add a pre-request script to the application's signup request:
pm.collectionVariables.set("sendStartedAt", new Date().toISOString());
pm.collectionVariables.unset("emailId");
pm.collectionVariables.unset("code");
Send the signup values as JSON instead of putting a password in the URL:
POST {{appBaseUrl}}/sign-up
Content-Type: application/json
{ "emailAddress": "{{emailAddress}}", "password": "{{password}}" }
Assert your application's accepted response, then wait for the expected message:
POST https://api.mailslurp.com/waitForMatchingFirstEmail?inboxId={{inboxId}}&since={{sendStartedAt}}&timeout=60000&unreadOnly=false
X-API-Key: {{apiKey}}
Content-Type: application/json
{
"matches": [
{ "field": "SUBJECT", "should": "CONTAIN", "value": "{{expectedSubject}}" }
]
}
Use this post-response script:
pm.test("received this account's verification email", () => {
pm.response.to.have.status(200);
const email = pm.response.json();
pm.expect(email.to).to.include(pm.collectionVariables.get("emailAddress"));
pm.expect(email.subject).to.include(pm.collectionVariables.get("expectedSubject"));
pm.expect(email.id).to.be.a("string").and.not.empty;
pm.collectionVariables.set("emailId", email.id);
});
The fresh inbox isolates the account, since bounds the send time, and the subject selects verification rather than a welcome message. Unread status is not a substitute: old mail can still be unread, and opening current mail in the dashboard can mark it read.
Extract and submit the code
For a six-digit template, request pattern-based extraction from the received email:
POST https://api.mailslurp.com/emails/{{emailId}}/codes
X-API-Key: {{apiKey}}
Content-Type: application/json
{
"method": "PATTERN",
"allowFallback": false,
"minLength": 6,
"maxLength": 6,
"maxCandidates": 5
}
Keep the code as text and reject multiple candidates. Change the format rule if your application uses a different code length:
pm.test("extracted one six-digit code", () => {
pm.response.to.have.status(200);
const result = pm.response.json();
pm.expect(result.found).to.eql(true);
pm.expect(result.code).to.be.a("string").and.match(/^[0-9]{6}$/);
const candidates = [...new Set(result.candidates.map(item => item.code))];
pm.expect(candidates).to.eql([result.code]);
pm.collectionVariables.set("code", result.code);
});
Submit the extracted value to your application:
POST {{appBaseUrl}}/confirm
Content-Type: application/json
{ "emailAddress": "{{emailAddress}}", "code": "{{code}}" }
Assert the confirmation response expected by your API. A code such as 004219 must remain a string; converting it to a number changes the value sent to the application.
Prove the account can sign in
Start a new login with the original account credentials:
POST {{appBaseUrl}}/login
Content-Type: application/json
{ "emailAddress": "{{emailAddress}}", "password": "{{password}}" }
For the token-based example contract, save the login token:
pm.test("login returned an access token", () => {
pm.response.to.have.status(200);
const session = pm.response.json();
pm.expect(session.accessToken).to.be.a("string").and.not.empty;
pm.collectionVariables.set("accessToken", session.accessToken);
});
Then inspect the authenticated account:
GET {{appBaseUrl}}/me
Authorization: Bearer {{accessToken}}
pm.test("session belongs to the verified account", () => {
pm.response.to.have.status(200);
const account = pm.response.json();
pm.expect(account.emailAddress).to.eql(pm.collectionVariables.get("emailAddress"));
pm.expect(account.emailVerified).to.eql(true);
});
If your app uses session cookies, preserve the cookie jar and perform its equivalent account request. A 200 login response alone cannot distinguish an authenticated session from an error wrapped in a successful HTTP response.
Clean up and add rejection cases
Delete only the inbox created by this run:
DELETE https://api.mailslurp.com/inboxes/{{inboxId}}
X-API-Key: {{apiKey}}
Clear code and accessToken from local variables after the run. Delete the application's test user through its approved test cleanup mechanism; removing the inbox does not remove that account. The ten-minute expiry is a fallback if the collection stops before its final request, not a substitute for normal cleanup.
Add separate tests for an incorrect code, expiry, a consumed code and resend behavior. Assert your application's actual rejection policy and use a fresh account or challenge for cases that change token state. For emails containing downloads rather than authentication codes, use the Postman receive-and-attachment workflow.