Slop Docs Home
Slop ยท API Reference

API Contracts

Two mutation surfaces: Next.js Server Actions (imported by React components) and REST API routes (consumed by client fetch and external skills).

Server Actions REST API ActionResult union SSE stream

Overview

Slop exposes mutations through two distinct layers. Server Actions are the primary surface for the React UI; REST routes serve external consumers like agent skills, health checks, and SSE clients. Both layers share the same underlying daemon and database.

Server Actions pattern

All actions return ActionResult: { ok: true } or { ok: false; error: string }. Validation failures return the error union - they never throw to the client. On success, actions call revalidatePath. The daemon is accessed via getRunningDaemon(). Form-bound actions use _prevState + FormData and wire through useActionState(action, null) with an inline role="alert" span for errors.

REST API pattern

All routes are force-dynamic. Authentication is handled at the middleware layer. The repoId parameter is the owner/name slug throughout - except /api/labels which uses the query param ?repoId= instead of ?repo=. Most routes accept ?repo=<owner/name>. Response shapes are plain JSON; SSE routes emit text/event-stream.

type ActionResult = | { ok: true } | { ok: false; error: string }

Server Actions - by domain

All actions live in src/app/actions/, split into one file per domain. Each delegates to the daemon via getRunningDaemon() after validating its inputs. The table columns note what is validated, which daemon method is called, and which path is revalidated on success.

Batch

4 actions

Manages issue batches: creation, membership, and archival. All scoped to a repoId.

ActionValidatesDaemon opRevalidates
createBatchrepoId, name non-empty/
addIssueToBatchbatchId, source, issueNumber, repoId/
removeIssueFromBatchbatchId, source, issueNumber/
archiveBatchbatchId non-empty/

Config

8 actions

Global settings mutations. saveConfig validates implementMode enum, acceptedReviewLevel enum, all timeout fields as positive integers, and boolean flags.

ActionValidatesDaemon opRevalidates
saveConfigimplementMode, acceptedReviewLevel, timeout ints, booleans/config
setConfigModekey in allowed set, value enum/config
saveRepoConcurrencyrepoId, cap null or positive int/config
setPollPausedpaused boolean/config
setPointsHarnessharness AgentHarness enum/config
saveGitHubTokentoken non-empty string/config
wipeDatabaseconfirmation textwipeDatabase()/
enableGuardsenableGuards()/config

Internal Issues

9 actions

CRUD operations for the internal issue tracker, plus label management and skill invocation on individual issues.

