SMS

Toll-free numbers

Send from one Canada-wide number at up to 1,200 messages a minute. Toll-free SMS needs a one-time verification of your business and a double opt-in from each recipient.

What toll-free is for

A toll-free number (833, 844, 855, 866, 877 or 888) is not tied to a province, so one number serves customers anywhere in Canada. It is also the higher-volume channel: once verified, a toll-free number sends up to 1,200 messages a minute, against 6 a minute from a local number. It suits one sender that has to reach many people in a short window, such as reminders for a chain of clinics or order updates for a national store.

Toll-free sends also have their own daily limits, counted apart from sends from your local numbers. There is no probation: from its toll-free numbers an account can send 10,000 live messages a day, reach up to 2,500 distinct recipients with one identical message, and send 30 contact-group broadcasts a day of up to 2,500 recipients each. The per-recipient cap, the automatic pause, the link-shortener rule and the top-up caps are the same as for local numbers (see Sending limits).

You buy a toll-free number the way you buy a local one: search with a toll-free prefix, then provision it (see Phone numbers). Before it can send SMS, the carrier has to verify the business behind it. Until then, a send from it is refused with 403 TOLL_FREE_NOT_VERIFIED before any charge, in test mode too, so an integration fails early.

Messages cost the same as from a local number: $0.03 per segment, sent or received. The number is $99.00 a month, and verification is a one-time $499.00 per application (see Pricing).

Verification

Verification is a one-time application that covers up to 5 of your toll-free numbers. It needs a live key, and the fee is paid by card when you submit, separately from your prepaid balance, so it never eats into what you send with. Each application goes through two reviews: HonkIO’s, then the carrier’s.

  1. Apply. In the dashboard, open Phone numbers and choose Verify for SMS on a toll-free number, or call POST /v1/toll-free-verifications. The application starts as a DRAFT you can edit with PATCH or cancel with DELETE.
  2. Submit and pay. POST /v1/toll-free-verifications/:id/submit moves it to AWAITING_PAYMENT and returns a payment_url: open it, signed in to the dashboard, to pay the $499.00 fee by card. Once paid, it moves to IN_REVIEW.
  3. HonkIO reviews it, usually within a few business days. We check the business against the public registries and read the use case, sample messages and opt-in process the way the carrier will. We then send it to the carrier (SUBMITTED), ask you for changes (CHANGES_REQUESTED, with a reviewer_note saying what to fix), or decline it (DECLINED, with the reason).
  4. The carrier reviews it, which typically takes about a week. APPROVED means every number on the application can send SMS. REJECTED comes with the carrier’s reason: fix the application and submit it again, and HonkIO checks the change before it goes back to the carrier. Or withdraw it with DELETE, which frees its numbers for a new application; the fee is not refunded.

The fee and refunds

The fee is $499.00 per application, whether it covers one number or five. It is refunded in full if HonkIO declines the application. Once HonkIO has sent the application to the carrier, the fee is not refunded, whatever the carrier decides; a carrier rejection can be corrected and resubmitted at no charge, as often as needed. Answering a request for changes costs nothing either, and an application cancelled before payment is never charged.

Each decision fires a webhook: toll_free_verification.changes_requested, toll_free_verification.approved or toll_free_verification.rejected (see the event reference). The account owner also hears about every decision by email, a decline included.

What the application asks for, and why

The carrier approves a toll-free number for a business and a purpose, not for an account, so the application describes both. Answers that reviewers can check against public records get through fastest.

  • The business: legal name, operating name if different, entity type, CRA business number and address. Reviewers match these against the federal and provincial registries, so use the name exactly as registered.
  • A contact: the person HonkIO and the carrier can reach about the application, with a phone number and an email address.
  • Website, privacy policy and terms: the site has to belong to the business, and the privacy policy should say how you use phone numbers and that you do not sell or share them for marketing.
  • Use case: a category from the list, and a short summary of what you send and to whom.
  • Opt-in: how people agree to receive your messages (a web form, a checkbox at checkout, a keyword texted to your number), with links to up to 5 screenshots of it. The consent step must be clear and never ticked in advance. This is the part applications are most often sent back for.
  • Messages: 1 to 5 sample messages as you will really send them, with your brand name and opt-out wording; the confirmation a subscriber gets after opting in; and your reply to HELP.
  • Volume and content: your expected monthly volume, whether the content is age-gated (alcohol, cannabis and the like), and anything else reviewers should know.

