Domains
Connect a custom domain to a workspace with automatic SSL. For an overview of all domain options (preview URLs, app subdomains, custom domains, enterprise prefixes), 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, sslExpiry: "2026-06-10" }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,
"sslExpiry": "2026-06-10"
}
}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,
"verified": true,
"cname": true,
"ownership": true,
"records": {
"cname": ["edge.oblien.com"],
"txt": ["verify=ws_a1b2c3d4"]
},
"errors": []
}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.
Connect domain
Connect a custom domain to a workspace. Verifies DNS, provisions an SSL certificate via ACME, and registers the routing rule.
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": "active",
"expiresAt": "2026-06-10"
},
"url": "https://app.example.com"
}What happens
- DNS verification — checks CNAME/A and TXT ownership records via Google DoH
- SSL provisioning — obtains a certificate from ACME (or reuses an existing valid cert)
- Route registration — writes the domain→workspace mapping to etcd
- Config storage — saves domain config in the workspace record
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 | CNAME or TXT records not set up correctly |
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 SSL entries.
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 | 400 | 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 |