Zernio
Zernio
PlatformsPhone NumbersAvailability & PricingBuying a NumberPortingKYC (Regulated Countries)Branded Calling
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources
Phone Numbers

Branded Calling

Show your business name, logo and reason for calling on the callee's phone: register the business, create a caller identity, pass vetting, then attach your US numbers.


When you finish this page, outbound calls from your US numbers show your business name, logo and reason for calling on the callee's phone instead of a bare number or "Spam Likely". You need a voice-enabled US number, usage-based billing, and a business registered in the US or Canada. Everything runs under /v1/branded-calling.

Vetting is asynchronous and involves people: the carrier phones the three references you name. Track the identity with GET /v1/branded-calling/identities/{id} or the webhooks, and do not attach numbers until status is verified.

What it costs

Branded Calling bills 2 lines to your usage invoice:

  • $100 per caller identity per month. The first month is charged when Zernio files the identity with the carrier (after review) and is not refunded if the carrier rejects it; the fee then bills monthly while the identity exists at the carrier. Deleting the identity ends it.
  • $0.10 per branded call: every outbound phone call placed from a number attached to a verified identity to a US number, on top of the per-minute rate. The carrier charges Zernio per displayed call; Zernio bills per call placed, because the carriers do not report which calls displayed.

Registering a business and attaching numbers cost nothing extra. The full table is in Call rates.

Where it displays

Branded Calling rides the STIR/SHAKEN signature of the call: once a number is attached to a verified caller identity, every call from it carries the identity and nothing changes on the dial. The carriers verify eligibility; they do not guarantee display.

  • Calls from US numbers to US mobiles on T-Mobile and Verizon.
  • Samsung, Motorola and TCL phones on Android 14 or later, and iPhone XS or newer on iOS 18.5 or later. Carrier-provisioned phones display most reliably; unlocked, BYOD and MVNO phones may show less or nothing.
  • Landlines, international destinations and other number types never display it.
  • SIP trunk and browser-softphone calls from an attached number are branded too: the identity belongs to the number, not to the call.

Step 1: Register the business

Call POST /v1/branded-calling/enterprises with the legal entity behind the identity. Only businesses registered in the US or Canada qualify (a FEIN or the Canadian equivalent is required); any other country returns 422 with code: "feature_not_available". The tax id is stored encrypted and only its last four digits are ever returned. Nothing is filed with the carrier yet: the business is created there when its first identity passes review.

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: business } = await zernio.brandedCalling.createBrandedCallingEnterprise({
  body: {
    legalName: 'Acme Plumbing LLC',
    doingBusinessAs: 'Acme Plumbing',
    organizationType: 'commercial',
    organizationLegalType: 'llc',
    countryCode: 'US',
    jurisdictionOfIncorporation: 'Delaware',
    website: 'https://acmeplumbing.example',
    fein: '12-3456789',
    industry: 'business',
    numberOfEmployees: '11-50',
    organizationContact: { firstName: 'Sam', lastName: 'Owner', email: 'sam@acmeplumbing.example', jobTitle: 'CEO', phoneNumber: '+13125550100' },
    billingContact: { firstName: 'Pat', lastName: 'Bills', email: 'pat@acmeplumbing.example', phoneNumber: '+13125550101' },
    physicalAddress: { streetAddress: '1 Main St', city: 'Chicago', administrativeArea: 'IL', postalCode: '60601', country: 'US' },
    billingAddress: { streetAddress: '1 Main St', city: 'Chicago', administrativeArea: 'IL', postalCode: '60601', country: 'US' },
  }
});
const enterpriseId = business.id;

Response (201):

{
  "id": "66f1a2b3c4d5e6f7a8b9c0d1",
  "legalName": "Acme Plumbing LLC",
  "doingBusinessAs": "Acme Plumbing",
  "countryCode": "US",
  "feinLast4": "6789",
  "registered": false,
  "createdAt": "2026-09-28T12:00:00.000Z"
}

