KeepMyNumber
Home Sign in

Call forwarding

Once a number is active on the platform you can control where its calls go. Two independent rules per number:

  • forward_all — every inbound call is diverted, to your SIP server (a full SIP address, user@host) or to another phone number.
  • on_missed — only unanswered/failed calls are diverted, to a phone number. ({"type": "voicemail"} is reserved for platform voicemail boxes — coming later; the API refuses it for now.)

Forwarding to a SIP server

{"type": "sip", "sip_endpoint": "+12165455345@pbx.example.com"} delivers every call as a SIP INVITE sip:+12165455345@pbx.example.com to pbx.example.com — your PBX, LiveKit SIP endpoint, Asterisk, 3CX, etc. Your server must accept calls from the carrier's signalling IPs (Telnyx publishes them); no SIP registration or password is involved. The user part is what your server sees as the called party — use the number itself (keep the + if your server matches E.164), or an extension. Telnyx-hosted addresses (user@yourname.sip.telnyx.com, including another Telnyx account's subdomain set to receive calls "from anyone") work the same way and are delivered on-net.

Combine it with on_missed → a phone number to ring your SIP server first and divert to a mobile when the server is down, busy or does not answer.

How a forwarded call behaves

The platform routes each call as it arrives, from the rules stored on the number, so rule changes take effect on the very next call. The caller is not answered by the platform: they hear your target ring and are connected only when it answers, with their own caller id presented to your target. If the target does not pick up within 25 s and an on_missed number is set, the still-ringing caller is sent there; otherwise the call ends as a normal no-answer. Every step is reported to your webhooks — see Call notifications.

A bare username (alice, no host) is refused with 422: platform-hosted SIP accounts are not available yet.

The rules live in the platform's number registry — the source of truth. The platform pushes them to whichever carrier currently hosts your number; you never talk to a carrier, and if a number moves providers the same rules are re-applied automatically.

Set the rules

PUT /v2/numbers/{id}/call_forwarding is a full replace: a rule you omit is cleared.

curl -X PUT "$API/v2/numbers/$NUMBER_ID/call_forwarding" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "forward_all": {"type": "sip", "sip_endpoint": "14155552671@pbx.example.com"},
    "on_missed": {"type": "number", "phone_number": "+447912345678"}
  }'
{
  "number_id": "66a7fc25-576f-4787-8ff1-92f79c14faa8",
  "phone_number": "+14155552671",
  "forward_all": {"type": "sip", "sip_endpoint": "14155552671@pbx.example.com"},
  "on_missed": {"type": "number", "phone_number": "+447912345678"},
  "status": "active",
  "updated_at": "2026-09-06T09:00:00Z"
}

Only forward everything to a number? Send just that rule:

{"forward_all": {"type": "number", "phone_number": "+447912345678"}}

Validation to expect:

  • Phone targets must be valid E.164; they are normalized for you.
  • A number cannot forward to itself (422).
  • SIP targets must be a full address (user@host, e.g. alice@sip.example.com or +12165455345@pbx.example.com); a sip: prefix is accepted and stripped. Letters/digits/./_/- in the user part, optionally led by + — keep the + when your server matches the called number in E.164 (most PBX / voice-API integrations do). A bare username is 422.
  • The number must be active or active_unconfigured (409 while it is still porting in).
  • An empty body is a 422 — use DELETE to clear forwarding.

Setting rules on an active_unconfigured number flips it to active.

Read and clear

curl "$API/v2/numbers/$NUMBER_ID/call_forwarding" -H "Authorization: Bearer $API_KEY"
curl -X DELETE "$API/v2/numbers/$NUMBER_ID/call_forwarding" -H "Authorization: Bearer $API_KEY"

GET returns both rules (null = not set, calls ring normally). DELETE removes both rules and tells the carrier to stop forwarding; if nothing else is configured on the number it returns to active_unconfigured.

Re-sync with the carrier

If calls are not following the rules you see on GET — the carrier side may have drifted after a failed update, a manual change in a carrier portal, or a change of hosting provider — push the stored rules again without changing them:

