> ## Documentation Index
> Fetch the complete documentation index at: https://docs.robase.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-country setup

> Ship to NG, GH, KE, CI in a weekend.

This guide takes you from a Nigeria-only integration to a four-country production deployment. Order of operations matters — some steps have multi-day lead times you want to start early.

<Steps>
  <Step title="Register sender IDs per country, in parallel (day 1)">
    Regulator approval is the longest lead time. Submit NG + GH + KE + CI sender-ID registrations on day 1 so they can process in parallel:

    ```typescript theme={null}
    await Promise.all([
      pm.senderIds.create({
        sender_id: 'SHUTTLERS', countries: ['NG'],
        registered_entity: 'Shuttlers Technology Limited',
        registration_doc_url: 'https://bucket.example/cac-ng.pdf',
        use_case: 'Ride booking confirmations, ETAs, and receipts',
        sample_message: 'Your Shuttlers ride with Chidi arrives in 3 min at Gate 2.',
      }),
      pm.senderIds.create({
        sender_id: 'SHUTTLERS', countries: ['GH'],
        registered_entity: 'Shuttlers Technology Ghana Ltd',
        registration_doc_url: 'https://bucket.example/cac-gh.pdf',
        use_case: 'Ride booking confirmations, ETAs, and receipts',
        sample_message: 'Your Shuttlers ride arrives at Independence Square in 5 min.',
      }),
      // …KE, CI
    ]);
    ```

    Typical approval times:

    * **NG:** 5–7 business days (NCC)
    * **GH:** 3–5 business days (NCA)
    * **KE:** 2–4 business days (CA)
    * **CI:** 3–5 business days (ARTCI)
  </Step>

  <Step title="Upgrade your plan (day 1)">
    Multi-country pricing is available on **Starter and above**. Upgrade via **Dashboard → Billing → Plans** if you're on Free.
  </Step>

  <Step title="Audit your code for hardcoded Nigerian assumptions (day 1–2)">
    Common offenders:

    * `+234` hardcoded in E.164 validation. Use a proper library ([libphonenumber](https://github.com/google/libphonenumber) or [phonenumbers](https://pypi.org/project/phonenumbers/)).
    * `₦` hardcoded in cost displays. Read `cost.currency` from the response.
    * `"mtn" | "airtel" | "glo" | "9mobile"` enum. Widen to a string — new carriers will appear.
    * Timezone `Africa/Lagos` in scheduled sends. Use the user's timezone or UTC.
  </Step>

  <Step title="Test each country (day 2–3)">
    Use test-mode keys with real E.164 numbers for each country:

    ```typescript theme={null}
    for (const phone of ['+2348012345678', '+233201234567', '+254712345678', '+225070000000']) {
      const sms = await testPm.sms.send({ to: phone, from: 'SHUTTLERS', body: 'Hello from Shuttlers!' });
      console.log(phone, sms.country, sms.cost);
    }
    ```

    Verify:

    * `sms.country` matches the expected country code.
    * `sms.cost.currency` matches (NGN / GHS / KES / XOF).
    * Webhooks fire with `data.country` set.
  </Step>

  <Step title="Set up billing alerts (day 3)">
    Your wallet is shared across countries but costs per country differ. Configure **auto top-up** in the dashboard with a safe threshold — a GH campaign blowing through quota shouldn't tank your NG transactional budget.
  </Step>

  <Step title="Roll out to users (day 5+)">
    Start with **one country's sender ID approved**. Feature-flag the rest off. As approvals come in, flip the flag:

    ```typescript theme={null}
    const ENABLED: Record<string, boolean> = {
      NG: true,   // approved day 5
      GH: false,  // still pending
      KE: false,
      CI: false,
    };

    async function notify(user: { phone: string }, body: string) {
      const country = detectCountry(user.phone);
      if (!ENABLED[country]) return fallbackEmail(user);
      await pm.sms.send({ to: user.phone, from: 'SHUTTLERS', body });
    }
    ```
  </Step>

  <Step title="Monitor per-country delivery (day 7+)">
    After one week, check `/sms/analytics/carriers?days=7` per project and look for:

    * Delivery rate differences across countries (>10% gap is worth investigating).
    * Unexpected carriers (e.g. roaming numbers showing up as a country you don't target).
    * Failover events — if Beem is flaky for one country, `provider_attempts` will show Termii winning a lot.
  </Step>
</Steps>

## Common gotchas

<AccordionGroup>
  <Accordion title="Users with roaming SIMs">
    A Nigerian user roaming in the UK still has a `+234` number — Robase will price it as NG and route via NG carriers. The carrier delivers it over international roaming. Cost is correct; latency may be higher.
  </Accordion>

  <Accordion title="Kenyan numbers without the country code">
    Locals sometimes enter `0712345678` instead of `+254712345678`. Normalize on signup — `libphonenumber` with a default country is the tool.
  </Accordion>

  <Accordion title="Ghana: keep `+233` even for local destinations">
    Unlike some other countries, Ghanaian MNOs reject non-international-formatted destinations from international aggregators. Always E.164.
  </Accordion>

  <Accordion title="Francophone countries: accents in names">
    `Côte d'Ivoire` user names often contain `é`, `à`, `ô` — which force UCS-2. See [Segments & encoding](/sms/segments-encoding) for the cost implications.
  </Accordion>
</AccordionGroup>

## Opening additional markets

We add countries as demand + coverage aligns. If you need Uganda, Tanzania, Senegal, or Cameroon:

1. Email **[sales@robase.dev](mailto:sales@robase.dev)** with projected volume + use case.
2. We add the route + pricing + sender-ID pipeline within 3 business days.
3. Some countries require a local entity — we'll tell you upfront if so.
