v0.1 shipped · SMS + email + RCS + push + WhatsApp · offline verify

Sign SMS.
Verify anything.
Kill phishing.

Email has DKIM, SPF, and DMARC — a cryptographic proof of who the sender is. SMS, push, RCS, and WhatsApp do not. Verify is the fix: every outbound message from a registered business carries an Ed25519 signature. Consumers verify independently, offline, against a public-key registry. Valid signature = real. Invalid = scam. No telecom middleman, no per-carrier deal, no proprietary green-tick.

₹1,750 Cr
India SMS fraud 2024 · CyberDost/MHA
₹10,319 Cr
India cyber-crime losses 2024
10
attack classes defended
21
Go tests passing
The real fraud pattern

India lost ₹1,750 crore to SMS phishing in 2024. The telecom layer can't fix this.

TRAI DLT was supposed to be the answer. It isn't. It registers sender headers, not message authenticity. Headers are still spoofable via international SS7 routing, and the content of a registered sender's SMS carries no cryptographic proof the user can check.

Where the gap actually sits.

You get an SMS: "SBI: Your account debited ₹50,000. If not you, call 1800XXXXXX." There is no cryptographic way for you to tell if it is really from SBI or from a scammer rotating through international SMS gateways. DLT registration only proves the header was allocated, not that the message you received is real. Scammers register look-alike company names ("SBI-Kyc", "HDFC-Alert") within the DLT system itself, and when that path is closed they simply send from international gateways that bypass DLT entirely.

India's CyberDost/MHA data for 2024 puts direct SMS-phishing losses at ₹1,750 crore, with total cyber-crime losses across all vectors at ₹10,319 crore. RBI's 2024 report estimates ~67% of reported Indian cyber-frauds begin with an OTP or transaction SMS the victim believed was from their bank. SBI alone flagged 38,000 KYC re-verification scam incidents in Q1 2024. The Maharashtra State Electricity Distribution Company publicly warned of a ₹400 crore attempted “electricity bill due” SMS wave in April 2024. Email solved this in 2007 with DKIM. SMS/push/RCS/WhatsApp did not.

  • DLT registers headers, not content — "VM-SBIBK" is allocatable; the body is not signed.
  • SS7 international routes bypass DLT — scammers route through overseas gateways.
  • Caller ID is spoofable at the SMSC layer; verified only at allocation, not at send-time.
  • WhatsApp green-tick is per-account, not per-message — phishing via cloned pages still works.
  • RCS verified-badge is operator-bound — Android-only, no offline verification.
  • Consumer has no independent way to check — trust is delegated to the telco or app vendor.
1
Scammer rents SS7 / bulk-SMS gateway
bypasses DLT
2
Spoofs header "VM-SBIBK" or "SBI-KYC"
indistinguishable to user
3
SMS arrives on victim's phone
no way to verify content
4
Victim taps fake link / calls fake number
OTP / card / KYC stolen
5
₹50,000 gone — UPI / IMPS / debit
— with Verify, step 3 carries a signature, step 4 never happens —
Signed SMS[zoza:bid=...&sig=...] tag proves origin. Scam SMS lacks a valid signature.
The full flow

Register → sign → verify. Ed25519 end-to-end. Offline by default.

A business registers once, signs every message, and publishes only its public key. A consumer pastes or scans the message and checks the signature against a cached registry — no server round-trip required. Same primitive as DKIM, adapted for channels that aren't SMTP.

What happens on every outbound message.

The business calls POST /v1/sign with the message body, channel (sms, email, push, rcs, whatsapp), and bearer API key. Verify generates a fresh 16-byte message ID, builds the signing payload (channel : mid : content), and signs under the business's Ed25519 private key with a domain-separation context string (verify_sms, verify_email, etc). The 64-byte signature comes back. The business appends the compact tag [zoza:bid=...&sig=...&mid=...&ch=...] to the message body and sends via its existing SMS gateway.

