1. Prepare
Verify a sending domain and issue an API key
Start in /dashboard/domains and /dashboard/api-keys. The public API is server-to-server only, and the domains surface now acts like a real registry instead of a single setup card.
Quickstart
This page is now the first real help surface for LoginRelay, not just a document index. The narrow live path is: verify one domain, issue one API key, send one OTP, verify it once, and inspect the outcome in the workspace.
1. Prepare
Start in /dashboard/domains and /dashboard/api-keys. The public API is server-to-server only, and the domains surface now acts like a real registry instead of a single setup card.
2. Send
POST /v1/send-otp returns 202 with a stable challengeId and expiry window.
3. Verify
POST /v1/verify-otp now uses a D1 compare-and-swap success claim so one challenge does not mint duplicate success responses.
Prerequisites
LOGINRELAY_API_BASE_URL=https://loginrelay.com
LOGINRELAY_API_KEY=lr_test_xxxxx_secret
LOGINRELAY_FROM_ADDRESS=auth@project-a.comcurl -X POST https://loginrelay.com/v1/send-otp \
-H "Authorization: Bearer lr_test_xxxxx_secret" \
-H "Content-Type: application/json" \
--data '{
"from": "auth@project-a.com",
"email": "builder@example.com",
"purpose": "login",
"metadata": {
"appUserId": "user_123"
}
}'
# 202 Accepted
{
"ok": true,
"challengeId": "chl_123",
"expiresAt": "2026-03-21T12:00:00Z"
}Send OTP
Verify OTP
curl -X POST https://loginrelay.com/v1/verify-otp \
-H "Authorization: Bearer lr_test_xxxxx_secret" \
-H "Content-Type: application/json" \
--data '{
"email": "builder@example.com",
"code": "123456",
"challengeId": "chl_123",
"purpose": "login"
}'
# 200 OK
{
"ok": true,
"verified": true,
"challengeId": "chl_123",
"verifiedAt": "2026-03-21T12:02:00Z",
"assertion": {
"token": "signed_assertion_here",
"expiresAt": "2026-03-21T12:07:00Z"
}
}const baseUrl =
process.env.LOGINRELAY_API_BASE_URL ?? "https://loginrelay.com";
export async function sendOtp(email: string) {
const response = await fetch(`${baseUrl}/v1/send-otp`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LOGINRELAY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: process.env.LOGINRELAY_FROM_ADDRESS,
email,
purpose: "login",
}),
cache: "no-store",
});
return response.json();
}
export async function verifyOtp(
email: string,
code: string,
challengeId: string,
) {
const response = await fetch(`${baseUrl}/v1/verify-otp`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LOGINRELAY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
code,
challengeId,
purpose: "login",
}),
cache: "no-store",
});
return response.json();
}Next.js Example
Common Failures
Runtime Truth
Commercial top-up path
Before self-serve checkout exists, the live commercial path is still auditable: the wallet opens the request, owner admin sends the next step, the workspace posts settlement confirmation back on the same request, and credits land only after review.
Step 1
The workspace opens `/dashboard/wallet` and records a top-up, plan-review, or cap-review request instead of relying on off-platform chat alone.
Step 2
Owner admin moves the request into review, writes the next-step message, and can attach an invoice, support-mail handoff, or other payment action link.
Step 3
After paying or replying on the invoice thread, the workspace posts the transfer reference back on the same request row so support and fulfillment stay auditable.
Step 4
Owner admin fulfills the request with settlement evidence, credits the wallet, and leaves the final resolution message visible in the same workspace history.
Document Map
The help page above is the public getting-started surface. The markdown pack below remains the technical source of truth for product, contracts, proof, and active rollout decisions.
docs/01-product-overview.md
ICP, wedge, promise, and stage-A success criteria.
docs/02-prd-v2.md
Functional requirements, anti-abuse rules, and acceptance criteria.
docs/03-mvp-scope.md
First ship surface, release gates, and phased build checklist.
docs/04-architecture-pack.md
Boundaries, Cloudflare primitives, shell direction, and risks.
docs/05-api-and-data-contracts.md
OTP endpoints, dashboard auth model, and data drafts.
docs/13-quickstart-nextjs-server-guide.md
First server-side integration guide for send, verify, and common failure handling.
docs/10-project-implementation-rules.md
Auth center, minimal admin, support inbox, and skill policy.
docs/11-staging-proof.md
Cloudflare proof-domain evidence, compatibility fixes, and next blockers.