Authentication
The Hostsmith API uses OAuth 2.0 with Authorization Code + PKCE for authentication. This is the same flow used by MCP clients like Claude Code and Cursor.
Overview
- Your application redirects the user to Hostsmith to authorize access
- The user reviews the requested permissions and approves
- Hostsmith redirects back to your application with an authorization code
- Your application exchanges the code for an access token
- Use the access token as a Bearer token in API requests
Access tokens are short-lived JWTs (1 hour). Refresh tokens are provided to obtain new access tokens without re-authorizing.
Discovery
The authorization server publishes its configuration at a well-known endpoint:
GET https://hostsmith.net/.well-known/oauth-authorization-server
Response:
{
"issuer": "https://hostsmith.net",
"authorization_endpoint": "https://hostsmith.net/api/oauth/authorize",
"token_endpoint": "https://hostsmith.net/api/oauth/token",
"registration_endpoint": "https://hostsmith.net/api/oauth/register",
"revocation_endpoint": "https://hostsmith.net/api/oauth/revoke",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
"scopes_supported": [
"sites:read",
"sites:write",
"domains:read",
"files:write"
]
}
Client registration
Before starting the OAuth flow, your application needs a client_id. There are two options:
Option 1: Client ID Metadata Document (recommended)
Host a JSON metadata document at an HTTPS URL and use that URL as your client_id. No registration step needed - Hostsmith fetches the metadata when the user authorizes.
Example: if your app is at https://myapp.example.com, host this at https://myapp.example.com/.well-known/oauth-client:
{
"client_id": "https://myapp.example.com/.well-known/oauth-client",
"client_name": "My App",
"redirect_uris": ["https://myapp.example.com/callback"]
}
Then use client_id=https://myapp.example.com/.well-known/oauth-client in the authorization request. The client_id field in the document must match the URL.
Option 2: Dynamic Client Registration
Register your client programmatically:
POST https://hostsmith.net/api/oauth/register
Content-Type: application/json
{
"client_name": "My App",
"redirect_uris": ["https://myapp.example.com/callback"]
}
Response:
{
"client_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client_name": "My App",
"redirect_uris": ["https://myapp.example.com/callback"]
}
Dynamically registered clients expire unless a user completes the authorization flow, which makes the client permanent.
Authorization flow
1. Generate PKCE challenge
PKCE is required for all authorization requests. Generate a random code_verifier and derive the code_challenge:
// Generate a random 43-character code verifier
const verifier = crypto.randomUUID() + crypto.randomUUID();
const encoder = new TextEncoder();
const digest = await crypto.subtle.digest("SHA-256", encoder.encode(verifier));
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
2. Redirect to authorize
Open the authorization URL in the user's browser:
https://hostsmith.net/api/oauth/authorize?
client_id=YOUR_CLIENT_ID&
redirect_uri=https://myapp.example.com/callback&
response_type=code&
code_challenge=YOUR_CHALLENGE&
code_challenge_method=S256&
scope=sites:read sites:write files:write&
state=RANDOM_STATE
Parameters:
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Your client ID (UUID or metadata URL) |
redirect_uri | Yes | Must match a registered redirect URI |
response_type | Yes | Always code |
code_challenge | Yes | S256 PKCE challenge |
code_challenge_method | Yes | Always S256 |
scope | Yes | Space-separated list of scopes |
state | Recommended | Random string to prevent CSRF - verify it on callback |
resource | Recommended | API resource identifier(s) - see Resource binding |
The user sees a consent screen listing the requested permissions. After approval, Hostsmith redirects to your redirect_uri:
https://myapp.example.com/callback?code=AUTH_CODE&state=RANDOM_STATE
3. Exchange code for tokens
POST https://hostsmith.net/api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTH_CODE&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://myapp.example.com/callback&
code_verifier=YOUR_VERIFIER
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "rt_a1b2c3d4..."
}
Making API requests
Include the access token as a Bearer token:
curl https://use2.api.hostsmith.net/v1/sites \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
A 401 response means the token is missing, expired, or invalid. Check the WWW-Authenticate header for details.
Refreshing tokens
Access tokens expire after 1 hour. Use the refresh token to get a new one:
POST https://hostsmith.net/api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=rt_a1b2c3d4...&
client_id=YOUR_CLIENT_ID
Response includes a new access token and a new refresh token (rotation). The old refresh token is invalidated.
Refresh tokens are valid for 90 days from the original authorization. After that, the user must re-authorize through the consent flow.
Revoking tokens
To revoke a refresh token (e.g. on user logout):
POST https://hostsmith.net/api/oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=rt_a1b2c3d4...&
client_id=YOUR_CLIENT_ID
Scopes
Request only the scopes your application needs:
| Scope | Description |
|---|---|
sites:read | List and view sites |
sites:write | Create, update, and delete sites |
domains:read | List and view domains |
files:write | Upload and manage site files |
Resource binding
The resource parameter binds the access token to specific API endpoints by setting the JWT aud (audience) claim. Each regional API validates that its identifier is present in the token's audience.
To bind a token to a single region, include one resource parameter:
https://hostsmith.net/api/oauth/authorize?
...&
resource=https://use2.api.hostsmith.net
To bind a token to multiple regions, include resource multiple times:
https://hostsmith.net/api/oauth/authorize?
...&
resource=https://use2.api.hostsmith.net&
resource=https://euc1.api.hostsmith.net
Each regional API publishes its resource identifier at /.well-known/oauth-protected-resource:
curl https://use2.api.hostsmith.net/.well-known/oauth-protected-resource
{
"resource": "https://use2.api.hostsmith.net",
"authorization_servers": ["https://hostsmith.net"],
"scopes_supported": ["sites:read", "sites:write", "domains:read", "files:write"]
}
If you need to call the API in both regions, include both resource identifiers when authorizing. The resulting token will be valid for both.
Regional endpoints
The API is deployed in multiple regions. Choose the region where your sites are hosted:
| Region | Endpoint | Resource identifier |
|---|---|---|
| US East (Ohio) | https://use2.api.hostsmith.net | https://use2.api.hostsmith.net |
| EU (Frankfurt) | https://euc1.api.hostsmith.net | https://euc1.api.hostsmith.net |
Include the resource identifier(s) in the resource parameter when authorizing to bind the token to the API.
