# Verify Passkeys quickstart

> \[!IMPORTANT]
>
> This document could change. Twilio might add or update features before the product becomes Generally Available. Beta products don't have a Service Level Agreement (SLA). To learn more about Twilio beta support, see [beta product support][beta-support].

## Twilio Verify support for Passkeys

The Verify API lets you add Passkeys into your existing authentication flows. The Verify API supports Passkey registration, public key storage, and authentication flows.

Verify Passkeys offers client-side supported SDKs for [iOS and Android][passkey-sdks]. This adds a low-friction, secure, cost-effective, device approval factor into your own mobile application.

Twilio belongs to the [Fast IDentity Online (FIDO) alliance][fido] that created the Passkeys standard.

## Quickstart guide: Verify Passkeys API

This tutorial covers Verify Passkeys service configuration and biometric Passkeys registration and authentication. This includes the following tasks:

* Create a Verify `Service` for passkeys.
* Generate a Passkey in your browser console.
* Register a Passkey `Factor` on the Twilio server.
* Save your Passkey to your password manager.
* Create an authentication challenge from the server.
* Sign the challenge with the Passkey and authenticate the signature of your user on the Twilio server.

To verify users at login, transaction, and other sensitive actions, follow this quickstart on Verify Passkeys.

> \[!NOTE]
>
> This quickstart alternates between server-side and client-side steps. To make the server-side requests, use the cURL command or an API client like [Postman][].
> As Passkeys requests bind to specific domains requests and require user interaction, you make your requests in your [browser's developer tools][dev-tools].

## Create Passkey-enabled Verify Service

A Passkey binds to an app or website domain. During Passkey creation and challenge verification, the underlying [`WebAuthn` API][webauthn] checks the domain in the browser URL.
WebAuthN requires passkeys use the Secure HTTP (HTTPS) protocol. If you try to create a passkey using the HTTP protocol, this resource returns an HTTP 400 error. Most browsers exclude `http://localhost` from this requirement.

To prevent issues with a production environment, create a Verify `Service` instance. Replace the Relying Party parameters with your application name and domains. If you like to update an existing service, check the [Service API](https://www.twilio.com/docs/verify/api/service) for more details.

```bash
POST /v2/Services
```

### Parameters

| Name                                       | Type           | Necessity | Description                                                                                                                                  |
| ------------------------------------------ | -------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `FriendlyName`                             | String         | Required  | Human-readable description of the Verify service. Maximum length: 255 characters.                                                            |
| `Passkeys`                                 | object         | Required  | Metadata for the Passkey.                                                                                                                    |
| `Passkeys`<br />`.RelyingParty.id`         | String         | Required  | Relying party identifier expressed as the origin domain without a scheme and port.                                                           |
| `Passkeys`<br />`.RelyingParty.name`       | String         | Required  | Description that the authenticator displays during the registration and authentication process.                                              |
| `Passkeys`<br />`.RelyingParty.origins`    | Array\[String] | Required  | List of Relying Party Server Origins or App IDs that your service accepts.                                                                   |
| `Passkeys`<br />`.AuthenticatorAttachment` | String         | Required  | Flag that indicates a requirement to attach only to a certain type of authenticator.<br /><br />Accepts: `platform`, `cross-platform`, `any` |
| `Passkeys`<br />`.DiscoverableCredentials` | String         | Required  | Flag that indicates the level of preference for discoverable credentials.<br /><br />Accepts: `required`, `preferred`, `discouraged`         |
| `Passkeys`<br />`.UserVerification`        | String         | Required  | Flag that indicates whether authentication requires user identity verification.<br /><br />Accepts: `required`, `preferred`, `discouraged`   |

### Procedure

1. Make a request to the `Services` resource. Pass all required parameters.

   ```bash {title="Create a new Passkey-enabled Verify Service request example"}
   curl -L 'https://verify.twilio.com/v2/Services' \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        -H 'Authorization: ••••••' \
        --data-urlencode 'FriendlyName=Fancy Service' \
        --data-urlencode 'Passkeys.RelyingParty.Id=example.com' \
        --data-urlencode 'Passkeys.RelyingParty.Name=My domain' \
        --data-urlencode 'Passkeys.RelyingParty.Origins=https://example.com,https://login.example.com' \
        --data-urlencode 'Passkeys.AuthenticatorAttachment=platform' \
        --data-urlencode 'Passkeys.DiscoverableCredentials=preferred' \
        --data-urlencode 'Passkeys.UserVerification=preferred'
   ```
2. If the response includes a `passkeys` JSON object that matches your request, you have succeeded in starting a Verify service.
   ```json {title="Create a new Passkey-enabled Verify Service response example"}
   {
     "sid": "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "friendly_name": "Fancy Service",
     ...
     "passkeys": {
       "relying_party": {
         "id": "example.com",
         "name": "My domain",
         "origins": [
           "https://example.com",
           "https://login.example.com"
         ]
       },
       "authenticator_attachment": "platform",
       "discoverable_credentials": "preferred",
       "user_verification": "preferred"
     },
     ...
   }
   ```

## Register a Passkey

To register a Passkey, complete three tasks:

1. On the Twilio server: Create a Passkey factor.
2. In your browser client: Register a Passkey with the factor details
3. On the Twilio server: Verify Passkey registration

### Add a Passkey factor with the Twilio API

A Passkey factor refers to the authentication component that the Passkey uses to verify your identity.
This could be something you know, like a personal identification number (PIN), or something you have, like a fingerprint.

To start registering a Passkey, call the Twilio `Factors` subresource with the following parameters.

```bash
POST /v2/Services/{service_sid}/Passkeys/Factors
```

#### Body Parameters

| Name                                      | Type   | Necessity | Description                                                                                                                                                                                                                                                                                                                          | PII           |
| ----------------------------------------- | ------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
| `friendly_name`                           | String | Required  | Human-readable description of the Factor. Maximum length: 255 characters.                                                                                                                                                                                                                                                            | [No][not-pii] |
| `identity`                                | String | Optional  | Unique external identifier for this Service. Your external system should generate an immutable, non-PII value between 8 and 64 characters in length. Acceptable values include your user's UUID, GUID, or SID. It can only contain hyphen (`-`) separated hexadecimal digits. If omitted in the request, the API creates this value. | [Yes][pii]    |
| `config`<br />`.authenticator_attachment` | String | Optional  | Flag that indicates a requirement to attach only to a certain type of authenticator.<br />Accepts: `platform`, `cross-platform`, `any`<br />Default: Per Service configuration                                                                                                                                                       | [No][not-pii] |
| `config`<br />`.discoverable_credentials` | String | Optional  | Flag that indicates the level of preference for discoverable credentials.<br />Accepts: `required`, `preferred`, `discouraged`<br />Default: Per Service configuration                                                                                                                                                               | [No][not-pii] |
| `config`<br />`.user_verification`        | String | Optional  | Flag that indicates whether user identity verification is required.<br />Accepts: `required`, `preferred`, `discouraged`<br />Default: Per Service configuration                                                                                                                                                                     | [No][not-pii] |

#### Procedure

1. Make `POST` request to the `Factors` subresource. Pass the `friendly_name` and `identity` keys and values as a JSON object payload.

   Create a new Passkey factor

   ```js
   // Download the helper library from https://www.twilio.com/docs/node/install
   const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   const accountSid = process.env.TWILIO_ACCOUNT_SID;
   const authToken = process.env.TWILIO_AUTH_TOKEN;
   const client = twilio(accountSid, authToken);

   async function createNewFactorPasskey() {
     const newFactor = await client.verify.v2
       .services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       .newFactors.create({
         friendly_name: "friendly_name",
         identity: "identity",
       });

     console.log(newFactor.binding);
   }

   createNewFactorPasskey();
   ```

   ```python
   # Download the helper library from https://www.twilio.com/docs/python/install
   import os
   from twilio.rest import Client
   from twilio.rest.verify.v2.service import NewFactorsList

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = os.environ["TWILIO_ACCOUNT_SID"]
   auth_token = os.environ["TWILIO_AUTH_TOKEN"]
   client = Client(account_sid, auth_token)

   new_factor = client.verify.v2.services(
       "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   ).new_factors.create(
       create_new_passkeys_factor_request=NewFactorsList.CreateNewPasskeysFactorRequest(
           {"friendly_name": "friendly_name", "identity": "identity"}
       )
   )

   print(new_factor.binding)
   ```

   ```csharp
   // Install the C# / .NET helper library from twilio.com/docs/csharp/install

   using System;
   using Twilio;
   using Twilio.Rest.Verify.V2.Service;
   using System.Threading.Tasks;
   using System.Collections.Generic;

   class Program {
       public static async Task Main(string[] args) {
           // Find your Account SID and Auth Token at twilio.com/console
           // and set the environment variables. See http://twil.io/secure
           string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
           string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");

           TwilioClient.Init(accountSid, authToken);

           var newFactor = await NewFactorResource.CreateAsync(
               createNewPasskeysFactorRequest: new NewFactorResource.CreateNewPasskeysFactorRequest
                   .Builder()
                   .WithFriendlyName("friendly_name")
                   .WithIdentity("identity")
                   .Build(),
               pathServiceSid: "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

           Console.WriteLine(newFactor.Binding);
       }
   }
   ```

   ```java
   // Install the Java helper library from twilio.com/docs/java/install

   import java.util.HashMap;
   import com.twilio.Twilio;
   import com.twilio.rest.verify.v2.service.NewFactor;

   public class Example {
       // Find your Account SID and Auth Token at twilio.com/console
       // and set the environment variables. See http://twil.io/secure
       public static final String ACCOUNT_SID = System.getenv("TWILIO_ACCOUNT_SID");
       public static final String AUTH_TOKEN = System.getenv("TWILIO_AUTH_TOKEN");

       public static void main(String[] args) {
           Twilio.init(ACCOUNT_SID, AUTH_TOKEN);

           NewFactor.CreateNewPasskeysFactorRequest createNewPasskeysFactorRequest =
               new NewFactor.CreateNewPasskeysFactorRequest();
           createNewPasskeysFactorRequest.setFriendlyName("friendly_name");
           createNewPasskeysFactorRequest.setIdentity("identity");

           NewFactor newFactor =
               NewFactor.creator("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", createNewPasskeysFactorRequest).create();

           System.out.println(newFactor.getBinding());
       }
   }
   ```

   ```go
   // Download the helper library from https://www.twilio.com/docs/go/install
   package main

   import (
   	"fmt"
   	"github.com/twilio/twilio-go"
   	verify "github.com/twilio/twilio-go/rest/verify/v2"
   	"os"
   )

   func main() {
   	// Find your Account SID and Auth Token at twilio.com/console
   	// and set the environment variables. See http://twil.io/secure
   	// Make sure TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN exists in your environment
   	client := twilio.NewRestClient()

   	params := &verify.CreateNewFactorPasskeyParams{}
   	params.SetCreateNewPasskeysFactorRequest(verify.CreateNewPasskeysFactorRequest{
   		FriendlyName: "friendly_name",
   		Identity:     "identity",
   	})

   	resp, err := client.VerifyV2.CreateNewFactorPasskey("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   		params)
   	if err != nil {
   		fmt.Println(err.Error())
   		os.Exit(1)
   	} else {
   		if resp.Binding != nil {
   			fmt.Println(*resp.Binding)
   		} else {
   			fmt.Println(resp.Binding)
   		}
   	}
   }
   ```

   ```php
   <?php

   // Update the path below to your autoload.php,
   // see https://getcomposer.org/doc/01-basic-usage.md
   require_once "/path/to/vendor/autoload.php";

   use Twilio\Rest\Client;
   use Twilio\Rest\Verify\V2\Service\NewFactorPasskeyModels;

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   $sid = getenv("TWILIO_ACCOUNT_SID");
   $token = getenv("TWILIO_AUTH_TOKEN");
   $twilio = new Client($sid, $token);

   $new_factor = $twilio->verify->v2
       ->services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       ->newFactors->create(
           NewFactorPasskeyModels::createCreateNewPasskeysFactorRequest([
               "friendlyName" => "friendly_name",
               "identity" => "identity",
           ])
       );

   print $new_factor->binding;
   ```

   ```ruby
   # Download the helper library from https://www.twilio.com/docs/ruby/install
   require 'twilio-ruby'

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = ENV['TWILIO_ACCOUNT_SID']
   auth_token = ENV['TWILIO_AUTH_TOKEN']
   @client = Twilio::REST::Client.new(account_sid, auth_token)

   new_factor = @client
                .verify
                .v2
                .services('VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
                .new_factors
                .create(
                  create_new_passkeys_factor_request: {
                    'friendly_name' => 'friendly_name',
                    'identity' => 'identity'
                  }
                )

   puts new_factor.binding
   ```

   ```bash
   # This endpoint is not currently supported by the Twilio CLI. You can open an issue to request it on https://github.com/twilio/twilio-cli/issues
     # For an alternative low-code solution, check out https://www.twilio.com/docs/openapi/using-twilio-postman-collections
   ```

   ```bash
   CREATE_NEW_PASSKEYS_FACTOR_REQUEST_OBJ=$(cat << EOF
   {
     "friendly_name": "friendly_name",
     "identity": "identity"
   }
   EOF
   )
   curl -X POST "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Passkeys/Factors" \
   --json "$CREATE_NEW_PASSKEYS_FACTOR_REQUEST_OBJ" \
   -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
   ```

   ```json
   {
     "sid": "YFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "service_sid": "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "entity_sid": "YEaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "identity": "identity",
     "binding": null,
     "date_created": "2015-07-30T20:00:00Z",
     "date_updated": "2015-07-30T20:00:00Z",
     "friendly_name": "friendly_name",
     "status": "unverified",
     "factor_type": "passkeys",
     "config": {
       "relying_party": {
         "id": "example.com",
         "name": "Example",
         "origins": [
           "https://example.com"
         ]
       },
       "authenticator_attachment": "platform",
       "discoverable_credentials": "preferred",
       "user_verification": "preferred"
     },
     "options": {
       "publicKey": {
         "rp": {
           "id": "example.com",
           "name": "Example"
         },
         "user": {
           "id": "WUU0ZmQzYWFmNGU0NTMyNGQwZjNlMTM0NjA3YjIxOTEyYg",
           "name": "friendly_name",
           "displayName": "friendly_name"
         },
         "challenge": "WUYwNDhkMWE3ZWMzYTJhNjk3MDA1OWMyNzY2YmJjN2UwZg",
         "pubKeyCredParams": {
           "type": "public-key",
           "alg": -7
         },
         "timeout": 600000,
         "excludeCredentials": [],
         "authenticatorSelection": {
           "authenticatorAttachment": "platform",
           "requireResidentKey": false,
           "residentKey": "preferred",
           "userVerification": "preferred"
         },
         "attestation": "none"
       }
     },
     "metadata": null,
     "url": "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Entities/ff483d1ff591898a9942916050d2ca3f/Factors/YFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   }
   ```
2. Copy the entire response. This JSON object becomes the `option` variable value in the [Set the Passkey factor response as a variable example](#set-the-passkey-factor-response-as-a-variable).

### Create Passkey in your browser

To create a Passkey in your browser, pass the entire [Create a Passkey factor response example](#create-a-passkey-factor-response-example) into your browser console.

1. Open your browser.
2. Go to the domain returned in `config.relying_party.id` in the [Create a Passkey factor response example](#create-a-passkey-factor-response-example).
3. Open the browser's developer tools and open your browser console. Perform the remaining steps in the browser console.
4. Paste the following code into the console to load the `webauthn-json` library.

   ```javascript {title="Load the webauthn-json library"}
   const { create, get, parseCreationOptionsFromJSON, parseRequestOptionsFromJSON, supported } = await
   import('https://cdn.jsdelivr.net/npm/@github/webauthn-json/dist/esm/webauthn-json.browser-ponyfill.js')
   ```
5. Create a variable to store the entire [Create a Passkey factor response example](#create-a-passkey-factor-response-example), then paste the response as that variable's value.

   ```javascript {title="Set the Passkey factor response as a variable"}
   let { options } = '<paste the entire Create a Passkey factor response example>';
   ```
6. Copy the following code.

   ```javascript {title="Create a Passkey request example"}
   // converts challenge and user.id to ArrayBuffers
   let creationOptions = parseCreationOptionsFromJSON(options);
   // wrapper for navigator.credentials.create
   let credential = await create(creationOptions);
   ```
7. Paste the code into your browser console then execute it. This generates a Passkey credential.

   #### View an example of the generated credential object

   ```json
   {
     "type": "public-key",
     "id": "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
     "rawId": "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
     "authenticatorAttachment": "platform",
     "response": {
       "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiV1VZd05EWTRZamhoTUdaaE5ESTVaV1JtWVRCbU0yUXlZVGc0WlRGbVpUUTBaUSIsIm9yaWdpbiI6Imh0dHBzOi8vYWNtZWluYy5jb20iLCJjcm9zc09yaWdpbiI6ZmFsc2V9",
       "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVifJbhPhsmhJlDNKGNzOhi06KSq06G_wU4CDtG-hmfvFZVdAAAAALraVWanqkAfvZZFYZpVEg0AG4mAqaFWmN6JsXPLcA0jRGheCY1Zb274zoEBlaUBAgMmIAEhWCBUAW4JG2gpPOpEfGPZ1uxHSHLTcgJe6Od-l6wvbFKCMCJYILebHcppbJ8O_h_kV3mFeHi2k7eJjoQewuM8gkNgb--m",
       "transports": [
         "internal",
         "hybrid"
       ]
     }
   }
   ```
8. Follow the prompts in your browser to register a Passkey with the password manager of your choice.
9. Copy the JSON response body to your clipboard with the following command.

   ```javascript {title="Copy generated credentials to clipboard"}
   copy(credential);
   ```

### Verify the Passkey factor with the Twilio API

To finish registering the Passkey, pass the copied `credential` as a JSON object to the `VerifyFactor` subresource.

```bash
POST /v2/Services/{service_sid}/Passkeys/VerifyFactor
```

#### Body Parameters

| Name                                 | Type           | Necessity | Description                                                                                                                                                                                                                                                        | PII           |
| ------------------------------------ | -------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
| `id`                                 | String         | Required  | [Base64-encoded, URL-safe][rfc4648] representation of `keyId`.                                                                                                                                                                                                     | [No][not-pii] |
| `rawId`                              | String         | Required  | Globally unique identifier for this `PublicKeyCredential`.                                                                                                                                                                                                         | [No][not-pii] |
| `authenticatorAttachment`            | String         | Required  | Mechanism by which the `WebAuthn` implementation is attached to the authenticator at the time the associated [`create(parseCreationOptionsFromJSON())`] or [`get(parseRequestOptionsFromJSON())`] call completes.<br /><br />Accepts: `platform`, `cross-platform` | [No][not-pii] |
| `type`                               | String         | Required  | Credential types the API supports. These values version the `AuthenticatorAssertion` and `AuthenticatorAttestation` structures according to the type of the authenticator.<br /><br />Accepts: `public-key`                                                        | [No][not-pii] |
| `response`                           | object         | Required  | Result of a `WebAuthn` credential registration as specified in `AuthenticatorAttestationResponse`.                                                                                                                                                                 | [No][not-pii] |
| `response`<br />`.clientDataJSON`    | String         | Required  | JSON-compatible serialization of the data passed from the browser to the authenticator that generated this credential.                                                                                                                                             | [No][not-pii] |
| `response`<br />`.attestationObject` | String         | Required  | Authenticator data and an attestation statement for a new key pair that the authenticator generated.                                                                                                                                                               | [No][not-pii] |
| `response`<br />`.transports`        | Array\[String] | Optional  | Hints as to the methods the client could use to communicate with the relevant authenticator of the public key credential to retrieve.<br /><br />Accepts: `usb`, `nfc`, `ble`, `smart-card`, `internal`, `hybrid`                                                  | [No][not-pii] |

#### Procedure

1. Make a `POST` request to the `VerifyFactor` subresource. Pass the properties of the `credential` you copied from your browser console as a JSON object payload.

   Verify your credentials request example

   ```js
   // Download the helper library from https://www.twilio.com/docs/node/install
   const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   const accountSid = process.env.TWILIO_ACCOUNT_SID;
   const authToken = process.env.TWILIO_AUTH_TOKEN;
   const client = twilio(accountSid, authToken);

   async function updatePasskeysFactor() {
     const newVerifyFactor = await client.verify.v2
       .services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       .newVerifyFactors.update({
         id: "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
         response: {
           attestationObject: "attestationObject",
           clientDataJSON: "clientDataJSON",
           transports: ["usb"],
         },
       });

     console.log(newVerifyFactor.sid);
   }

   updatePasskeysFactor();
   ```

   ```python
   # Download the helper library from https://www.twilio.com/docs/python/install
   import os
   from twilio.rest import Client
   from twilio.rest.verify.v2.service import NewVerifyFactorsList

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = os.environ["TWILIO_ACCOUNT_SID"]
   auth_token = os.environ["TWILIO_AUTH_TOKEN"]
   client = Client(account_sid, auth_token)

   passkeys_factor = client.verify.v2.services(
       "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   ).new_verify_factors.update(
       verify_passkeys_factor_request=NewVerifyFactorsList.VerifyPasskeysFactorRequest(
           {
               "id": "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
               "response": NewVerifyFactorsList.VerifyPasskeysFactorRequestResponse(
                   {
                       "attestationObject": "attestationObject",
                       "clientDataJSON": "clientDataJSON",
                       "transports": ["usb"],
                   }
               ),
           }
       )
   )

   print(passkeys_factor.sid)
   ```

   ```csharp
   // Install the C# / .NET helper library from twilio.com/docs/csharp/install

   using System;
   using Twilio;
   using Twilio.Rest.Verify.V2.Service;
   using System.Threading.Tasks;
   using System.Collections.Generic;

   class Program {
       public static async Task Main(string[] args) {
           // Find your Account SID and Auth Token at twilio.com/console
           // and set the environment variables. See http://twil.io/secure
           string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
           string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");

           TwilioClient.Init(accountSid, authToken);

           var newVerifyFactor = await NewVerifyFactorResource.UpdateAsync(
               verifyPasskeysFactorRequest: new NewVerifyFactorResource.VerifyPasskeysFactorRequest
                   .Builder()
                   .WithId("iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV")
                   .WithResponse(
                       new NewVerifyFactorResource.VerifyPasskeysFactorRequestResponse.Builder()
                           .WithAttestationObject("attestationObject")
                           .WithClientDataJSON("clientDataJSON")
                           .WithTransports(new List<string> { "usb" })
                           .Build())
                   .Build(),
               pathServiceSid: "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

           Console.WriteLine(newVerifyFactor.Sid);
       }
   }
   ```

   ```java
   // Install the Java helper library from twilio.com/docs/java/install

   import java.util.Arrays;
   import java.util.HashMap;
   import com.twilio.Twilio;
   import com.twilio.rest.verify.v2.service.NewVerifyFactor;

   public class Example {
       // Find your Account SID and Auth Token at twilio.com/console
       // and set the environment variables. See http://twil.io/secure
       public static final String ACCOUNT_SID = System.getenv("TWILIO_ACCOUNT_SID");
       public static final String AUTH_TOKEN = System.getenv("TWILIO_AUTH_TOKEN");

       public static void main(String[] args) {
           Twilio.init(ACCOUNT_SID, AUTH_TOKEN);

           NewVerifyFactor.VerifyPasskeysFactorRequestResponse response =
               new NewVerifyFactor.VerifyPasskeysFactorRequestResponse();
           response.setAttestationObject("attestationObject");
           response.setClientDataJSON("clientDataJSON");
           response.setTransports(Arrays.asList("usb"));

           NewVerifyFactor.VerifyPasskeysFactorRequest verifyPasskeysFactorRequest =
               new NewVerifyFactor.VerifyPasskeysFactorRequest();
           verifyPasskeysFactorRequest.setId("iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV");
           verifyPasskeysFactorRequest.setResponse(verifyPasskeysFactorRequestResponse);

           NewVerifyFactor newVerifyFactor =
               NewVerifyFactor.updater("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", verifyPasskeysFactorRequest).update();

           System.out.println(newVerifyFactor.getSid());
       }
   }
   ```

   ```go
   // Download the helper library from https://www.twilio.com/docs/go/install
   package main

   import (
   	"fmt"
   	"github.com/twilio/twilio-go"
   	verify "github.com/twilio/twilio-go/rest/verify/v2"
   	"os"
   )

   func main() {
   	// Find your Account SID and Auth Token at twilio.com/console
   	// and set the environment variables. See http://twil.io/secure
   	// Make sure TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN exists in your environment
   	client := twilio.NewRestClient()

   	params := &verify.UpdatePasskeysFactorParams{}
   	params.SetVerifyPasskeysFactorRequest(verify.VerifyPasskeysFactorRequest{
   		Id: "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
   		Response: verify.VerifyPasskeysFactorRequestResponse{
   			AttestationObject: "attestationObject",
   			ClientDataJSON:    "clientDataJSON",
   			Transports: []string{
   				"usb",
   			},
   		},
   	})

   	resp, err := client.VerifyV2.UpdatePasskeysFactor("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   		params)
   	if err != nil {
   		fmt.Println(err.Error())
   		os.Exit(1)
   	} else {
   		if resp.Sid != nil {
   			fmt.Println(*resp.Sid)
   		} else {
   			fmt.Println(resp.Sid)
   		}
   	}
   }
   ```

   ```php
   <?php

   // Update the path below to your autoload.php,
   // see https://getcomposer.org/doc/01-basic-usage.md
   require_once "/path/to/vendor/autoload.php";

   use Twilio\Rest\Client;
   use Twilio\Rest\Verify\V2\Service\PasskeysFactorModels;

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   $sid = getenv("TWILIO_ACCOUNT_SID");
   $token = getenv("TWILIO_AUTH_TOKEN");
   $twilio = new Client($sid, $token);

   $passkeys_factor = $twilio->verify->v2
       ->services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       ->newVerifyFactors->update(
           PasskeysFactorModels::createVerifyPasskeysFactorRequest([
               "id" => "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
               "response" => PasskeysFactorModels::createVerifyPasskeysFactorRequestResponse(
                   [
                       "attestationObject" => "attestationObject",
                       "clientDataJSON" => "clientDataJSON",
                       "transports" => ["usb"],
                   ]
               ),
           ])
       );

   print $passkeys_factor->sid;
   ```

   ```ruby
   # Download the helper library from https://www.twilio.com/docs/ruby/install
   require 'twilio-ruby'

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = ENV['TWILIO_ACCOUNT_SID']
   auth_token = ENV['TWILIO_AUTH_TOKEN']
   @client = Twilio::REST::Client.new(account_sid, auth_token)

   new_verify_factor = @client
                       .verify
                       .v2
                       .services('VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
                       .new_verify_factors
                       .update(
                         verify_passkeys_factor_request: {
                           'id' => 'iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV',
                           'response' => {
                             'attestationObject' => 'attestationObject',
                             'clientDataJSON' => 'clientDataJSON',
                             'transports' => [
                               'usb'
                             ]
                           }
                         }
                       )

   puts new_verify_factor.sid
   ```

   ```bash
   # This endpoint is not currently supported by the Twilio CLI. You can open an issue to request it on https://github.com/twilio/twilio-cli/issues
     # For an alternative low-code solution, check out https://www.twilio.com/docs/openapi/using-twilio-postman-collections
   ```

   ```bash
   VERIFY_PASSKEYS_FACTOR_REQUEST_OBJ=$(cat << EOF
   {
     "id": "iYCpoVaY3omxc8twDSNEaF4JjVlvbvjOgQGV",
     "response": {
       "attestationObject": "attestationObject",
       "clientDataJSON": "clientDataJSON",
       "transports": [
         "usb"
       ]
     }
   }
   EOF
   )
   curl -X POST "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Passkeys/VerifyFactor" \
   --json "$VERIFY_PASSKEYS_FACTOR_REQUEST_OBJ" \
   -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
   ```

   ```json
   {
     "sid": "YFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "service_sid": "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "entity_sid": "YEaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "identity": "ff483d1ff591898a9942916050d2ca3f",
     "date_created": "2015-07-30T20:00:00Z",
     "date_updated": "2015-07-30T20:00:00Z",
     "friendly_name": "friendly_name",
     "status": "verified",
     "factor_type": "passkeys",
     "config": {
       "relying_party": {
         "id": "example.com",
         "name": "Example",
         "origins": [
           "https://example.com"
         ]
       },
       "authenticator_attachment": "platform",
       "discoverable_credentials": "preferred",
       "user_verification": "preferred"
     },
     "metadata": null,
     "url": "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Entities/ff483d1ff591898a9942916050d2ca3f/Factors/YFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   }
   ```
2. If the response returns `"status": "verified"`, you have succeeded in creating and verifying a Passkey.

## Authenticate with your Passkey

To authenticate with your Passkey, complete three tasks:

1. On the Twilio server: Create an authentication challenge.
2. In your browser client: Fetch the Passkey with the challenge data. The browser signs the challenge with the Passkey.
3. On the Twilio server: Validate the signature and approve the authentication challenge.

### Create a Passkey authentication challenge with the Twilio API

```bash
POST /v2/Services/{service_sid}/Passkeys/Challenges
```

#### Body Parameters

| Name       | Type   | Necessity | Description                                 | PII        |
| ---------- | ------ | --------- | ------------------------------------------- | ---------- |
| `identity` | String | Required  | Unique identifier for a person or end user. | [Yes][pii] |

#### Procedure

1. Make a `POST` request to the `Challenges` subresource. Use the same `identity` value from [creating the Passkey factor](#create-a-passkey-factor-request-example).

   Create a new Passkey challenge

   ```js
   // Download the helper library from https://www.twilio.com/docs/node/install
   const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   const accountSid = process.env.TWILIO_ACCOUNT_SID;
   const authToken = process.env.TWILIO_AUTH_TOKEN;
   const client = twilio(accountSid, authToken);

   async function createChallengePasskeys() {
     const newChallenge = await client.verify.v2
       .services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       .newChallenge()
       .create({
         identity: "identity",
       });

     console.log(newChallenge.options);
   }

   createChallengePasskeys();
   ```

   ```python
   # Download the helper library from https://www.twilio.com/docs/python/install
   import os
   from twilio.rest import Client
   from twilio.rest.verify.v2.service import NewChallengeList

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = os.environ["TWILIO_ACCOUNT_SID"]
   auth_token = os.environ["TWILIO_AUTH_TOKEN"]
   client = Client(account_sid, auth_token)

   new_challenge = (
       client.verify.v2.services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       .new_challenge()
       .create(
           create_passkeys_challenge_request=NewChallengeList.CreatePasskeysChallengeRequest(
               {"identity": "identity"}
           )
       )
   )

   print(new_challenge.options)
   ```

   ```csharp
   // Install the C# / .NET helper library from twilio.com/docs/csharp/install

   using System;
   using Twilio;
   using Twilio.Rest.Verify.V2.Service;
   using System.Threading.Tasks;
   using System.Collections.Generic;

   class Program {
       public static async Task Main(string[] args) {
           // Find your Account SID and Auth Token at twilio.com/console
           // and set the environment variables. See http://twil.io/secure
           string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
           string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");

           TwilioClient.Init(accountSid, authToken);

           var newChallenge = await NewChallengeResource.CreateAsync(
               createPasskeysChallengeRequest: new NewChallengeResource.CreatePasskeysChallengeRequest
                   .Builder()
                   .WithIdentity("identity")
                   .Build(),
               pathServiceSid: "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

           Console.WriteLine(newChallenge.Options);
       }
   }
   ```

   ```java
   // Install the Java helper library from twilio.com/docs/java/install

   import java.util.HashMap;
   import com.twilio.Twilio;
   import com.twilio.rest.verify.v2.service.NewChallenge;

   public class Example {
       // Find your Account SID and Auth Token at twilio.com/console
       // and set the environment variables. See http://twil.io/secure
       public static final String ACCOUNT_SID = System.getenv("TWILIO_ACCOUNT_SID");
       public static final String AUTH_TOKEN = System.getenv("TWILIO_AUTH_TOKEN");

       public static void main(String[] args) {
           Twilio.init(ACCOUNT_SID, AUTH_TOKEN);

           NewChallenge.CreatePasskeysChallengeRequest createPasskeysChallengeRequest =
               new NewChallenge.CreatePasskeysChallengeRequest();
           createPasskeysChallengeRequest.setIdentity("identity");

           NewChallenge newChallenge =
               NewChallenge.creator("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", createPasskeysChallengeRequest).create();

           System.out.println(newChallenge.getOptions());
       }
   }
   ```

   ```go
   // Download the helper library from https://www.twilio.com/docs/go/install
   package main

   import (
   	"fmt"
   	"github.com/twilio/twilio-go"
   	verify "github.com/twilio/twilio-go/rest/verify/v2"
   	"os"
   )

   func main() {
   	// Find your Account SID and Auth Token at twilio.com/console
   	// and set the environment variables. See http://twil.io/secure
   	// Make sure TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN exists in your environment
   	client := twilio.NewRestClient()

   	params := &verify.CreateChallengePasskeysParams{}
   	params.SetCreatePasskeysChallengeRequest(verify.CreatePasskeysChallengeRequest{
   		Identity: "identity",
   	})

   	resp, err := client.VerifyV2.CreateChallengePasskeys("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   		params)
   	if err != nil {
   		fmt.Println(err.Error())
   		os.Exit(1)
   	} else {
   		if resp.Options != nil {
   			fmt.Println(resp.Options)
   		} else {
   			fmt.Println(resp.Options)
   		}
   	}
   }
   ```

   ```php
   <?php

   // Update the path below to your autoload.php,
   // see https://getcomposer.org/doc/01-basic-usage.md
   require_once "/path/to/vendor/autoload.php";

   use Twilio\Rest\Client;
   use Twilio\Rest\Verify\V2\Service\ChallengePasskeysModels;

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   $sid = getenv("TWILIO_ACCOUNT_SID");
   $token = getenv("TWILIO_AUTH_TOKEN");
   $twilio = new Client($sid, $token);

   $new_challenge = $twilio->verify->v2
       ->services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       ->newChallenge()
       ->create(
           ChallengePasskeysModels::createCreatePasskeysChallengeRequest([
               "identity" => "identity",
           ])
       );

   print $new_challenge->options;
   ```

   ```ruby
   # Download the helper library from https://www.twilio.com/docs/ruby/install
   require 'twilio-ruby'

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = ENV['TWILIO_ACCOUNT_SID']
   auth_token = ENV['TWILIO_AUTH_TOKEN']
   @client = Twilio::REST::Client.new(account_sid, auth_token)

   new_challenge = @client
                   .verify
                   .v2
                   .services('VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
                   .new_challenge
                   .create(
                     create_passkeys_challenge_request: {
                       'identity' => 'identity'
                     }
                   )

   puts new_challenge.options
   ```

   ```bash
   # This endpoint is not currently supported by the Twilio CLI. You can open an issue to request it on https://github.com/twilio/twilio-cli/issues
     # For an alternative low-code solution, check out https://www.twilio.com/docs/openapi/using-twilio-postman-collections
   ```

   ```bash
   CREATE_PASSKEYS_CHALLENGE_REQUEST_OBJ=$(cat << EOF
   {
     "identity": "identity"
   }
   EOF
   )
   curl -X POST "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Passkeys/Challenges" \
   --json "$CREATE_PASSKEYS_CHALLENGE_REQUEST_OBJ" \
   -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
   ```

   ```json
   {
     "sid": "YCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "service_sid": "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "entity_sid": "",
     "identity": "identity",
     "factor_sid": "",
     "factor_type": "passkeys",
     "status": "pending",
     "date_created": "2025-07-30T20:00:00Z",
     "date_updated": "2025-07-30T20:00:00Z",
     "date_responded": null,
     "details": null,
     "expiration_date": null,
     "hidden_details": null,
     "links": null,
     "metadata": null,
     "responded_reason": "none",
     "url": "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Passkeys/Challenges",
     "options": {
       "publicKey": {
         "rp": {
           "id": "example.com",
           "name": "Example"
         },
         "user": {
           "id": "WUU0ZmQzYWFmNGU0NTMyNGQwZjNlMTM0NjA3YjIxOTEyYg",
           "name": "friendly_name",
           "displayName": "friendly_name"
         },
         "challenge": "WUYwNDhkMWE3ZWMzYTJhNjk3MDA1OWMyNzY2YmJjN2UwZg",
         "pubKeyCredParams": {
           "type": "public-key",
           "alg": -7
         },
         "timeout": 600000,
         "excludeCredentials": [],
         "authenticatorSelection": {
           "authenticatorAttachment": "platform",
           "requireResidentKey": false,
           "residentKey": "preferred",
           "userVerification": "preferred"
         },
         "attestation": "none"
       }
     }
   }
   ```
2. Copy the response for use in the [Fetch the Passkey from your browser section](#fetch-the-passkey-from-your-browser).

### Fetch the Passkey from your browser

To fetch the Passkey from your browser, pass the entire response from the [Create a Passkey authentication challenge response example](#create-a-passkey-authentication-challenge-response-example) into your browser console.

This requires using the same browser session. If you closed the browser console, open a new one, then [reload the `webauthn-json` helper functions](#load-the-webauthn-json-library).

#### Procedure

1. Open your browser.
2. Go to the domain found in `publicKey.rpid` in the [Create a Passkey authentication challenge response example](#create-a-passkey-authentication-challenge-response-example).
3. Open the browser's developer tools and open your browser console. Perform the remaining steps in the browser console.
4. Create a variable to store the entire [Create a Passkey authentication challenge response example](#create-a-passkey-authentication-challenge-response-example), then paste the response as that variable's value.

   ```javascript {title="Set the Passkey challenge response as a variable"}
   let { options } = '<paste the entire Create a Passkey authentication challenge response example>';
   ```
5. Copy the following code.

   ```javascript {title="Fetch the Passkey request example"}
   // converts challenge and user.id to ArrayBuffers
   let requestOptions = parseRequestOptionsFromJSON(options);
   // wrapper for navigator.credentials.create
   let credential = await get(requestOptions);
   ```
6. Paste the code into your browser console then execute it. This generates a credential that needs server-side validation.
7. Follow the prompts from your password manager.
8. Copy the JSON response body to your clipboard with the following command.

   ```javascript {title="Copy fetched credentials to clipboard"}
   copy(credential);
   ```

### Approve the Passkey challenge with the Twilio API

To finish authenticating your Passkey challenge, pass the response from the [Fetch the Passkey request example](#fetch-the-passkey-request-example) request as a JSON object to the `ApproveChallenge` subresource.

```bash
POST /v2/Services/{service_sid}/Passkeys/ApproveChallenge
```

#### Body Parameters

| Name                                 | Type   | Necessity | Description                                                                                                                                                                                                                                                       | PII           |
| ------------------------------------ | ------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `id`                                 | String | Required  | [Base64-encoded, URL-safe][rfc4648] representation of `keyId`.                                                                                                                                                                                                    | [No][not-pii] |
| `rawId`                              | String | Required  | Globally unique identifier for this `PublicKeyCredential`.                                                                                                                                                                                                        | [No][not-pii] |
| `authenticatorAttachment`            | String | Optional  | Mechanism by which the WebAuthn implementation attaches to the authenticator at the time the associated [`create(parseCreationOptionsFromJSON())`][] or [`get(parseRequestOptionsFromJSON())`][] call completes.<br /><br />Accepts: `platform`, `cross-platform` | [No][not-pii] |
| `type`                               | String | Required  | Credential types the API supports. These values version the `AuthenticatorAssertion` and `AuthenticatorAttestation` structures according to the type of the authenticator.<br /><br />Accepts: `public-key`                                                       | [No][not-pii] |
| `response`                           | object | Required  | Result of a `WebAuthn` credential registration as specified in `AuthenticatorAttestationResponse`.                                                                                                                                                                | [No][not-pii] |
| `response`<br />`.clientDataJSON`    | String | Optional  | JSON-compatible serialization of the data passed from the browser to the authenticator that generated this credential.                                                                                                                                            | [No][not-pii] |
| `response`<br />`.signature`         | String | Optional  | Assertion signature over `authenticatorData` and `clientDataJSON`.                                                                                                                                                                                                | [No][not-pii] |
| `response`<br />`.userHandle`        | String | Optional  | User handle stored in the authenticator as a [Base64-encoded, URL-safe][rfc4648] contact ID.                                                                                                                                                                      | [No][not-pii] |
| `response`<br />`.authenticatorData` | String | Optional  | Serialized data encoded in the Client to Authenticator Protocol (CTAP2) canonical Concise Binary Object Representation (CBOR) encoding form.                                                                                                                      | [No][not-pii] |

#### Procedure

1. Make a `POST` request to the `ApproveChallenge` subresource. Pass the properties of the `credential` you copied from your browser console as a JSON object payload.

   Approve Passkey challenge

   ```js
   // Download the helper library from https://www.twilio.com/docs/node/install
   const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   const accountSid = process.env.TWILIO_ACCOUNT_SID;
   const authToken = process.env.TWILIO_AUTH_TOKEN;
   const client = twilio(accountSid, authToken);

   async function updateChallengePasskeys() {
     const approveChallenge = await client.verify.v2
       .services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       .approveChallenge.update({
         id: "fr4561ff591898a9942916050d2ca3f",
         rawId: "rawId",
         authenticatorAttachment: "platform",
         response: {
           authenticatorData: "authenticatorData",
           clientDataJSON: "clientDataJSON",
           signature: "signature",
           userHandle: "userHandle",
         },
       });

     console.log(approveChallenge.options);
   }

   updateChallengePasskeys();
   ```

   ```python
   # Download the helper library from https://www.twilio.com/docs/python/install
   import os
   from twilio.rest import Client
   from twilio.rest.verify.v2.service import ApproveChallengeList

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = os.environ["TWILIO_ACCOUNT_SID"]
   auth_token = os.environ["TWILIO_AUTH_TOKEN"]
   client = Client(account_sid, auth_token)

   challenge_passkeys = client.verify.v2.services(
       "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   ).approve_challenge.update(
       approve_passkeys_challenge_request=ApproveChallengeList.ApprovePasskeysChallengeRequest(
           {
               "id": "fr4561ff591898a9942916050d2ca3f",
               "rawId": "rawId",
               "authenticatorAttachment": "platform",
               "response": ApproveChallengeList.ApprovePasskeysChallengeRequestResponse(
                   {
                       "authenticatorData": "authenticatorData",
                       "clientDataJSON": "clientDataJSON",
                       "signature": "signature",
                       "userHandle": "userHandle",
                   }
               ),
           }
       )
   )

   print(challenge_passkeys.options)
   ```

   ```csharp
   // Install the C# / .NET helper library from twilio.com/docs/csharp/install

   using System;
   using Twilio;
   using Twilio.Rest.Verify.V2.Service;
   using System.Threading.Tasks;
   using System.Collections.Generic;

   class Program {
       public static async Task Main(string[] args) {
           // Find your Account SID and Auth Token at twilio.com/console
           // and set the environment variables. See http://twil.io/secure
           string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
           string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");

           TwilioClient.Init(accountSid, authToken);

           var approveChallenge = await ApproveChallengeResource.UpdateAsync(
               approvePasskeysChallengeRequest: new ApproveChallengeResource
                   .ApprovePasskeysChallengeRequest.Builder()
                   .WithId("fr4561ff591898a9942916050d2ca3f")
                   .WithRawId("rawId")
                   .WithAuthenticatorAttachment("platform")
                   .WithResponse(
                       new ApproveChallengeResource.ApprovePasskeysChallengeRequestResponse.Builder()
                           .WithAuthenticatorData("authenticatorData")
                           .WithClientDataJSON("clientDataJSON")
                           .WithSignature("signature")
                           .WithUserHandle("userHandle")
                           .Build())
                   .Build(),
               pathServiceSid: "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

           Console.WriteLine(approveChallenge.Options);
       }
   }
   ```

   ```java
   // Install the Java helper library from twilio.com/docs/java/install

   import java.util.Arrays;
   import java.util.HashMap;
   import com.twilio.Twilio;
   import com.twilio.rest.verify.v2.service.ApproveChallenge;

   public class Example {
       // Find your Account SID and Auth Token at twilio.com/console
       // and set the environment variables. See http://twil.io/secure
       public static final String ACCOUNT_SID = System.getenv("TWILIO_ACCOUNT_SID");
       public static final String AUTH_TOKEN = System.getenv("TWILIO_AUTH_TOKEN");

       public static void main(String[] args) {
           Twilio.init(ACCOUNT_SID, AUTH_TOKEN);

           ApproveChallenge.ApprovePasskeysChallengeRequestResponse response =
               new ApproveChallenge.ApprovePasskeysChallengeRequestResponse();
           response.setAuthenticatorData("authenticatorData");
           response.setClientDataJSON("clientDataJSON");
           response.setSignature("signature");
           response.setUserHandle("userHandle");

           ApproveChallenge.ApprovePasskeysChallengeRequest approvePasskeysChallengeRequest =
               new ApproveChallenge.ApprovePasskeysChallengeRequest();
           approvePasskeysChallengeRequest.setId("fr4561ff591898a9942916050d2ca3f");
           approvePasskeysChallengeRequest.setRawId("rawId");
           approvePasskeysChallengeRequest.setAuthenticatorAttachment("platform");
           approvePasskeysChallengeRequest.setResponse(approvePasskeysChallengeRequestResponse);

           ApproveChallenge approveChallenge =
               ApproveChallenge.updater("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", approvePasskeysChallengeRequest).update();

           System.out.println(approveChallenge.getOptions());
       }
   }
   ```

   ```go
   // Download the helper library from https://www.twilio.com/docs/go/install
   package main

   import (
   	"fmt"
   	"github.com/twilio/twilio-go"
   	verify "github.com/twilio/twilio-go/rest/verify/v2"
   	"os"
   )

   func main() {
   	// Find your Account SID and Auth Token at twilio.com/console
   	// and set the environment variables. See http://twil.io/secure
   	// Make sure TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN exists in your environment
   	client := twilio.NewRestClient()

   	params := &verify.UpdateChallengePasskeysParams{}
   	params.SetApprovePasskeysChallengeRequest(verify.ApprovePasskeysChallengeRequest{
   		Id:                      "fr4561ff591898a9942916050d2ca3f",
   		RawId:                   "rawId",
   		AuthenticatorAttachment: "platform",
   		Response: verify.ApprovePasskeysChallengeRequestResponse{
   			AuthenticatorData: "authenticatorData",
   			ClientDataJSON:    "clientDataJSON",
   			Signature:         "signature",
   			UserHandle:        "userHandle",
   		},
   	})

   	resp, err := client.VerifyV2.UpdateChallengePasskeys("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   		params)
   	if err != nil {
   		fmt.Println(err.Error())
   		os.Exit(1)
   	} else {
   		if resp.Options != nil {
   			fmt.Println(resp.Options)
   		} else {
   			fmt.Println(resp.Options)
   		}
   	}
   }
   ```

   ```php
   <?php

   // Update the path below to your autoload.php,
   // see https://getcomposer.org/doc/01-basic-usage.md
   require_once "/path/to/vendor/autoload.php";

   use Twilio\Rest\Client;
   use Twilio\Rest\Verify\V2\Service\ChallengePasskeysModels;

   // Find your Account SID and Auth Token at twilio.com/console
   // and set the environment variables. See http://twil.io/secure
   $sid = getenv("TWILIO_ACCOUNT_SID");
   $token = getenv("TWILIO_AUTH_TOKEN");
   $twilio = new Client($sid, $token);

   $challenge_passkeys = $twilio->verify->v2
       ->services("VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
       ->approveChallenge->update(
           ChallengePasskeysModels::createApprovePasskeysChallengeRequest([
               "id" => "fr4561ff591898a9942916050d2ca3f",
               "rawId" => "rawId",
               "authenticatorAttachment" => "platform",
               "response" => ChallengePasskeysModels::createApprovePasskeysChallengeRequestResponse(
                   [
                       "authenticatorData" => "authenticatorData",
                       "clientDataJSON" => "clientDataJSON",
                       "signature" => "signature",
                       "userHandle" => "userHandle",
                   ]
               ),
           ])
       );

   print $challenge_passkeys->options;
   ```

   ```ruby
   # Download the helper library from https://www.twilio.com/docs/ruby/install
   require 'twilio-ruby'

   # Find your Account SID and Auth Token at twilio.com/console
   # and set the environment variables. See http://twil.io/secure
   account_sid = ENV['TWILIO_ACCOUNT_SID']
   auth_token = ENV['TWILIO_AUTH_TOKEN']
   @client = Twilio::REST::Client.new(account_sid, auth_token)

   approve_challenge = @client
                       .verify
                       .v2
                       .services('VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
                       .approve_challenge
                       .update(
                         approve_passkeys_challenge_request: {
                           'id' => 'fr4561ff591898a9942916050d2ca3f',
                           'rawId' => 'rawId',
                           'authenticatorAttachment' => 'platform',
                           'response' => {
                             'authenticatorData' => 'authenticatorData',
                             'clientDataJSON' => 'clientDataJSON',
                             'signature' => 'signature',
                             'userHandle' => 'userHandle'
                           }
                         }
                       )

   puts approve_challenge.options
   ```

   ```bash
   # This endpoint is not currently supported by the Twilio CLI. You can open an issue to request it on https://github.com/twilio/twilio-cli/issues
     # For an alternative low-code solution, check out https://www.twilio.com/docs/openapi/using-twilio-postman-collections
   ```

   ```bash
   APPROVE_PASSKEYS_CHALLENGE_REQUEST_OBJ=$(cat << EOF
   {
     "id": "fr4561ff591898a9942916050d2ca3f",
     "rawId": "rawId",
     "authenticatorAttachment": "platform",
     "response": {
       "authenticatorData": "authenticatorData",
       "clientDataJSON": "clientDataJSON",
       "signature": "signature",
       "userHandle": "userHandle"
     }
   }
   EOF
   )
   curl -X POST "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Passkeys/ApproveChallenge" \
   --json "$APPROVE_PASSKEYS_CHALLENGE_REQUEST_OBJ" \
   -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
   ```

   ```json
   {
     "sid": "YCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "service_sid": "VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     "entity_sid": "",
     "identity": "",
     "factor_sid": "",
     "factor_type": "passkeys",
     "status": "approved",
     "date_created": "2025-07-30T20:00:00Z",
     "date_updated": "2025-07-30T20:00:00Z",
     "date_responded": null,
     "details": null,
     "expiration_date": null,
     "hidden_details": null,
     "links": null,
     "metadata": null,
     "responded_reason": "none",
     "url": "https://verify.twilio.com/v2/Services/VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Passkeys/ApproveChallenge",
     "options": {
       "publicKey": {
         "rp": {
           "id": "example.com",
           "name": "Example"
         },
         "user": {
           "id": "WUU0ZmQzYWFmNGU0NTMyNGQwZjNlMTM0NjA3YjIxOTEyYg",
           "name": "friendly_name",
           "displayName": "friendly_name"
         },
         "challenge": "WUYwNDhkMWE3ZWMzYTJhNjk3MDA1OWMyNzY2YmJjN2UwZg",
         "pubKeyCredParams": {
           "type": "public-key",
           "alg": -7
         },
         "timeout": 600000,
         "excludeCredentials": [],
         "authenticatorSelection": {
           "authenticatorAttachment": "platform",
           "requireResidentKey": false,
           "residentKey": "preferred",
           "userVerification": "preferred"
         },
         "attestation": "none"
       }
     }
   }
   ```
2. If the response returns `"status": "approved"`, you have succeeded in creating and verifying a Passkey.

[`create(parseCreationOptionsFromJSON())`]: https://www.w3.org/TR/webauthn-3/#sctn-parseCreationOptionsFromJSON

[`get(parseRequestOptionsFromJSON())`]: https://www.w3.org/TR/webauthn-3/#sctn-parseRequestOptionsFromJSON

[beta-support]: https://help.twilio.com/articles/115002413087-Twilio-Beta-product-support

[fido]: https://fidoalliance.org/members/

[webauthn]: https://github.com/github/webauthn-json

[Postman]: https://www.postman.com/

[dev-tools]: https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Tools_and_setup/What_are_browser_developer_tools

[pii]: /docs/glossary/what-is-personally-identifiable-information-pii#pii-fields

[not-pii]: /docs/glossary/what-is-personally-identifiable-information-pii#fields-marked-not-pii

[rfc4648]: https://datatracker.ietf.org/doc/html/rfc4648#section-5

[passkey-sdks]: https://github.com/twilio/twilio-verify-passkeys