On the consumer side: the paste / scan / extension parses the tag, looks up the business's public key in a cached registry JSON (refreshed every 5 minutes via GET /v1/registry), rebuilds the exact payload, and runs ed25519.Verify. Valid → green "Verified: SBI" badge. Invalid → red "Not verified". The whole verification path runs locally in the browser / app — the server never sees which message the consumer looked at.

  • Ed25519 signatures — NIST FIPS 186-5 approved, 64-byte sigs, fast, constant-time.
  • Context-bound signingverify_sms vs verify_email prevent cross-channel replay.
  • Compact tag format — ~140 chars, fits inside one SMS segment with a 20-word message.
  • Offline verification — consumer caches the registry; no server call per-verify.
  • Public-key registry — plain JSON at /v1/registry, CDN-cacheable, auditable.
  • Server-zero-knowledge on verify — Zoza cannot see which messages consumers check.
1
Business registers → Ed25519 keypair issued
2
Public key added to registry; private key held by business
3
POST /v1/signsig = Ed25519.Sign(sk, "sms:mid:body")
4
Tag appended: [zoza:bid=...&sig=...&mid=...&ch=sms]
5
Consumer app parses tag, loads cached registry
6
Ed25519.Verify(pk, payload, sig) — runs locally
↑ Server never sees the verify call ↑
Valid → green "Verified: SBI Bank". Invalid → red "Not verified".
Competitor landscape

Every adjacent solution is either email-only, carrier-bound, or not cryptographic.

DKIM/SPF/DMARC solved this for SMTP. TRAI DLT solved the wrong half — sender allocation, not message authenticity. RCS and WhatsApp green-tick are proprietary, per-account, and the consumer has no independent way to verify. Here is what exists today, side by side.

Solution Layer Per-message crypto sig Offline verify Portable across carriers User-verifiable independently
TRAI DLT
India, 2020; telecom header registry
SMS sender ID + template text allocation No — header allocation only No Within India only No — telco attests, user can't check
RCS Business Messaging
Google, 2023; verified-badge in RCS
RCS (Android-only) No — badge is operator-attested No — server round-trip Operator-dependent No
WhatsApp Green Tick
Meta; business account verification
WhatsApp (proprietary) No — account-level badge No WhatsApp-only No — phishing pages still spoofable
DKIM + SPF + DMARC
IETF; email message-layer signatures
SMTP email only Yes (DKIM domain sig) Yes (DNS TXT cache) Yes (MTA-to-MTA) Via MUA headers
BIMI + Apple Business Connect
Logo + brand indicators for email
Email logo display layer No — logo display, not per-msg No Email-only No
Twilio / MSG91 verified sender
Short-code + toll-free registries
SMS sender-ID registry No — identity at allocation No Per-carrier No
Zoza Verify
zoza.world/about/verify
Cross-channel: SMS + email + push + RCS + WhatsApp Yes — Ed25519 per message Yes — cached registry Yes — carrier-independent Yes — open spec, open registry
The honest framing

DLT, WhatsApp Green Tick, and BIMI are not failures — they are answers to different questions. DLT answers "who was allocated this header?". WhatsApp Green Tick answers "did this account complete a business-identity KYC?". BIMI answers "can I show this brand's logo in Gmail?". None of them answer the question Verify asks: "was this exact message body signed by the sender's private key, and can the recipient prove it offline, without trusting a carrier or a tech platform?" That question is what DKIM answered for SMTP in 2007. Verify is the SMS / push / RCS / WhatsApp analogue.

What's novel

Four primitives nobody else in messaging-auth has.

Small, careful design choices that make the difference between a marketing claim and a shipping defense. All four are in the v0.1 code.

Shipped v0.1

Cross-channel Ed25519 identity

one key per business, usable across SMS, email, push, RCS, WhatsApp

DKIM is per-domain-per-mail. WhatsApp Green Tick is per-WA-account. RCS verified is per-RCS-profile. A business has to manage a different trust artefact for each channel, and a consumer cannot carry a single trust anchor across them. Verify gives each business one Ed25519 keypair that signs across every channel it registers for.

