API Reference

Domains

Connect a custom domain to a workspace with managed SSL. Routes are registered immediately; SSL becomes active once the domain resolves to an approved edge target. For an overview of all domain options, see the Domains guide.

Hostname redirects and routing

Open Dashboard → Routes → hostname → Routing to select a proxy, static Page or redirect, set an application upload limit, and view publication/version history. The Routes API provides the same controls for resellers.

Apex and www are separate registrations and each HTTPS hostname needs a valid certificate. To redirect apex to www (or the reverse), configure a redirect on the source hostname and keep the destination serving the application. No implicit www stripping occurs. The existing include_www option creates a www → apex forward; it cannot be combined with an opposing apex → www redirect.

A successful save can return publication.state: "pending" while automatic registry delivery retries. Consult the routing status separately from TLS status.

Get domain

Get the current custom domain configuration for a workspace.

const domain = await ws.domains.get('ws_a1b2c3d4');

console.log(domain);
// {
//   customDomain: "app.example.com",
//   port: 3000,
//   sslStatus: "pending",
//   sslExpiry: null,
//   sslError: "DNS is not pointed to edge yet",
//   includeWww: false
// }
GET /workspace/:workspaceId/domain
curl "https://api.oblien.com/workspace/ws_a1b2c3d4/domain" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "domain": {
    "customDomain": "app.example.com",
    "port": 3000,
    "sslStatus": "pending",
    "sslExpiry": null,
    "sslError": "DNS is not pointed to edge yet",
    "includeWww": false
  }
}

Returns "domain": null if no custom domain is connected.


Check DNS

Verify DNS records are correctly configured before connecting. This is a dry-run — no changes are made.

const check = await ws.domains.checkDNS('ws_a1b2c3d4', {
  domain: 'app.example.com',
});

if (check.verified) {
  console.log('DNS is ready — CNAME and ownership verified');
} else {
  console.log('Issues:', check.errors);
}
POST /workspace/:workspaceId/domain/check
{
  "domain": "app.example.com"
}
curl -X POST "https://api.oblien.com/workspace/ws_a1b2c3d4/domain/check" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "app.example.com" }'

Parameters

ParameterTypeRequiredDescription
domainstringYesThe domain to verify

Response

{
  "success": true,
  "domain": "app.example.com",
  "enterprise": false,
  "verified": true,
  "cname": true,
  "ownership": true,
  "records": {
    "CNAME": ["edge.oblien.com"],
    "TXT": ["verify=ws_a1b2c3d4"]
  },
  "errors": [],
  "required_records": {
    "cname": { "host": "app.example.com", "target": "edge.oblien.com" },
    "txt": { "host": "_oblien.app.example.com", "value": "verify=ws_a1b2c3d4" }
  },
  "edge_ips": ["65.109.38.240"]
}

DNS records required

Before connecting, add these DNS records at your domain provider:

TypeNameValuePurpose
CNAMEapp (or @ for root)edge.oblien.comRoute traffic to Oblien edge
TXT_oblien.app.example.comverify=ws_a1b2c3d4Prove domain ownership

If using Cloudflare with proxy enabled (orange cloud), set an A record pointing to 65.109.38.240 instead of a CNAME. The TXT ownership record is still required.

Enterprise custom-domain grants can connect with edge DNS only. Those accounts may also use a branded TXT prefix such as _opsh instead of _oblien.


Connect domain

Connect a custom domain to a workspace. Oblien verifies ownership rules, registers the route immediately, and requests SSL if the domain is already pointed at an approved edge target.

const result = await ws.domains.connect('ws_a1b2c3d4', {
  domain: 'app.example.com',
  port: 3000,
  include_www: false,
});

console.log(result.url);
// "https://app.example.com"
POST /workspace/:workspaceId/domain
{
  "domain": "app.example.com",
  "port": 3000,
  "include_www": false
}
curl -X POST "https://api.oblien.com/workspace/ws_a1b2c3d4/domain" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "app.example.com", "port": 3000 }'

