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 server generates the token unless you supply your own on create or rotate, for example a secret your receiving system already generated. 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 - |
token | string | Your own signing token: 32β256 printable ASCII characters, no whitespace. Omit it to have the server generate a wsk_ token |
Response:
| Field | Type | Notes |
|---|---|---|
name | string | |
token | string | The generated wsk_ token, or the one you supplied β 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 πβReplaces the token of an existing secret with a newly generated one, or with the token you supply. The old token is invalidated immediately.
Input:
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | β | 1β64 ASCII letters, digits, ., _, or - |
token | string | Your own signing token: 32β256 printable ASCII characters, no whitespace. Omit it to have the server generate one |
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