GET /v1/toll-free-verifications/options lists the values use_case, monthly_volume and entity_type accept, each with an English and a French label, and the current fee_cents.

With the API

// The values useCase, monthlyVolume and entityType accept, with labels
// in English and French, and the current fee
const { data: options, error } = await honkio.tollFreeVerifications.options()
if (error) throw new Error(error.message)

// { use_cases: [{ value: 'Appointments', label_en: '...', label_fr: '...' }, ...],
//   monthly_volumes: [...], entity_types: [...], fee_cents: 49900 }
console.log(options.use_cases, options.fee_cents)
// Create the application as a draft (nothing is charged yet)
const { data: application, error } = await honkio.tollFreeVerifications.create({
  phoneNumberIds: ['clxxxnumberxxxxxxxxxxxxxx'],
  application: {
    businessName: 'Acme Clinics Inc.',
    entityType: 'PRIVATE_PROFIT',
    businessRegistrationNumber: '123456789RC0001',
    businessAddress: { line1: '100 King St W', city: 'Toronto', province: 'ON', postalCode: 'M5X 1A9' },
    contact: { firstName: 'Ada', lastName: 'Lovelace', email: 'ada@acme.ca', phone: '+1416XXXXXXX' },
    website: 'https://acme.ca',
    useCase: 'Appointments',
    useCaseSummary: 'Appointment reminders and rescheduling links for our patients.',
    sampleMessages: ['Acme Clinics: your appointment is tomorrow at 2 pm. Reply C to confirm. Reply STOP to opt out.'],
    optInWorkflow: 'Patients tick an unchecked SMS box on the booking form, then confirm by replying YES.',
    optInImageUrls: ['https://acme.ca/img/booking-form-sms-box.png'],
    optInConfirmationMessage: 'Acme Clinics: you are subscribed to appointment texts. Reply STOP to opt out, HELP for help.',
    helpMessage: 'Acme Clinics: call 1-833-555-0100 or visit acme.ca/help. Reply STOP to opt out.',
    privacyPolicyUrl: 'https://acme.ca/privacy',
    monthlyVolume: '10,000',
    ageGated: false,
  },
})
if (error) throw new Error(`${error.name}: ${error.message}`)

// { id: 'tfv_...', status: 'DRAFT', phone_numbers: [...], application: {...}, fee_cents: 49900, ... }
console.log(application.id, application.status)
// Submit it: a draft moves to AWAITING_PAYMENT and returns where to pay
const { data: submitted, error } = await honkio.tollFreeVerifications.submit('tfv_...')
if (error) throw new Error(error.message)

// { id: 'tfv_...', status: 'AWAITING_PAYMENT', ...,
//   payment_url: 'https://honkio.ca/dashboard/numbers/toll-free/tfv_.../pay' }
console.log(submitted.status, submitted.payment_url)

// After CHANGES_REQUESTED or REJECTED: fix the fields, then submit again (no charge)
const { error: updateError } = await honkio.tollFreeVerifications.update('tfv_...', {
  application: { optInImageUrls: ['https://acme.ca/img/booking-form-v2.png'] },
})
if (updateError) throw new Error(updateError.message)
// Check on it, or list every application on the account
const { data: application, error } = await honkio.tollFreeVerifications.get('tfv_...')
if (error) throw new Error(error.message)

const { data: all, error: listError } = await honkio.tollFreeVerifications.list()
if (listError) throw new Error(listError.message)

// { id: 'tfv_...', status: 'CHANGES_REQUESTED',
//   reviewer_note: 'The screenshot does not show the SMS checkbox.', ... }
console.log(application.status, application.reviewer_note, all.data.length)

Creating and submitting an application need the phone_numbers:w permission. A number can belong to one open application at a time (409 NUMBER_IN_OPEN_VERIFICATION), and an application can be edited only as a draft, after changes were requested, or after a rejection (409 VERIFICATION_NOT_EDITABLE).