One business serves any number of identities. When you already hold a 10DLC registration for the same business, its brand carries the same legal name, tax id, address and contacts: copy them from GET /v1/sms/registrations/{id} instead of asking twice. Register a business lists every field.

Step 2: Create the caller identity

A caller identity is what the callee sees, plus what the carrier vets. Call POST /v1/branded-calling/identities with:

  • displayName: 1 to 35 characters, no emoji. The name the public knows the business by; a name that is not the business's own (another brand, a bank, a government body) is rejected as impersonation.
  • callReasons: 1 to 10 reasons you call, up to 64 characters each. Reasons taken from GET /v1/branded-calling/call-reasons (the carrier catalogue: "Appointment Reminder", "Order Confirmation", "Delivery ETA", "Customer Service" and 40 more) pass automatically; any other wording is vetted by hand and takes longer.
  • logoUrl (optional): an HTTPS PNG, JPEG, WebP or SVG. Zernio converts it to the 256x256 BMP the carriers require and hosts it. Use the business's own mark, the one on its website.
  • authorizer: a real person at the business who authorizes the registration. The carrier emails a 6-digit code to this address after review, and any trademark complaint later.
  • references: two business references (senior contacts at a vendor, partner or client) and one financial reference (the company's CPA, or a contact at its bank). The carrier vetting team phones each of them, in their local 8am to 9pm window, so give a direct number, a monitored email and the right IANA timezone.

Send an Idempotency-Key header so a retried request returns the same identity instead of creating a second one (idempotency).

import { randomUUID } from 'crypto';
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: identity } = await zernio.brandedCalling.createBrandedCallingIdentity({
  headers: { 'Idempotency-Key': randomUUID() },
  body: {
    enterpriseId: '66f1a2b3c4d5e6f7a8b9c0d1',
    displayName: 'Acme Plumbing',
    callReasons: ['Appointment Reminder', 'Customer Service'],
    logoUrl: 'https://acmeplumbing.example/logo.png',
    authorizer: { name: 'Sam Owner', email: 'sam@acmeplumbing.example' },
    references: {
      business: [
        { fullName: 'Dana Reyes', jobTitle: 'VP Operations', organization: 'Reyes Supply', relationshipToRegistrant: 'Supplier', phoneNumber: '+14155550123', email: 'dana@reyessupply.example', timezone: 'America/New_York' },
        { fullName: 'Lee Park', jobTitle: 'Owner', organization: 'Park Realty', relationshipToRegistrant: 'Client', phoneNumber: '+14155550124', email: 'lee@parkrealty.example', timezone: 'America/Chicago' },
      ],
      financial: { fullName: 'Morgan Cole', jobTitle: 'CPA', organization: 'Cole Accounting', phoneNumber: '+14155550125', email: 'morgan@coleaccounting.example', timezone: 'America/Los_Angeles' },
    },
  }
});
const identityId = identity.id;

Response (201):

{
  "id": "66f1a2b3c4d5e6f7a8b9c0d2",
  "enterpriseId": "66f1a2b3c4d5e6f7a8b9c0d1",
  "displayName": "Acme Plumbing",
  "callReasons": ["Appointment Reminder", "Customer Service"],
  "callReasonsPreApproved": true,
  "status": "requested",
  "rejectionReasons": [],
  "reviewRequest": null,
  "numbers": [],
  "createdAt": "2026-09-28T12:05:00.000Z"
}

The identity starts in Zernio review (requested). A 403 with code: "usage_billing_required" means the team is not on usage-based billing; a 422 on logoUrl means the image could not be downloaded or is not an image.

Check before you submit

Call POST /v1/branded-calling/identities/preflight with the same body first. It runs the checks our review runs, without creating anything, and returns every finding at once:

curl -X POST "https://zernio.com/api/v1/branded-calling/identities/preflight" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d @identity.json

Response (200):

{
  "ok": false,
  "findings": [
    { "code": "call_reason_not_in_catalogue", "severity": "warn", "param": "callReasons", "message": "\"Billing questions\" is not in the carrier catalogue and will be vetted by hand; \"Account Services\" is." },
    { "code": "duplicate_reference_phone", "severity": "block", "param": "references", "message": "Two references share +14155550123." }
  ]
}

