XRPC API
The main app serves AT Protocol XRPC endpoints at /xrpc/{nsid}. All authenticated endpoints require a service JWT in the Authorization: Bearer <token> header, scoped to the called NSID (lxm claim).
Each endpoint also accepts kebab-case and lowercase NSID aliases (e.g. place.wisp.v2.domain.add-site, place.wisp.v2.domain.addsite).
place.wisp.v2.domain.getStatus β query
Section titled βplace.wisp.v2.domain.getStatus β queryβReturns the registration status of any domain. Auth is optional β if authenticated, also returns ownership info for domains you own.
Params:
| Field | Type | Required |
|---|---|---|
domain |
string |
β |
Response:
| Field | Type |
|---|---|
domain |
string |
status |
"unclaimed" | "pendingVerification" | "verified" | "alreadyClaimed" |
kind |
"wisp" | "custom" |
verified |
boolean |
siteRkey |
string |
lastCheckedAt |
string (datetime) |
lastError |
string |
place.wisp.v2.domain.getList β query π
Section titled βplace.wisp.v2.domain.getList β query πβReturns all domains (wisp subdomains and custom domains) owned by the authenticated DID.
Response:
{ "domains": [ { "domain": "alice.wisp.place", "kind": "wisp", "status": "verified", "verified": true, "siteRkey": "my-site" }, { "domain": "example.com", "kind": "custom", "status": "pendingVerification", "verified": false, "lastCheckedAt": "2024-01-15T10:30:00.000Z" } ]}Errors: AuthenticationRequired
place.wisp.v2.domain.claimSubdomain β procedure π
Section titled βplace.wisp.v2.domain.claimSubdomain β procedure πβClaims a *.wisp.place subdomain for the authenticated DID. Max 3 wisp subdomains per DID.
Input:
| Field | Type | Required | Notes |
|---|---|---|---|
handle |
string |
β | Subdomain label only, e.g. alice (3β63 chars, a-z0-9-) |
siteRkey |
string |
Map a site immediately after claim |
Response:
| Field | Type |
|---|---|
domain |
string |
kind |
"wisp" |
status |
"verified" | "alreadyClaimed" |
siteRkey |
string |
Errors: AuthenticationRequired, InvalidDomain, AlreadyClaimed, DomainLimitReached, RateLimitExceeded
place.wisp.v2.domain.claim β procedure π
Section titled βplace.wisp.v2.domain.claim β procedure πβClaims a custom domain for the authenticated DID. Returns DNS challenge details for ownership verification.
Input:
| Field | Type | Required | Notes |
|---|---|---|---|
domain |
string |
β | Custom FQDN (3β253 chars) |
siteRkey |
string |
Map a site immediately after claim |
Response:
| Field | Type | Notes |
|---|---|---|
domain |
string |
|
kind |
"custom" |
|
status |
"alreadyClaimed" | "pendingVerification" | "verified" |
|
challengeId |
string |
Used to derive DNS targets |
txtName |
string |
TXT record hostname for ownership proof |
txtValue |
string |
TXT record value (your DID) |
cnameTarget |
string |
Advisory CNAME target |
siteRkey |
string |
Errors: AuthenticationRequired, InvalidDomain, AlreadyClaimed, DomainLimitReached, RateLimitExceeded
place.wisp.v2.domain.addSite β procedure π
Section titled βplace.wisp.v2.domain.addSite β procedure πβMaps a site to a domain you own.
Input:
| Field | Type | Required |
|---|---|---|
domain |
string |
β |
siteRkey |
string |
β |
Response:
| Field | Type |
|---|---|
domain |
string |
kind |
"wisp" | "custom" |
status |
"pendingVerification" | "verified" |
siteRkey |
string |
mapped |
true |
Errors: AuthenticationRequired, InvalidDomain, InvalidRequest, NotFound
place.wisp.v2.domain.delete β procedure π
Section titled βplace.wisp.v2.domain.delete β procedure πβDeletes a domain (wisp subdomain or custom domain) owned by the authenticated DID.
Params:
| Field | Type | Required |
|---|---|---|
domain |
string |
β |
Response:
{ "domain": "alice.wisp.place", "deleted": true }Errors: AuthenticationRequired, InvalidDomain, NotFound
place.wisp.v2.site.getList β query π
Section titled βplace.wisp.v2.site.getList β query πβReturns all sites owned by the authenticated DID, with their mapped domains.
Response:
{ "sites": [ { "siteRkey": "my-site", "displayName": "My Site", "createdAt": "2024-01-15T10:30:00.000Z", "updatedAt": "2024-01-15T10:30:00.000Z", "domains": [ { "domain": "alice.wisp.place", "kind": "wisp", "status": "verified", "verified": true } ] } ]}Errors: AuthenticationRequired
place.wisp.v2.site.getDomains β query
Section titled βplace.wisp.v2.site.getDomains β queryβReturns all domains mapped to a specific site. Public β no auth required.
Params:
| Field | Type | Required |
|---|---|---|
did |
string |
β |
rkey |
string |
β |
Response:
{ "domains": [ { "domain": "alice.wisp.place", "kind": "wisp", "status": "verified", "verified": true } ]}place.wisp.v2.site.delete β procedure π
Section titled βplace.wisp.v2.site.delete β procedure πβDeletes a site and detaches all mapped domains.
Input:
| Field | Type | Required |
|---|---|---|
siteRkey |
string |
β |
Response:
{ "siteRkey": "my-site", "deleted": true, "unmappedDomains": [ { "domain": "alice.wisp.place", "kind": "wisp", "status": "verified" } ]}Errors: AuthenticationRequired, InvalidRequest, NotFound
Private Sites
Section titled βPrivate SitesβPrivate-site methods are authenticated and only operate on sites owned by the calling DID. Private-site files are stored by wisp.place and are not written to the callerβs PDS. See Private Sites for the featureβs access and expiry rules.
place.wisp.v2.privateSite.create β procedure π
Section titled βplace.wisp.v2.privateSite.create β procedure πβCreates a private site from a multipart/form-data upload.
Input:
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
β | Display name, up to 128 characters |
files |
file |
β | Repeat for each file; the filename is used as its path |
expiryMinutes |
integer |
Omit for seven days, 0 for no expiry, maximum 525600 |
Uploads are limited to 500 files and 100 MB in total.
Response:
| Field | Type | Notes |
|---|---|---|
siteId |
string (record-key) |
Stable private-site identifier |
name |
string |
Display name |
fileCount |
integer |
|
totalBytes |
integer |
|
expiresAt |
string (datetime) |
Omitted when the site does not expire |
createdAt |
string (datetime) |
|
url |
string |
Requires an account with access or a share link |
Errors: AuthenticationRequired, InvalidRequest, PayloadTooLarge
place.wisp.v2.privateSite.list β query π
Section titled βplace.wisp.v2.privateSite.list β query πβLists private sites owned by the authenticated DID.
Response:
{ "sites": [ { "siteId": "3mabc...", "name": "review", "fileCount": 12, "totalBytes": 48321, "expiresAt": "2026-08-22T12:00:00.000Z", "createdAt": "2026-08-15T12:00:00.000Z", "shareCount": 1, "expired": false } ]}expiresAt is omitted when the site does not expire. shareCount includes only active
shares.
Errors: AuthenticationRequired
place.wisp.v2.privateSite.delete β procedure π
Section titled βplace.wisp.v2.privateSite.delete β procedure πβDeletes an owned private site, its files, and all of its share links.
Input:
| Field | Type | Required |
|---|---|---|
siteId |
string (record-key) |
β |
Response:
{ "siteId": "3mabc...", "deleted": true }Errors: AuthenticationRequired, InvalidRequest, NotFound
place.wisp.v2.privateSite.createShare β procedure π
Section titled βplace.wisp.v2.privateSite.createShare β procedure πβCreates a share link for an owned private site. The returned URL contains the credential and is only returned once.
Input:
| Field | Type | Required | Notes |
|---|---|---|---|
siteId |
string (record-key) |
β | |
label |
string |
Human label, up to 128 characters | |
expiryMinutes |
integer |
Omit for seven days; 0 for no independent expiry |
|
audienceDid |
string (DID) |
Restricts the link to this account; omit for an unrestricted link |
A shareβs expiry is clamped to the siteβs expiry.
Response:
| Field | Type | Notes |
|---|---|---|
shareId |
string |
|
siteId |
string (record-key) |
|
url |
string |
Short share URL; returned once |
directUrl |
string (URI) |
Equivalent URL on the private-site origin |
expiresAt |
string (datetime) |
Omitted when the share does not expire |
createdAt |
string (datetime) |
|
audienceDid |
string (DID) |
Present for account-restricted links |
Errors: AuthenticationRequired, InvalidRequest, NotFound
place.wisp.v2.privateSite.listShares β query π
Section titled βplace.wisp.v2.privateSite.listShares β query πβLists share links for an owned private site. Share credentials are never returned.
Params:
| Field | Type | Required |
|---|---|---|
siteId |
string (record-key) |
β |
Response:
{ "shares": [ { "shareId": "3mdef...", "tokenPrefix": "wss_abcd", "label": "review", "audienceDid": "did:plc:...", "expiresAt": "2026-08-22T12:00:00.000Z", "createdAt": "2026-08-15T12:00:00.000Z", "lastUsedAt": "2026-08-16T09:00:00.000Z", "status": "active" } ]}status is active, expired, or revoked. tokenPrefix is for identification only
and does not grant access.
Errors: AuthenticationRequired, NotFound
place.wisp.v2.privateSite.revokeShare β procedure π
Section titled βplace.wisp.v2.privateSite.revokeShare β procedure πβPermanently revokes a share link.
Input:
| Field | Type | Required |
|---|---|---|
siteId |
string (record-key) |
β |
shareId |
string |
β |
Response:
{ "shareId": "3mdef...", "revoked": true }Errors: AuthenticationRequired, InvalidRequest, NotFound
Signing Secrets
Section titled βSigning SecretsβServer-managed HMAC signing secrets for webhooks. The token is returned once at creation time and is stored only as an AES-256-GCM encrypted server-side envelope. It cannot be retrieved again, only rotated. Operators must configure the webhook secret encryption key on both the main and delivery services; see Self-Hosting for deployment, rotation, and backup caveats.
All four endpoints require authentication (AuthenticationRequired on failure).
place.wisp.v2.secret.create β procedure π
Section titled βplace.wisp.v2.secret.create β procedure πβCreates a new signing secret scoped to the authenticated DID.
Input:
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
β | Unique per DID; 1β64 ASCII letters, digits, ., _, or - |
Response:
| Field | Type | Notes |
|---|---|---|
name |
string |
|
token |
string |
wsk_ prefixed β store this now, never shown again |
createdAt |
string (datetime) |
Errors: AuthenticationRequired, InvalidRequest, AlreadyExists, WebhookSecretEncryptionUnavailable
WebhookSecretEncryptionUnavailable is a stable generic 503 response. It does not reveal whether a key is missing, malformed, or unavailable.
place.wisp.v2.secret.list β query π
Section titled βplace.wisp.v2.secret.list β query πβLists all secrets for the authenticated DID. Token values are never returned.
Response:
{ "secrets": [ { "name": "my-secret", "createdAt": "2024-01-15T10:30:00.000Z", "lastRotatedAt": "2024-02-01T09:00:00.000Z" } ]}Errors: AuthenticationRequired
place.wisp.v2.secret.rotate β procedure π
Section titled βplace.wisp.v2.secret.rotate β procedure πβGenerates a new token for an existing secret. The old token is invalidated immediately.
Input:
| Field | Type | Required |
|---|---|---|
name |
string |
β |
Response:
| Field | Type | Notes |
|---|---|---|
name |
string |
|
token |
string |
New token β store this now, never shown again |
rotatedAt |
string (datetime) |
Errors: AuthenticationRequired, InvalidRequest, NotFound, WebhookSecretEncryptionUnavailable
place.wisp.v2.secret.delete β procedure π
Section titled βplace.wisp.v2.secret.delete β procedure πβDeletes a signing secret. Any webhooks referencing this secretId will stop being signed.
Input:
| Field | Type | Required |
|---|---|---|
name |
string |
β |
Response: {}
Errors: AuthenticationRequired, InvalidRequest, NotFound