runlot
ReferenceAPI

Schema

Describes the structure of API request bodies and responses. Each name maps to a body field in an API operation.

RepoRefs

FieldTypeRequiredDescription
defaultBranchstringYesThe HEAD on disk is authoritative. Repo.defaultBranch is a copy of it, and if they diverge, this value is correct (0032_repos.sql).
refsobject[]Yes

RepoTreeEntry

FieldTypeRequiredDescription
namestringYes
pathstringYespath relative to the repository root
kindstringYes
sizeintegerYesfiles only. directories are 0
oidstringYes

RepoTree

FieldTypeRequiredDescription
refstringYes
commitstringYes
pathstringYes
entriesRepoTreeEntry[]Yes
truncatedbooleanYesTruncated at 1000 entries

RepoBlob

FieldTypeRequiredDescription
refstringYes
commitstringYes
pathstringYes
oidstringYes
sizeintegerYes
binarybooleanYesContains a NUL byte or is not UTF-8. Content is empty
tooLargebooleanYesExceeds 1 MiB; content is not included
contentstringYes

RepoCommit

FieldTypeRequiredDescription
oidstringYes
messagestringYes
authorstringYes
emailstringYes
timestampstringYes

RepoName

Since this becomes a URL path segment, it cannot contain .. or a slash. The source of truth is the CHECK constraint in migration 0032 (keeping validation only in the application would let admin scripts bypass it); what is here is a copy of that. It cannot start with a dot. It is broader than the project name (a hostname label) — uppercase letters, dots, and underscores are allowed.

Type: string

RepoVisibility

Defaulting to private is intentional — if the default were open, the direction of a mistake would be a leak. public means anonymous clone is open (§5.1).

Type: string

TokenScope

A token inherits the user's permissions but can only lower them (§5.2). There is no arithmetic in the raising direction.

Type: string

Repo

FieldTypeRequiredDescription
repoIdstringYes
orgSlugSlugYes
projectSlugYesThe project this repository belongs to (§19). The name is unique only within it.
nameRepoNameYes
visibilityRepoVisibilityYes
defaultBranchstringYesThe HEAD on disk is the source of truth, and this value is a copy of it (0032_repos.sql). If they diverge, disk wins — this is pushed back to node-git's maintenance.
sizeBytesintegerYesAn approximation used for quota enforcement. The exact value lives on disk (§8.2).
lastPushedAtstringNo
createdAtstringYes
cloneUrlstringNohttps://<git-domain>/<org>/<project>/<repo>.git. Built by the CP — if the CLI assembled it, the local shape would leak into production (docs/dogfood-friction.md ⑤). Empty string if --git-domain is empty.
sshUrlstringNogit@<git-ssh-host>:<org>/<project>/<repo>.git. The name matches cloneUrl (§18). Empty string if --git-ssh-host is empty.

RefUpdate

FieldTypeRequiredDescription
refstringYes
oldstringNoNull means create. It is the only clue for recovering commits lost to a force-push.
newstringNoNull means delete
forcedbooleanYesWas a non-fast-forward push. Since the MVP has no protected branches, it is recorded rather than blocked (§5.1).
actorUserstringNo
createdAtstringYes

SSHKey

FieldTypeRequiredDescription
keyIdstringYes
namestringYes
keyTypestringYes
fingerprintstringYesGlobally UNIQUE, because the key is the identity (§5.3).
createdAtstringNo
lastUsedAtstringNo

AccessToken

FieldTypeRequiredDescription
tokenIdstringYesCarried in plaintext within the token string (runlot_pat_<id>_<secret>). That is why the lookup is a single index hit and the comparison runs in constant time (§5.2).
namestringYes
scopeTokenScopeYes
createdAtstringYes
expiresAtstringNo
lastUsedAtstringNo
revokedAtstringNo

AccessTokenCreated

FieldTypeRequiredDescription
tokenIdstringYes
namestringYes
scopeTokenScopeYes
tokenstringYesPlaintext. Carried in this response only once. The server stores only the SHA-256 hash, so it cannot be recreated.
expiresAtstringNo

Slug

Becomes a single label of the hostname, so it follows DNS rules. Since the project name goes directly into <name>.lvh.me, not enforcing this here would let you create a project that exists but is unreachable.

Type: string

Error

FieldTypeRequiredDescription
errorobjectYes

Session

FieldTypeRequiredDescription
tokenstringYes
expiresAtstringNo
userUserYes

User

FieldTypeRequiredDescription
userIdstringYes
providerstringYes
subjectstringYes
emailobjectNo
nameobjectNo

Org

FieldTypeRequiredDescription
orgIdstringYes
slugSlugYes
namestringYes
personalbooleanYes

