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

# DTMF Confirmation Verification

> Verify phone number ownership with a single keypress. No codes - press 1 to confirm.

DTMF confirmation calls a phone number, plays a TTS prompt, and collects a single keypress (`1`) to confirm ownership. No verification code is generated — the keypress is the confirmation.

The `POST /verifications/{id}/actions/verify` endpoint is **not used**. Verification completes on the call itself.

<Tip>
  DTMF confirmation is unique to Telnyx — Twilio and Vonage Verify APIs only support code-based voice verification (read a code, then type it). Single-keypress confirmation reduces user friction and works on landlines.
</Tip>

## Flow

```mermaid theme={null}
sequenceDiagram
    participant App
    participant Telnyx as Verify API
    participant Phone
    
    App->>Telnyx: POST /verifications/dtmf_confirm
    Telnyx->>Phone: Outbound call
    Phone-->>Telnyx: Answered
    Telnyx->>Phone: TTS prompt + DTMF gather
    Phone-->>Telnyx: Digit "1"
    Telnyx-->>App: Webhook: verification.complete (accepted)
```

| Outcome   | Trigger           | Status     |
| --------- | ----------------- | ---------- |
| Confirmed | Digit `1` pressed | `accepted` |
| Rejected  | Wrong digit       | `invalid`  |
| Timed out | No keypress (10s) | `expired`  |
| Failed    | Call not answered | `error`    |

<Note>
  Up to 3 attempts per call. After 3 wrong digits, the call ends with status `invalid`.
</Note>

***

## Use cases

<CardGroup cols={2}>
  <Card title="Caller ID verification" icon="phone-arrow-up-right">
    Confirm ownership before allowing a number as outbound Caller ID.
  </Card>

  <Card title="Landline verification" icon="phone-office">
    Verify numbers that cannot receive SMS.
  </Card>

  <Card title="Accessibility" icon="universal-access">
    Single keypress instead of reading and typing a code.
  </Card>

  <Card title="Account recovery" icon="key">
    Confirm phone ownership without code entry.
  </Card>
</CardGroup>

***

## Create a verify profile

