LoopLoop

TypeScript SDK

Type-safe programmatic access to the Loop API for custom applications, scripts, and agent frameworks.

TypeScript SDK

The @dork-labs/loop-sdk package provides a type-safe client for the Loop API. It wraps every endpoint in a resource-based interface with automatic pagination, structured error handling, built-in retries, and idempotency keys for mutating operations.

Installation

bash npm install @dork-labs/loop-sdk
bash pnpm add @dork-labs/loop-sdk
bash yarn add @dork-labs/loop-sdk
bash bun add @dork-labs/loop-sdk

The SDK ships ESM and CJS builds with full TypeScript declarations. It has two runtime dependencies: ky for HTTP and @dork-labs/loop-types for shared type definitions.

Quick start

import { LoopClient } from '@dork-labs/loop-sdk'

const loop = new LoopClient({
  apiKey: 'loop_abc123',
  baseURL: 'https://your-loop-instance.example.com',
})

// Claim the next prioritized issue with a hydrated prompt
const task = await loop.dispatch.next()
if (task) {
  console.log(task.issue.title)
  console.log(task.prompt)
}

// List open issues
const issues = await loop.issues.list({ status: 'todo' })
for (const issue of issues.data) {
  console.log(`#${issue.number} ${issue.title}`)
}

Client configuration

The LoopClient constructor accepts the following options:

OptionTypeDefaultDescription
apiKeystringrequiredLoop API key (must start with loop_)
baseURLstringhttp://localhost:5667Base URL of the Loop API
timeoutnumber30000Request timeout in milliseconds
maxRetriesnumber2Automatic retries on transient errors
retryStatusCodesnumber[][429, 500, 503]HTTP status codes that trigger a retry
const loop = new LoopClient({
  apiKey: process.env.LOOP_API_KEY!,
  baseURL: 'https://api.looped.me',
  timeout: 15_000,
  maxRetries: 3,
})

Resources

The client organizes all endpoints into resource namespaces. Each resource is accessible as a property on the LoopClient instance:

ResourceNamespaceDescription
Dispatchloop.dispatchClaim prioritized work with hydrated prompts
Issuesloop.issuesCRUD for the atomic unit of work
Signalsloop.signalsIngest external events into the pipeline
Projectsloop.projectsGroup issues toward shared objectives
Goalsloop.goalsMeasurable success indicators for projects
Templatesloop.templatesVersioned prompt instructions for dispatch
Dashboardloop.dashboardSystem health metrics and activity overview
Commentsloop.commentsThreaded discussion on issues
Errors--Structured error classes and handling patterns

Additional resources available on the client:

  • loop.labels -- create, list, and delete categorical tags for issues
  • loop.relations -- create and delete blocking/related dependencies between issues
  • loop.reviews -- submit and list quality feedback on prompt versions

Pagination

List endpoints return a PaginatedList<T> with data, total, and a hasMore flag:

const page = await loop.issues.list({ limit: 20, offset: 0 })
console.log(page.data)    // Issue[]
console.log(page.total)   // total count across all pages
console.log(page.hasMore) // true if more pages exist

Some resources (like issues) also expose an iter() method that returns an async generator, automatically fetching subsequent pages as you consume items:

for await (const issue of loop.issues.iter({ status: 'todo' })) {
  console.log(issue.title)
}

The auto-paginator fetches pages of 50 items by default. It handles offset management internally so you can iterate through the entire dataset without manual page tracking.

Per-request options

Mutating methods (create, update, delete) accept an optional RequestOptions object for per-request overrides:

OptionTypeDescription
idempotencyKeystringCustom idempotency key (auto-generated if omitted)
timeoutnumberOverride the client-level timeout for this request
signalAbortSignalCancel the request via an AbortController
const controller = new AbortController()

const issue = await loop.issues.create(
  { title: 'Fix login page', type: 'task' },
  {
    idempotencyKey: 'create-login-fix-001',
    timeout: 10_000,
    signal: controller.signal,
  }
)

The SDK automatically generates an Idempotency-Key header for every POST, PATCH, and DELETE request. You only need to provide a custom key if you want to ensure exactly-once semantics across retries in your own code.

Error handling

All API errors are thrown as typed LoopError subclasses with structured status, code, and details fields:

import { LoopNotFoundError, LoopValidationError } from '@dork-labs/loop-sdk'

try {
  await loop.issues.get('nonexistent-id')
} catch (error) {
  if (error instanceof LoopNotFoundError) {
    console.log('Issue not found')
  } else if (error instanceof LoopValidationError) {
    console.log('Validation failed:', error.details)
  }
}

See Errors for the full error class hierarchy and handling patterns.

Type re-exports

The SDK re-exports all types from @dork-labs/loop-types for convenience. You can import entity types, request params, response shapes, and enums directly from the SDK package:

import type { Issue, CreateIssueParams, IssueStatus } from '@dork-labs/loop-sdk'

What comes next

  • Dispatch -- the core agent loop: claim work and receive hydrated prompts
  • Issues -- create, list, update, and delete the atomic unit of work
  • Signals -- ingest external events into Loop's pipeline
  • Errors -- structured error handling with typed exception classes