A block finding fails our review; a warn finding slows vetting. The codes: display_name_differs_from_legal_name, call_reason_not_in_catalogue, duplicate_reference_phone, reference_inside_business, invalid_timezone, free_mail_authorizer and logo_unreachable. ok: true means the body would enter review with nothing flagged.

Step 3: Review, then the authorizer's code

Zernio reviews every identity before filing it, the way it reviews 10DLC registrations: the business is checked against the registry and its website, the display name and logo must be the business's own, and the references must be credible. Nothing is filed or billed while it is in review.

  • If something needs to change, the identity moves to changes_requested and branded_calling.identity.action_required fires with the request as points. Answer the review below.
  • When it passes, Zernio registers the business and the identity with the carrier and bills the first month. The carrier emails a 6-digit code to the authorizer, the identity moves to pending_email_verification, and action_required fires with reason: "email_code".

Confirm the code with POST /v1/branded-calling/identities/{id}/verify-email/confirm. On success Zernio files the references and submits the identity to carrier vetting in the same call, so it comes back in_review. If a later step fails the identity stays pending_email_verification with the email already verified, and calling again resumes from that step. POST /v1/branded-calling/identities/{id}/verify-email sends a fresh code (the previous one stops working).

curl -X POST "https://zernio.com/api/v1/branded-calling/identities/66f1a2b3c4d5e6f7a8b9c0d2/verify-email/confirm" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "482915"}'

The vetting team then phones the three references. The carrier publishes no turnaround; expect days rather than minutes, and tell the references to expect the call. Track the outcome with GET /v1/branded-calling/identities/{id} or branded_calling.identity.status_updated:

StatusMeaningWhat to do
requestedIn Zernio review.Wait; our review usually finishes the same day.
changes_requestedWe need something from you.Answer the review.
rejectedZernio declined it before filing, or the carrier rejected it.Fix the rejection.
pending_email_verificationFiled.Confirm the authorizer's code.
in_reviewWith the carrier vetting team.Wait; warn the references.
verifiedLive.Attach numbers.
suspendedA trademark or impersonation claim is open against the identity.Nothing on your side while it is open; branding is paused and the identity cannot be deleted until it resolves.
expiredThe yearly verification lapsed and the renewal failed.Edit and PATCH to resubmit.
permanently_rejectedTerminal.Create a new identity.

Step 4: Attach numbers

Once the identity is verified, attach up to 15 numbers per call with POST /v1/branded-calling/identities/{id}/numbers. The numbers must be active US numbers you own (phoneNumberIds from List phone numbers); a number belongs to one identity at a time.

Attaching files a Letter of Authorization with the carrier: the document that lets the carrier register these numbers under the identity, naming Zernio as the agent managing them. The carrier renders it from the business and identity on file; you supply the signature as a PNG (base64, a data:image/png;base64, prefix is accepted) and the signer's printed name. Send an Idempotency-Key here too: a retry must not open a second batch for the same numbers.

curl -X POST "https://zernio.com/api/v1/branded-calling/identities/66f1a2b3c4d5e6f7a8b9c0d2/numbers" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Idempotency-Key: 1f4c9a7e-2d3b-4a5c-8e6f-7a8b9c0d1e2f" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberIds": ["66d4e5f6a7b8c9d0e1f2a3b4"],
    "signature": { "imageBase64": "iVBORw0KGgoAAAANSUhEUgAA...", "signerName": "Sam Owner" }
  }'

Response (201):

{
  "numbers": [
    { "phoneNumberId": "66d4e5f6a7b8c9d0e1f2a3b4", "phoneNumber": "+13125550100", "status": "submitted", "rejectionReason": null, "verifiedAt": null, "addedAt": "2026-10-06T09:00:00.000Z" }
  ]
}

