# Forwarding calls

This guide covers connecting a caller to another phone number with the `<Dial>` verb: choosing a caller ID, reacting to how the dial ends, and ringing more than one number at once or in order.

## Forward to another number

`<Dial>` bridges the current call to a phone number, given either as text content or as one or more `<Number>` children. The following server answers an inbound call and forwards it:

<CodeTabs syncKey="lang">

```javascript title="Node.js"
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: false }));

app.post("/voice/incoming", (req, res) => {
  const oneml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Connecting your call.</Say>
  <Dial callerId="+2342013300100">+2348051234567</Dial>
</Response>`;

  res.type("text/xml").send(oneml);
});

app.listen(3000);
```

```python title="Python"
from flask import Flask, Response

app = Flask(__name__)

@app.route("/voice/incoming", methods=["POST"])
def voice_incoming():
    oneml = """<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Connecting your call.</Say>
  <Dial callerId="+2342013300100">+2348051234567</Dial>
</Response>"""

    return Response(oneml, mimetype="text/xml")

if __name__ == "__main__":
    app.run(port=3000)
```

</CodeTabs>

With no `action` set, the call ends once the dialed party hangs up, because there are no more verbs to run. The next sections cover the caller ID and how to react to the outcome.

## Choose a caller ID

Set `callerId` to the number the dialed party sees. It must be a number your Signals project owns. If it is not, the dial fails, with `DialCallStatus` set to `failed` on the `action` request.

When you omit `callerId`, Signals uses `To` for inbound calls and `From` for calls created through the Calls API. The selected number must be eligible and owned by your project. Set `callerId` when you need to present a different owned number. An unowned or ineligible number makes the dial fail.

Dialed numbers go out through your project's outbound policy and limits, the same as calls from your SIP trunks. See [Outbound policies](/sip-trunks/outbound-policies).

## Handle the outcome with action and DialCallStatus

Set `action` to have Signals request your server once the dialed call ends. The request carries `DialCallStatus`, one of `completed`, `busy`, `no-answer`, `canceled` or `failed`. A common pattern falls back to voicemail when the call does not complete:

<CodeTabs syncKey="lang">

```javascript title="Node.js"
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: false }));

app.post("/voice/incoming", (req, res) => {
  const oneml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial action="/voice/dial-complete" callerId="+2342013300100" timeout="20">
    <Number>+2348051234567</Number>
  </Dial>
</Response>`;

  res.type("text/xml").send(oneml);
});

app.post("/voice/dial-complete", (req, res) => {
  const status = req.body.DialCallStatus;

  if (status === "failed") {
    console.warn("Dial failed for call", req.body.CallSid);
  }

  const oneml =
    status === "completed"
      ? `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Hangup/>
</Response>`
      : `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Our team is unavailable right now. Leave a message after the beep.</Say>
  <Record action="/voice/voicemail-complete" maxLength="120" finishOnKey="#"/>
  <Hangup/>
</Response>`;

  res.type("text/xml").send(oneml);
});

app.post("/voice/voicemail-complete", (req, res) => {
  console.log("Voicemail received:", {
    recordingSid: req.body.RecordingSid,
    recordingUrl: req.body.RecordingUrl,
    durationSeconds: req.body.RecordingDuration,
  });

  res.type("text/xml").send(`<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Thank you. Your message has been recorded. Goodbye.</Say>
  <Hangup/>
</Response>`);
});

app.listen(3000);
```

```python title="Python"
from flask import Flask, Response, request

app = Flask(__name__)

@app.route("/voice/incoming", methods=["POST"])
def voice_incoming():
    oneml = """<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial action="/voice/dial-complete" callerId="+2342013300100" timeout="20">
    <Number>+2348051234567</Number>
  </Dial>
</Response>"""

    return Response(oneml, mimetype="text/xml")

@app.route("/voice/dial-complete", methods=["POST"])
def dial_complete():
    status = request.form.get("DialCallStatus")

    if status == "failed":
        app.logger.warning("Dial failed for call %s", request.form.get("CallSid"))

    if status == "completed":
        oneml = """<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Hangup/>
</Response>"""
    else:
        oneml = """<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Our team is unavailable right now. Leave a message after the beep.</Say>
  <Record action="/voice/voicemail-complete" maxLength="120" finishOnKey="#"/>
  <Hangup/>
</Response>"""

    return Response(oneml, mimetype="text/xml")

@app.route("/voice/voicemail-complete", methods=["POST"])
def voicemail_complete():
    print("Voicemail received:", {
        "recordingSid": request.form.get("RecordingSid"),
        "recordingUrl": request.form.get("RecordingUrl"),
        "durationSeconds": request.form.get("RecordingDuration"),
    })

    oneml = """<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Thank you. Your message has been recorded. Goodbye.</Say>
  <Hangup/>
</Response>"""

    return Response(oneml, mimetype="text/xml")

if __name__ == "__main__":
    app.run(port=3000)
```

</CodeTabs>

Every outcome other than `completed` falls back to voicemail. The handler also logs `failed` separately, because a failed dial often means a configuration problem, such as a caller ID your project does not own or a destination your outbound policy blocks, rather than a caller who did not answer. See [Recording calls](/programmable-voice/recording-calls) for handling `RecordingSid`, `RecordingUrl` and `RecordingDuration`.

## Control when the caller's leg answers

`answerOnBridge` controls what the caller hears while Signals places the dialed call. It defaults to `false`, which answers the caller's leg before dialing starts, so the caller usually hears silence until the call connects. Set it to `true` to leave the caller's leg unanswered until the dialed party picks up, so the caller hears ringing instead:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial callerId="+2342013300100" answerOnBridge="true">+2348051234567</Dial>
</Response>
```

## Ring multiple numbers in parallel or in order

`<Dial>` accepts more than one `<Number>` child. By default, `sequential` is `false`, so Signals rings every number at once and bridges the caller to whichever answers first:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial callerId="+2342013300100" action="/voice/dial-complete">
    <Number>+2348051234567</Number>
    <Number>+2348051234568</Number>
    <Number>+2348051234569</Number>
  </Dial>
</Response>
```

Set `sequential` to `true` to dial each number in turn, moving to the next only if the previous one does not complete. Use it for an ordered escalation, such as a primary agent followed by a backup:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial callerId="+2342013300100" action="/voice/dial-complete" sequential="true">
    <Number>+2348051234567</Number>
    <Number>+2348051234568</Number>
  </Dial>
</Response>
```

In both cases, `action` receives one `DialCallStatus` for the whole attempt: `completed` once any number answers and the call ends. If none of the numbers complete, Signals reports a single failure status. For sequential dialing this is always `no-answer`, whatever the reason the last number failed.

## Next steps

- [Dial](/programmable-voice/oneml/dial): every `<Dial>` and `<Number>` attribute.
- [Recording calls](/programmable-voice/recording-calls): record a voicemail or a whole call.
- [Moving from Twilio](/programmable-voice/twilio-compatibility): `<Dial>` targets that are not available.
- [Calls API reference](/api/voice-calls): every Calls API endpoint.
