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

# Verify a Persona transfer challenge

> Verify fresh DNS control and prepare the new operator acceptance payload

After publishing the transfer TXT record, submit the complete unsigned transfer intent. The Directory validates it against finalized state, verifies DNS, and returns an attested `acceptance_payload` for the new operator to sign.

This step does not transfer the Persona. Decode and sign the exact `acceptance_payload` bytes; do not sign the hexadecimal characters or reconstruct the payload locally.

## Request

<ParamField path="persona" type="string" required>
  Canonical Persona being transferred.
</ParamField>

<ParamField path="challenge_id" type="string" required>
  Transfer challenge ID.
</ParamField>

<ParamField body="current_operator_did" type="string" required>
  Operator DID currently registered for the Persona.
</ParamField>

<ParamField body="new_operator_did" type="string" required>
  Active DID accepting the Persona.
</ParamField>

<ParamField body="new_controller_keys" type="string[]" required>
  One through eight unique signing-key IDs published by `new_operator_did`.
</ParamField>

<ParamField body="new_controller_threshold" type="integer" required>
  Must equal `1` in Persona v1.
</ParamField>

<ParamField body="new_operator_signer_key_id" type="string" required>
  Key that will sign `acceptance_payload`. It must be one of `new_controller_keys`.
</ParamField>

<ParamField body="action" type="string" required>
  Must be `transfer_persona`.
</ParamField>

<ParamField body="nonce" type="string" required>
  Current operator nonce returned with the challenge.
</ParamField>

<ParamField body="valid_until" type="string" required>
  Short-lived Unix-millisecond or ISO-8601 expiration for the transfer authorization.
</ParamField>

<ParamField body="signer_key_id" type="string" required>
  Active current Persona controller that will sign the final transfer payload.
</ParamField>

<ParamField body="delivery_constraints" type="object">
  New Persona delivery constraints. Omit `effective_ttl_seconds`. If omitted, the transferred Persona has no Persona-specific constraints.
</ParamField>

<ParamField body="verification_expires_at" type="integer" required>
  Unix-millisecond expiration for the renewed DNS verification. It must be in the future and cannot be more than 90 days away.
</ParamField>

`persona` and `challenge_id` may also be repeated in the body. When supplied, they must match the path.

<RequestExample>
  ```bash Request theme={null}
  curl --request POST \
    --url "https://directory.example.com/personas/example.com/transfer/dns-challenges/<challenge-id>/verify" \
    --header "Content-Type: application/json" \
    --data '{
      "action": "transfer_persona",
      "persona": "example.com",
      "current_operator_did": "did:openpayload:1111111111111111111111",
      "new_operator_did": "did:openpayload:2222222222222222222222",
      "new_controller_keys": [
        "did:openpayload:2222222222222222222222#keys-1"
      ],
      "new_controller_threshold": 1,
      "delivery_constraints": {
        "requested_cache_seconds": 86400,
        "max_http_envelope_bytes": 26214400,
        "max_unchunked_message_bytes": 16777216,
        "max_chunk_bytes": 2097152,
        "max_replicas": 2
      },
      "challenge_id": "<challenge-id>",
      "verification_expires_at": 1796083200000,
      "new_operator_signer_key_id": "did:openpayload:2222222222222222222222#keys-1",
      "nonce": "<current-operator-nonce>",
      "valid_until": "<epoch-milliseconds>",
      "signer_key_id": "did:openpayload:1111111111111111111111#keys-1"
    }'
  ```
</RequestExample>

## Response

```json 200 Response theme={null}
{
  "challenge_id": "<challenge-id>",
  "persona": "example.com",
  "action": "transfer_persona",
  "challenge_nonce": "3",
  "operator_nonce": "7",
  "record_name": "_openpayload-persona.example.com",
  "record_type": "TXT",
  "record_value": "openpayload-persona-v1=<challenge>",
  "expires_at": "2026-09-17T18:00:00Z",
  "current_operator_did": "did:openpayload:1111111111111111111111",
  "new_operator_did": "did:openpayload:2222222222222222222222",
  "status": "verified",
  "dns_proof_hash": "0x<64-hex-dns-proof-hash>",
  "verification_expires_at": 1796083200000,
  "acceptance_payload": "0x<new-operator-acceptance-payload>",
  "attestor": "0x<directory-attestor-account>",
  "attestation_signature": "0x<directory-attestation-signature>"
}
```

The new operator signs the decoded `acceptance_payload` with `new_operator_signer_key_id`, then sends that signature and the exact echoed `verification_expires_at` to the prepare endpoint.

## Straight to the point

```http theme={null}
POST /personas/{persona}/transfer/dns-challenges/{challenge_id}/verify
```

* Verifies the fresh Persona TXT record
* Validates both DIDs, both controller roles, constraints, nonces, and expiration
* Returns the payload the new operator must sign
* Does not consume the challenge or submit the transfer


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.