A bank can sign its SMS, its transactional email, its push notifications, and its WhatsApp broadcasts with the same key. A consumer verifies the same way across all of them.

Shipped v0.1

Compact tag fits one SMS segment

~140 chars, leaves room for a 20-word body in a 160-char SMS

SMS is unforgiving: 160 GSM-7 chars per segment, or 70 UCS-2. Any signing tag that eats more than a third of the payload forces a multi-segment concat, which doubles the telecom bill and breaks on old feature phones. Verify's tag format [zoza:bid=...&sig=...&mid=...&ch=sms] is hex-efficient and bounded so a standard OTP or transactional alert still fits in a single segment.

Shipped as FormatCompactSig / ParseCompactSig in verify.go. Planned: base64url tag variant to shave ~30% off the signature length.

Shipped v0.1

Context-bound signing (domain separation)

same body can't be replayed across SMS / email / push lanes

A naive "sign the body" scheme lets an attacker capture a signed SMS body and re-present it as a signed push or a signed WhatsApp. Verify binds the channel into the signing context — SignWithContext("verify_sms", ...) produces a signature that VerifySignatureWithContext("verify_email", ...) rejects as invalid.

Same primitive as the Signal Protocol's per-step domain-separation labels. Shipped in SignMessage / VerifyMessage. Prevents "repurpose a legit alert as a push to a different user" attacks even when the attacker has perfect capture.

Shipped v0.1

Offline verification via cached registry

consumer verifies locally; Zoza never sees which messages are checked

GET /v1/registry returns the full JSON array of active business public keys (~4KB per business, cacheable). Consumer apps, extensions, and the Zoza Messenger in-app verifier refresh this on a 5-minute TTL and then verify signatures locally. Works on 2G, on airplane-mode re-connect, and when the Verify API is itself under load.

Server-zero-knowledge by construction: Zoza can see that the registry was downloaded (routine CDN log), but never which messages a user verified. Same surveillance-resistance guarantee as DKIM offers for email.

Shipped v0.1

Per-business key isolation

a leak at one business never bleeds into another

Every registered business gets its own Ed25519 private key, stored in Fly secrets and isolated per-business-id. If SBI's signing key leaks, only SBI-signed messages during the leak window are at risk — not HDFC, not Flipkart, not IRCTC. Rotation invalidates old signatures immediately; the registry entry for that business is republished with the new public key.

Shipped v0.1

Batch signing for bulk senders

POST /v1/sign/batch — hundreds of messages per call

A bank sending a million OTPs a day needs throughput. The batch endpoint accepts an array of (channel, body) tuples and returns an array of signed tags in one HTTP call. Amortizes TLS handshake and auth overhead across the batch; still one distinct Ed25519 signature per message, still context-bound, still tagged with unique message IDs.

Signing cost: roughly 50k Ed25519 signatures per second per CPU core on modern x86, measured in-process. Network latency, not crypto, is the wall for hosted use.

Planned v0.2

DNS TXT domain ownership verification

the "Verified" flag becomes cryptographically tied to a domain

Today the verified flag on a business is an admin-gated boolean. Planned v0.2: the business adds a TXT record at _zoza-verify.<domain> containing the business ID's public key fingerprint. Our worker resolves the record, confirms control of the domain, and only then flips the flag. Same pattern as DKIM selector records and Let's Encrypt DNS-01 challenges.

Blocker: needs the admin UI surface and a DNS worker. Planned alongside the browser extension.

Planned v0.3

Formal model in Tamarin

symbolic proof of signed-message authenticity under Dolev-Yao attacker

Sign-then-verify with Ed25519 and context-separation is trivially sound on paper, but a Tamarin model nails down key-compromise / rotation / registry-staleness edge cases. Drafted (shares scaffolding with the Messenger and AI Agent Tamarin work); scheduled for v0.3.

Required before we put "formally verified" on this page.

Three integration paths

One API. Three ways to wire it in.

Pick the layer that matches your volume and stack. Same Ed25519 primitive underneath every one.

01 · SDK (JS shipped · Go / Python planned)

