API
Two surfaces, and the split is deliberate.
Public and cacheable — the resolver and search. REST, because these must be cacheable by intermediaries and readable by third parties who will never hold a schema.
Authenticated — checkout and owner writes. REST today; the BFF will front these in GraphQL, and the authorization boundary stays in the service.
Base URL: https://api.appsterisk.me
Public
GET /v1/search?q=<name> | Availability, classification, price |
GET /r/:slug | The manifest |
GET /r/:slug/manifest | Alias, named for third-party consumers |
GET /r/:slug/collections | Collections and the active one |
GET /r/:slug/qr | Stable QR code, SVG |
Search
{
"slug": "hotel",
"state": "PREMIUM",
"price": { "amountCents": 250000, "currency": "USD" },
"message": "This is a premium name.",
"purchasable": true
}
state is one of AVAILABLE, PREMIUM, RESERVED, TAKEN, INVALID. A reserved name carries no
price. purchasable is decided by the server rather than inferred from the state, so a client
cannot decide on its own that a reserved name looks buyable.
Authenticated
Bearer token from Firebase — Google sign-in or a passwordless email link.
POST /v1/checkout | Reserve a name and start Stripe Checkout |
GET /v1/namespaces/:slug/renewal | Expiry, renewal state, and what a renewal costs |
POST /v1/namespaces/:slug/renewal | Start a Checkout session that adds a year |
POST /v1/namespaces/:slug/claims | Claim a reserved or held name |
GET /v1/claims | Your own claims and their status |
PATCH /v1/namespaces/:slug/identity | Display name, description, locale |
POST /v1/namespaces/:slug/contacts | Add a contact |
DELETE /v1/namespaces/:slug/contacts/:id | Remove one |
POST /v1/namespaces/:slug/avatar | Upload an avatar (multipart, 5 MB) |
POST /v1/namespaces/:slug/collections | Create a collection |
PATCH /v1/namespaces/:slug/collections/:id | Update one |
POST /v1/namespaces/:slug/collections/:id/items | Add a link |
Errors worth knowing
A name you cannot write answers 404, not 403. A 403 would confirm the name is owned and that you
merely lack the right — which is what someone enumerating would like to learn. The one exception is
a VIEWER in the owning organization, who already knows it exists and gets a 403.
Destinations are validated when you save them. Schemes outside http, https, mailto and
tel are refused, and the error names the scheme rather than echoing your URL back.
A reserved name and a taken name refuse checkout identically. You do not need to know whether a name is held by someone or withheld by us.
Renewal reads and renewal payments are separate calls. GET tells you the price and the dates
without creating a Stripe session for someone who was only looking.
Renewing a suspended name is refused. Suspension is an enforcement decision; paying your way out of one would make it a fee. Grace and quarantine are both renewable — that is what they are for.
Submitting a claim moves nothing. It records an assertion for a human to review. The response says so in as many words, because a form that looks like it took a name is worse than one that plainly did not.