white-label enrollment in your own admin
Run number onboarding, profile/identity management, verification payment, and trust monitoring
for your customers entirely from your own admin UI, using only a nol_ account key. Nothing
in this flow requires sending a person to a numbers.online page — except the Stripe-hosted card
form itself, which opens in a tab and returns automatically.
This is designed for platforms that manage many numbers on behalf of their own users (softphones, PBX fleets, AI voice/chat agents): one account per channel/customer, each onboarding its own number at its own tier under its own brand.
Two independent axes. A number's verification level (unverified · personal $9 one-time · business $29/yr) is not the same thing as your API billing tier (free vs. prepaid
standard, via/api/v1/account/topup). Verifying a number does not top up API credit, and vice-versa. This page covers number verification + profile; top-ups are separate.
| Step | Where it runs |
|---|---|
| Sign up, issue key | API — POST /api/v1/account/signup |
| Prove number ownership (OTP) | API — POST /api/v1/account/verify-phone/start → /confirm |
| Stage business/personal profile (name, legal name, tax id, address, website, bio) | API — PUT /api/v1/account/listings/{e164} |
| Upload a logo | API — POST /api/v1/account/listings/{e164}/logo |
| Pay for verification ($9 / $29) | Stripe-hosted page, opened in a tab; publishes server-side on payment |
| See verified state, trust grade, live risk, reports | API — GET /api/v1/account/listings/{e164} + /reports |
| Edit the live profile later | API — PUT /api/v1/account/listings/{e164} |
| Update account name/email | API — PATCH /api/v1/account |
1. POST /api/v1/account/signup → nol_ key (once per channel)
2. POST /api/v1/account/verify-phone/start → OTP sent to the number
POST /api/v1/account/verify-phone/confirm → number bound to the account
3. PUT /api/v1/account/listings/{e164} → stage the profile (draft)
POST /api/v1/account/listings/{e164}/logo → attach a logo (business)
4. POST /api/v1/account/listings/{e164}/verify-checkout → { url }
→ open url in a new tab; customer pays on Stripe
→ numbers.online PUBLISHES the listing server-side (no return trip)
5. GET /api/v1/account/listings/{e164} → poll until state == "verified"
A partial PUT merges — send only the fields you have. kind fixes whether this number
verifies as business ($29/yr, public profile) or personal ($9, "verified & online", no
public profile fields). Tax id (ein) is stored privately and is never published.
curl -X PUT https://numbers.online/api/v1/account/listings/+14155550142 \
-H "Authorization: Bearer $NOL_KEY" -H "Content-Type: application/json" \
-d '{
"kind": "business",
"dba": "Acme Plumbing",
"legal_name": "Acme Plumbing LLC",
"ein": "12-3456789",
"formation_date": "2019-04-01",
"industry": "Home services",
"address": "1 Main St, Austin TX",
"website": "https://acme.example",
"bio": "Licensed plumbers, same-day service."
}'
# → { "ok": true, "state": "draft", "verification_id": "…", "kind": "business", "profile": {…} }
Raw image bytes in the body; image/png · image/jpeg · image/webp, max 512 KB.
curl -X POST https://numbers.online/api/v1/account/listings/+14155550142/logo \
-H "Authorization: Bearer $NOL_KEY" -H "Content-Type: image/png" \
--data-binary @logo.png
# → { "ok": true, "media_id": "…", "logo_url": "https://numbers.online/api/media/…", "byte_size": 8123 }
curl -X POST https://numbers.online/api/v1/account/listings/+14155550142/verify-checkout \
-H "Authorization: Bearer $NOL_KEY"
# → { "ok": true, "provider": "stripe", "state": "pending", "url": "https://checkout.stripe.com/…" }
Open url in a new tab (the same pattern as a hosted OAuth/consent button). When the customer
pays, the Stripe webhook publishes the listing on our side — there is no page for them to
return to and no second API call for you to make. (In a dev environment with the dev payments
provider, this endpoint verifies instantly and returns the handle.)
curl https://numbers.online/api/v1/account/listings/+14155550142 \
-H "Authorization: Bearer $NOL_KEY"
{
"e164": "+14155550142",
"state": "verified",
"trust_grade": "A", // static verification badge, set at verification
"risk": { // LIVE community signal — moves as the number is reported
"score": 12, // 0–100, higher = more risk (a supplementary signal)
"report_count": 0, "reports_last_30d": 0,
"review_total": 4, "review_positive": 4
},
"listing": { "handle": "acme-plumbing-9f3a", "name": "Acme Plumbing", "website": "https://acme.example",
"logo_url": "/api/media/…", "verified_since": "2026-06-08T…", "next_billing": "2027-06-08T…" },
"draft": null
}
trust_gradeandrisk.scoreare different things. The grade is a fixed badge proving the number was verified. The score is live community reputation — a verified business can still accrue risk if it gets reported. Show both; act on the score.
curl "https://numbers.online/api/v1/account/listings/+14155550142/reports?limit=50" \
-H "Authorization: Bearer $NOL_KEY"
# → { summary: {…}, reports: [{ created_at, tags, rating, body, reporter, lane, provenance }], reviews: [...] }
Anonymous reporters are redacted (reporter: null).
| Personal ($9 one-time) | Business ($29/yr) | |
|---|---|---|
| Public profile page | No — shows "verified & online", name not exposed | Yes — /business/{handle} with name, website, address, logo |
| Profile fields (name/url/tax id/address/logo) | Not applicable | Yes (tax id stays private) |
| Trust grade | A- | A |
| Reporting lane | crowd | accountable (first-party) |
{e164} must be OTP-bound to the calling account.