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.
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/domaincurl "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
| Parameter | Type | Required | Description |
|---|---|---|---|
domain | string | Yes | The 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:
| Type | Name | Value | Purpose |
|---|---|---|---|
CNAME | app (or @ for root) | edge.oblien.com | Route traffic to Oblien edge |
TXT | _oblien.app.example.com | verify=ws_a1b2c3d4 | Prove 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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
domain | string | Yes | — | Domain to connect (e.g. example.com, app.example.com) |
port | number | No | 3000 | Workspace port to route traffic to (1–65535) |
include_www | boolean | No | false | Also 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
- DNS verification — checks ownership requirements and current edge DNS signal
- Route registration — writes the domain→workspace mapping to etcd immediately
- SSL request — reuses or requests a certificate when the domain already resolves to an approved edge target
- Config storage — saves domain config, including
ssl.status,ssl.expiresAt, and optionalssl.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
| Code | Status | Cause |
|---|---|---|
invalid_domain | 400 | Domain format is invalid |
invalid_port | 400 | Port out of range (1–65535) |
domain_in_use | 409 | Domain is connected to another workspace |
dns_not_configured | 400 | Standard plans: TXT ownership missing or wrong. Enterprise: no valid edge DNS signal yet |
ssl_failed | 500 | SSL certificate provisioning failed |
vm_not_running | 409 | Workspace 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/domaincurl -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/renewcurl -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
| Code | Status | Cause |
|---|---|---|
no_domain | 404 | No custom domain connected to this workspace |
ssl_failed | 500 | ACME certificate renewal failed |
Endpoint summary
| Endpoint | Method | Description |
|---|---|---|
/workspace/:id/domain | GET | Get current domain config |
/workspace/:id/domain | POST | Connect a custom domain |
/workspace/:id/domain | DELETE | Disconnect custom domain |
/workspace/:id/domain/check | POST | Verify DNS configuration |
/workspace/:id/domain/ssl/renew | POST | Renew SSL certificate |
/domain/check-slug | POST | Check slug availability |
/domain/verify | POST | Verify 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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
slug | string | Yes | — | Desired slug (3-48 chars, lowercase alphanumeric + hyphens) |
domain | string | No | preview.oblien.com | Base 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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
domain | string | Yes | — | Custom domain to verify (e.g. app.example.com) |
resource_id | string | No | — | 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.