Parameters

ParameterTypeRequiredDefaultDescription
domainstringYes—Domain to connect (e.g. example.com, app.example.com)
portnumberNo3000Workspace port to route traffic to (1–65535)
include_wwwbooleanNofalseAlso redirect www. to the domain

Response

{
  "success": true,
  "domain": "app.example.com",
  "port": 3000,
  "ssl": {
    "status": "pending",
    "expiresAt": null,
    "error": "A/AAAA resolves to 104.21.1.12, 172.67.2.34 — not a known edge IP. Either CNAME to edge.oblien.com or point A record to one of: 65.109.38.240"
  },
  "dns": {
    "verified": false,
    "cname": false,
    "ownership": true,
    "errors": [
      "A/AAAA resolves to 104.21.1.12, 172.67.2.34 — not a known edge IP. Either CNAME to edge.oblien.com or point A record to one of: 65.109.38.240"
    ]
  },
  "url": "https://app.example.com"
}

What happens

  1. DNS verification — checks ownership requirements and current edge DNS signal
  2. Route registration — writes the domain→workspace mapping to etcd immediately
  3. SSL request — reuses or requests a certificate when the domain already resolves to an approved edge target
  4. Config storage — saves domain config, including ssl.status, ssl.expiresAt, and optional ssl.error

If ownership is valid but edge DNS is not ready yet, the connect request can still succeed with ssl.status: "pending". Run renew SSL later or wait for propagation.

Errors

CodeStatusCause
invalid_domain400Domain format is invalid
invalid_port400Port out of range (1–65535)
domain_in_use409Domain is connected to another workspace
dns_not_configured400Standard plans: TXT ownership missing or wrong. Enterprise: no valid edge DNS signal yet
ssl_failed500SSL certificate provisioning failed
vm_not_running409Workspace VM has no IP (not running)

Disconnect domain

Remove a custom domain from a workspace. Deletes the routing rule and clears the active SSL material from the edge.

await ws.domains.disconnect('ws_a1b2c3d4');
DELETE /workspace/:workspaceId/domain
curl -X DELETE "https://api.oblien.com/workspace/ws_a1b2c3d4/domain" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "removed": "app.example.com"
}

Renew SSL

Force SSL certificate renewal for the connected domain. Certificates auto-renew, but you can trigger a manual renewal if needed.

const result = await ws.domains.renewSSL('ws_a1b2c3d4');

console.log(result.ssl.expiresAt);
// "2026-09-08"
POST /workspace/:workspaceId/domain/ssl/renew
curl -X POST "https://api.oblien.com/workspace/ws_a1b2c3d4/domain/ssl/renew" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "domain": "app.example.com",
  "ssl": {
    "status": "active",
    "expiresAt": "2026-09-08"
  }
}

Errors

CodeStatusCause
no_domain404No custom domain connected to this workspace
ssl_failed500ACME certificate renewal failed

Endpoint summary

EndpointMethodDescription
/workspace/:id/domainGETGet current domain config
/workspace/:id/domainPOSTConnect a custom domain
/workspace/:id/domainDELETEDisconnect custom domain
/workspace/:id/domain/checkPOSTVerify DNS configuration
/workspace/:id/domain/ssl/renewPOSTRenew SSL certificate
/domain/check-slugPOSTCheck slug availability
/domain/verifyPOSTVerify custom domain DNS

Standalone endpoints

These endpoints are not scoped to a specific workspace — use them for pre-flight checks before connecting domains or claiming slugs.

Check slug availability

Check whether a slug is available on a wildcard domain. No DNS check is needed since these are platform-managed subdomains.

POST /domain/check-slug
{
  "slug": "my-app",
  "domain": "preview.oblien.com"
}
curl -X POST "https://api.oblien.com/domain/check-slug" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "slug": "my-app" }'

Parameters

