Skip to main content
Docs Authentication

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.

Looking for the Hostsmith MCP server (Claude Desktop, Claude Code, Cursor, etc.)? See /docs/mcp - the OAuth flow is automated by your client.

Overview

  1. Your application redirects the user to Hostsmith to authorize access
  2. The user reviews the requested permissions and approves
  3. Hostsmith redirects back to your application with an authorization code
  4. Your application exchanges the code for an access token
  5. 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",
    "domains:write",
    "files:write",
    "account:read"
  ]
}

Client registration

Before starting the OAuth flow, your application needs a client_id. There are three options:

Sign in to Hostsmith and open Developers → OAuth Clients. Click New client, give it a name and one or more redirect URIs, then copy the generated client_id. The client is owned by your organization and can be revoked at any time from the same page. No client secret is issued (Hostsmith clients are public + PKCE).

Use this when you are setting up a third-party integration interactively (for example, configuring the n8n Hostsmith node for an n8n Cloud or self-hosted instance).

Option 2: Client ID Metadata Document (for self-hosted apps)

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 3: Dynamic Client Registration (for programmatic / MCP clients)

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:

ParameterRequiredDescription
client_idYesYour client ID (UUID or metadata URL)
redirect_uriYesMust match a registered redirect URI
response_typeYesAlways code
code_challengeYesS256 PKCE challenge
code_challenge_methodYesAlways S256
scopeYesSpace-separated list of scopes
stateRecommendedRandom string to prevent CSRF - verify it on callback
resourceRecommendedAPI 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://us.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:

ScopeDescription
sites:readList and view sites
sites:writeCreate, update, and delete sites
domains:readList and view domains
domains:writeCreate, update, and delete domains
files:writeUpload and manage site files
account:readView account info and plan limits

Resource binding

The resource parameter binds the access token to specific API endpoints by setting the JWT aud (audience) claim. Each partition's API validates that its identifier is present in the token's audience.

To bind a token to a single partition, include one resource parameter:

https://hostsmith.net/api/oauth/authorize?
  ...&
  resource=https://us.api.hostsmith.net

To bind a token to multiple partitions, include resource multiple times:

https://hostsmith.net/api/oauth/authorize?
  ...&
  resource=https://us.api.hostsmith.net&
  resource=https://eu.api.hostsmith.net

Each partition's API publishes its resource identifier at /.well-known/oauth-protected-resource:

curl https://us.api.hostsmith.net/.well-known/oauth-protected-resource
{
  "resource": "https://us.api.hostsmith.net",
  "authorization_servers": ["https://hostsmith.net"],
  "scopes_supported": ["sites:read", "sites:write", "domains:read", "domains:write", "files:write", "account:read"]
}

If you need to call the API in both partitions, include both resource identifiers when authorizing. The resulting token will be valid for both.

Partition endpoints

The API is served per data partition. Choose the partition where your sites are hosted:

PartitionLabelEndpointResource identifier
usUnited Stateshttps://us.api.hostsmith.nethttps://us.api.hostsmith.net
euEuropean Unionhttps://eu.api.hostsmith.nethttps://eu.api.hostsmith.net

The unauthenticated GET /v1/partitions endpoint returns the live list with labels.

Include the resource identifier(s) in the resource parameter when authorizing to bind the token to the API.