The batch is vetted as a unit and is all-or-nothing: one ineligible number refuses the whole request with 422 naming it. Each number shows the identity once its own status reaches verified; branded_calling.number.status_updated fires on each change. A number the carrier marks permanently_rejected can never be attached to any identity again.

Detach numbers with DELETE /v1/branded-calling/identities/{id}/numbers and phoneNumbers (E.164, up to 100); the carrier deregisters them and they can join another identity.

Hand the form off (white-label)

When the business behind the identity is your customer, not you, call POST /v1/branded-calling/share. It returns a single-use link (valid 7 days) where that business fills in the caller identity itself, with no Zernio login: the display name, logo and call reasons, the authorizer the carrier will email, and the three references the carrier will phone. What they submit lands under your team as requested, goes through the same review as an API submission, and fires branded_calling.identity.status_updated so you know it arrived.

Scope the link with one of:

  • identityId: an identity of yours that is requested or changes_requested, to be completed or corrected by the business.
  • enterpriseId: a registered business, to add a new identity to.
  • Neither: the business registers itself (legal name, tax id, contacts, addresses) and its first identity in one go.
curl -X POST "https://zernio.com/api/v1/branded-calling/share" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enterpriseId": "66f1a2b3c4d5e6f7a8b9c0d1"}'

Response (200):

{
  "url": "https://zernio.com/branded-calling/9Fh2...",
  "expiresAt": "2026-10-05T12:00:00.000Z"
}

The person opening the link enters their email (so you know who filled it in), then walks the same stepped form as the dashboard. If they are not the right person, "Send to someone else" on the page mints a fresh link for them to forward and retires theirs. The link dies on submit; mint another for a second identity. The same action lives on the dashboard's Branded calling page as "Send to the business".

Answer the review

A changes_requested identity carries the request as points in reviewRequest, one per thing to change, each with an id, a title, a detail and the kind of answer it takes (text, link, file or link_or_file). Read it from GET /v1/branded-calling/identities/{id}:

{
  "status": "changes_requested",
  "reviewRequest": {
    "id": "5b1c0d3e-9f2a-4b8c-8d7e-6f5a4b3c2d1e",
    "intro": "Thanks for the Acme Plumbing caller identity. Two things before we file it.",
    "points": [
      { "id": "p1", "title": "Financial reference", "detail": "Morgan Cole's email is a free-mail address. Send the firm's own address, or a bank contact.", "answer": "text" },
      { "id": "p2", "title": "Logo", "detail": "The logo on file is not the one on acmeplumbing.example. Send the mark from your site.", "answer": "link_or_file" }
    ]
  }
}

Answer with PATCH /v1/branded-calling/identities/{id}: reviewAnswers keyed by point id, plus any field you edited in the same call. A text point takes text; a link point takes url; a file point takes url too, pointing at the file after a media upload. The identity goes back to requested and the review resumes where it stopped.

import Zernio from '@zernio/node';

const zernio = new Zernio();

await zernio.brandedCalling.updateBrandedCallingIdentity({
  path: { id: '66f1a2b3c4d5e6f7a8b9c0d2' },
  body: {
    references: { /* the same references, with the CPA's firm address */ },
    logoUrl: 'https://acmeplumbing.example/logo-official.png',
    reviewAnswers: {
      p1: { text: 'Updated to morgan@coleaccounting.example, the firm address.' },
      p2: { url: 'https://acmeplumbing.example/logo-official.png' },
    },
  }
});

Response (200): the identity, with status: "requested" and reviewRequest: null. Answer every point in one call: a point you skip is asked again.

Fix a rejection

Two reviews can reject an identity, and rejectionReasons tells them apart.

  • Our review, before anything is filed: the identity is rejected with the reason in reviewNote, rejectionReasons is empty, and nothing was billed. Impersonation, prohibited content and a business we could not verify end here. Fix the fields and PATCH; the identity goes back to requested.
  • The carrier, after filing: the identity is rejected with rejectionReasons, each with a code, a title, a detail, and on the first entry a message written by the vetting team:
{
  "status": "rejected",
  "rejectionReasons": [
    { "code": "documentation_incomplete", "title": "Documentation incomplete", "detail": "Provided documents do not establish business identity.", "message": "The registry lists the entity as Acme Plumbing Services LLC; the legal name on file must match exactly." }
  ]
}