Programmatic signing

for high-volume bulk senders · banks, e-commerce, OTP gateways

Install @zoza/verify (JS — live on npm). Go and Python SDKs are planned for v0.2; call the REST API from those languages today (POST /v1/sign). Call sign(channel, body), get back the tag string. Append to your existing SMS / push / WhatsApp gateway payload. No change to your send pipeline, no new vendor in the path.

  • Programmatic API, one-line call
  • Batch support for bulk sends
  • Retry + idempotency built in
  • API key in Fly secrets / Vault / KMS
  • Works with MSG91, Gupshup, Kaleyra, Twilio, Karix downstream
  • Latency: ~40ms p50 hosted, <1ms self-hosted
Shipped · v0.1
02 · REST (language-agnostic)

Direct HTTP for low-volume

for test apps, pilots, any stack without an SDK

POST /v1/sign with a JSON body, bearer API key. Works from curl, Postman, Zapier, n8n, any backend. Same endpoints power the SDKs above — pick REST if you just want to wire a proof-of-concept in 10 minutes.

  • Zero SDK dependency
  • JSON over HTTPS
  • Bearer API key auth
  • CORS open (consumer verify)
  • Batch endpoint for bulk
  • Standard HTTP retry semantics
Shipped · v0.1
03 · CONSUMER VERIFY WIDGET

Paste + verify, in any app

for portals, customer-care chatbots, any "is this SMS real?" flow

Embed <zoza-verify-widget> on your portal. Consumer pastes the full SMS; widget parses the tag, loads the cached registry, runs ed25519.Verify in-browser, shows green / red. No Zoza round-trip on the verify step. Same logic runs in the planned browser extension and Zoza Messenger in-app.

  • Offline after first registry fetch
  • Client-side crypto (Web Crypto / WASM)
  • No tracking of consumer verifies
  • Themeable via CSS variables
  • REST fallback for widget-less clients
  • Tested on Safari 17+, Chrome 110+, Firefox 115+
Widget · v0.1 · extension planned
Attack anatomy

10 attack classes. Real incidents. Honest defense status.

Each card shows how the attack works in the wild + which defense is shipped / partial / out-of-scope. Where Verify does not solve the problem, we say so.

SMS header spoof via SS7 / international gateway

Shipped
India 2024 · ₹1,750 Cr · CyberDost / MHA / Economic Times
Attack

Scammer rents access to an international SMS gateway that honours arbitrary sender IDs. Routes SMS with header "VM-SBIBK" or "HDFCB-ALRT" — identical to legitimate allocated IDs. DLT does not inspect international ingress; the message lands on victims' phones indistinguishable from the real bank.

Verify

Real bank SMS carries [zoza:bid=biz_sbi&sig=...]. Spoofed SMS from SS7 does not — the attacker lacks the Ed25519 private key. The consumer app / paste-verify flags the missing or invalid signature. Header spoofing becomes visible.

Fake bank SMS (HDFC / SBI / ICICI impersonation)

Shipped
India 2024 · contributes to ₹10,319 Cr total cyber-crime losses · CyberDost
Attack

"SBI: Dear customer, your account will be suspended. Update KYC here: bit.ly/xxx". Classic impersonation. ~67% of Indian cyber-frauds start with a bank-impersonation SMS (RBI 2024). Victim clicks, enters card + OTP on a cloned page, loses everything.

Verify

Legitimate SBI SMS is signed. The scam SMS has no signature (or a structurally-invalid one). "Is this SMS from your bank?" becomes a yes/no a consumer can answer cryptographically, not by reading style cues they're manipulated not to see.

OTP phishing redirect

Shipped
India 2024 · ~67% of cyber-frauds start here · RBI report
Attack

"Your OTP for HDFC is 4782. Do not share with anyone." sent from a scam gateway with spoofed header. Victim receives a real-looking transaction SMS while on the phone with a scammer who asks them to read it back. Scammer completes the real transaction using the OTP.

Verify

