Base URL: http(s)://<gateway-host>/api/v1/registry
All endpoints that modify state require Bearer token auth (Authorization: Bearer <key>) unless noted otherwise. Read endpoints are public and require no authentication.
Register a new .tpt name. Each name is registered once and never renewed.
Authentication: Required (Bearer token)
Request body:
{
"name": "alice",
"did": "did:web:alice.tpt",
"owner_pubkey": "abc123...",
"fee_tx": "optional-payment-proof"
}| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Desired name without the .tpt suffix. Must match ^[a-z0-9][a-z0-9-]{0,62}$. |
did |
string | yes | DID to associate with this name, typically did:web:<name>.tpt. |
owner_pubkey |
string | yes | Hex-encoded Ed25519 public key of the registrant. Used to verify future transfer signatures. |
fee_tx |
string | no | Payment proof token (provider-specific). Required when the gateway has fee validation enabled. |
Responses:
| Status | Meaning |
|---|---|
| 201 Created | Registration successful. Returns the DomainRecord JSON. |
| 400 Bad Request | Invalid name format or missing required fields. |
| 402 Payment Required | Fee validation failed (gateway has payment enforcement enabled). |
| 409 Conflict | Name already registered. |
| 503 Service Unavailable | Registry not enabled on this gateway. |
Example response (201):
{
"name": "alice",
"did": "did:web:alice.tpt",
"owner_pubkey": "abc123...",
"registered_at": "2026-05-31T12:00:00Z",
"dormant": false
}Resolve a .tpt name to its DID and metadata. Public — no authentication required.
Path parameter: name — the name without .tpt suffix (e.g. alice).
Responses:
| Status | Meaning |
|---|---|
| 200 OK | Name found. Returns DomainRecord JSON. |
| 404 Not Found | Name not registered. |
Example:
GET /api/v1/registry/resolve/alice
{
"name": "alice",
"did": "did:web:alice.tpt",
"owner_pubkey": "abc123...",
"registered_at": "2026-05-31T12:00:00Z",
"dormant": false,
"metadata": { "bio": "Hi, I'm Alice." }
}dormant: true indicates the name is registered but the owner has not yet published a DID document at https://<name>.tpt/.well-known/did.json.
Find all .tpt names owned by a given DID. Public — no authentication required.
Query parameter: did — the full DID string (e.g. did:web:alice.tpt).
Response: Array of DomainRecord objects (may be empty).
GET /api/v1/registry/lookup?did=did%3Aweb%3Aalice.tpt
[
{ "name": "alice", "did": "did:web:alice.tpt", ... }
]Fuzzy search for registered names. Returns up to 50 results. Public — no authentication required.
Query parameter: q — partial name string to search for.
Response: Array of DomainRecord objects.
GET /api/v1/registry/search?q=ali
Transfer ownership of a name to a new DID + keypair. The current owner must sign the transfer payload with their Ed25519 private key.
Authentication: Required (Bearer token)
Signature payload — sign the following string with the current owner's Ed25519 key:
transfer:<name>:<new_did>:<new_owner_pubkey>:<timestamp_unix>
Request body:
{
"name": "alice",
"new_did": "did:web:alice-v2.tpt",
"new_owner_pubkey": "def456...",
"signature": "<hex-encoded Ed25519 signature>",
"timestamp": 1748692800
}Responses:
| Status | Meaning |
|---|---|
| 200 OK | Transfer successful. |
| 403 Forbidden | Signature verification failed. |
| 404 Not Found | Name not registered. |
Retrieve the full ownership change log for a name. Public — no authentication required.
Response: Array of Transfer objects, ordered oldest-first.
[
{
"id": 1,
"name": "alice",
"from_did": "did:web:alice.tpt",
"to_did": "did:web:alice-v2.tpt",
"to_owner_pubkey": "def456...",
"transferred_at": "2026-06-15T09:00:00Z",
"signature": "abcdef..."
}
]Update the JSON metadata blob for a registered name. The current owner must sign the update.
Authentication: Required (Bearer token)
Signature payload — sign the following string with the current owner's Ed25519 key:
metadata:<name>:<sha256-hex-of-metadata-json>
Request body:
{
"metadata": { "bio": "Hi, I'm Alice.", "url": "https://alice.example.com" },
"signature": "<hex-encoded Ed25519 signature>"
}Responses:
| Status | Meaning |
|---|---|
| 200 OK | Metadata updated. |
| 403 Forbidden | Signature verification failed. |
| 404 Not Found | Name not registered. |
Returns the gateway's own DID document, including any .tpt names owned by the gateway DID in the alsoKnownAs array. Public.
Content-Type: application/did+json
Serves an HTML profile page for a registered .tpt name, showing identity, DID, pubkey, metadata, and transfer history. Suitable for linking in email signatures or social profiles.
Example: http://localhost:8080/p/alice
{
name: string // name without .tpt
did: string // associated DID
owner_pubkey: string // hex Ed25519 public key
registered_at: string // RFC 3339 timestamp
fee_tx?: string // payment proof (if any)
metadata?: object // arbitrary JSON metadata
dormant?: boolean // true = no DID doc published yet
}{
id: number // sequential ID
name: string
from_did: string
to_did: string
to_owner_pubkey: string
transferred_at: string // RFC 3339 timestamp
signature: string // hex Ed25519 signature by previous owner
}All error responses use the same JSON envelope:
{ "error": "human-readable message" }By default, the gateway uses a no-op fee validator that accepts any fee_tx value (including empty string). To require payment:
- Implement the
api.FeeValidatorinterface in your deployment:type FeeValidator interface { ValidateFee(ctx context.Context, feeTx string) error }
- Set
api.Config.FeeValidatorbefore starting the server.
Common implementations: Stripe payment intent verification, Lightning invoice preimage check, or on-chain transaction confirmation.