Double opt-in

Toll-free messaging in Canada needs double opt-in. Someone first agrees to hear from you, which you record as consent (see Consent); then they confirm by text before you message them from a toll-free number. The confirmation counts for the toll-free number that sent it: if you send from several toll-free numbers, each one asks for its own. A live toll-free send to a recipient who has not confirmed with that number is refused with 451 DOUBLE_OPT_IN_REQUIRED, before any charge. Local numbers are not affected, and test mode skips this check like the other consent checks.

Send the confirmation with POST /v1/compliance/opt-in-confirmations. from is one of your verified toll-free numbers, to a Canadian mobile number with an active express or implied consent already on file, and brand_name the name the recipient knows you by. Set language to fr for the French text. It is billed as a normal message. HonkIO sends exactly this:

English (the default){brand_name}: reply YES to confirm you want text messages from us. Msg & data rates may apply. Reply STOP to opt out.
French (language: fr){brand_name} : répondez OUI pour confirmer que vous voulez recevoir nos textos. Des frais de messagerie et de données peuvent s’appliquer. Répondez ARRET pour vous désabonner.
// Ask the recipient to confirm (they need an active consent on file first)
const { data: confirmation, error } = await honkio.optInConfirmations.send({
  from: '+1833XXXXXXX',
  to: '+1613XXXXXXX',
  brandName: 'Acme Clinics',
  language: 'en',
})
if (error) throw new Error(`${error.name}: ${error.message}`)

// { id: 'oic_...', status: 'PENDING', from: '+1833XXXXXXX',
//   to: '+1613XXXXXXX', expires_at: '...', ... }
console.log(confirmation.id, confirmation.status)

The confirmation stays PENDING for 7 days. When the recipient texts back YES, Y, OUI or O (in any case; spaces and trailing punctuation are ignored, but the reply must be just that word) to that toll-free number, it becomes CONFIRMED, their consent records the confirmation, and the consent.double_opt_in_confirmed webhook fires. From then on, sends to them from that number go through. The reply is still an ordinary received message: it is billed and fires message.received. STOP works as usual: it revokes the consent, declines the pending confirmation, and withdraws the double opt-in for the number it was sent to.

One confirmation can be pending per recipient; sending another replaces it. You can send at most 3 to the same recipient in 24 hours (429 OPT_IN_CONFIRMATION_LIMIT). A confirmation nobody answers becomes EXPIRED after 7 days; send a new one if the person still wants your messages.

// Where a recipient stands: PENDING, CONFIRMED, EXPIRED or DECLINED
const { data, error } = await honkio.optInConfirmations.list({ to: '+1613XXXXXXX' })
if (error) throw new Error(error.message)
for (const c of data.data) console.log(c.id, c.status)

The confirmation itself is exempt from the double opt-in check, since it is how the check is met, and so are verification codes (POST /v1/verify), which answer a request the recipient just made; both still need a verified toll-free number. The payload of consent.double_opt_in_confirmed is in the event reference.

Errors

The codes toll-free numbers and double opt-in add. Every other code, with its HTTP status, is on the errors page.

  • 403 TOLL_FREE_NOT_VERIFIED: The toll-free number is not verified for SMS yet. Apply for verification, then send once it is approved.
  • 451 DOUBLE_OPT_IN_REQUIRED: Toll-free send to a recipient who has not confirmed double opt-in. Send an opt-in confirmation and wait for their YES.
  • 422 NOT_A_TOLL_FREE_NUMBER: The number is not one of your toll-free numbers, so it cannot be verified or send opt-in confirmations.
  • 409 NUMBER_IN_OPEN_VERIFICATION: The number already belongs to another open toll-free verification application.
  • 409 VERIFICATION_NOT_EDITABLE: The toll-free verification application cannot be changed or submitted in its current status.
  • 422 LIVE_MODE_REQUIRED: This action needs a live key. Toll-free verification, for one, exists in live mode only.
  • 429 OPT_IN_CONFIRMATION_LIMIT: Already 3 opt-in confirmations to this recipient in the last 24 hours. Try again later.