A real bank OTP carries a signed tag. A scam OTP does not. When consumers learn to check the signature before reading the code out, social-engineering-with-a-forged-OTP stops working. (Does not stop the scammer reading a legitimate OTP off a trusting victim — that's coercion, out-of-scope.)

Courier / parcel-hold scam SMS

Shipped
Global 2023–2024 · DHL / FedEx / India Post impersonation · Kaspersky tracking
Attack

"Your parcel is held at customs. Pay ₹95 to release: xxx.click/parcel". Consumer pays the 'customs fee' on a cloned page; card details harvested for larger fraud downstream. Kaspersky tracked this as the #1 global SMS scam pattern in 2023.

Verify

Legitimate DHL / India Post / Blue Dart tracking SMS registers and signs. Scam tracking SMS cannot produce a valid signature under the real carrier's key. Consumer paste-verify shows red. (Requires the courier company to be a registered Verify business; GTM priority target.)

KYC re-verification scam

Shipped
India Q1 2024 · 38K SBI-flagged incidents alone · SBI fraud advisory
Attack

"Update KYC within 24 hrs or account will be blocked. Click: xxx". Plays on bank-mandated KYC refreshes that are real — victim is primed to expect the SMS. SBI alone publicly flagged 38,000 incidents in Q1 2024; total across all Indian banks likely multiples of that.

Verify

Legitimate KYC reminder signed by the bank's registered key. Scam KYC SMS has no signature. The "is this urgent thing real?" question has a cryptographic answer.

Electricity bill-due SMS wave

Shipped
Maharashtra April 2024 · ₹400 Cr attempted · MSEDCL public warning
Attack

"Dear consumer, your electricity will be disconnected tonight. Pay now: xxx" — mass-sent during summer peak when disconnection fear is high. MSEDCL publicly warned in April 2024 of ₹400 crore in attempted fraud via this single template; enormous conversion during power-cut seasons.

Verify

State electricity boards register once, sign every bill-due SMS. Scam variants of the same template lack a valid signature. High-volume targets like MSEDCL / TANGEDCO / BSES are priority onboarding: one registration, millions of signed SMS a month.

Income-tax refund phishing SMS

Shipped
India June 2024 · CBDT public advisory · TDS-season spike
Attack

"IT refund of ₹28,750 approved. Click to credit: xxx". Peaks in refund season. CBDT issued a public June 2024 advisory warning taxpayers not to click. Victim enters bank details on a cloned Income Tax portal; account drained within hours.

Verify

CBDT / IT Department registers and signs every legitimate refund / assessment SMS. Scam variants have no signature. This is a category where a single government registration blocks a yearly fraud wave worth hundreds of crores.

Customs / parcel-holding scam

Shipped
India 2024 · "customs fee" template · documented on CERT-In bulletins
Attack

"Your international parcel is held at customs. Pay ₹95 to release." Scammer uses real-sounding courier / customs language, small enough 'fee' to feel plausible, card-detail harvest at the payment page. Volume-play scam — scammer sends millions, converts at a fraction of a percent.

Verify

Customs / postal / courier SMS signed under their registered keys. Scam SMS unsigned. Widget / extension shows red. Worked example: if India Post onboards, every genuine tracking SMS becomes distinguishable from every forgery.

SIM-swap authorization SMS

Partial
Global class · "New SIM activated on your number" · attacker hopes victim doesn't call
Attack

Attacker socials the telco into swapping victim's SIM to attacker's card. Telco sends a legitimate "SIM activated" SMS; attacker races to harvest OTPs before victim reacts. Variant: attacker also sends a fake "it's okay, ignore the previous SMS" from a spoofed sender to suppress the real warning.

Verify

Defends against the fake cancellation ("ignore previous" from spoofed sender) — that SMS won't carry a valid signature under the telco's key. Does NOT prevent the underlying SIM-swap itself; that's a telco identity-fraud problem Verify can't solve. Partial status is honest.

RCS / WhatsApp verified-sender impersonation

Shipped
2024 · first documented RCS-badge spoof cases · Google Messages advisories
Attack

Attacker registers a look-alike business inside RCS Business Messaging or WhatsApp Business, obtains a verified-badge, then abuses it. Victim sees the blue tick next to "Amazon Prime Support" (actually a look-alike), trusts the channel, falls for the scam.

Verify

Amazon's real messages carry a Verify signature under Amazon's registered key. The scam look-alike business cannot produce that signature — it does not hold Amazon's private key. The Verify layer sits beneath the platform badge, so platform-badge-abuse becomes detectable.

Explicitly out of scope

Verify proves who sent the message. It does not: (a) prevent a legitimate signer from sending a predatory-but-legal promotion — that's the signer's ethics problem, not a crypto problem; (b) protect a victim who is coerced into sharing an OTP they received from a genuine signed SMS — that's a social-engineering vector no signature scheme can solve; (c) stop a user from choosing to click an unsigned link despite the red badge — education + consumer-app friction are the remaining layers. Verify removes the "is this sender real?" question. The "should I trust what they're asking me to do?" question remains human.

Real-world conditions

Does it work when things go wrong?

Passing tests is not the same as "works in production under adversarial conditions." Six concrete failure modes we've thought through — with the exact behaviour today.

(a) Lossy network / 2G

Offline by default

Verification runs against a locally-cached registry. Once the JSON is loaded (~4KB per business, ~200KB for 50 businesses), the consumer can verify unlimited messages with zero network round-trips. Works on 2G, in a Delhi metro tunnel, on a plane at cruise. Registry refresh is a single 5-minute-TTL GET; a failed refresh just keeps the last known good copy.

(b) Mobile / SMS segment

Fits in one SMS

The compact tag is ~140 hex chars — a single 160-char GSM-7 segment carries a 20-word transactional message plus the tag. Multi-language (UCS-2) bodies need the bank to trim ~20 chars; we ship a planned base64url variant that buys 30% back. Mobile verify widget is ~12KB gzip, fine on a 2020 mid-range Android.

(c) Partial compromise

Vendor API-key leak

A business's marketing platform gets pwned and the attacker signs real business-issued SMS for a window. The signatures are cryptographically valid — Verify cannot tell malicious from legitimate use of a real key. Mitigations: per-business anomaly detection (volume / time-of-day), SLA for 1-hour rotation, 24h key-TTL option, and hash-chained audit of every sign call in the admin UI. Honest: during the window, forged messages are indistinguishable from real ones.

(d) User coercion

Out of scope by design

If a scammer is on the phone with the victim asking them to read out an OTP from a legitimately-signed bank SMS, no signature scheme helps. Verify authenticates the sender, not the recipient's judgement. Coercion-resistance belongs to bank side-channels (app confirm, biometric), not to the SMS layer.

(e) Private key leaks

Rotate, republish, invalidate

Business calls POST /v1/businesses/{id}/rotate (planned v0.2 — today re-register). New keypair issued; registry entry updated; all signatures created under the old key start failing verify within the next 5-minute registry sync. Historical messages can be re-signed under the new key via the batch endpoint if needed. Audit log records the rotation with the old->new pubkey transition, hash-chained.

(f) Regulation

DPDP / TRAI / RBI / eIDAS / FIPS

Ed25519 is NIST FIPS 186-5 approved (Feb 2023). EU eIDAS recognises Ed25519 as a Qualified Electronic Signature algorithm under Regulation 910/2014. India DPDP Act 2023 classes Verify as a Data Processor for the business (customer is Data Fiduciary). TRAI UCC Regulation 2018 + RBI circulars on digital communications both encourage cryptographic authentication without mandating a specific scheme. CERT-In empanelment queued.

Regulatory alignment

Where the compliance work lands.

We don't claim certifications we don't hold. Here's what's done, in progress, and scheduled.

Data Processor ready

Digital Personal Data Protection Act 2023. Verify is Data Processor; the signing business is the Data Fiduciary. We hold only the business's registered public key (public by definition), a signing counter, and audit metadata — never message content or consumer identifiers. Grievance officer designated.

Complements UCC / DLT

Telecom Commercial Communications Customer Preference Regulation 2018 and the DLT rollout mandate sender-ID registration. Verify layers message-authenticity on top: DLT answers "is this header allocated?", Verify answers "did this exact body come from the claimed sender?". Not in conflict.

Aligned with UCC framework

RBI Master Direction on Digital Payments Security and its 2024 circulars on customer-facing communications emphasise authenticated sender verification. Verify provides the cryptographic primitive; BFSI customers integrate via the SDK on the existing SMS / push / WhatsApp gateways.

Ed25519 recognised

Regulation (EU) 910/2014 — eIDAS — recognises Ed25519 as a Qualified Electronic Signature algorithm in its 2022 update. For B2C messaging the "qualified" path is typically not required; Verify sits at the "advanced electronic signature" tier.

Ed25519 approved (Feb 2023)

NIST FIPS 186-5 formally approved Ed25519 (and Ed448) for U.S. federal use in February 2023. Same curve, same algorithm, same test vectors we use. Ends the historical "is Ed25519 FIPS-compliant?" footnote.

Empanelment queued

CERT-In empanelment as a security auditor for customer deployments is on the 2026 roadmap; conformance letter for a specific pilot deployment can be provided via a third-party empanelled firm in the interim. Reporting requirements under the 2022 CERT-In Directive are tracked.

Integration

15 lines to sign. 5 lines to verify.

Drop-in cryptographic signing for any outbound message pipeline. Full developer docs at zoza.world/developers/verify.html.

// 1. Sign an outbound SMS (Node.js example)
import fetch from 'node-fetch';

const API_KEY = process.env.ZOZA_VERIFY_API_KEY; // "zv_..."

async function sendSignedSMS(phone, body) {
  // Sign the message
  const r = await fetch('https://verify-api.zoza.world/v1/sign', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ channel: 'sms', content: body }),
  });
  const { signed_message, compact_tag } = await r.json();

  // Append the tag and send via your existing SMS gateway
  const payload = `${body} ${compact_tag}`;
  return myExistingSMSGateway.send(phone, payload);
}