Create a profile with `dtmf_confirm` settings. This can be combined with other verification types (SMS, call) on the same profile.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.telnyx.com/v2/verify_profiles \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $TELNYX_API_KEY" \
    -d '{
      "name": "caller-id-verification",
      "language": "en-US",
      "dtmf_confirm": {
        "default_timeout_secs": 300
      }
    }'
  ```

  ```python Python theme={null}
  import os
  import telnyx

  telnyx.api_key = os.environ["TELNYX_API_KEY"]

  profile = telnyx.VerifyProfile.create(
      name="caller-id-verification",
      language="en-US",
      dtmf_confirm={"default_timeout_secs": 300},
  )
  profile_id = profile.id
  ```

  ```javascript Node theme={null}
  const telnyx = require('telnyx')(process.env.TELNYX_API_KEY);

  const profile = await telnyx.verifyProfiles.create({
    name: 'caller-id-verification',
    language: 'en-US',
    dtmf_confirm: { default_timeout_secs: 300 },
  });
  const profileId = profile.data.id;
  ```

  ```ruby Ruby theme={null}
  require 'telnyx'

  Telnyx.api_key = ENV['TELNYX_API_KEY']

  profile = Telnyx::VerifyProfile.create(
    name: 'caller-id-verification',
    language: 'en-US',
    dtmf_confirm: { default_timeout_secs: 300 }
  )
  profile_id = profile.id
  ```

  ```go Go theme={null}
  package main

  import (
      "os"
      telnyx "github.com/telnyx/telnyx-go"
  )

  func main() {
      client := telnyx.NewClient(os.Getenv("TELNYX_API_KEY"))

      profile, _ := client.VerifyProfiles.Create(&telnyx.VerifyProfileParams{
          Name:     "caller-id-verification",
          Language: "en-US",
          DTMFConfirm: &telnyx.DTMFConfirmSettings{
              DefaultTimeoutSecs: 300,
          },
      })
      profileID := profile.ID
  }
  ```

  ```java Java theme={null}
  import com.telnyx.sdk.*;
  import com.telnyx.sdk.api.VerifyApi;
  import com.telnyx.sdk.model.*;

  ApiClient client = Configuration.getDefaultApiClient();
  client.setBearerToken(System.getenv("TELNYX_API_KEY"));

  VerifyApi api = new VerifyApi(client);
  CreateVerifyProfileRequest request = new CreateVerifyProfileRequest()
      .name("caller-id-verification")
      .language("en-US")
      .dtmfConfirm(new DTMFConfirmSettings().defaultTimeoutSecs(300));

  VerifyProfileResponse profile = api.createVerifyProfile(request);
  String profileId = profile.getData().getId();
  ```

  ```csharp .NET theme={null}
  using Telnyx;

  TelnyxConfiguration.SetApiKey(Environment.GetEnvironmentVariable("TELNYX_API_KEY"));

  var service = new VerifyProfileService();
  var profile = service.Create(new VerifyProfileCreateOptions
  {
      Name = "caller-id-verification",
      Language = "en-US",
      DtmfConfirm = new DtmfConfirmSettings
      {
          DefaultTimeoutSecs = 300
      }
  });
  var profileId = profile.Id;
  ```

  ```php PHP theme={null}
  require 'vendor/autoload.php';

  \Telnyx\Telnyx::setApiKey(getenv('TELNYX_API_KEY'));

  $profile = \Telnyx\VerifyProfile::create([
      'name' => 'caller-id-verification',
      'language' => 'en-US',
      'dtmf_confirm' => ['default_timeout_secs' => 300],
  ]);
  $profileId = $profile->id;
  ```
</CodeGroup>

The returned `id` is required for verification requests.

***

## Trigger verification

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.telnyx.com/v2/verifications/dtmf_confirm \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $TELNYX_API_KEY" \
    -d '{
      "phone_number": "+13035551234",
      "verify_profile_id": "YOUR_PROFILE_ID"
    }'
  ```

  ```python Python theme={null}
  verification = telnyx.Verification.create(
      phone_number="+13035551234",
      verify_profile_id=profile_id,
      type="dtmf_confirm",
  )
  ```

  ```javascript Node theme={null}
  const verification = await telnyx.verifications.create({
    phone_number: '+13035551234',
    verify_profile_id: profileId,
    type: 'dtmf_confirm',
  });
  ```

  ```ruby Ruby theme={null}
  verification = Telnyx::Verification.create(
    phone_number: '+13035551234',
    verify_profile_id: profile_id,
    type: 'dtmf_confirm'
  )
  ```

  ```go Go theme={null}
  verification, _ := client.Verifications.Create(&telnyx.VerificationParams{
      PhoneNumber:     "+13035551234",
      VerifyProfileID: profileID,
      Type:            "dtmf_confirm",
  })
  ```

  ```java Java theme={null}
  CreateVerificationRequest verReq = new CreateVerificationRequest()
      .phoneNumber("+13035551234")
      .verifyProfileId(profileId)
      .type(CreateVerificationRequest.TypeEnum.DTMF_CONFIRM);

  VerificationResponse ver = api.createVerification(verReq);
  ```

  ```csharp .NET theme={null}
  var verService = new VerificationService();
  var verification = verService.Create(new VerificationCreateOptions
  {
      PhoneNumber = "+13035551234",
      VerifyProfileId = profileId,
      Type = "dtmf_confirm"
  });
  ```

  ```php PHP theme={null}
  $verification = \Telnyx\Verification::create([
      'phone_number' => '+13035551234',
      'verify_profile_id' => $profileId,
      'type' => 'dtmf_confirm',
  ]);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "phone_number": "+13035551234",
    "record_type": "verification",
    "status": "pending",
    "type": "dtmf_confirm",
    "timeout_secs": 300,
    "verify_profile_id": "YOUR_PROFILE_ID",
    "created_at": "2026-02-20T15:30:00.000000",
    "updated_at": "2026-02-20T15:30:00.000000"
  }
}
```

Default TTS prompt:

> *"This is a verification call to confirm that this phone number is going to be used as a Caller ID for outbound calls. If you did not request this verification, or if someone is asking you to accept this call, please ignore this message. If you did request this verification, please press 1."*

