API
Authenticate external automation and operate routines through the versioned Hono API.
The service API is versioned under /v1. Browser sessions use Better Auth.
External automation uses a bearer API key.
Authentication
curl https://api.workonrepeat.com/v1/routines \
-H "Authorization: Bearer $WORK_ON_REPEAT_API_KEY"An API key is bound to exactly one project. A session can select its active
project with the X-Project-Id header, which takes precedence over the
wor_project cookie; the API verifies membership either way.
Create a key
Create a key from Dashboard → API keys. Its plaintext form is shown once:
wor_<prefix>_<secret>The database stores only a SHA-256 hash. Closing the one-time dialog removes the plaintext key from the UI state.
Scopes
| Scope | Grants |
|---|---|
models:read | GET /v1/models |
routines:read | Read routines and their runs. |
routines:write | Create, update, archive, activate, pause. |
runs:read | List runs and read run detail. |
runs:write | Start a manual run. |
connections:read | Read connections and tool policies. |
connections:write | Create connections, discover tools, set policies. |
New keys default to models:read, routines:read, runs:read, and
runs:write. Free and Starter permit read-only API requests; Pro and Scale
permit writes. Browser sessions use membership and plan checks rather than
API-key scopes.
Envelopes
{ "data": {}, "requestId": "01J..." }{
"error": { "code": "routine_not_found", "message": "…" },
"requestId": "01J..."
}Endpoints
Models
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /v1/models | models:read | Managed LLMGateway options plus ready, enabled connected-account catalogs for the active project. |
Each option carries the full reference provider, nullable connectionId, and
modelId. A managed reference has provider llmgateway and a null connection;
personal references require the exact owning connection. The catalog does not
collapse two accounts that expose the same model ID.
Personal model connections
Listing uses the normal read scopes. Connect, reauthorize, and revoke require an authenticated browser session for the exact connection owner; an API key cannot act as that personal owner. Provider flags can make a descriptor visible while its connect action remains disabled.
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /v1/model-connections/providers | connections:read | Four provider descriptors, enablement, auth type, billing policy, and current session viewer. |
GET | /v1/model-connections | connections:read | Safe account label, owner, status, validation time, fixed public error, and current model count. |
GET | /v1/model-connections/:connectionId/models | models:read | Exact ready connection's current account catalog. |
POST | /v1/model-connections/:connectionId/revoke | connections:write + owner session | Revokes credential use and returns the preserved audit record. |
POST | /v1/model-connections/openai-codex/device/start | connections:write + owner session | Starts or replaces the owner's bounded ChatGPT device attempt. |
GET | /v1/model-connections/openai-codex/device/:attemptId | connections:write + owner session | Polls only the initiating owner's opaque attempt. |
POST | /v1/model-connections/openai-codex/device/:attemptId/cancel | connections:write + owner session | Cancels that device attempt. |
POST | /v1/model-connections/github-copilot/oauth/start | connections:write + owner session | Starts dedicated Copilot OAuth; optional connectionId reauthorizes the same owned connection. |
GET | /v1/model-connections/github-copilot/oauth/callback | connections:write + owner session | Completes the session-bound, single-use OAuth state. |
GET | /v1/model-connections/anthropic-agent | connections:read | Anthropic API-key descriptor and enablement. |
POST | /v1/model-connections/anthropic-agent/api-key | connections:write + owner session | Validates a request-body-only key and creates or replaces the exact owned connection. |
See Connect a model provider for product ownership, billing, status, and recovery semantics. Live-smoke diagnostics are default-off operator gates and are intentionally omitted from the normal integration surface.
Organizations and projects
Session-only. An API key receives 403 session_required.
| Method | Path | Notes |
|---|---|---|
GET | /v1/organizations | Organizations you belong to. |
POST | /v1/organizations | Creates the organization and its default project. |
GET | /v1/projects | Optional ?organizationId= filter. |
POST | /v1/projects | Requires owner or admin in that organization. |
Routines
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /v1/routines | routines:read | All routines in the project. |
POST | /v1/routines | routines:write | 201. Returns webhookSecret once when a URL is set. |
GET | /v1/routines/:routineId | routines:read | |
PATCH | /v1/routines/:routineId | routines:write | Material changes return the routine to draft. |
DELETE | /v1/routines/:routineId | routines:write | Archives; history is retained. |
POST | /v1/routines/:routineId/run | runs:write | 202. Enqueues a manual run. |
POST | /v1/routines/:routineId/activate | routines:write | 409 successful_run_required without a proof run. |
POST | /v1/routines/:routineId/pause | routines:write | |
GET | /v1/routines/:routineId/runs | runs:read | Runs for one routine. |
Creating or updating a routine validates the schedule against the cron parser
and the model against the live directory. Tool policies must reference a saved
connection — inline tools return 400 tool_policy_requires_connection.
Runs
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /v1/runs | runs:read | All runs in the project. |
GET | /v1/runs/:runId | runs:read | Full detail: steps, usage, webhook deliveries. |
Connections and tool policies
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /v1/connections | connections:read | Encrypted material is never returned. |
POST | /v1/connections | connections:write | 201. Self-managed MCP server. |
DELETE | /v1/connections/:connectionId | connections:write | Deletes stored credentials and the connection. |
GET | /v1/connections/providers | — | Managed providers and whether each is configured. |
POST | /v1/connections/oauth/:provider/start | — | Begins an OAuth authorization. |
POST | /v1/connections/api-key/:provider | — | Verifies and stores an API-key provider. |
GET | /v1/connections/:connectionId/accounts | connections:read | Live provider accounts, e.g. Zernio. |
POST | /v1/routines/:routineId/connections/:connectionId/discover | connections:write | Creates disabled tool policies. |
GET | /v1/routines/:routineId/tools | connections:read | Tool policies for one routine. |
PATCH | /v1/routines/:routineId/tools/:policyId | connections:write | Requires confirmAutomaticExecution: true to automate. |
API keys
Session-only.
| Method | Path | Notes |
|---|---|---|
GET | /v1/api-keys | Never returns hashes. |
POST | /v1/api-keys | 201. Returns key exactly once. |
DELETE | /v1/api-keys/:apiKeyId | Revokes. |
Billing
Session-only. Billing mutations also require the active project's organization
owner and an exact Origin matching UI_ORIGIN; bearer API keys cannot start
Checkout or create customer-portal sessions.
| Method | Path | Notes |
|---|---|---|
GET | /v1/billing | Effective plan, projected status, period, allowances, usage, and owner flag. |
POST | /v1/billing/checkout | Body: { "plan": "pro", "entrypoint": "pricing" }. Returns a Stripe URL. |
POST | /v1/billing/portal | Body: {}. Returns a short-lived Stripe customer-portal URL. |
The API chooses the Price, customer, and success/cancel URLs. Client-supplied Stripe IDs or redirect URLs are rejected. An already subscribed workspace is sent to the portal rather than creating a second subscription. Checkout's success redirect never grants access. Paid access changes only after a verified Stripe webhook or an authenticated server-side reconciliation against canonical Stripe subscription state.
See Billing for plan periods, cancellation, failed-payment grace,
and the stable HTTP 402 contract.
Outside /v1
| Method | Path | Notes |
|---|---|---|
GET | /health | Liveness. Unauthenticated. |
GET | /oauth/callback/:provider | The UI forwards the provider's redirect here with the session cookie attached. |
POST | /internal/scheduler/tick | X-Scheduler-Secret. For hosts driving the schedule externally. Returns 202. |
POST | /public/business-leads | Trusted UI origin, rate-limited Business enquiry. Returns 202. |
POST | /webhooks/stripe | Raw signed Stripe lifecycle events. Never call from the browser. |
Limits
Authenticated /v1 traffic is limited per credential in a fixed 60-second
window. Responses include X-RateLimit-Limit and X-RateLimit-Remaining; a
429 also includes Retry-After. Request bodies are capped at 1 MB. Run
execution is claimed from the durable SQLite queue and processed with bounded
worker concurrency.
See Limits and quotas for every bound in one place, and Error codes for the complete list of failures.