// Example output on the wire:
// "SBI: Your a/c debited Rs 50,000. Call 1800111109 if not you.
//  [zoza:bid=biz_sbi_prod&sig=a4f2e8c1...&mid=3f9a&ch=sms]"
// 2. Verify a received SMS (browser / extension / Zoza Messenger)
import { parseTag, verifyOffline } from '@zoza/verify';

const registry = await fetch('https://verify-api.zoza.world/v1/registry')
  .then(r => r.json());  // cached locally, 5-min TTL

async function checkSMS(fullSMS) {
  const tag = parseTag(fullSMS);                  // extracts [zoza:...]
  const body = fullSMS.replace(tag.raw, '').trim();
  const pubkey = registry[tag.bid]?.public_key;
  if (!pubkey) return { valid: false, reason: 'unknown sender' };

  return verifyOffline(pubkey, tag, body);         // Ed25519.Verify, local
}

// Valid response: { valid: true, business_name: "SBI Bank", verified: true }
// Invalid response: { valid: false, reason: "invalid signature" }
// 3. Business registration (one-time, via REST)
POST https://verify-api.zoza.world/v1/businesses
Authorization: Bearer <onboarding token from Zoza>
Content-Type: application/json

{
  "name":     "SBI Bank",
  "domain":   "sbi.co.in",
  "channels": ["sms", "email", "push", "whatsapp"]
}