ActionValidatesDaemon opRevalidates
startIssueSkillrepoId, issueNumber, mode ∈ {refine, review}
createInternalIssuerepoId, title non-empty/[repoId]/issues
importBmadStoriesrepoId, files array non-empty/[repoId]/issues
updateInternalIssueid, input fields/[repoId]/issues
setInternalIssueStateid, state InternalIssueState enum/[repoId]/issues
createLabelActionrepoId, name, color (#rrggbb)/[repoId]/labels
updateLabelActionid, input fields/[repoId]/labels
deleteLabelActionid non-empty/[repoId]/labels
addInternalIssueCommentid, body non-empty/[repoId]/issues/[id]

Issues

7 actions

Controls issue lifecycle on the Kanban board: readiness, ordering, config overrides, and triggering implementation.

ActionValidatesDaemon opRevalidates
setIssueConfigrepoId, issueNumber, overrides shape/
suggestIssuePointsrepoId, issueNumber
startIssuerepoId + issueNumber from FormDatastartIssue()/
setIssueReadyrepoId, issueNumber, ready booleansetIssueReady()/
returnIssueToBacklogrepoId, issueNumberreturnIssueToBacklog()/
reorderReadyIssuerepoId, issueNumber, toIndex ≥ 0reorderReadyIssue()/
closeIssuerepoId + issueNumber from FormData/

PRs

6 actions

PR lifecycle actions: merging, syncing branches, closing, reviewing, and addressing review comments.

ActionValidatesDaemon opRevalidates
mergePrrepoId + prNumber from FormDatamergePr()/
syncBranchrepoId from FormDatasyncBranch()/
fixBaseCirepoId from FormDatafixBaseCi()/
closePrrepoId + prNumber from FormDataclosePr()/
reviewPrrepoId + prNumber from FormDatareviewPr()/
addressPrrepoId + prNumber from FormDataaddressPr()/

Repos

2 actions

addRepo validates that the slug matches owner/name format, the path is absolute, and <path>/.git exists on disk. removeRepo cascades worker/worktree cleanup through the daemon.

ActionValidatesDaemon opRevalidates
addRepoowner/name slug, absolute path, .git existsaddRepo()/config
removeReporepoId non-emptyremoveRepo()/config

Runs

5 actions

Manages skill runs and worker report issues. saveDoc dispatches a haiku+low-effort skill run to write a document file.

ActionValidatesDaemon opRevalidates
createWorkerReportIssueworkerId non-empty/
cancelWorkerReportIssuerunId non-empty/
runSkillRunskillName in allowed set, args shape/
saveDocfilename allowlisted, content non-empty/docs
cancelSkillRunrunId non-empty/

Workers

9 actions

Full worker lifecycle control. Most accept a workerId from FormData and delegate directly to the daemon. clearFinishedWorkers is a bulk sweep with no daemon call.

ActionValidatesDaemon opRevalidates
startWorkerrepoId + issueNumber from FormDatastartWorker()/
pauseWorkerworkerId from FormDatapauseWorker()/
restartWorkerworkerId from FormDatarestartWorker()/
cancelWorkerworkerId from FormDatacancelWorker()/
retryWorkerworkerId from FormData/
setWorkerOverridesworkerId, overrides shape/
overrideToMergedworkerId from FormDataoverrideToMerged()/
mergeWorkerworkerId from FormDatamergeWorker()/
clearFinishedWorkers/

REST API Routes

All routes live under src/app/api/. Every route file exports export const dynamic = "force-dynamic". Authentication is enforced at the Next.js middleware layer - unauthenticated requests receive 401 before reaching the route handler.

Events

SSE
Method + PathQuery / BodyResponseNotes
GET/api/events text/event-stream Server-Sent Events stream. Heartbeat comment every 25 s to keep the connection alive through proxies. Used by the board UI for live worker status updates.

Workers

2 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/workers ?repo=owner/name Worker array Includes computed costUsd and netTokens fields derived from session stats.
GET/api/workers/[id]/events ?all flag Event array Without ?all, returns only recent events. With ?all, returns full event history for the worker.

Runs

2 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/runs/[id] { status, lastError } Lightweight status poll for a skill run or worker report run.
GET/api/runs/[id]/events Event array (≤ 100) Event log for a run, capped at 100 entries.

Internal Issues

5 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/internal-issues ?repo=owner/name Issue array Returns all internal issues for the repo, ordered by creation.
POST/api/internal-issues { repoId, title, body?, labels? } Created issue, 201 Creates a new internal issue. Labels is an array of label IDs.
GET/api/internal-issues/[id] Issue object Single issue with full body and metadata.
PATCH/api/internal-issues/[id] Partial issue fields Updated issue Partial update - only provided fields are changed.
GET/api/internal-issues/[id]/comments Comment array All comments on a single internal issue, ordered chronologically.

Labels

4 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/labels ?repoId=owner/name Label array Uses repoId param, not repo.
POST/api/labels { repoId, name, color, description? } Created label, 201 Color must be a valid hex string like #6c63ff.
PATCH/api/labels/[id] Partial label fields Updated label Partial update of name, color, or description.
DELETE/api/labels/[id] 204 No Content Hard deletes the label and removes it from all associated issues.
Query param naming inconsistency: Most routes use ?repo=<owner/name> but /api/labels uses ?repoId=<owner/name>. This is a known inconsistency - use the correct param for each route.

Documents

2 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/docs ?repo=owner/name Filename array Lists available document filenames from the repo's docs directory.
GET/api/docs/[filename] text/plain Serves the raw document content. Restricted to an ALLOWED_DOC_FILES allowlist - path traversal is structurally impossible since the filename segment cannot contain slashes.

Features

2 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/features ?repo=owner/name .html filename array Lists available feature documentation HTML filenames.
GET/api/features/[filename] text/html Serves a feature doc HTML file for sandboxed iframe rendering in the UI. Filenames are validated against the allowed set.

Other routes

5 endpoints
Method + PathQuery / BodyResponseNotes
GET/api/health { ready: boolean } Daemon readiness check used by the UI to show the guard screen.
GET/api/repo ?repo=owner/name Annotated repo state or null Returns the cached repo snapshot state with PR and CI annotations.
POST/api/repo ?repo=owner/name Updated repo state Forces a live GitHub refresh, bypassing the cache. Used by the repo view's manual refresh button.
GET/api/agents ?repo=owner/name Agent row array Returns agent harness rows for the configured repo.
GET/api/bmad-stories ?repo=owner/name Story row array with imported flag Returns BMAD story records, each annotated with whether it has been imported as an internal issue.

Daemon Interface

The Daemon interface is defined in src/server/daemon/types.ts and implemented in src/server/daemon/index.ts. All Server Actions that need to trigger daemon behavior call getRunningDaemon() to obtain the singleton and invoke a method on it.

Lifecycle

4 methods
MethodSignatureNotes
start(): voidBoots the daemon: runs guard check, seeds config, starts the tick interval.
stop(): voidClears the tick interval. Does not cancel in-flight workers.
tick(): Promise<void>Single poll cycle execution, exposed for testing.
cycle(): Promise<void>Full 13-step reconciliation loop: lifecycle poll, claim worker, CI health, repo snapshot, and more.

Workers

4 methods
MethodSignatureNotes
startWorker(id: string): Promise<void>Resumes a paused worker by transitioning it back to running state.
pauseWorker(id: string): Promise<void>Signals the worker to pause at its next safe checkpoint.
restartWorker(id: string): Promise<void>Cancels and re-enqueues the worker from the beginning of its current phase.
cancelWorker(id: string): Promise<void>Aborts the worker, cleans up the worktree, and marks it cancelled.

Issues

5 methods
MethodSignatureNotes
startIssue(repoId, issue: {number, title, htmlUrl, issueSource?}): Promise<void>Creates a worker row and enqueues immediate implementation.
returnIssueToBacklog(repoId, issueNumber, issueSource?): Promise<void>Removes the issue from the ready queue and cancels any active worker.
setIssueReady(repoId, issueNumber, ready, issueSource?): Promise<void>Adds or removes the issue from the ready queue.
reorderReadyIssue(repoId, input: {issueNumber, toIndex, issueSource?}): Promise<void>Moves the issue to a new position in the ready queue.
retryIssue(repoId, issueNumber, issueSource?): Promise<void>Resets a failed worker and re-enqueues the issue for another attempt.

PRs

5 methods
MethodSignatureNotes
mergePr(repoId, prNumber): Promise<void>Initiates squash merge via GitHub API.
closePr(repoId, prNumber): Promise<void>Closes the PR without merging and transitions the worker to cancelled.
reviewPr(repoId, prNumber): Promise<void>Triggers a Claude Code review skill run on the open PR.
addressPr(repoId, prNumber): Promise<void>Triggers an address-comments phase for the PR.
mergeWorker(workerId): Promise<void>Manually merges a worker that is waiting in the merge queue.

Admin

8 methods + property
MethodSignatureNotes
overrideToMerged(workerId): Promise<void>Force-marks a worker as merged, skipping GitHub state checks.
addRepo(input: {slug, path, baseBranch?}): Promise<void>Registers a new watched repo and seeds its initial snapshot.
removeRepo(repoId): Promise<void>Unregisters a repo, cancels all its workers, and deletes worktrees.
refreshRepoSnapshot(): Promise<void>Forces an immediate GitHub poll for all watched repos.
fixBaseCi(repoId): Promise<void>Dispatches the fix-base-CI skill to repair failing base branch checks.
syncBranch(repoId): Promise<void>Rebases an open worker PR onto the latest base branch HEAD.
enableGuards(): Promise<void>Writes the guard-passed state to the DB after the setup wizard completes.
wipeDatabase(): Promise<void>Drops all rows except Repo config. Irreversible.
inFlightMap<string, Promise<void>>Property - live map of worker IDs to their running promises. Used by the runner registry to prevent double-spawning.
Adding a new daemon operation: Add it to the Daemon interface in src/server/daemon/types.ts first, then implement in the appropriate ops/*.ts file. Wire the implementation into the main class in src/server/daemon/index.ts. The Server Action then calls it via getRunningDaemon().yourNewMethod().