The TTS language is determined by the `language` field on the Verify Profile (default: `en-US`).

***

## Handle the result

Verification completes on the call — no verify endpoint call needed. Receive the outcome via [webhooks](/docs/identity/verify/receiving-webhooks).

### Accepted (digit `1` pressed)

```json theme={null}
{
  "data": {
    "event_type": "verification.complete",
    "payload": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "phone_number": "+13035551234",
      "status": "accepted",
      "type": "dtmf_confirm",
      "verify_profile_id": "YOUR_PROFILE_ID"
    }
  }
}
```

### Failed (wrong digit, timeout, or call failure)

```json theme={null}
{
  "data": {
    "event_type": "verification.complete",
    "payload": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "phone_number": "+13035551234",
      "status": "invalid",
      "type": "dtmf_confirm",
      "verify_profile_id": "YOUR_PROFILE_ID"
    }
  }
}
```

### Polling alternative

```bash theme={null}
curl https://api.telnyx.com/v2/verifications/{verification_id} \
  -H "Authorization: Bearer $TELNYX_API_KEY"
```

***

## Complete example

Full flow: create profile, trigger verification, handle webhook.

<Tabs>
  <Tab title="Python (Flask)">
    ```python theme={null}
    import os
    import telnyx
    from flask import Flask, request, jsonify

    telnyx.api_key = os.environ["TELNYX_API_KEY"]
    app = Flask(__name__)

    # Create profile (once)
    profile = telnyx.VerifyProfile.create(
        name="dtmf-verification",
        language="en-US",
        dtmf_confirm={"default_timeout_secs": 300},
    )

    # Trigger verification
    verification = telnyx.Verification.create(
        phone_number="+13035551234",
        verify_profile_id=profile.id,
        type="dtmf_confirm",
    )
    print(f"Verification {verification.id} — {verification.status}")


    @app.route("/webhooks/verify", methods=["POST"])
    def handle_webhook():
        payload = request.json["data"]["payload"]

        if payload["status"] == "accepted":
            print(f"✅ {payload['phone_number']} verified")
        else:
            print(f"❌ {payload['phone_number']} failed: {payload['status']}")

        return jsonify({"status": "ok"}), 200


    app.run(port=5000)
    ```
  </Tab>

  <Tab title="Node (Express)">
    ```javascript theme={null}
    const express = require('express');
    const telnyx = require('telnyx')(process.env.TELNYX_API_KEY);

    const app = express();
    app.use(express.json());

    (async () => {
      // Create profile (once)
      const profile = await telnyx.verifyProfiles.create({
        name: 'dtmf-verification',
        language: 'en-US',
        dtmf_confirm: { default_timeout_secs: 300 },
      });

      // Trigger verification
      const verification = await telnyx.verifications.create({
        phone_number: '+13035551234',
        verify_profile_id: profile.data.id,
        type: 'dtmf_confirm',
      });
      console.log(`Verification ${verification.data.id} — ${verification.data.status}`);

      // Handle webhook
      app.post('/webhooks/verify', (req, res) => {
        const { phone_number, status } = req.body.data.payload;

        if (status === 'accepted') {
          console.log(`✅ ${phone_number} verified`);
        } else {
          console.log(`❌ ${phone_number} failed: ${status}`);
        }

        res.json({ status: 'ok' });
      });

      app.listen(5000, () => console.log('Webhook server on :5000'));
    })();
    ```
  </Tab>

  <Tab title="Ruby (Sinatra)">
    ```ruby theme={null}
    require 'telnyx'
    require 'sinatra'
    require 'json'

    Telnyx.api_key = ENV['TELNYX_API_KEY']

    # Create profile (once)
    profile = Telnyx::VerifyProfile.create(
      name: 'dtmf-verification',
      language: 'en-US',
      dtmf_confirm: { default_timeout_secs: 300 }
    )

    # Trigger verification
    verification = Telnyx::Verification.create(
      phone_number: '+13035551234',
      verify_profile_id: profile.id,
      type: 'dtmf_confirm'
    )
    puts "Verification #{verification.id} — #{verification.status}"

    # Handle webhook
    post '/webhooks/verify' do
      payload = JSON.parse(request.body.read)['data']['payload']

      if payload['status'] == 'accepted'
        puts "✅ #{payload['phone_number']} verified"
      else
        puts "❌ #{payload['phone_number']} failed: #{payload['status']}"
      end

      { status: 'ok' }.to_json
    end
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "encoding/json"
        "fmt"
        "net/http"
        "os"

        telnyx "github.com/telnyx/telnyx-go"
    )

    func main() {
        client := telnyx.NewClient(os.Getenv("TELNYX_API_KEY"))

        // Create profile (once)
        profile, _ := client.VerifyProfiles.Create(&telnyx.VerifyProfileParams{
            Name:     "dtmf-verification",
            Language: "en-US",
            DTMFConfirm: &telnyx.DTMFConfirmSettings{
                DefaultTimeoutSecs: 300,
            },
        })

        // Trigger verification
        ver, _ := client.Verifications.Create(&telnyx.VerificationParams{
            PhoneNumber:     "+13035551234",
            VerifyProfileID: profile.ID,
            Type:            "dtmf_confirm",
        })
        fmt.Printf("Verification %s — %s\n", ver.ID, ver.Status)

        // Handle webhook
        http.HandleFunc("/webhooks/verify", func(w http.ResponseWriter, r *http.Request) {
            var event struct {
                Data struct {
                    Payload struct {
                        PhoneNumber string `json:"phone_number"`
                        Status      string `json:"status"`
                    } `json:"payload"`
                } `json:"data"`
            }
            json.NewDecoder(r.Body).Decode(&event)

            p := event.Data.Payload
            if p.Status == "accepted" {
                fmt.Printf("✅ %s verified\n", p.PhoneNumber)
            } else {
                fmt.Printf("❌ %s failed: %s\n", p.PhoneNumber, p.Status)
            }
            w.Write([]byte(`{"status":"ok"}`))
        })

        http.ListenAndServe(":5000", nil)
    }
    ```
  </Tab>
