Skip to content

Cloud API

The REST API exposed by Repster Cloud. Used by the Repster CLI and the Repster Cloud UI. You can call it directly for automation or integration.

Base URL: https://cloud.repster.online/api — the paths below are relative to it. (The browser UI is served at https://cloud.repster.online/; GET /health lives at the host root, outside /api.)

Auth: API key in the Authorization: Bearer rpk_... header. Keys are org-scoped, so all endpoints automatically scope to the calling key's org.

Client header: X-Repster-Client: <version>, sent automatically by the CLI.

Async runs: POST /runs returns a run_id immediately. Poll GET /runs/{id} or stream logs via GET /runs/{id}/logs?follow=true.

Auth endpoints (no token required)

Method Path Body Returns
POST /auth/register {email, password, org_slug?, org_name?} {org, api_key} (raw key shown once), or {needs_org: true} when org_slug is omitted
POST /auth/login {email, password, org_slug?} {org, api_key} (plus orgs: [...] when the user has multiple orgs) or {needs_org: true} when the user belongs to none
POST /auth/create-org {email, password, org_slug, org_name} {org, api_key}

Registering without org_slug creates the account alone. It holds no API key — keys are org-scoped — so it can reach nothing until it is added to an org or creates one via POST /auth/create-org. Since POST /orgs/{org_id}/members only ever adds an existing account, this is how somebody joins an org they were told about.

Passwords must be at least 12 characters, on registration and on change alike.

A failed login returns 401 {"detail": "invalid email or password", "email_known": bool}, so a client can offer registration to a stranger and a password retry to someone who already has an account.

When a user belongs to multiple orgs and org_slug is omitted from POST /auth/login, the response includes orgs listing all their orgs. Re-POST with org_slug to select one.

The login response also includes must_change_password: true when the account was created with a system-generated temporary password (operator provisioning) and has not yet set its own. Password-authenticated surfaces require the reset first: the browser UI redirects to a change-password screen, and rp login walks the user through it.

User identity

Method Path Body Returns
GET /users/me {user_id, email, created_at}
GET /users/me/orgs [{org_id, slug, name, role, joined_at}]
POST /users/me/password {current_password, new_password} {org, api_key} — a fresh session key; clears the forced-reset flag and revokes other sessions

new_password must be at least 12 characters (422 otherwise); a wrong current_password returns 401.

Org management

Method Path Body Returns
GET /orgs/me {org_id, slug, name, created_at, terms_required, terms_accepted}
POST /orgs {slug, name} {org, api_key}

terms_required is the terms version your org must have accepted (null when none is in force) and terms_accepted says whether it has. The CLI reads them here to detect the gate before running a command — see Terms.

Terms

Repster Cloud can require your org to accept its legal terms before it may deploy, run flows, or write secrets.

Method Path Body Returns
GET /terms {required, accepted, accepted_version, accepted_at, accepted_via, can_accept}
POST /terms/accept — (admin only) the updated terms state

required is the version in force (null when none is), accepted whether your org has accepted it, and can_accept whether the calling key may accept — acceptance is org-level and admin-only, so a member is told to find an admin rather than offered a call that would 403. accepted_via records whether acceptance came from the browser (ui) or the CLI (cli).

POST /terms/accept returns 409 when no version is in force — there is nothing to bind the org to.

While terms are outstanding, POST /deployments, POST /runs and PUT /projects/{project}/secrets return 403. Reads, POST /auth/login and this section stay open, so a gated org can still authenticate, inspect its state, and accept.

The CLI handles all of this for you: any cloud command prompts an admin to accept inline before it does any work, and rp terms show / rp terms accept cover the scripted cases.

Key management (admin only)

Method Path Body Returns
POST /orgs/{org_id}/keys {name} {key_id, raw_key, ...} (raw key shown once)
GET /orgs/{org_id}/keys [{key_id, name, key_prefix, created_at, last_used_at, revoked_at}]
DELETE /orgs/{org_id}/keys/{key_id} {ok: true}

Member management (admin only)

Method Path Body Returns
GET /orgs/{org_id}/members [{user_id, email, role, created_at}]
POST /orgs/{org_id}/members {email, role?} {user_id, email, role, created_at} (201), or 404 if the email has no account
PATCH /orgs/{org_id}/members/{user_id} {role} updated member
DELETE /orgs/{org_id}/members/{user_id} {ok: true}

role defaults to "member". Valid values: "admin", "member". Cannot remove or demote the last admin (409). Cannot remove yourself (409).

Deployments

Method Path Body Returns
POST /deployments multipart: manifest + archive {deployment_id, code_digest}
GET /deployments?project= deployments for the caller's org, latest first

Creating a deployment (POST) is admin only; any org member can list them (GET). Deployments are immutable and content-addressed, so uploading the same archive twice returns the existing deployment.

POST also returns 403 while your org has terms outstanding.

Flows

Method Path Params Returns
GET /flows?project= optional project filter [{project, flow, schedule, entrypoint}] for the caller's org

The flows across the org's deployments, used by rp list --cloud and the UI Flows page. Available to any org member.

Runs

Method Path Body / Params Returns
POST /runs {project, flow, trigger, retry_of?} {run_id, status: "pending"} — the hosted service enqueues the run for the reconciler; it then progresses to running and a terminal state
GET /runs?project=&flow=&limit= run records for the caller's org
GET /runs/{id} {run_id, project, flow, status, trigger, deployment_id, started_at, finished_at, rows_loaded, summary, retry_of, duration_seconds, cost_usd, error}
GET /runs/{id}/logs ?follow=bool full log text; SSE stream when follow=true
POST /runs/{id}/cancel — (admin only) the updated run record with status: "cancelled"

POST /runs returns 404 with a helpful message if the flow has never been deployed. It also returns 403 while the org has terms outstanding, and enforces spend guardrails before dispatch: 403 if the org is disabled (kill switch), 429 if the org has reached its monthly cost cap. A finished run records duration_seconds (wall-clock) and cost_usd (an estimate from the sandbox's pinned resources at Modal's published rates); both are null until the run finishes. A failed run also carries a short error reason (the full output stays in the run logs).

POST /runs/{id}/cancel terminates an in-flight run (Modal sandbox) and marks it cancelled. Returns 409 if the run has already finished (or is a local inline run with no detached handle), 404 if it isn't in the caller's org.

Usage

Method Path Returns
GET /usage {org_id, enabled, month, run_count, compute_seconds, cost_usd, monthly_cost_cap_usd, by_flow: [...]}

Current calendar-month (UTC) usage for the caller's org: run count, wall-clock compute seconds, estimated spend, and the effective monthly cost cap (null = unlimited). by_flow breaks the same figures down per (project, flow). Metering only — there is no billing.

Secrets (admin only)

Method Path Body Returns
PUT /projects/{project}/secrets {KEY: VALUE, ...} {ok: true}
GET /projects/{project}/secrets key names only (values are never returned)
DELETE /projects/{project}/secrets/{key} {ok: true}

PUT also returns 403 while your org has terms outstanding.

Health

Method Path Returns
GET /health {ok: true} (no auth required)