PATCH the fields the reasons name. On a carrier rejection the edits are applied at the carrier and the identity is resubmitted straight away, so it comes back in_review without a second Zernio review. The first month is not refunded, and nothing more is charged for the resubmission. A permanently_rejected identity is terminal: create a new one, and get the display name and references right before filing, because a number the carrier marks permanently_rejected can never be branded again on your account.

Renewal

A verified identity lasts one year; expiringAt on the identity is the date. Zernio resubmits the identity to the carrier 30 days before that date on its own, the registration stays live during the renewal review, and branded_calling.identity.status_updated reports the new verifiedAt and expiringAt when it passes. You only see expired when that renewal failed: edit whatever the carrier flagged in rejectionReasons and PATCH to resubmit.

Stop paying

DELETE /v1/branded-calling/identities/{id} detaches its numbers, removes the identity at the carrier and ends the monthly fee. The month in progress is not prorated. A 409 means a claim is open against the identity (suspended); it can be deleted once the claim resolves.

Common questions

The identity is verified and the number is verified, but the callee sees a bare number. Display is decided by the receiving carrier and phone, and the carrier only verifies eligibility. Check the destination against where it displays: a landline, an AT&T subscriber, an MVNO, an unlocked phone or an old OS shows nothing, and that is expected. Calls to a T-Mobile or Verizon subscriber on a carrier-provisioned Samsung, Motorola, TCL or iPhone XS+ are the reliable case.

How long does vetting take? The carrier publishes no turnaround. Our review usually finishes the same day; carrier vetting takes days, because the vetting team phones the three references in their local business hours and cannot proceed until it reaches them.

What are the references asked? Whether they know the business, in what capacity, and whether it is the business the identity describes. Nothing about you: the calls confirm the business exists and operates as stated. Pick people who will answer a call from an unknown US number.

Are SIP trunk and browser calls branded? Yes. The identity belongs to the number, so every outbound call from an attached number carries it, whichever path placed the call.

What happens to the fee on rejection? The first month is charged when we file the identity and is not refunded if the carrier rejects it. A rejection by our review, before filing, bills nothing. Resubmitting after a carrier rejection costs nothing extra.

Can I use one identity for several businesses? No. The display name must be the registered business's own name, and the carrier vets that match. Register each business and give each its own identity.

If it fails

  • 422 with code: "feature_not_available" on the business: the country is not US or CA. On numbers: a number is not a US number, or is not active.
  • 409 with code: "invalid_resource_state": the identity is not in a status that allows the action (editing outside requested, changes_requested or rejected; attaching numbers before verified; a number already on an identity; deleting while a claim is open).
  • 400 on code: the 6-digit code is wrong or expired. Request a new one.
  • 400 on a field of the identity names the field in param; run preflight first to catch every gap at once.

Every error uses the envelope in error handling.

Related

  • Outbound calls: placing the calls that carry the identity.
  • Branded Calling webhooks: every event, with payloads.
  • Call rates: the monthly and per-call prices.
  • Carrier registration (10DLC): the same business details, for SMS.
  • Create a caller identity: every field of the request.
Was this page helpful?

KYC (Regulated Countries)

Collect the identity details a regulated country requires, submit them with POST /v1/phone-numbers/kyc or a hosted link, and fix a declined number.

Voice & Calls

Receive and place phone (PSTN) calls on your numbers, route inbound calls to a phone, a SIP endpoint or an AI voice agent, and read every call from one history.

On this page

What it costsWhere it displaysStep 1: Register the businessStep 2: Create the caller identityCheck before you submitStep 3: Review, then the authorizer's codeStep 4: Attach numbersHand the form off (white-label)Answer the reviewFix a rejectionRenewalStop payingCommon questionsIf it failsRelated