curl -X POST "$API/v2/numbers/$NUMBER_ID/call_forwarding/sync" \
  -H "Authorization: Bearer $API_KEY"
{
  "number_id": "66a7fc25-576f-4787-8ff1-92f79c14faa8",
  "phone_number": "+14155552671",
  "forward_all": {"type": "number", "phone_number": "+447912345678"},
  "on_missed": null,
  "status": "active",
  "updated_at": "2026-09-06T09:00:00Z",
  "provider_slug": "telnyx",
  "synced": true
}

provider_slug names the carrier that received the rules. A carrier refusal comes back as 502 with the carrier's own message, so you can see exactly what it objected to. Nothing is stored and no webhook fires — this is a read-your-registry, write-the-carrier operation. In the dashboard it is the Re-sync with carrier button on the forwarding editor.

Call notifications

Every time a call to one of your numbers is forwarded you get a webhook, and a second one when that call ends. Three event types:

Event When Key payload fields
call.forwarded the call was sent to a target forwarded_to, rule (forward_all / on_missed), stage (primary / fallback), from, forwarded_by
call.completed an answered call ended answered_at, ended_at, duration_seconds, hangup_cause, ended_by
call.missed the call was never answered reason (no_answer / busy / rejected / caller_hung_up / no_forwarding_rules / failed), hangup_cause

All three carry the same call_id, number, from, provider and provider_call_ref, so you can correlate them. forwarded_by tells you who did the forwarding:

  • "platform" — the platform routed the call itself (this is how Telnyx-hosted numbers work: the carrier asks us what to do with every call, and we send it to your target without answering the caller first).
  • "carrier" — the hosting carrier forwarded the call natively and reported it to us afterwards. Some carriers we integrate with only work this way; the events look the same, they may just arrive a little later.

A call that rings your forward_all target for 25 s without an answer and then goes to your on_missed number produces two call.forwarded events (stage: "primary", then stage: "fallback" with primary_hangup_cause), followed by one call.completed or call.missed.

{
  "type": "call.forwarded",
  "payload": {
    "call_id": "4b1c9f02-3c7e-4d1a-9b0e-8a2f1c6d7e55",
    "number": {"id": "a7e7c447-162a-4f49-998c-add655fd6e61", "phone_number": "+12165455345"},
    "from": "+16462427059",
    "forwarded_to": {"type": "sip", "sip_endpoint": "+12165455345@kmn-comms.sip.telnyx.com"},
    "rule": "forward_all",
    "stage": "primary",
    "forwarded_by": "platform",
    "provider": "telnyx",
    "provider_call_ref": "9560e1a6-ad01-11f1-bc48-2221f7f6ad71",
    "started_at": "2026-09-11T09:24:26Z",
    "occurred_at": "2026-09-11T09:24:26Z"
  }
}

Full captured examples of all three (including a carrier-native one) are in Webhook event examples. The same calls are browsable at GET /v2/calls (filter by number_id, phone_number, status) and GET /v2/calls/{id}.

Webhook: rule changes

Every change (set or clear) emits a number.forwarding_updated event to your webhook endpoints. changed_by is "customer" when you made the change and "ops" when our support team adjusted the rules for you (always at your request — they use the same editor and the same rules):

{
  "type": "number.forwarding_updated",
  "payload": {
    "number": {"id": "66a7…", "phone_number": "+14155552671"},
    "forwarding": {
      "forward_all": {"type": "sip", "sip_endpoint": "alice@pbx.example.com"},
      "on_missed": null
    },
    "changed_by": "customer"
  }
}

Configure at port time

You can also pre-set forwarding on the porting order (routing_config on POST /v2/porting_orders) using the same shape:

{
  "phone_numbers": ["+447912345678"],
  "routing_config": {
    "forward_all": {"type": "sip", "sip_endpoint": "alice@pbx.example.com"}
  }
}

When the port completes the number activates as active with those rules already stored and pushed to the carrier.

Dashboard

On the Numbers page every number has a Forwarding button that opens a dedicated editor page — the same two rules with examples, no JSON required, plus Re-sync with carrier. (For a number still porting in the page explains that forwarding unlocks at activation.)

If a number's rules are correct here but calls still do not follow them after a re-sync, contact support: the registry may list the wrong hosting carrier for the number, which our operations team can correct — the number.provider_changed event tells you when that happens, and your rules are re-applied to the right carrier.