API

Domains and TLS

Attach hostnames, verify DNS, and manage certificates.

Domains connect hostnames to a project and track DNS verification and TLS certificates. The SDK takes a project ID as the first argument when creating or listing domains; REST includes it in the body or query.

Add a domain

const result = await ship.domains.create("proj_123", {
  hostname: "app.example.com",
});
console.log(result);
curl -X POST "$OPENSHIP_URL/api/domains" \
  -H "Authorization: Bearer $OPENSHIP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"proj_123","hostname":"app.example.com"}'

Prop

Type

Adding a domain doesn't make it live by itself

After adding, you must point your DNS at Openship and then verify. Use POST /api/domains/preview (or openship domain preview <hostname>) to see the exact records to add at your DNS provider first.

Preview required DNS

Side-effect-free: pass a hostname, get back the records to create. POST is used only so the hostname travels in the body.

const result = await ship.domains.preview({
  hostname: "app.example.com",
});
console.log(result);
curl -X POST "$OPENSHIP_URL/api/domains/preview" \
  -H "Authorization: Bearer $OPENSHIP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"app.example.com"}'

The response's mode tells you which setup applies: selfhosted (an A record to your server), cloud (a CNAME to Openship Cloud), or external (a TXT record only — you front TLS yourself).

Verify a domain

const verification = await ship.domains.verify("dom_123");
console.log(verification);
curl -X POST "$OPENSHIP_URL/api/domains/dom_123/verify" \
  -H "Authorization: Bearer $OPENSHIP_TOKEN"

Once DNS is in place, verify it. Openship Cloud checks its routing and ownership records. Self-hosted verification obtains or reuses the certificate on the deployment server. Domains with external ingress keep TLS at their upstream proxy.

Wildcard HTTPS and manual TXT

Wildcard and DNS-01 domains on self-hosted targets use the same operations in the dashboard, SDK and MCP. Deploy and enable the project first. An external ingress or uploaded certificate keeps its existing TLS flow.

// "automatic" uses a connected DNS provider. "manual" generates a real ACME TXT.
const attempt = await ship.domains.startDnsChallenge("dom_123", { mode: "manual" });
const current = await ship.domains.dnsChallenge("dom_123");
// Once current.status is "waiting", publish current.record at the DNS provider.
// Then explicitly ask Openship to check it:
await ship.domains.checkDnsChallenge("dom_123", { attemptId: attempt.id });

Start and check return 202 with { data: attempt }; they do not hold the HTTP request open for certificate issuance. Read GET /api/domains/:id/dns/challenge until the result settles. It returns { data: null } before any attempt. The safe attempt includes id, domainId, mode, status, record, expiresAt, logs, error, createdAt and updatedAt. It never includes private keys, account credentials or serialized ACME orders.

preparing, checking, installing and cancelling represent running work. waiting needs the operator's TXT; stop polling until they act. completed, failed, cancelled and expired are settled results. A missing TXT or retryable validation/install error keeps the same order in waiting with its error. Repeated starts reuse an active attempt; check and cancel require its current attemptId and reject a stale ID with 409. An installation that has already started cannot be cancelled.

Manual renewal uses startDnsChallenge(id, { mode: "manual", force: true }) and a fresh TXT value. cancelDnsChallenge(id, { attemptId }) discards an unfinished manual attempt without removing existing TLS. Connected-provider mode renews automatically when the SSL job is enabled. See the wildcard guide for DNS names, record cleanup and renewal steps.

Errors

422 — verification failed

The DNS records aren't resolving yet, or don't match. DNS can take minutes to hours to propagate. Confirm the records match preview exactly, then retry. The response's cnameVerified / txtVerified flags tell you which check is still failing.

404 on a per-domain route

The :id doesn't belong to your organization (or was deleted). List your domains with GET /api/domains to get valid IDs.

Operations

OperationSDKREST API
Stream domain verification progress.domains.verifyStream(id, input?, options?)POST /api/domains/:id/verify/stream
domain:write
List domains using the supplied filters.domains.list(id)GET /api/domains
domain:list
Attach a domain using the requested project and routing configuration.domains.create(id, input)POST /api/domains
domain:write
Read one domain record.domains.get(id)GET /api/domains/:id
domain:read
Remove a domain and its routing binding.domains.remove(id)DELETE /api/domains/:id
domain:admin
Run domain verification.domains.verify(id, input?)POST /api/domains/:id/verify
domain:write
Read DNS records required by a domain.domains.records(id, input?)GET /api/domains/:id/records
domain:read
Preview DNS changes before applying them.domains.dnsPlan(id, input?)GET /api/domains/:id/dns/plan
domain:read
Apply the authorized DNS plan.domains.dnsApply(id, input?)POST /api/domains/:id/dns/apply
domain:write
Read the saved DNS certificate attempt, TXT record, deadline and logs.domains.dnsChallenge(id)GET /api/domains/:id/dns/challenge
domain:read
Start automatic provider setup or a manual TXT challenge; reuse an active attempt. Self-hosted targets only.domains.startDnsChallenge(id, input)POST /api/domains/:id/dns/challenge
domain:write
Check the saved attempt's TXT, complete ACME validation and install HTTPS on the serving host.domains.checkDnsChallenge(id, input)POST /api/domains/:id/dns/challenge/check
domain:write
Cancel a manual DNS certificate attempt before installation starts; retain existing certificates.domains.cancelDnsChallenge(id, input)POST /api/domains/:id/dns/challenge/cancel
domain:write
Select the project’s primary domain.domains.setPrimary(id)POST /api/domains/:id/primary
domain:write
Request certificate renewal for a domain record.domains.renewSsl(id)POST /api/domains/:id/renew
domain:write
Check the domain’s current certificate.domains.verifySsl(id)POST /api/domains/:id/verify-ssl
domain:write
Install a supplied certificate and private key.domains.uploadCert(id, input)POST /api/domains/:id/certificate
domain:write · Self-hosted
Preview domain connection requirements.domains.preview(input)POST /api/domains/preview
domain:read
Request renewal for eligible certificates.domains.renewAllSsl()POST /api/domains/renew-all
domain:write
Verify pending domains visible to the caller.domains.verifyPending(input?)POST /api/domains/verify-pending
domain:write

On this page