Mailbox challenges
Passlock exposes mailbox challenge endpoints for email one-time code flows such as signup verification, passwordless login, and email-change verification.
Create challenge
Section titled “Create challenge”POST https://api.passlock.dev/{tenancyId}/challenges HTTP/1.1Authorization: Bearer {apiKey}Accept: application/jsonContent-Type: application/json
{ "email": "jdoe@example.com", "purpose": "signup", "metadata": { "signupId": "signup_123" }, "invalidateOthers": true}HTTP/1.1 201 CreatedContent-Type: application/json
{ "_tag": "ChallengeCreated", "challenge": { "challengeId": "abc123def456ghi", "purpose": "signup", "email": "jdoe@example.com", "createdAt": 1710000000000, "expiresAt": 1710000600000, "metadata": { "signupId": "signup_123" }, "secret": "ABC123def-GHI456jkl-MNO789pqr", "code": "123456", "message": { "html": "<html><body><p>Your Passlock code is <strong>123456</strong>.</p></body></html>", "text": "Your Passlock code is 123456." } }}import { createMailboxChallenge } from "@passlock/server";
const result = await createMailboxChallenge({ tenancyId: "myTenancyId", apiKey: "myApiKey", email: "jdoe@example.com", purpose: "signup", invalidateOthers: true,});
console.log(result.challenge.challengeId);console.log(result.challenge.secret);console.log(result.challenge.code);console.log(result.challenge.message.html);Create success returns the raw code plus a rendered message object with html and text content. You can email that rendered content directly, or use code to build your own template.
Create errors
Section titled “Create errors”429@error/ChallengeRateLimitedwithRetry-AfterandretryAfterSeconds403@error/Forbidden
Get challenge
Section titled “Get challenge”GET https://api.passlock.dev/{tenancyId}/challenges/{challengeId} HTTP/1.1Authorization: Bearer {apiKey}Accept: application/jsonHTTP/1.1 200 OKContent-Type: application/json
{ "_tag": "Challenge", "challengeId": "abc123def456ghi", "purpose": "signup", "email": "jdoe@example.com", "createdAt": 1710000000000, "expiresAt": 1710000600000, "metadata": { "signupId": "signup_123" }}import { getMailboxChallenge } from "@passlock/server";
const challenge = await getMailboxChallenge({ tenancyId: "myTenancyId", apiKey: "myApiKey", challengeId,});
console.log(challenge.email);Get errors
Section titled “Get errors”404@error/NotFound403@error/Forbidden
Verify challenge
Section titled “Verify challenge”POST https://api.passlock.dev/{tenancyId}/challenges/verify HTTP/1.1Authorization: Bearer {apiKey}Accept: application/jsonContent-Type: application/json
{ "challengeId": "abc123def456ghi", "secret": "ABC123def-GHI456jkl-MNO789pqr", "code": "123456"}HTTP/1.1 200 OKContent-Type: application/json
{ "_tag": "ChallengeVerified", "challenge": { "_tag": "Challenge", "challengeId": "abc123def456ghi", "purpose": "signup", "email": "jdoe@example.com", "createdAt": 1710000000000, "expiresAt": 1710000600000, "metadata": { "signupId": "signup_123" } }}import { verifyMailboxChallenge } from "@passlock/server";
const result = await verifyMailboxChallenge({ tenancyId: "myTenancyId", apiKey: "myApiKey", challengeId, secret, code,});
console.log(result.challenge.email);Verification success returns a readable nested challenge. The secret and one-time code are not returned.
Verify errors
Section titled “Verify errors”400@error/InvalidChallenge400@error/InvalidChallengeCode400@error/ChallengeExpired400@error/ChallengeAttemptsExceeded403@error/Forbidden
Delete challenge
Section titled “Delete challenge”DELETE https://api.passlock.dev/{tenancyId}/challenges/{challengeId} HTTP/1.1Authorization: Bearer {apiKey}Accept: application/jsonHTTP/1.1 202 AcceptedContent-Type: application/json
{ "_tag": "ChallengeDeleted"}import { deleteMailboxChallenge } from "@passlock/server";
await deleteMailboxChallenge({ tenancyId: "myTenancyId", apiKey: "myApiKey", challengeId,});Delete errors
Section titled “Delete errors”403@error/Forbidden