> ## 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 DNS challenge

> Verify the TXT record and prepare an attested canonical Persona payload for local signing

After publishing the Persona TXT record, submit the intended registration or renewal. The Directory checks DNS, validates the intent against finalized state, obtains a chain-compatible Directory attestation, and returns the exact canonical payload your operator key must sign.

This step does not register or renew the Persona. Do not sign a locally reconstructed payload; sign the decoded bytes returned in `canonical_payload`.

## Request

<ParamField path="persona" type="string" required>
  Canonical Persona from the challenge.
</ParamField>

<ParamField path="challenge_id" type="string" required>
  Challenge ID returned by the challenge endpoint.
</ParamField>

<ParamField body="persona" type="string">
  Optional body copy of the canonical path Persona. When supplied, it must match after normalization.
</ParamField>

<ParamField body="action" type="string" required>
  `register_persona` or `renew_persona`. It must equal the challenge action.
</ParamField>

<ParamField body="operator_did" type="string" required>
  Operator DID bound to the challenge. It must be active in finalized chain state.
</ParamField>

<ParamField body="controller_keys" type="string[]">
  One through eight unique signing-key IDs published by `operator_did`. Required for registration. Renewal must omit this field or repeat the finalized set exactly.
</ParamField>

<ParamField body="controller_threshold" type="integer">
  Required for registration and must equal `1`. Renewal cannot change the finalized value.
</ParamField>

<ParamField body="delivery_constraints" type="object">
  Optional complete requested limits. Omit `effective_ttl_seconds`. A renewal that omits this field retains the finalized Persona constraints.
</ParamField>

<ParamField body="challenge_id" type="string">
  Optional body copy of the path challenge. When supplied, it must match.
</ParamField>

<ParamField body="nonce" type="string" required>
  Current `operator_nonce` returned with the challenge or by the Persona nonce endpoint.
</ParamField>

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

<ParamField body="signer_key_id" type="string" required>
  Operator-DID verification-method ID that will sign the returned canonical payload. It must also be an active Persona controller for renewal.
</ParamField>

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

<RequestExample>
  ```bash Request theme={null}
  curl --request POST \
    --url "https://directory.example.com/personas/example.com/dns-challenges/<challenge-id>/verify" \
    --header "Content-Type: application/json" \
    --data '{
      "action": "register_persona",
      "persona": "example.com",
      "operator_did": "did:openpayload:1111111111111111111111",
      "controller_keys": [
        "did:openpayload:1111111111111111111111#keys-1"
      ],
      "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,
      "nonce": "<operator-nonce>",
      "valid_until": "<epoch-milliseconds>",
      "signer_key_id": "did:openpayload:1111111111111111111111#keys-1"
    }'
  ```
</RequestExample>

For renewal, use `renew_persona`. Omit controllers and constraints to retain their finalized values, or repeat the existing controllers exactly. Renewal cannot rotate controllers.

## Response

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

Decode `canonical_payload`, sign those bytes with `signer_key_id`, and submit the result to `PUT /personas/{persona}` before the challenge and `valid_until` expire. Repeat the echoed `verification_expires_at` exactly.

## Straight to the point

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

* DNS TXT record must already be visible
* Response prepares but does not submit the chain operation
* Sign the returned canonical bytes exactly
* DNS or attestation failure does not consume a successful registration


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