For platform and infrastructure engineers
You are being asked to put a third-party call in the signup path. The questions that matter are what happens when it is slow, what happens when it is down, and what you can prove afterwards.
What happens when it fails
POST /v1/verify does not fail closed. If every provider in the pool errors or times out, the call returns 200 with degraded: true, calibration: "prior" and a probability drawn from the domain prior.
This is a deliberate contract, not an accident of implementation. A verification API that 500s breaks the signup flow it was added to protect, and a degraded answer you can branch on is strictly better than an exception you have to catch in a request path that was already working.
Worst case is a wide interval and a flag, not an outage in your funnel. 503 exists but is reserved for the case where not even a prior can be produced — which, given the prior is a constant table, means a bug on our side.
Latency and cost ceilings are inputs
You state the budget; the router degrades to fewer providers and a wider interval rather than blowing through it.
await fetch("https://api.sortance.com/v1/verify", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SORTANCE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
context: {
purpose: "signup",
max_latency_ms: 400, // hard ceiling, not a hint
max_cost_usd: 0.001,
},
}),
signal: AbortSignal.timeout(600), // yours as well. Always.
})Most traffic never reaches a paid provider at all: a local gate resolves syntax, MX and known-bad domains for free, and a cache resolves what has been seen recently. Those paths return in single or low double-digit milliseconds with providers: {} and cost_usd: 0.
What you can prove afterwards
- Every response carries a
trace_id. Including every error — the id is minted before authentication runs, so a401carries one you can quote. - Every decision is replayable.
GET /v1/traces/:trace_idreturns which providers were asked, what each said, how long each took and what each cost. When a bad send gets escalated, you can show what the system knew at the time. - Per-decision receipts inline. The
providersmap is on the response itself, so you do not need a second call to attribute a decision. - One error envelope.
{ error: { code, message, trace_id } }for every non-2xx, without exception. Six codes, listed in the reference.
Data and key handling
- No raw address is persisted. It exists in the request and in the outbound provider call; everything written to storage, logs or metrics is a peppered hash.
- Keys are stored as SHA-256 only. Sortance can check a key you present and can never look one up or re-display one. Lose it and you mint another.
- Quota is metered on the owning user, across every key including revoked ones, so key rotation is not a quota reset.
- CORS is exactly one origin. No wildcard — every non-dashboard caller is a server holding a key, and a server does not need CORS.
- Full detail on trust and data handling.
Integration shape
- No SDK. One POST with a bearer token — an SDK would be a dependency you maintain for no gain.
- No inbound webhook to receive, so no signing secret to rotate and no public endpoint to expose. Outcome reporting is a POST you make.
- 600 requests per minute per key on verify, ten times that on outcome reporting.
- Verification is a read. Retries are safe, and a repeat inside the cache window costs nothing.