NumbersOnline
API reference/Integration guides/API-first onboarding
Accounts & onboarding

API-first onboarding

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.

What's in-admin vs. hosted

StepWhere it runs
Sign up, issue keyAPI — 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 logoAPI — 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, reportsAPI — GET /api/v1/account/listings/{e164} + /reports
Edit the live profile laterAPI — PUT /api/v1/account/listings/{e164}
Update account name/emailAPI — PATCH /api/v1/account

The onboarding sequence

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"

3 · Stage the profile

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": {…} }

3b · Upload a logo

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 }

4 · Mint the payment link and open it

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.)

5 · Poll for the result, then show trust

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_grade and risk.score are 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.

Reports detail

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 vs. business — what you get

Personal ($9 one-time)Business ($29/yr)
Public profile pageNo — shows "verified & online", name not exposedYes — /business/{handle} with name, website, address, logo
Profile fields (name/url/tax id/address/logo)Not applicableYes (tax id stays private)
Trust gradeA-A
Reporting lanecrowdaccountable (first-party)

Notes & limits

  • Account-level key required for every endpoint here — identity and money are owner actions; tenant sub-keys are rejected.
  • You can only manage a number you proved{e164} must be OTP-bound to the calling account.
  • A free account can bind one number; binding additional numbers requires verifying a number first (the per-number fee is the anti-Sybil gate).
  • All of this is a supplementary signal, never an official or compliance determination.
Need a key? Get one self-service. It’s shown once and stored only as a hash. Browse the other guides, grab copy-paste artifacts on the integrations page, or read the full machine-readable schema at /api/spec.
FCC robocall-mitigation evidence bundle