Durabull Documentation

HTTP API Reference

Endpoint-level overview of Durabull API resources, capabilities, and key limits.

This is an endpoint overview of the current HTTP API. Paths are relative to the app origin unless a section specifies a connection-scoped prefix. JSON request bodies need Content-Type: application/json. URL-encode path parameters such as queue names, job IDs, and Redis keys.

Base URL

REST endpoints use /api. MCP transport uses /mcp, outside that prefix. In development, the API is at http://localhost:3001; the Vite web origin proxies /api to it.

curl -fsS http://localhost:3001/api/health
curl -fsS http://localhost:3001/api/mode

Bootstrap and health:

  • GET /api/health
  • GET /api/app/config
  • GET /api/app/version
  • GET /api/mode
  • GET /api/telemetry/status
  • POST /api/telemetry/events

Authentication and Session

  • Better Auth endpoints under /api/auth/*
  • Session snapshot: GET /api/session

Protected REST routes use the Better Auth session and its active organization. Send the session cookie with requests made outside the browser. A valid session without an active organization can receive 403; select or create an organization first. MCP OAuth bearer tokens authenticate /mcp; they do not replace the REST session flow.

In authless mode, auth endpoints return local session/sign-out behavior and reject unsupported auth actions. All reachable callers have owner access.

Remote App Config

GET /api/app/config is the client bootstrap endpoint used by the web app to load runtime mode and analytics settings.

Behavior:

  • Returns 200 with JSON config on a successful request; middleware or initialization failures can still produce errors.
  • Works for both authenticated and unauthenticated requests.
  • In authless mode, returns authless/runtime flags from server env and mode.
  • If a valid signed-in user is present (non-authless mode), the API attempts to update that user's lastSignInAt timestamp before returning config.

Response shape:

{
  "authless": false,
  "envConnections": false,
  "persistence": "postgres",
  "stateless": false,
  "environment": "production",
  "posthog": {
    "enabled": true,
    "key": "phc_***",
    "host": "/ingest",
    "uiHost": "https://us.posthog.com"
  },
  "telemetry": {
    "enabled": true,
    "collectionRequired": true,
    "dedupeIdentifiedPosthogEvents": false,
    "disclosureUrl": "https://durabull.io/privacy"
  }
}

Field notes:

  • authless: whether DURABULL_AUTHLESS mode is active.
  • envConnections: whether env-driven connection mode is active.
  • persistence: server persistence mode (postgres or pglite).
  • stateless: true when running in pglite mode. This is an API mode label; PGlite still persists state on disk.
  • environment: NODE_ENV (development, test, or production; defaults to development).
  • posthog.enabled: true when POSTHOG_KEY is set.
  • posthog.key: PostHog project key (or null when disabled). When set, Durabull preserves PostHog's native browser collection and sends manual analytics calls with their original properties to that project.
  • posthog.host: always /ingest (Durabull's reverse-proxied analytics endpoint).
  • posthog.uiHost: PostHog UI host used by the frontend.
  • telemetry.enabled: whether Durabull anonymous/pseudonymous telemetry is active for this runtime.
  • telemetry.collectionRequired: always true as a disclosure flag, even when telemetry.enabled is false in development or CI.
  • telemetry.dedupeIdentifiedPosthogEvents: true only when the configured PostHog project is Durabull-managed, so anonymous Durabull telemetry can be deduplicated once a user has been identified in the same Durabull-owned PostHog stream.
  • telemetry.disclosureUrl: public documentation for Durabull telemetry collection.

Anonymous Telemetry

Durabull routes sanitized product analytics through POST /api/telemetry/events. Event names remain the same canonical names used by the app, such as queue_paused, connection_created, and $pageview. Self-hosted instances re-sanitize payloads before forwarding them to Durabull's existing cloud API at POST /api/telemetry/collect.

POSTHOG_KEY configures a PostHog destination for the cloud product or instance owner. It does not disable Durabull anonymous telemetry, and Durabull does not provide a product-level telemetry opt-out. When a self-hosted owner provides POSTHOG_KEY, they receive the full PostHog browser stream for their project. Sanitized forwarding to Durabull requires a configured signing secret and is best-effort; see Environment Variables.

Durabull telemetry includes feature and route usage, safe runtime context, booleans, enums, and aggregate buckets. It does not include Redis URLs, queue names, Redis key names, job data, logs, emails, names, organizations, hostnames, raw URLs, search patterns, stack traces, or raw error messages.

When telemetry is enabled, endpoints return 202 after accepting a sanitized event or batch into the bounded background queue. When disabled, /events returns 202 with accepted: false and enabled: false, without enqueueing. If the queue is full, the endpoint returns 503 and the API emits a stdout telemetry_queue signal with signal: "queue_dropped" and the operational queue name. That signal contains only queue depth counters and does not include product event properties.

Connection Management

  • GET /api/connections
  • GET /api/connections/:id
  • POST /api/connections
  • PATCH /api/connections/:id
  • DELETE /api/connections/:id
  • POST /api/connections/test

When DURABULL_ENV_CONNECTIONS=true, create/update/delete routes return 403. The test route is still available. Connection payloads support name, url, environment, isDefault, prefix, and allowSelfSignedCerts; set prefix to match the BullMQ application (default bull).

Organization resources

MethodPathPurpose
GET/api/team/membersList organization members
GET/api/invitations/:idPublic invitation preview
GET, PUT/api/user-settingsRead or update the signed-in user's theme
GET/api/alerts/eventsList alert events across the organization
GET/api/alerts/summaryOrganization alert summary
POST/api/alerts/events/:eventId/resolveResolve an event
POST, DELETE/api/alerts/events/:eventId/acknowledgeAcknowledge or clear acknowledgement
GET, POST/api/alerts/destinationsList or create reusable notification destinations
PATCH, DELETE/api/alerts/destinations/:destinationIdUpdate or delete a destination
POST/api/alerts/destinations/:destinationId/testTest a destination
GET, POST/api/alerts/webhook-destinationsList or create saved webhooks
PATCH, DELETE/api/alerts/webhook-destinations/:destinationIdUpdate or delete a saved webhook
POST/api/alerts/webhook-destinations/:destinationId/testSend a test webhook

Organization event lists accept offset, limit (maximum 100), status, acknowledged, and optional connectionId. Member/invitation mutations are provided by the Better Auth organization endpoints under /api/auth, rather than /api/team.

Linear integration

MethodPathPurpose
GET, PUT, DELETE/api/alerts/integrations/linearRead, configure defaults, or disconnect
POST/api/alerts/integrations/linear/connectStart OAuth authorization
GET/api/alerts/integrations/linear/callbackOAuth callback
POST/api/alerts/integrations/linear/testValidate the connected integration
GET/api/alerts/integrations/linear/metadataFetch teams and routing metadata

See Linear Integration for configuration and delivery behavior.

Connection-Scoped Resources

All routes below are under:

  • /api/c/:connectionId/...

Queues

  • GET /queues
  • GET /queues/discovery
  • POST /queues/discovery
  • GET /queues/:queueName
  • GET /queues/:queueName/metrics
  • POST /queues/:queueName/pause
  • POST /queues/:queueName/resume
  • POST /queues/:queueName/clean
  • POST /queues/:queueName/purge
  • POST /queues/:queueName/obliterate
  • DELETE /queues/:queueName
  • GET /queues/:queueName/can-delete

Queue discovery scans for <prefix>:*:meta and stores the discovered inventory. POST /queues/discovery starts a scan and returns 202; add wait=true to wait for completion and receive 200.

Jobs

  • GET /queues/:queueName/jobs
  • GET /queues/:queueName/jobs/:jobId
  • GET /queues/:queueName/jobs/:jobId/logs
  • DELETE /queues/:queueName/jobs/:jobId/logs
  • GET /queues/:queueName/jobs/:jobId/stacktraces
  • POST /queues/:queueName/jobs
  • POST /queues/:queueName/jobs/retry
  • POST /queues/:queueName/jobs/remove
  • POST /queues/:queueName/jobs/invoke
  • POST /queues/:queueName/jobs/:jobId/data
  • POST /queues/:queueName/jobs/:jobId/retry
  • POST /queues/:queueName/jobs/:jobId/logs/clear
  • POST /queues/:queueName/jobs/:jobId/stacktraces/clear

Job listing accepts status, name, jobId, and data search. Without search filters, page, index cursor (not Redis SCAN), and pageSize paginate results with a maximum page size of 100. Name and payload searches return all matches for client-side pagination. jobId tries an exact lookup first; if absent, it returns all matching ID substrings. These searches scan the selected job states and can be expensive on large queues.

Data edits require { "data": ... } and return 409 while a job is active. Single-job retry accepts {} or { "data": ... } and requires a failed job. Bulk retry accepts either jobIds (maximum 100) or statuses (failed, completed, or all), but not both. Retrying completed work can repeat application side effects.

The two /clear routes accept { "keepMostRecent": N }, defaulting to 0. They permanently remove older logs or stack traces. DELETE .../logs removes all logs.

POST /queues/:queueName/jobs accepts optional BullMQ job options alongside name and data:

{
  "name": "send-welcome-email",
  "data": { "userId": "123" },
  "delay": 5000,
  "priority": 5,
  "attempts": 3,
  "backoff": {
    "type": "exponential",
    "delay": 1000
  },
  "removeOnComplete": 100,
  "removeOnFail": true
}

Supported option fields match scheduled job template options plus delay: attempts, priority, backoff, removeOnComplete, and removeOnFail.

Scheduled Jobs

  • GET /scheduled-jobs
  • GET /scheduled-jobs/queue/:queueName
  • POST /scheduled-jobs/queue/:queueName
  • GET /scheduled-jobs/queue/:queueName/:schedulerId
  • PUT /scheduled-jobs/queue/:queueName/:schedulerId
  • DELETE /scheduled-jobs/queue/:queueName/:schedulerId

Workers

  • GET /workers

Metrics

  • GET /metrics

Native Queue Metrics

Route:

  • GET /queues/:queueName/metrics

Query parameters:

  • windowMinutes (optional): convenience range in minutes
  • start (optional): BullMQ metrics start index (0 is newest point)
  • end (optional): BullMQ metrics end index (-1 means oldest available)
  • priorities (optional): comma-separated buckets for getCountsPerPriority (for example 1,2,5,10,20,50)
  • includePrometheus (optional): 1 or true to include native exportPrometheusMetrics() output

Response includes BullMQ-native telemetry only:

  • raw completed/failed metrics buckets + metadata
  • queue state (isPaused, isMaxed, workers/schedulers)
  • native job counts by status
  • queue meta fields (concurrency, max, duration, maxLenEvents, paused, version)
  • native rate-limit + global limiter info
  • sampled counts per priority bucket
  • provider-compat warnings if some introspection calls are unavailable

Connection Metrics API

Route:

  • GET /metrics

Optional query parameters:

  • detailed: 1 or true to return full native payload per queue
  • windowMinutes
  • priorities
  • includePrometheus

Redis Health History

  • GET /metrics/redis-health

Query parameters: windowMinutes (5–43200, default 1440) and targetPoints (30–1000, default 480). Returns bounded history, coverage, the latest sample, active thresholds, collection status, retention days, and the freshness threshold. This is separate from BullMQ throughput metrics.

Connection Alerts

These paths are relative to /api/c/:connectionId:

MethodPathPurpose
GET, POST/alerts/rulesList or create rules
PATCH, DELETE/alerts/rules/:ruleIdUpdate or delete a rule
POST, DELETE/alerts/rules/:ruleId/snoozeSnooze or unsnooze a rule
POST/alerts/rules/:ruleId/testEvaluate a rule without creating an incident
GET/alerts/eventsList events for this connection
POST/alerts/events/resolve-bulkResolve selected events
POST/alerts/events/:eventId/resolveResolve one event
POST, DELETE/alerts/events/:eventId/acknowledgeAcknowledge or clear acknowledgement
POST/alerts/events/:eventId/deliveries/:deliveryId/retryRetry a failed delivery
POST/alerts/webhooks/testSend a test to a custom webhook

Snooze requests use { "minutes": N } with 1–10080 minutes. Rules support job failures, queue thresholds, failure rates, stalled queues, and Redis health conditions. See Redis Health Alerts and the integration guides.

Redis Keys

  • GET /redis-keys/search
  • GET /redis-keys/value/:key
  • DELETE /redis-keys/:key

Rate Limits and Payload Constraints

  • Production general API limits are 600/minute; auth is 50/10 seconds; connection tests are 10/minute per limiter key.
  • Limits are in-memory and per process; test, development, CI, or DISABLE_RATE_LIMIT=true skip them.
  • Request body limit is 1 MiB.
  • Bulk job actions cap jobIds at 100 per request.
  • Pagination page sizes are typically capped at 100.

Error Semantics

Common status patterns:

  • 400: validation/input mismatch
  • 401: unauthenticated
  • 403: forbidden (org scope, env connection mode restrictions)
  • 404: missing connection/resource
  • 409: state conflict or partial operation failure
  • 429: rate limited
  • 500: internal error
  • 503: service unavailable or telemetry queue full

Queue Purge API (Destructive)

Use this endpoint to remove jobs from a queue by selected statuses, or purge all supported statuses in one operation.

Route:

  • POST /api/c/:connectionId/queues/:queueName/purge

Required Safety Confirmation

This route is intentionally destructive and always requires exact queue-name confirmation:

  • request body confirmName must exactly equal :queueName
  • mismatch returns 400

Request Body

{
  "confirmName": "send-welcome-email",
  "statuses": ["active", "failed", "waiting"]
}

For all supported statuses (excluding waiting-children and schedulers):

{
  "confirmName": "send-welcome-email",
  "statuses": ["all"]
}

Allowed statuses values:

  • all
  • waiting
  • active
  • delayed
  • completed
  • failed
  • paused
  • prioritized

Optional keepMostRecent is an integer from 0 to 1000000 (default 0). It keeps the newest N jobs across all selected statuses, ordered by finished, processed, or created timestamp.

Operational Behavior

  • The API deduplicates requested statuses.
  • If statuses contains all, it expands to all purgeable statuses.
  • Without retention, the API cleans in batches of 1000; prioritized jobs are removed individually.
  • With keepMostRecent, the API selects the newest jobs to retain, then removes the rest individually.
  • Locked jobs can remain or produce a conflict. A purge is not atomic; inspect counts after failure.
  • all expands to the listed purgeable statuses; it does not include waiting-children or remove schedulers.
  • A safety cap of 500 batches per status is enforced to avoid unbounded loops.
    • If reached, route returns 409 and indicates which status hit the cap.

Success Response

{
  "success": true,
  "queueName": "send-welcome-email",
  "statusesPurged": ["waiting", "active", "failed"],
  "keepMostRecent": 0,
  "keptMostRecent": 0,
  "totalRemoved": 18,
  "removedByStatus": {
    "waiting": 3,
    "active": 10,
    "failed": 5
  },
  "removedJobIdsSample": ["1", "2", "3"]
}

Notes:

  • removedJobIdsSample is intentionally capped (sample only), not an exhaustive list.
  • removedByStatus contains counts for statuses actually purged in that request.

Error Responses

  • 400:
    • queue name confirmation mismatch
    • empty/invalid statuses payload
  • 409:
    • purge safety cap reached for a specific status
    • job removal conflict (some earlier removals may already have succeeded)