ParameterTypeRequiredDefaultDescription
slugstringYes—Desired slug (3-48 chars, lowercase alphanumeric + hyphens)
domainstringNopreview.oblien.comBase domain to check against

Response

{
  "success": true,
  "slug": "my-app",
  "domain": "preview.oblien.com",
  "hostname": "my-app.preview.oblien.com",
  "url": "https://my-app.preview.oblien.com",
  "available": true
}

Verify custom domain DNS

Pre-flight DNS check for custom domains. Verifies CNAME or A/AAAA resolution to the edge and, for standard plans, the TXT ownership record.

POST /domain/verify
{
  "domain": "app.example.com",
  "resource_id": "ws_a1b2c3d4"
}
curl -X POST "https://api.oblien.com/domain/verify" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "app.example.com", "resource_id": "ws_a1b2c3d4" }'

Parameters

ParameterTypeRequiredDefaultDescription
domainstringYes—Custom domain to verify (e.g. app.example.com)
resource_idstringNo—Workspace ID or page slug for TXT ownership check

Response

{
  "success": true,
  "domain": "app.example.com",
  "enterprise": false,
  "verified": true,
  "cname": true,
  "ownership": true,
  "records": {
    "CNAME": ["edge.oblien.com"],
    "TXT": ["verify=ws_a1b2c3d4"]
  },
  "errors": [],
  "required_records": {
    "cname": { "host": "app.example.com", "target": "edge.oblien.com" },
    "txt": { "host": "_oblien.app.example.com", "value": "verify=ws_a1b2c3d4" }
  },
  "edge_ips": ["65.109.38.240"]
}

Enterprise users with custom domains see "enterprise": true — only edge DNS verification is required, and required_records won't include a TXT entry. The TXT host may also use a branded prefix instead of _oblien.

List routes

All registered routes you own — ports, pages, edge proxies, edge tunnels, workspace and custom domains — each with the resource it's bound to (owner_type / owner_id).

GET /domain/routes
GET /domain/routes?namespace=<slug>

Namespace scoping. A namespace-scoped API key is pinned to its bound namespace and sees only that namespace's routes — ?namespace cannot widen it. Admin/dashboard callers may pass ?namespace to filter to one of their own namespaces. Results are always scoped to the caller's account.

Response

{
  "success": true,
  "data": [
    {
      "hostname": "api.example.com",
      "slug": "api",
      "domain": "example.com",
      "route_type": "host",
      "target": "https://203.0.113.45:8080",
      "owner_type": "edge_proxy",
      "owner_id": "12",
      "namespace": "team-a",
      "is_custom": true,
      "status": "active"
    }
  ]
}

List SSL certificates

Your SSL certificates with renewal status — expiry, whether auto-renew is on, and the last renewal error/attempt count (so a failing cert is visible before it expires).

GET /domain/ssls
GET /domain/ssls?namespace=<slug>

Same namespace scoping as /domain/routes.

Response

{
  "success": true,
  "data": [
    {
      "domain": "app.example.com",
      "namespace": "team-a",
      "active": true,
      "auto_renew": true,
      "expires_at": 1789200000000,
      "source": "workspace",
      "last_attempt_at": "2026-06-18 04:00:11",
      "last_error": null,
      "renew_attempts": 0
    }
  ]
}

Certificates are auto-renewed by a scheduler ~14 days before expiry. last_error / renew_attempts surface a cert whose renewal is failing.

Toggle SSL auto-renew

Enable or disable automatic renewal for a certificate you own. New certificates auto-renew by default.

PUT /domain/ssls/{domain}/auto-renew
{ "enabled": false }

Parameters

ParameterTypeRequiredDescription
domainstring (path)YesThe certificate's domain
enabledbooleanYestrue to auto-renew, false to opt out

The cert must be in the caller's account (and namespace, for scoped keys) — otherwise an opaque 404.

Response

{ "success": true, "domain": "app.example.com", "auto_renew": false }