OrgMembership

Type: Org & object

Notification

FieldTypeRequiredDescription
idintegerYes
kindstringYesThe kind of event (docs/limits.md §3.1). This is a value for machines to branch on; the human-readable sentence is produced separately by the dashboard and the email templates.
projectNamestringNoIf this is a project-related notification, the name of that project. Absent for org-level notifications (project_limit)
keystringYesA deduplication key per kind. Only one is created per (kind, key) in 24 hours.
detailobjectYesA few numbers that differ by kind. project_limit is {limit, count}, a usage alert is {bytes, capBytes}, and an operation alert is {opId, opKind, reason?}.
createdAtstringYes
sentAtstringNoWhen the email was sent. If absent, it is either still queued or there was no inbound email.

Project

FieldTypeRequiredDescription
projectIdstringYes
orgSlugSlugYes
nameSlugYes
hostnamesstring[]Yes
urlsstring[]Nohostnames turned into reachable addresses. The scheme and port are deployment-specific (production https://…, local http://…:8787), so the server builds this — if the client assembled it, the local shape would leak into production.
createdAtstringYes

ProjectDetail

Type: Project & object

Placement

FieldTypeRequiredDescription
statestringYes
epochintegerYes
homeNodeobjectNo
suspendedbooleanYesSeparate from the lifecycle state. A suspended project can still have state active, and lifting the suspension returns it to that state.

ProjectOverview

Project home page (getProjectOverview).

FieldTypeRequiredDescription
liveOverviewDeployment | nullYesThe deployment currently serving traffic. Null before the first deployment.
deploymentsOverviewDeployment[]YesRecent deployments, newest first. Up to 5 — use listDeployments for all of them.
deploymentCountintegerYesTotal number of deployments for this project.
databaseOverviewDatabaseYes
featuresOverviewFeaturesYes
customHostnamesintegerYesThe number of custom domain rows. Rows still being verified count too.
reposintegerYesNumber of repositories in this project.
notificationsNotification[]YesThis project's recent notifications, newest first. Up to 5.

OverviewDeployment

A deployment row from the overview. Has the same shape as Deployment (includes actor).

Type: Deployment

OverviewDatabase

FieldTypeRequiredDescription
grantedbooleanYeswhether it is turned on with "database": true
lastBackupAtstringNoTimestamp of the last generation. Absent if there is no generation.
lastCheckedAtstringNoLast heartbeat of the no-change skip (DatabaseGenerations.lastCheckedAt). If this value keeps moving even when the generation does not advance, the backup is alive.
generationsintegerYesnumber of generations

OverviewFeatures

Whether a resource enabled by declaration has been granted (docs/provisioning.md). The onboarding "next steps" screen picks the ones not yet enabled this way.

FieldTypeRequiredDescription
databasebooleanYes
storagebooleanYes
emailbooleanYes
authbooleanYes

Deployment

FieldTypeRequiredDescription
deploymentIdstringYes
versionintegerYes
artifactShastringYesSHA-256 (hex) of the deployment bundle tarball
createdAtstringYes
createdByobjectNo
livebooleanYes
actorstringNoDisplay name of the person who deployed (in order: name, then email, then provider subject). createdBy is a uuid, so the UI cannot turn it into a person. It appears only in lists and overviews, and is empty if there is no user row.
manifestManifestNoThe manifest exactly as it passed validation. Absent for deployments before 0040.
bundleBytesintegerNoThe bundle size after unpacking, not the compressed size.
bundleFilesintegerNoNumber of entries in the bundle (files + directories).
gitCommitstringNoThe HEAD hash sent by the CLI. For display only — the CP does not verify it.
gitMessagestringNoThe first line of the commit message, up to 200 characters.
gitBranchstringNo
gitDirtybooleanNoWhether the deployment shipped with uncommitted changes. If true, gitCommit is not exactly the code.

DatabaseEndpoint

FieldTypeRequiredDescription
hoststringYes
portintegerYes
databasestringYes
sslmodestringYes

DatabaseTokenMeta

FieldTypeRequiredDescription
userstringYes
expiresAtstringYes
createdAtstringYes

DatabaseToken

FieldTypeRequiredDescription
userstringYes
passwordstringYesIncluded only in this response.
expiresAtstringYes
connectConnectInfoYesSame format as DatabaseConnect.connect; user/password are the values of this token.

ConnectInfo

FieldTypeRequiredDescription
hoststringYesThe project's hostname. It is not a UUID because front decides placement by SNI (docs/actor-placement-routing.md §8).
portintegerYesThe wire listener port on node-front. This value is not computed by the CP; the operator tells both front and the CP what it is — the default is defined by the DefaultFrontWirePort constant in internal/slots.
databasestringYesThis is the project UUID. It is authoritative because front overwrites startup's database with pin's UUID.
userstringYesBootstrap role. Names do not collide because each database has its own.
passwordstringYes
sslmodestringYesThe disabled value never responds. front cuts off the SSLRequest at the rejection stage, so disable never connects in the first place.

DatabaseConnect

FieldTypeRequiredDescription
dbbooleanYes
keyVersionintegerNoThe version of this project's DEK (docs/phase5.md C2). Included only in the database creation response, and the field is absent entirely if it could not be secured. The reason it is not returned as 0 is that keyVersion=0 already means "plaintext generation". Marking a failed acquisition with this value could lead a user to mistakenly believe the backup is confirmed as plaintext. When absent, the backup scheduler secures it later.
connectConnectInfoYes

DatabaseExport

FieldTypeRequiredDescription
projectIdstringYes
epochintegerYesThe incarnation that created the image. Even for the same project, a different epoch means a different database, so this value matters as much as the key.
nodestringYesNode that took the image
exportobjectYes

DatabaseGenerations

FieldTypeRequiredDescription
projectIdstringYes
epochintegerYesThe currently running incarnation. Rows in the list can belong to an older epoch.
statestringYesPlacement status (active, cold, restoring, and so on)
lastCheckedAtstringNoLast heartbeat of the no-change skip. If empty, it has never been checked yet. This is the value that distinguishes a stalled generation from a dead backup, so it cannot be removed from the list (docs/actor-placement-routing.md §4.6-1).
generationsGenerationRow[]YesNewest first. An empty array is an array, not null

GenerationRow

A single backup generation. The field names are the same as GenerationRow in internal/backupproto. The actor metadata, the generation the node reports to the CP, and the records included in this API must use the same names, so that a change in one place is not silently interpreted as 0 elsewhere.

FieldTypeRequiredDescription
epochintegerYes
seqintegerYesIncreases monotonically within an epoch. Restore identifies a generation by (epoch, seq).
keystringYesOffsite key (gen/<project>/<epoch>/<stamp>-<seq>.feather)
bytesintegerYesThe size of the object stored offsite. An encrypted generation is larger than the plaintext, and the admission reservation is also calculated based on this value.
sha256stringYes
plainBytesintegerNoThe size of the plaintext (feather image) inside the encrypted data. For plaintext generations, this equals bytes
keyVersionintegerNoThe DEK version used for encryption. 0 means plaintext (a generation created without a DEK)
txnIdintegerNoCommit number that the image contains. 0 means unknown.
kindstringNoWhy the generation was created. Not used for decisions
createdAtstringYes
nodestringNoThe node that captured the generation
latestbooleanYesThe generation that gen/<project>/latest.json points to. It is conditionally monotonic, so a later generation from an old epoch cannot claim this marker (docs/phase5.md A1).
finalbooleanYesA safety copy taken right before eviction, deletion, or restore. It lives in the same table as regular generations, but pruning does not count it.

RestoreRequest

FieldTypeRequiredDescription
epochintegerYes
seqintegerYes

DeleteRequest

FieldTypeRequiredDescription
confirmstringYesMust match the project name exactly. The CLI's prompt alone is not enough — a script with --yes running in the wrong directory never shows the prompt at all.

OperationStarted

This response means the multi-step operation was queued (202). It does not mean it finished — check progress at …/database/operations/{opId}.

FieldTypeRequiredDescription
projectIdstringYes
opIdstringYes
kindstringYes
epochintegerYes
nodestringNo

Operation

FieldTypeRequiredDescription
opIdstringYes
kindstringYes
projectIdstringYes
phasestringYesThe step currently in progress (draining, sealing, committing, and so on). The CLI prints a line only when this value changes — printing on every poll would show the polling interval, not progress.
attemptintegerNo
deferralsintegerNo
ownerstringNo
terminalCodestringNoThe reason it ended. aborted means a person stopped it, and any other value is a failure. Empty with done means success.
failedAtstringNo
errorstringNo
donebooleanYesDoes not proceed further. terminalCode distinguishes success, failure, and abort
retryAfterSecondsintegerNoSeconds remaining until the next attempt. 0 means it is ready to process now. Once backoff applies, "in progress" can refer to two states, so this value distinguishes them.

Manifest

The runlot.json inside the bundle. It does not travel in the HTTP body, but it is part of the contract, so it is included here — the CLI writes it, cp-public reads it, and the config generator consumes it.

FieldTypeRequiredDescription
mainobjectNoThe worker entry point's path within the bundle. If absent, this is a pure static site (the same model as Workers Static Assets).
assetsobjectNoPath to the static assets directory within the bundle
compatibilityDatestringNo
notFoundstringNoThe answer for a path that is not in the assets. With spa, a path without an extension is served /index.html (client-side routing). The default is 404. A path with an extension is still a 404 under spa — a missing bundle must not become a silent 200.
compatibilityFlagsstring[]NoThe workerd compatibility flags to enable for the worker. Only flags on the allow list (internal/bundle.AllowedCompatibilityFlags) are accepted; the deployment is rejected otherwise — a single unsafe_module lets user code take over the process. Do not include experimental; the generator adds it on its own. nodejs_compat: for libraries (TypeORM, Sequelize, Knex) that require Node built-in modules (node:events, node:util, …). The CLI adds this when the bundle imports a Node built-in.
modulesManifestModule[]NoA workerd module loaded separately next to the entry point. Currently only wasm — since workerd blocks compiling WASM inside the bundle (for example, Prisma 7's query compiler), WASM can only enter this way. name is exactly the specifier the worker code imports, and path is the file inside the bundle. The CLI writes one entry per .wasm import left by the bundle.
frameworkstringNoA marker that the deploy tool bundled with a framework adapter (docs/nextjs.md §3). If next, the node also renders the two bindings that OpenNext looks for (ASSETS alias, WORKER_SELF_REFERENCE). Unknown values cause the deployment to be rejected.
triggersManifestTriggersNo
emailbooleanNoDeclares email sending and receiving (docs/email.md). When true, the worker receives env.email, and export default { email() } becomes the receiving handler. This only makes sense if main is present — static assets alone have no code to send or receive with.
aibooleanNoDeclares the AI runtime binding (docs/ai.md). When true and the project has an AI grant, the worker receives env.ai. This must travel in the bundle manifest: the control-plane grant alone cannot tell a particular deployment whether its code requested the binding.

ManifestTriggers

Scheduled execution (docs/cron.md). It ships in the bundle exactly as the user wrote it in runlot.json — it matches the shape of wrangler's triggers because people migrating over copy those three lines as is.

The only trigger known today is cron, but it is accepted as an object. If a queue or email trigger is added later, the string the user writes must not have to change.

FieldTypeRequiredDescription
cronsstring[]NoA 5-field cron expression (minute hour day month weekday), in UTC. It has 1-minute granularity and allows up to 5 per project (bundle.MaxCronsPerProject); the org-wide limit is set by the plan (plan.CronLimitFor). The expression is actually parsed at deploy time — invalid syntax or an expression that never fires in the future (0 0 30 2 *) returns 400. Accepting anything that merely looks like a cron expression would let the deployment succeed while nothing ever runs. If triggers is set, main must also be set: a deployment with only static assets uses the generator's entry point, which has no scheduled handler.

ManifestModule

FieldTypeRequiredDescription
namestringYesThe specifier the worker imports. Alphanumeric and ._-, slash-separated, does not start with .. worker cannot be used because it is the entry module.
typestringYes
pathstringYesFile path inside the bundle

Role

This is a containment relationship — anything a viewer can do, a member can also do, and anything a member can do, an admin can also do (cp/internal/public/public.go roleRank).

Type: string

Member

FieldTypeRequiredDescription
userIdstringYes
providerstringYes
subjectstringYes
emailobjectNo
nameobjectNo
roleRoleYes
addedAtstringYes

OrgHolding

One kind of thing the org still holds (the org_not_empty details).

FieldTypeRequiredDescription
kindstringYes
countintegerYes
namesstring[]NoOnly the first few. Listing all of them would make the error message overflow the screen.

InviteState

expired is the result of a time comparison, not a column — an invite isn't marked expired, its expiry time has simply passed.

Type: string

Invite

No token. The plaintext token only goes in the email, and cp-public can't even read that column (the column-level GRANT from migration 0043).

FieldTypeRequiredDescription
inviteIdstringYes
emailstringYes
roleRoleYes
stateInviteStateYes
invitedBystringNoThe inviter's name or email.
createdAtstringYes
expiresAtstringYes
sentAtstringNoIf empty, the send is still queued (sending is a cp-core cycle).
sendErrorstringNoThe reason the last send failed. This can remain set even after a success.

InviteView

The invite as seen by whoever holds the token (answered without authentication).

FieldTypeRequiredDescription
orgSlugstringYes
orgNamestringYes
roleRoleYes
emailstringYesThe address the invite was sent to. It can differ from the address of the account that accepts it, and the UI should say so.
invitedBystringNo
stateInviteStateYes
expiresAtstringYes

SecretName

Becomes the worker's env.<NAME>. DB and ASSETS (the uppercase counterparts of the env.db and env.assets bindings) and the RUNLOT_ prefix are reserved — the first two are already bindings, and the latter is the namespace Node uses for the process environment (docs/phase6.md B4).

Type: string

SecretMeta

FieldTypeRequiredDescription
nameSecretNameYes
versionintegerYesIncreases by 1 on each PUT. The node determines replacement by the digest of the name and version
updatedAtstringYes

SecretList

FieldTypeRequiredDescription
secretsSecretMeta[]Yes

LogEvent

A single line of the log stream. Distinguished by typeline is an output line, ping is a 15-second heartbeat, and end is the reason the server ended it.

FieldTypeRequiredDescription
typestringYes
tsstringNoThe time the node received the line (line)
epochintegerNo
generationintegerNoThe process generation within the node. Increments on replacement.
streamstringNorunlot is a line written by the node, not the worker — events such as startup, exit code, and the number of dropped lines.
linestringNoWithout a trailing newline. Past 64 KiB it is truncated and truncated is set.
truncatedbooleanNo
reasonstringNoReason for end: process_exited, client_gone, node_shutdown

Usage

FieldTypeRequiredDescription
projectIdstringYes
hoursintegerYes
rowsUsageRow[]Yes
totalsobjectYes
dbBytesintegerYesThe most recently observed repository bytes. 0 if there is no observation.
objBytesintegerYesThe most recently observed object bytes. This is a gauge, not a sum — adding it up across time buckets counts the same repository once per hour bucket (docs/storage.md §4.5).

UsageRow

FieldTypeRequiredDescription
hourstringYesstart of the time window (UTC, on the hour)
requestsintegerYes
cpuMsintegerYes
dbBytesintegerYes
objBytesintegerYesgauge (last observed value)
objOpsintegerYescumulative (sum of deltas)
mailOutintegerYescumulative (sum of deltas)
mailInintegerYescumulative (sum of deltas)

AIModel

카탈로그 한 줄 (docs/ai.md §2.3).

FieldTypeRequiredDescription
idstringYesenv.ai.run() 에 적는 문자열. 프론티어는 <provider>/<model>, 가성비 모델은 @runlot/<model>
lanestringYes
displaystringYes
vendorstringYes모델을 만든 곳 (Anthropic·Google·Meta…)
providerstringNo우리가 부르는 곳. 프론티어에서만 실린다 — 가성비 모델은 우리가 갈아끼우는 값이라 계약이 아니다.
contextTokensintegerYes모델 자체의 컨텍스트
maxInputTokensintegerYesRunlot 이 이 모델에 허용하는 입력 상한. 대부분 contextTokens 와 같고, GPT-5.6 Terra 는 272,000 — 그 뒤는 요청 전체가 장문 단가로 바뀌므로 표준 단가 하나만 광고하는 카탈로그가 그 앞에서 자른다.
maxOutputTokensintegerYes
inPerMTokintegerYes100 만 입력 토큰당 마이크로센트
outPerMTokintegerYes
cachedInPerMTokintegerNo캐시된 입력의 단가. 없으면 캐시 할인이 없는 모델이고 캐시된 토큰도 정가다
listPricebooleanYes참이면 프로바이더 정가 그대로다 (마진 0). 화면이 그 사실을 말하는 것이 이 제품의 문장 하나이고, 말하지 않으면 사용자는 마크업을 가정한다.

AIStatus

FieldTypeRequiredDescription
grantedbooleanYes
perMinuteintegerYes프로젝트마다 분당 호출 상한
concurrentintegerYes프로젝트마다 동시 호출 상한
maxOutputTokensintegerNo0 이면 모델이 정한다
maxInputTokensintegerNo0 이면 모델이 정한다
creditMicroCentsintegerYesorg 잔액 (음수 가능)
freeDailyMicroCentsintegerNo가성비 모델에만 주는 하루치
calls24hintegerNo
tokensIn24hintegerNo
tokensOut24hintegerNo
spentMicroCents24hintegerNo최근 24 시간 원장의 합 (양수 = 쓴 금액). usage_hourly 에서는 못 만드는 수다

AIAutoTopup

FieldTypeRequiredDescription
belowMicroCentsintegerYes
microCentsintegerYes

AICredit

FieldTypeRequiredDescription
enabledbooleanYes거짓이면 이 배치에 크레딧 상품이 없다 — 충전 끝점은 501
sandboxbooleanYes
microCentsintegerYes잔액 (음수 가능)
lowMicroCentsintegerNo
freeDailyMicroCentsintegerNo
tiersinteger[]Yes충전 단위. 자동 충전이 같은 목록을 쓴다
hasPaymentMethodbooleanYes거짓이면 자동 충전을 못 켠다 — 첫 충전이 카드를 저장한다
autoTopupAIAutoTopupNo
autoTopupsTodayintegerYes
autoTopupPerDayintegerYes이 수가 곧 하루 지출 천장이다

AILedgerRow

FieldTypeRequiredDescription
kindstringYes
modelstringNo
projectstringNo
tokensInintegerNo
tokensOutintegerNo
microCentsintegerYescall 은 음수
atstringYes

EmailStatus

The project's email status (docs/email.md §5).

FieldTypeRequiredDescription
grantedbooleanYesWhether it is on. The existence of the project_email row is the only representation (0038).
addressstringYesThe project's mail domain. It receives everything at *@<address>.
fromstringYesSender address. The From of mail sent by env.email.send.
perHourintegerYesHourly send limit. Shares the same bucket as authentication email.
sentLastHourintegerYesnumber sent in the current time window
receivedLastHourintegerYesnumber received in the current time window
sent24hintegerYesnumber sent in the last 24 hours
received24hintegerYesnumber received in the last 24 hours
mailDomainstringYesThe mail domain suffix for this batch. Email cannot be enabled if it is empty.

MailLogEntry

One line of the email log (docs/email.md §6.1).

FieldTypeRequiredDescription
mailIdstringYes
projectIdstringYes
directionstringYes
atstringYes
fromstringYes
tostring[]Yes
subjectstringYes
outcomestringYesOutbound: sentdelivered, bounced, complained (relay feedback, docs/email.md §6.2), or failed / Inbound: ok, rejected, no_handler, exception
detailstringNorejection sentence or error sentence — a human-readable line
authResultsstringNoAuthentication-Results of the received message
sizeBytesintegerYes
hasRawbooleanYeswhether the original text is present

MailLogPage

FieldTypeRequiredDescription
entriesMailLogEntry[]Yes
cursorstringNonext page. absent means the end
keepDaysintegerYesnumber of days this org retains

BillingStatus

FieldTypeRequiredDescription
enabledbooleanYesbilling is configured for this deployment
sandboxbooleanYesPolar test environment — no real billing
planstringYes
statusstringNoThe Pro status. Canceled stays Pro until the period ends. Free has none
periodEndstringNoend of the current billing period
hasPortalbooleanYeshas billing history, so the portal can be opened
projectCountintegerYesCurrent project count. The UI explains when an org exceeds the limit after downgrading to Free — existing projects are not deleted; only new creation is blocked
limitsobjectYes

PlanLimits

The limits for one plan (docs/limits.md §1)

FieldTypeRequiredDescription
projectsintegerYes
storageBytesintegerYes
backupKeepintegerYesnumber of retained generations (= time)
cronsintegerYes
mailPerHourintegerYes

StorageStatus

This is the state of the project's dedicated object storage (docs/storage.md §7).

Objects are not part of DB generationsrunlot pg restore does not roll back files (§4.6). Read-after-write is guaranteed, but list may lag.

FieldTypeRequiredDescription
grantedbooleanYesIndicates whether storage usage is provisioned. Since the existence of the usage record is the only representation of this (migration 0022), there is no state of "provisioned but turned off".
prefixstringYesThe prefix for the actual object key. User keys are stored only under this, and node-agent adds the prefix. If user keys required a project ID, sending a different one would be enough to access another project's bucket (§4.4). If storage is not configured, this is an empty string.
quotaBytesintegerYesThe quota (1 GiB on Free). Once you exceed it, only put and presign(PUT) are rejected — get, list, and delete remain allowed, since blocking deletes along with writes would leave a project stuck at the cap with no way out (§4.5). Without a grant, it is 0.
usedBytesintegerYesThe last observed byte count. This lags by the node's reporting interval, so you can slightly exceed the quota in between (open item in §10). 0 if there is no observation.

AccessPolicy

Deployment access policy (docs/access.md §3.1). Provides only non-secret state. Passwords and bypass keys are stored only as hashes, and the read API does not return values (docs/access.md §6, the rule for the secret surface).

A project that has never set a policy has mode: public with no updatedAt. The reason for distinguishing the two is that the UI states "this deployment is public" as a status statement (§7).

FieldTypeRequiredDescription
modestringYespublic means anyone, org means member or higher in the org the project belongs to, and password means one shared password per project. There is no other mode, because these values are the basis for the authorization (A3) policy — "anyone whose email domain is @acme.com" is not a boundary question.
hasPasswordbooleanYesWhether a password is stored. The value itself is never returned.
hasBypassbooleanYesIndicates whether a bypass key for automation exists. The value is shown only once, at issuance.
updatedAtstringNoOmitted if never set

AccessBypass

A bypass key for automation. It is included only in this response. You cannot view it again, and reissuing it immediately invalidates the previous key (docs/access.md §3.6).

FieldTypeRequiredDescription
secretstringYesPassed through as-is in the Runlot-Access-Bypass header

AccessAuthorize

The URL back to the protected deployment. A one-time code (60 seconds, single use, scoped to that project) is included in the query string, and the front exchanges it, sets a cookie, then issues a 302 back to the original URL without the query string. This 302 removes the code from the address bar (docs/access.md §3.4).

FieldTypeRequiredDescription
redirectstringYes

AuthProvider

A social login provider you can register a per-project OAuth app for. Currently supported providers are GitHub, Google, and Kakao, and additional providers are added based on demand (docs/auth.md §12 — SAML and OIDC arrive when enterprise customers do).

Type: string

AuthSettings

End-user auth settings (docs/auth.md §9). §12 explicitly does not support the email template editor, MFA, or the device list.

FieldTypeRequiredDescription
allowSignupbooleanYesWhether new signups are accepted. Turn it off to let only existing users log in — useful for invite-only apps or closing off a beta.
requireVerifiedEmailbooleanYesWhether email verification is required to log in. When enabled, unverified users cannot get a session from env.auth.user(request).
sessionDaysintegerYesThe lifetime of the session cookie, in days. It also affects the size of the session table. Since the project database is within the 150 MB cap (docs/auth.md §13), 10,000 active users each using three devices would produce 6 MB of session data.
brandAuthBrandYes

AuthBrand

Branding for the hosted login page (docs/auth.md §4). For finer-grained branding, build your own page and call the JSON endpoint in §5. If all values are empty strings, the project name and default design tokens are used.

FieldTypeRequiredDescription
namestringYesThe name shown on the page. Falls back to the project name if empty
logostringYesLogo image URL. Nothing is drawn if empty
colorstringYesAccent color #rrggbb. Defaults to @runlot/design's default when empty.

AuthProviderMeta

A single registered login provider. Secrets are not included. The client ID is a public value also exposed to the browser, and the client secret is encrypted and never included in any response (secret-surface rule, docs/access.md §6).

FieldTypeRequiredDescription
providerAuthProviderYes
clientIdstringYesThe public id given by the provider
updatedAtstringYesWhen it was last registered or replaced

AuthStatus

The project's end-user auth status (docs/auth.md §9).

This API does not include the signup table. User data is stored in the runlot_auth schema of the project's Postgres (§6), and this table is handled together with project deletion, pg export, generational backup, and restore. If CP served this table, the data lifecycle would be split.

FieldTypeRequiredDescription
grantedbooleanYesIndicates whether auth is configured for use. Since the existence of a usage-configuration record is the only representation, there is no "configured but disabled" state. When disabled, settings holds default values and providers is an empty array.
settingsAuthSettingsYes
providersAuthProviderMeta[]YesRegistered login providers. Sorted alphabetically by name, not by registration order
hostsstring[]YesEvery hostname this project answers on. Each host needs its own callback URL registered in the login provider's console — https://<host>/__runlot/auth/callback/<provider>. Add the callback URL to the login provider every time you connect a host. That is why both the dashboard and the CLI hand you this list verbatim. On a host you miss, login fails on that host alone, and the reason is visible only in the provider's console. There are two default hostnames — <project>.<org>.<domain> and the older single-label name <project>--<org>.<domain>. A deploy registers both, so links already in the wild keep working, which is why both callback URLs are needed.

AuthSettingsPatch

A partial update of the settings. Only the fields you send change. Sending the entire settings object for every single switch change can let two users editing the same settings have a later save overwrite an earlier one.

FieldTypeRequiredDescription
settingsobjectYesA subset of AuthSettings. An empty object changes nothing

AuthProviderPut

These are the login provider credentials. Replacement also happens through this same PUT, so both values are required. If you send only the ID and omit the secret, the response cannot confirm whether the previous secret remains.

FieldTypeRequiredDescription
clientIdstringYesThe public id given by the provider
clientSecretstringYesStored encrypted. It is never included in any response again. If you lose it, issue a new one from the login provider's console and PUT it again.

Hostname

A single hostname attached to the project. state progresses from pending_dns (verifying ownership) → pending_tls (issuing the certificate) → active (connected), and the failure state is error. Only records with active are included in routing.

FieldTypeRequiredDescription
hostnamestringYesName normalized to punycode and lowercase
kindstringYesdefault is the default name — this includes both <project>.<org>.<app-domain> and the legacy alias <project>--<org>.<app-domain>, which merges it into a single label. custom is a customer domain
statestringYes
lastErrorstringNoThe reason the last check failed. Users read this to fix their own DNS
createdAtstringYes
verifyHostnameVerifyNo

HostnameVerify

The two lines the customer needs to add to their own DNS. Getting either one right proves ownership — CNAME also handles the connection, so it is the default guidance. CP computes and returns the value (the fallback origin is a deployment setting, so if clients hardcode it, they break the day it changes).

FieldTypeRequiredDescription
cnameHostnameRecordYes
txtHostnameRecordYes

HostnameRecord

FieldTypeRequiredDescription
namestringYesrecord name
targetstringNoCNAME target
valuestringNoTXT value

HostnameList

FieldTypeRequiredDescription
hostnamesHostname[]Yes

Zone

A zone in our authoritative DNS (docs/domains.md §4.3). serial is the rev — it increases on every change, and once publishedSerial catches up, the nameservers are answering with that content.

FieldTypeRequiredDescription
zoneIdstringYes
orgIdstringYes
namestringYesexample.com (lowercase punycode, no trailing dot)
serialintegerYes
publishedSerialintegerYesThe serial last written to the zone file. If it is smaller than serial, propagation is still in progress (usually 2 seconds)
dnssecstringYes
dsstring[]YesThe DS to submit to the parent. If we are the registrar, D3 submits it; otherwise the screen shows it.
delegationstringYesResult of a periodic check: whether the registrar's NS points to us
delegationCheckedAtstringNo
nameserversstring[]YesThe NS to enter at the registrar. A copy of the CP configuration, not the zone's value
createdAtstringYes

ZoneDetail

A single zone and all its records.

Type: Zone & object

RRset

A (name, type) value pair. If managedBy is set, the row was created by another layer and is locked in the editor — system (zone's NS), hostname:<host> (app connection), acme (certificate).

FieldTypeRequiredDescription
namestringYesrelative name. @ is the apex
typestringYes
ttlintegerYes
recordsstring[]Yes
managedBystringNo
updatedAtstringYes

ZoneList

FieldTypeRequiredDescription
zonesZone[]Yes

DomainAvailability

Availability and value of a single name. No wholesale price is included — cost is a fact about our operations, not the customer's (cp-core provides it alongside, for operators).

FieldTypeRequiredDescription
namestringYes
availablebooleanYes
premiumbooleanYesThe name the registry prices separately. Registration and renewal values differ significantly
priceCentsintegerYesfirst registration, 1 year (not KRW — cents)
renewalCentsintegerYesrenewal, 1 year

DomainContact

The registrant's contact information. It belongs to the customer — ICANN verification email goes to this address, and the domain is suspended if it is not confirmed within 15 days. The UI must state this fact before registration (docs/domains.md §5.5).

FieldTypeRequiredDescription
firstNamestringYes
lastNamestringYes
orgstringNo
emailstringYes
phonestringYes
address1stringYes
address2stringNo
citystringYes
statestringNo
zipstringYes
countrystringYesISO 3166-1 alpha-2

Domain

A single domain. Expiration date, lock status, and nameservers are copies of the registrar's source of truth and are synced by domain-sync (every 6 h) (docs/domains.md §5.4).

FieldTypeRequiredDescription
namestringYes
zoneIdstringNoOur zone. Always created on purchase or import
registrarstringYesexternal is a domain held in someone else's account — renewal and locking are not our responsibility
statestringYesThis is derived from the expiration date: expiring starts 30 days before expiration, expired covers the 30-day grace period after expiration, redemption (redemption fee) covers the following 30 days, and released comes after that. transferred_out is when the registrar reports that it is no longer in our account.
expiresAtstringNo
autoRenewbooleanYesThis is the customer's decision. Auto-renewal on the registrar side is always on — we renew first and bill afterward (§5.4).
lockedbooleanYesTransfer lock
nameserversstring[]Yes
yearsintegerYes
priceCentsintegerNovalue the customer sent
sagaStepstringYesIf not done, the purchase is still in progress (docs/domains.md §5.3)
sagaErrorstringNoreason it stopped. can be present even if the status is not failed
createdAtstringYes

DomainEvent

A ledger row (append-only). kind is one of purchase, register_attempt, register, register_reconciled, zone, ds, done, renew, lock, unlock, auto_renew, auth_code, state, transfer_out, import, error, and so on. Previous codes and secrets are never stored here.

FieldTypeRequiredDescription
eventIdintegerYes
kindstringYes
detailobjectNo
atstringYes

DomainDetail

Type: Domain & object

DomainList

FieldTypeRequiredDescription
domainsDomain[]Yes

On this page