Skip to main content
Ownership challenges let the node verify that you control the private key behind a DID before allowing provider registration or key rotation. The workflow is:
  1. POST to create a challenge — the node returns a short-lived string to sign.
  2. Sign the challenge string with the Ed25519 private key for your DID.
  3. Submit the challenge_id and your signature in the subsequent provider register or rotate-key request.

Create an ownership challenge

POST /v1/providers/ownership-challenges

curl -X POST http://your-node:8042/v1/providers/ownership-challenges \
  -H 'content-type: application/json' \
  -d '{
    "provider_did": "did:key:z6MkhaXgBZDvotD1X9gRrYkM5Xq9jYQqK6d8r8bQdE1mV2Xa",
    "operation": "register"
  }'

Body parameters

provider_did
string
required
The DID you will prove ownership of (e.g. "did:key:z6Mk…"). Must match the DID you intend to use when registering or rotating.
operation
string
required
The operation this challenge authorises. Either "register" (new provider) or "rotate_key" (key rotation on an existing provider).
provider_id
string
Required when operation is "rotate_key". The provider_id of the existing provider whose key you are rotating.

Response

Returns a ProviderOwnershipChallenge with status 201 Created.
challenge_id
string (UUID)
Unique identifier for this challenge. Pass this as ownership_challenge_id in the subsequent request.
provider_id
string
The provider ID associated with this challenge.
provider_did
string
The DID this challenge was issued for.
operation
string
Either "register" or "rotate_key".
challenge
string
The raw string you must sign with the Ed25519 private key for provider_did. Pass the resulting base64 signature as ownership_signature.
issued_at
string
ISO 8601 timestamp of when the challenge was created.
expires_at
string
ISO 8601 timestamp after which this challenge is no longer valid. Default TTL is 300 seconds.
completed_at
string
ISO 8601 timestamp of when the challenge was successfully consumed. Omitted until the challenge is used.

Status codes

CodeMeaning
201 CreatedChallenge issued successfully.
400 Bad RequestInvalid provider_did format or missing required fields.

Example response

{
  "challenge_id": "b3d2e1f0-1234-5678-abcd-000000000001",
  "provider_id": "acme-labs",
  "provider_did": "did:key:z6MkhaXgBZDvotD1X9gRrYkM5Xq9jYQqK6d8r8bQdE1mV2Xa",
  "operation": "register",
  "challenge": "servicenet-challenge:register:acme-labs:1736934000000",
  "issued_at": "2025-01-15T10:00:00Z",
  "expires_at": "2025-01-15T10:05:00Z"
}

Retrieve an ownership challenge

GET /v1/providers/ownership-challenges/:challenge_id

curl http://your-node:8042/v1/providers/ownership-challenges/b3d2e1f0-1234-5678-abcd-000000000001

Path parameters

challenge_id
string (UUID)
required
The challenge_id returned when you created the challenge.

Response

Returns the same ProviderOwnershipChallenge object described above. The completed_at field is set once the challenge has been successfully used.

Status codes

CodeMeaning
200 OKChallenge found.
404 Not FoundNo challenge with the given challenge_id exists.

Example response

{
  "challenge_id": "b3d2e1f0-1234-5678-abcd-000000000001",
  "provider_id": "acme-labs",
  "provider_did": "did:key:z6MkhaXgBZDvotD1X9gRrYkM5Xq9jYQqK6d8r8bQdE1mV2Xa",
  "operation": "register",
  "challenge": "servicenet-challenge:register:acme-labs:1736934000000",
  "issued_at": "2025-01-15T10:00:00Z",
  "expires_at": "2025-01-15T10:05:00Z",
  "completed_at": "2025-01-15T10:01:30Z"
}