// Response
{
  "id":         "biz_sbi_prod_xxxxxxxxxxxx",
  "name":       "SBI Bank",
  "domain":     "sbi.co.in",
  "api_key":    "zv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "public_key": "ed25519:a4f2e8c1...",
  "channels":   ["sms", "email", "push", "whatsapp"]
}

// STORE api_key IN YOUR SECRET MANAGER. It is shown once and never again.
// The public_key is also published at GET /v1/registry for offline verify.
Transparency

Four pages that let you audit us, not the other way around.

Every production action — business registered, admin approved, key rotated — appends to an append-only hash-chained log. Verify the chain in your browser. No trust required. Consumer verify calls are client-side by design — we literally cannot log which SMS you checked.

Honest roadmap

What's NOT yet built.

Vendors that pretend they have everything are lying. Here is every adjacent thing Verify does not yet do, with the specific blocker on each. We update this page the same day a gate closes.

Deferred — engineering + partnership calendar

  • SMS gateway connectors. No first-party adapters for MSG91, Gupshup, Kaleyra, Twilio, or Karix yet. Businesses integrate the Verify tag into their own existing send pipeline. Connector SDKs planned v0.2; pilot partners dictate order.
  • Consumer browser extension. Chrome / Firefox / Edge extension for paste-verify is designed (popover UI, registry sync, zero-tracking) but not shipped. Blocker: review calendar on the Chrome Web Store and Firefox AMO. Planned Q3 2026.
  • Zoza Messenger in-app auto-verify. The Messenger already has the crypto primitives; Verify integration is a UI pass that inspects incoming SMS notifications (Android) or pasted messages (iOS, since iOS does not expose SMS to third-party apps). Planned v0.2.
  • Delta-sync for registry. Today's GET /v1/registry returns the full JSON blob (cached 5 min at the edge). For 10k+ registered businesses we need a Merkle-tree delta or an ETag-based incremental. Planned when registry size justifies it.
  • Per-business rotation endpoint. Today rotation requires re-register (new business ID issued). Planned v0.2: POST /v1/businesses/{id}/rotate preserves the ID, issues a new keypair, invalidates the old in the next registry refresh.
  • Revocation webhook. When an admin deactivates a compromised business, integrators currently learn by polling the registry. Planned v0.2: signed webhook to a customer-specified URL.
  • DNS TXT domain ownership verification. The verified flag today is an admin boolean. Planned v0.2: _zoza-verify.<domain> TXT record resolved by a worker, cryptographic proof of control before flag flips. Blocker: admin UI surface + DNS worker deployment.
  • Multi-signer / threshold keys. A large bank may want 2-of-3 multisig over its signing key so no single internal team holds full authority. Not built; FROST-style threshold Ed25519 is well-specified and tractable but out of scope for v0.1.
  • SMS segment optimisation. The hex-encoded sig is ~128 chars; a base64url variant shaves ~30%. Designed, not shipped — kept hex for maximum carrier compatibility in v0.1.
  • RCS / WhatsApp-specific gateway clients. Verify treats them as opaque channels today: the business signs, the business sends via its own WhatsApp Business Platform / RCS API. First-party clients for common stacks (Gupshup WhatsApp, Kaleyra RCS) planned alongside SMS connectors.
  • Consumer iOS / Android native apps. Today's consumer surface is the web verify widget. Native wrapping (share-sheet handoff on iOS, foreground-service SMS inspection on Android) is a small engineering pass; queued behind browser-extension ship.
  • Signed Certificate Revocation List (CRL). When a business is deactivated / key rotated, integrators learn via next registry sync. A dedicated signed CRL with short TTL gives faster fail-closed behaviour. Planned v0.2.
  • CERT-In / RBI empanelment conformance letter. Not an accreditation Verify itself needs, but specific pilot customers in the BFSI space require an empanelled-auditor letter. Queued alongside the first BFSI pilot.
  • Formal verification (Tamarin model). Sign-then-verify with context-separation is trivially sound on paper; a Tamarin proof nails down key-compromise / rotation / registry-staleness edge cases. Drafted alongside the Messenger and AI Agent models; planned v0.3.
Transparency commitment

We'll never call a gate "closed" without a dated commit, a passing test, and an explicit note on this page. If something on the "not yet built" list ships, this page updates the same day. Changelog is git history of frontend-web/public/about/verify.html — auditable forever.

Sign your first SMS this afternoon.

Verify is live at verify-api.zoza.world. 21 Go tests passing, five channels supported, offline verification shipped. Register a business, sign a message, paste it into the widget — the whole round-trip is a coffee break.