# Verifence — integration guide (for LLMs and AI coding assistants) You are integrating Verifence, a pre-send firewall for SMS verification. The goal: call Verifence's HTTP API from a backend immediately BEFORE sending a one-time-passcode (OTP) SMS, and only send the SMS if Verifence returns allow. A deny is a message the customer never pays for. This blocks SMS pumping / AIT (artificially inflated traffic) fraud. ## Base URL and authentication - Base URL: https://api.verifence.dev - Auth: every request sends `Authorization: Bearer ` where the key looks like `vf_live_...`. Create keys in the dashboard at https://verifence.dev/api-keys. Never expose the key in client-side code — call from your backend only. ## The two calls you must wire ### 1) POST /v1/check — call this right before you send the OTP Request JSON body: - `phone` (string, required): the destination number in E.164, e.g. "+14155552671". - `client_ip` (string, required by default): the END USER's IP address, forwarded from your inbound request — NOT your server's IP. Two of the four fraud layers key off it. If your backend genuinely cannot supply it, a team can enable "Allow checks without a client IP" in Settings; then this field may be omitted and the request runs on account + phone velocity only. - `user_id` (string, optional): a stable id for the account/user requesting the code, so velocity can be tracked per user. - `require_mobile` (bool, optional): reject non-mobile numbers. Response JSON (200 for ANY well-formed request, including a block — a block is a successful decision, not an error): - `allow` (bool): true → send the SMS; false → do NOT send it. - `reason` (string|null): why it was blocked (e.g. "velocity_phone", "reputation_network", "invalid_number", "premium_rate"). - `detail` (string): human-readable explanation. - `degraded` (bool): true if the cache was unreachable and the request failed open (allowed without full checks). - plus metadata (country, network, counts, confirmation rate) unless the team has enabled a minimal response. Error responses (4xx): - 400 `bad_request` — malformed input (bad phone, bad types). - 400 `client_ip_required` — client_ip missing in hosted mode (unless the team opted out via Settings). - 401 `unauthorized` — missing/unknown API key. - 429 — plan quota exhausted for the billing period. Integration rule: if `allow` is true, send the OTP through your SMS provider. If `allow` is false, do not send — show the user a generic "couldn't send a code" message. Quota only counts sends that happened; a blocked attempt costs nothing. ### 2) POST /v1/confirm — call this when the user enters the CORRECT code This is the most important signal: real users type codes in, fraud farms never do. It is what powers the network-reputation layer. Body: `phone` (E.164) and `client_ip` (same end-user IP as the check). Fire-and-forget; a failure here must never block your login/signup flow. (Optional) GET /v1/stats returns allowed/blocked counts for your account. ## Official SDKs (recommended over raw HTTP) PHP: composer require verifence/verifence-php $client = new Verifence\Client('vf_live_...'); $r = $client->check(phone: '+14155552671', clientIp: $request->ip(), userId: (string) $user->id); if ($r['allow']) { /* send OTP */ } // after the user enters the right code: $client->confirm(phone: '+14155552671', clientIp: $request->ip()); JavaScript / TypeScript: npm i verifence import { Verifence } from 'verifence'; const vf = new Verifence('vf_live_...'); const r = await vf.check({ phone: '+14155552671', clientIp: req.ip, userId: String(user.id) }); if (r.allow) { /* send OTP */ } await vf.confirm({ phone: '+14155552671', clientIp: req.ip }); Python: pip install verifence from verifence import Verifence vf = Verifence("vf_live_...") r = vf.check(phone="+14155552671", client_ip=request.remote_addr, user_id=str(user.id)) if r["allow"]: ... # send OTP vf.confirm(phone="+14155552671", client_ip=request.remote_addr) ## Where to place the calls in a typical signup/login flow 1. User submits phone number. 2. Backend calls POST /v1/check with the phone + the user's real IP. 3. If allow → send the OTP via Twilio/Vonage/etc. If deny → skip the send. 4. User submits the code; if it matches, call POST /v1/confirm. ## Design invariants to respect when integrating - Call /v1/check on your BACKEND, never the browser (the API key is a secret). - Forward the END USER's IP as client_ip, not your server's. - Treat a 200 with allow=false as a normal block, not an error. - Always call /v1/confirm on success — reputation depends on it. - If a call fails or times out, fail open (allow the send) rather than blocking real users; Verifence itself is designed to fail open. Full docs: https://verifence.dev · SDK source: https://github.com/verifence