Docs Getting started
API and SDK
Everything you do in the dashboard you can do from code: create a project, send it code, wait until it is live and read its URL. A service that gives each of its own customers a server, a CI job that deploys previews, or a script that spins up a database for a test run all work this way.
Call the API from outside Dawnrose: your own servers, your laptop or CI. An app deployed on Dawnrose can’t reach the API or the dashboard from inside its container. To start a deploy from CI, you can also use the service’s deploy hook.
The REST API at https://app.dawnrose.ai/v1 works from any language. The SDKs for TypeScript, Python, Go, Ruby and PHP wrap it, one method per endpoint, and add deploy(), which does the whole create, send, wait cycle in one call. Every endpoint is listed in the API reference, and the machine-readable spec is at /v1/openapi.json.
Tokens
Every request carries a token in the Authorization header: Authorization: Bearer drt_…. Call the API from your server, never from a browser, since anyone who sees the token can use it.
| Token | Where to create it | Reaches |
|---|---|---|
| Project token | A project’s Settings → Tokens, or tokens.create | One project: its environments, services, deployments, variables and domains. Nothing else in the workspace. |
| Personal token | Account → Tokens | Everything you can reach yourself, including creating projects. |
A token acts with the permissions of the person who created it. Use a project token wherever you can: if it leaks, it exposes one project. A project token cannot create projects or tokens, and cannot read workspace settings, members or billing. Revoke a project token under the project’s Settings → Tokens; anything using it gets 401 from then on.
Install an SDK
npm install https://dawnrose.ai/sdk/dawnrose-sdk-0.1.0.tgz
pip install https://dawnrose.ai/sdk/dawnrose-0.1.0-py3-none-any.whl
go get dawnrose.ai/go
gem install dawnrose --clear-sources --source https://dawnrose.ai/gems
# or in a Gemfile:
# source "https://dawnrose.ai/gems" do
# gem "dawnrose"
# end
# in composer.json:
# "repositories": [
# { "type": "composer", "url": "https://dawnrose.ai/composer" }
# ]
composer require dawnrose/sdkNone of them has dependencies. They need Node.js 18 (or Bun or Deno), Python 3.8, Go 1.21, Ruby 3.0 or PHP 8.1. Each reads the token from DAWNROSE_TOKEN when you don’t pass one, and the address from DAWNROSE_URL for a self-hosted Dawnrose.
Every file under https://dawnrose.ai/sdk/, such as the TypeScript tarball and the Python wheel, is listed with its SHA-256 in a checksum file. To check a file before you install it, download it, then run this in the same folder. Each file you downloaded should print OK.
curl -fLO https://dawnrose.ai/sdk/SHA256SUMS
shasum -a 256 -c --ignore-missing SHA256SUMS
On Linux, sha256sum -c --ignore-missing SHA256SUMS does the same as the second line. Then install from the file, such as npm install ./dawnrose-sdk-0.1.0.tgz. The Go module and the Ruby gem come from other addresses and aren’t in the list.
Deploy a server in one call
import { Dawnrose } from "@dawnrose/sdk";
const dawnrose = new Dawnrose({ token: process.env.DAWNROSE_TOKEN });
const { url, service } = await dawnrose.deploy({
project: "customer-42",
service: "api",
files: {
"package.json": JSON.stringify({ scripts: { start: "node server.js" } }),
"server.js": `require("http")
.createServer((q, r) => r.end("hi"))
.listen(process.env.PORT)`,
},
variables: { CUSTOMER_ID: "42" },
});
console.log(url); // https://api-….up.dawnrose.ai
from dawnrose import Dawnrose
dawnrose = Dawnrose() # reads DAWNROSE_TOKEN
result = dawnrose.deploy(
project="customer-42",
service="api",
files={"main.py": open("main.py").read()}, # a server that listens on $PORT
deploy_config={"startCommand": "python main.py"},
variables={"CUSTOMER_ID": "42"},
)
print(result.url) # https://api-….up.dawnrose.ai
client, err := dawnrose.New("") // reads DAWNROSE_TOKEN
if err != nil {
log.Fatal(err)
}
res, err := client.Deploy(ctx, dawnrose.DeployOptions{
Project: "customer-42",
Service: "api",
Files: map[string]string{"main.go": code, "go.mod": "module api\n\ngo 1.22\n"},
Variables: map[string]string{"CUSTOMER_ID": "42"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.URL) // https://api-….up.dawnrose.ai
require "dawnrose"
dawnrose = Dawnrose::Client.new # reads DAWNROSE_TOKEN
result = dawnrose.deploy(
project: "customer-42",
service: "api",
files: { "app.rb" => File.read("app.rb") }, # a server that listens on $PORT
deploy_config: { "startCommand" => "ruby app.rb" },
variables: { "CUSTOMER_ID" => "42" }
)
puts result.url # https://api-….up.dawnrose.ai
<?php
use Dawnrose\Client;
$dawnrose = new Client(); // reads DAWNROSE_TOKEN
$result = $dawnrose->deploy(
project: 'customer-42',
service: 'api',
files: ['index.php' => file_get_contents('index.php')],
variables: ['CUSTOMER_ID' => '42'],
);
echo $result->url; // https://api-….up.dawnrose.aideploy() finds the project by name or id and creates it if it doesn’t exist, then does the same for the environment (production by default) and the service. It sends the code, sets the variables, gives the service a generated domain, and resolves once the new version is live. Calling it again with the same names deploys a new version of the same service. If the build fails or the app crashes, it throws a DeploymentFailedError (in Go, returns a *dawnrose.DeploymentFailedError) whose deployment says what happened.
The options are the same in every language, written the way that language writes names: deployConfig in TypeScript and PHP, deploy_config in Python and Ruby, Deploy in Go.
| Option | What it does |
|---|---|
project | Project id or name. A new name creates the project; with a project token, use the token’s project. |
environment | Environment id or name. Default production; a new name creates it. |
service | Service name. Default app. |
files, image or repo | The code: files as path → content (up to 20 MB), a Docker image such as nginx:1.27, or a GitHub repository owner/name with branch and rootDir. |
variables | Variables to set. Values can reference other services, such as ${{ Postgres.DATABASE_URL }}. |
build, deployConfig | Build and run settings: builder, start command, port, replicas, healthcheck, CPU and memory limits, sleep when idle. See Deploying. |
domain | Give the service a public URL. Default true. |
wait, timeoutSec | Wait until it is live, default true, for up to 900 seconds. |
Files are built like any other source: Dawnrose detects the language, installs dependencies and starts the app on $PORT, or uses your Dockerfile when there is one. Binary files can be passed as Uint8Array. For a bigger source, send a .tar.gz with services.upload(id, archive) (up to 500 MB).
More than one service
Every SDK has a method for every endpoint, grouped like the reference: projects, environments, services, deployments, variables, domains, functions, templates and tokens. The names follow the reference in each language’s style: services.deployFiles is services.deploy_files in Python and Ruby, Services.DeployFiles in Go and services->deployFiles in PHP. Responses are plain dictionaries, hashes and arrays, except in Go and TypeScript, which have types. A customer project with its own database and a one-file function, in TypeScript:
const project = await dawnrose.projects.create({ name: "customer-42" });
const env = project.environments[0];
// a Postgres with its volume and DATABASE_URL
await dawnrose.templates.deploy(env.id, "postgres");
// the app, from a repository, wired to the database over the private network
const api = await dawnrose.services.create(env.id, {
name: "api",
source: { repo: "acme/api", branch: "main" },
variables: { DATABASE_URL: "${{ Postgres.DATABASE_URL }}" },
generateDomain: true,
});
await dawnrose.deployments.wait(api.deployment.id, { throwOnFailure: true });
// a TypeScript function that answers HTTP on its own URL
const fn = await dawnrose.functions.create(env.id, {
name: "webhook",
runtime: "typescript",
code,
});
// a token for this project only, for code that runs outside Dawnrose
const { token } = await dawnrose.tokens.create(project.id, {
name: "customer-42 CI",
});
services.request(id, { path: "/health" }) sends an HTTP request to a service over the private network, without a public domain, and services.exec(id, "npm run migrate") runs a command in it. To call an endpoint the methods don’t wrap, use dawnrose.request(method, path, { query, body }).
The REST API
The same steps with curl. Every response is JSON.
TOKEN=drt_…
API=https://app.dawnrose.ai/v1
# a project comes with a production environment
curl -s -X POST $API/projects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "customer-42"}'
# a service with a public URL
curl -s -X POST $API/environments/ENV_ID/services \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "api", "generateDomain": true}'
# send the code: this starts a build and returns the deployment
curl -s -X POST $API/services/SERVICE_ID/files \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"files": [
{ "path": "server.js", "content": "…" },
{ "path": "package.json", "content": "…" }
]
}'
# wait until it is live; the service's url field is its address
curl -s "$API/deployments/DEPLOYMENT_ID/wait?timeout=600" \
-H "Authorization: Bearer $TOKEN"
curl -s $API/services/SERVICE_ID -H "Authorization: Bearer $TOKEN"
Generate a client in another language from /v1/openapi.json; it is OpenAPI 3.0.
Errors
A request that fails returns a 4xx or 5xx status and a body like this:
{
"error": {
"message": "A service named \"api\" already exists in production",
"hint": "Pick another name.",
"code": "error"
}
}
The hint says what to do about it. In the SDK, failures throw DawnroseError with status, code and hint. Read requests are retried on network errors and on 429, 502, 503 and 504; writes are not, so nothing happens twice.
| Status | Means |
|---|---|
401 | No token, or it was revoked. |
402 | Over a plan limit, such as projects or services per project. The message names the limit; see Scaling and plan limits. |
403 | The token’s owner lacks the role, or a project token reached outside its project. |
404 | Not found, or not visible to this token. |
409 | A conflict, such as a duplicate name or a service with no running deployment. |
Versioning
Everything under /v1 stays compatible: new endpoints and new fields can appear, but nothing is removed or renamed. Responses can carry fields the reference doesn’t list yet; ignore the ones you don’t use. The dashboard’s own API under /api changes with the dashboard, so build on /v1.