</Tabs>

***

## Verification type comparison

| Feature                | SMS                    | Call               | Flash Call      | DTMF Confirm |
| ---------------------- | ---------------------- | ------------------ | --------------- | ------------ |
| **User action**        | Type code              | Listen + type code | None            | Press 1      |
| **Code generated**     | Yes                    | Yes                | Yes (caller ID) | No           |
| **Verify endpoint**    | Required               | Required           | Required        | Not needed   |
| **Landline support**   | No                     | Yes                | No              | Yes          |
| **Fraud risk**         | SIM swap, interception | Low                | Low             | Low          |
| **Competitor support** | Twilio, Vonage         | Twilio, Vonage     | Twilio          | Telnyx only  |

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Call reaches voicemail">
    Verification times out with status `expired`. Implement a retry with delay, or fall back to SMS.
  </Accordion>

  <Accordion title="Wrong digit pressed">
    Up to 3 attempts per call. After 3 failures, status is `invalid`. Trigger a new verification to retry.
  </Accordion>

  <Accordion title="Stuck in pending">
    Call was not answered and no webhook received. Verify the webhook URL is configured and reachable. Poll the status endpoint as fallback.
  </Accordion>

  <Accordion title="Custom TTS prompt">
    The prompt is fixed to the standard verification message. Voice and language are determined by the Verify Profile's `language` setting. Custom prompt text is not yet supported.
  </Accordion>

  <Accordion title="Rate limits">
    Standard Verify API rate limits apply. Avoid triggering multiple concurrent verifications for the same phone number — the previous call must complete or time out first.
  </Accordion>
</AccordionGroup>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Receiving Webhooks" icon="webhook" href="/docs/identity/verify/receiving-webhooks">
    Real-time verification status updates.
  </Card>

  <Card title="Custom Templates" icon="palette" href="/docs/identity/verify/custom-templates">
    Branded verification messages for SMS and call types.
  </Card>

  <Card title="Verify API Reference" icon="code" href="/api-reference/verify/create-a-verification">
    Full API specification.
  </Card>

  <Card title="Verify Quickstart" icon="rocket" href="/docs/identity/verify/quickstart">
    SMS and call verification guide.
  </Card>
</CardGroup>
