# Record

The `<Record>` verb records the caller's voice and, once recording ends, sends the result to your `action` URL.

## Attributes

| Name | Type | Default | Description |
|------|------|---------|-------------|
| `action` | string (uri) | — | URL to send the recording result to. When absent, execution continues with the next verb in the document once recording ends. |
| `method` | string | `POST` | HTTP method used to request `action`. One of `GET` or `POST`. |
| `timeout` | integer | `5` | Seconds of silence that automatically end the recording. Set to `0` to disable the silence timeout. |
| `finishOnKey` | string | `#` | DTMF key that ends the recording when pressed. A single character: any digit, `*`, or `#`. Set to an empty string to disable ending on any key. |
| `maxLength` | integer | `3600` | Maximum recording duration, in seconds. |
| `playBeep` | boolean | `true` | Whether to play a beep tone before recording starts. |
| `recordingStatusCallback` | string (uri) | — | URL to notify when the recording finishes. PressOne requests this URL once, for the `completed` event only: it does not send an `in-progress` event. |
| `recordingStatusCallbackMethod` | string | `POST` | HTTP method used to request `recordingStatusCallback`. One of `GET` or `POST`. |

> **Warning:** Recording URLs returned in `RecordingUrl` are publicly readable: anyone who has the URL can play or download the recording, with no authentication required. Treat recording URLs as sensitive data, and avoid exposing them in client-side code or logs you do not control.

## Nesting

`<Record>` is a self-closing element with no text content and no nested verbs.

## Action parameters

| Name | Type | Description |
|------|------|-------------|
| `RecordingSid` | string | Unique identifier for the recording. |
| `RecordingUrl` | string (uri) | Public URL of the recorded audio file. |
| `RecordingDuration` | integer | Duration of the recording, in seconds. |

### `recordingStatusCallback` parameters

When `recordingStatusCallback` is set, PressOne posts the following parameters to it once the recording completes:

| Name | Type | Description |
|------|------|-------------|
| `AccountSid` | string | Your account SID. |
| `CallSid` | string | The unique identifier of the call. |
| `RecordingSid` | string | Unique identifier for the recording. |
| `RecordingUrl` | string (uri) | Public URL of the recorded audio file. |
| `RecordingStatus` | string | Always `completed`. |
| `RecordingDuration` | integer | Duration of the recording, in seconds. |
| `RecordingChannels` | integer | Always `1`. |
| `RecordingSource` | string | Always `RecordVerb`. |

## Examples

A voicemail prompt that records a message and requests `action` when done:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Please leave a message after the beep. Press pound when finished.</Say>
  <Record action="/voicemail-complete" maxLength="120" finishOnKey="#"/>
  <Say>We did not receive a recording. Goodbye.</Say>
  <Hangup/>
</Response>
```

The `/voicemail-complete` handler, complete and runnable:

<CodeTabs syncKey="lang">

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

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

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

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

app.listen(3000);
```

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

app = Flask(__name__)

@app.route("/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>

A silent log recording of the whole call, using `recordingStatusCallback` instead of `action`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>This call may be recorded for quality purposes.</Say>
  <Record playBeep="false" timeout="0" maxLength="3600" recordingStatusCallback="/recording-status"/>
  <Hangup/>
</Response>
```

The `/recording-status` receiver, complete and runnable:

<CodeTabs syncKey="lang">

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

app.post("/recording-status", (req, res) => {
  console.log("Recording completed:", {
    recordingSid: req.body.RecordingSid,
    recordingUrl: req.body.RecordingUrl,
    durationSeconds: req.body.RecordingDuration,
  });
  res.sendStatus(200);
});

app.listen(3000);
```

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

app = Flask(__name__)

@app.route("/recording-status", methods=["POST"])
def recording_status():
    print("Recording completed:", {
        "recordingSid": request.form.get("RecordingSid"),
        "recordingUrl": request.form.get("RecordingUrl"),
        "durationSeconds": request.form.get("RecordingDuration"),
    })
    return "", 200

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

</CodeTabs>

## See also

- [Say](/voice/oneml/say)
- [Gather](/voice/oneml/gather)
- [OneML overview](/voice/oneml/overview)
