Skip to main content
FeatureFactory
Docs API Reference

Docs

FeatureFactory API

v1

The FeatureFactory public API. Base URL: https://featurefactory.com/api/v1 All endpoints require a Bearer API key (ff_...). Every key carries a set of ability scopes; endpoints that require a scope return 403 when the key lacks it. The /me endpoint requires no specific ability beyond a valid key. FeatureFactory currently exposes the production API only; there is no sandbox endpoint or separate test-key namespace. Rate limits: POST /signals uses the signals-ingest bucket (600 rpm). Work order runner endpoints use the runner bucket (600 rpm). All other endpoints use the general api bucket (60 rpm). Exceeded limits return 429 with a Retry-After header. Error responses use RFC 7807 Problem+JSON (Content-Type: application/problem+json). All error bodies include type, title, status, and detail fields. Validation errors also include an errors map.

Authentication

Every request requires a FeatureFactory API key sent as an HTTP Bearer token. Keys use the ff_ prefix. The API currently exposes a production environment only; there is no separate sandbox or test-key namespace.

Authorization: Bearer ff_<your-key>

Create and manage keys in Settings › API. Each key carries a set of ability scopes. Endpoints return 403 when the key lacks the required ability.

Ability scopes

AbilityGrants access to
measure:readMeasure - ReadRead pull request and commit activity, line changes, velocity metrics, and quality analysis.
plan:readPlan - ReadRead access to planning insights, themes, and plans.
plan:writePlan - WriteCreate and update planning insights, themes, and plans.
intel:readIntel - ReadRead access to intel sources and enriched signal data.
intel:writeIntel - WriteIngest raw intel events and update intel sources.
build:readBuild - ReadRead access to work orders and build pipeline state.
build:writeBuild - WriteCreate and update work orders and build pipeline jobs.
triage:readTriage - ReadRead access to triage queues and routing rules.
triage:writeTriage - WriteCreate and update triage items and routing decisions.
knowledge:readKnowledge - ReadANN vector search and read access to knowledge pages.
outcomes:readOutcomes - ReadRead access to outcome targets, actuals, and trends.
usage:readUsage - ReadRead access to API usage, token consumption, and cost data.
signals:writeSignals - WriteIngest signals from external systems into FeatureFactory.
signals:readSignals - ReadCursor-paginated read access to team signals.
connectors:ingestConnectors - IngestStart connector runs and submit source records, relationships, and tombstones.
connectors:commandsConnectors - CommandsClaim and report results for outbound connector commands.
connectors:healthConnectors - HealthReport remote connector health and permission status.
work-orders:claimLocal Runner - ExecuteRegister a local runner, claim work, report progress, and submit results.
work-orders:readLocal Runner - ReadInspect work that is waiting for an eligible runner.
work-orders:writeWork Orders - WriteCreate custom work orders for connected repositories.

Verify your key

curl "https://featurefactory.com/api/v1/me" \
  -H "Authorization: Bearer ff_<your-key>"

Returns 200 with { organization_id, name, abilities }. A 401 means the key is missing or invalid.

Pagination

All list endpoints use cursor-based pagination. The default page size is 50 items. Cursors are opaque strings - do not parse or construct them.

FieldLocationDescription
cursorQuery parameterPass the previous response's next_cursor to fetch the next page.
meta.next_cursorResponse bodyOpaque string for the next page. null means you are on the last page.
meta.prev_cursorResponse bodyOpaque string for the previous page. null on the first page.
meta.per_pageResponse bodyNumber of items per page (fixed at 50 for most endpoints).

Response envelope

{
  "data": [ ... ],
  "meta": {
    "next_cursor": "eyJpZCI6NX0",
    "prev_cursor": null,
    "per_page": 50
  }
}

Iterating all pages

cursor=""
while true; do
  resp=$(curl -s "https://featurefactory.com/api/v1/signals${cursor:+?cursor=$cursor}" \
    -H "Authorization: Bearer $FF_KEY")
  echo "$resp" | jq '.data[]'
  cursor=$(echo "$resp" | jq -r '.meta.next_cursor // empty')
  [ -z "$cursor" ] && break
done

Rate limits

BucketLimitApplies to
api60 requests/minuteAll endpoints except signals-ingest and runner routes.
signals-ingest600 requests/minutePOST /signals only.
runner600 requests/minuteWork order execution loop (claim, heartbeat, release, complete, context, notes).

When a limit is exceeded the API returns 429 with Retry-After, X-RateLimit-Limit, and X-RateLimit-Remaining headers. Respect the Retry-After value before retrying.

Errors

All API errors use RFC 7807 Problem+JSON (Content-Type: application/problem+json). Every error body includes the following fields:

FieldTypeDescription
typestring (URI)Problem type URI. Format: https://featurefactory.com/problems/<slug>
titlestringShort summary of the problem type.
statusintegerHTTP status code (mirrors the response status).
detailstringHuman-readable description of this specific occurrence.
errorsobject (422 only)Field-level validation errors. Keys are field names, values are arrays of error strings.

Common status codes

401UnauthenticatedMissing or invalid Bearer token.
403ForbiddenKey is valid but lacks the required ability scope.
404Not FoundResource does not exist or belongs to another team.
409ConflictResource is not in the required state (e.g. work order not active).
413Payload Too LargeRequest body exceeds the 256 KB limit.
422Unprocessable EntityRequest body failed validation. Check the errors field.
429Too Many RequestsRate limit exceeded. Retry after the Retry-After header value.
500Internal Server ErrorUnexpected server-side failure.

Example error response

{
  "type": "https://featurefactory.com/problems/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "This API key does not have the [measure:read] ability."
}

Example validation error (422)

{
  "type": "https://featurefactory.com/problems/validation-failed",
  "title": "Validation Failed",
  "status": 422,
  "detail": "The external_id field is required.",
  "errors": {
    "external_id": ["The external_id field is required."]
  }
}

Auth

get

/me

Inspect current API key

Returns the identity associated with the Bearer token. Useful for verifying a key is valid and checking which abilities it carries. Required ability: none (any valid key).

Responses

200Key identity.
401Missing or invalid API key.

Example request

curl "https://featurefactory.com/api/v1/me" \
  -H "Authorization: Bearer ff_<your-key>"

Signals

get

/signals

List signals

Requires signals:read

Cursor-paginated list of signals scoped to the authenticated team, ordered newest first (50 per page). Required ability: signals:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.

Responses

200Paginated signal list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/signals" \
  -H "Authorization: Bearer ff_<your-key>"
post

/signals

Ingest a signal

Requires signals:write

Ingest a signal idempotently. Deduplication key: (team_id, source_provider, external_id). - No existing record: creates and returns 201. - Record exists: updates mutable fields and returns 200. Rate limit: 600 rpm (signals-ingest bucket). Payload cap: 256 KB. Required ability: signals:write

Request body

application/json

FieldTypeRequiredDescription
source_providerstringnoOrigin system. Defaults to "api" when omitted.
external_idstringyesUnique identifier in the source system. Required.
titlestring | nullnoShort human-readable label.
bodystring | nullnoLong-form description or raw event body.
occurred_atstring | nullnoWhen the event happened in the source system (ISO 8601).
payloadobject | nullnoArbitrary JSON object for additional structured data.

Examples

GitHub pull request event

{
  "source_provider": "github",
  "external_id": "pr-8801",
  "title": "Fix memory leak in cache driver",
  "occurred_at": "2026-01-15T10:00:00Z",
  "payload": {
    "pr_number": 8801,
    "merged_by": "alice"
  }
}

Minimal - only external_id required

{
  "external_id": "evt-0042"
}

Responses

200Signal updated (idempotent replay).
201Signal created.
401Missing or invalid API key.
403The key does not carry the required ability scope.
413Request body exceeds the 256 KB limit.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/signals" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"source_provider":"github","external_id":"pr-8801","title":"Fix memory leak in cache driver","occurred_at":"2026-01-15T10:00:00Z","payload":{"pr_number":8801,"merged_by":"alice"}}'
post

/inbound/{integration}

Inbound webhook receiver

Receives inbound webhook events from external providers via a team integration. Persists the event and dispatches async processing. Two authentication paths are supported: - API key with signals:write ability (HMAC check skipped). - Unauthenticated callers with a valid X-Hub-Signature-256 header computed against the integration's webhook_secret. Returns 202 Accepted on success or idempotent replay.

Parameters

integrationpathrequiredIntegration UUID.
X-Hub-Signature-256headerHMAC-SHA256 signature for unauthenticated callers.
X-Delivery-IdheaderUnique delivery identifier for idempotency.
X-Event-TypeheaderEvent type label from the originating provider.

Request body

application/json

Responses

202Event accepted for processing.
401Missing or invalid authentication.
404Resource not found.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/inbound/<id>" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"

Measure

get

/repositories

List repositories

Requires measure:read

Cursor-paginated list of repositories for the authenticated team, ordered newest first (50 per page). Required ability: measure:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.

Responses

200Paginated repository list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/repositories" \
  -H "Authorization: Bearer ff_<your-key>"
get

/pull-requests

List pull requests

Requires measure:read

Cursor-paginated pull requests for the authenticated team, ordered by most recently merged or updated (50 per page). Required ability: measure:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.
repository_idqueryFilter by repository ID.
statusqueryFilter by status: open | merged | closed
originqueryFilter by origin: human | agent_local | agent_cloud

Responses

200Paginated pull request list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/pull-requests" \
  -H "Authorization: Bearer ff_<your-key>"
get

/pull-requests/{id}

Get pull request

Requires measure:read

Full detail for a single pull request including analysis scores and AI attribution evidence. Required ability: measure:read

Parameters

idpathrequiredPull request ID.

Responses

200Pull request detail.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/pull-requests/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
get

/metrics/series

Get daily metrics series

Requires measure:read

Daily metric rollup data for charting. Returns one data point per day within the requested period. Required ability: measure:read

Parameters

periodqueryRolling window in days: 7 | 30 | 90. Default: 30.
scopequeryAggregation scope: team | repo | person. Default: team.
originqueryFilter by origin: human | agent. Omit for combined.

Responses

200Metric series data.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/metrics/series" \
  -H "Authorization: Bearer ff_<your-key>"
get

/metrics/dora

Get DORA metrics

Requires measure:read

Aggregated DORA metrics over a rolling period. Required ability: measure:read

Parameters

periodqueryRolling window in days: 7 | 30 | 90. Default: 30.

Responses

200DORA metrics.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/metrics/dora" \
  -H "Authorization: Bearer ff_<your-key>"

Plan

get

/insights

List insights

Requires plan:read

Cursor-paginated list of insights for the authenticated team, ordered newest first (50 per page). Required ability: plan:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.
typequeryFilter by type: bug | request | friction | praise | insight
statusqueryFilter by status: pending_review | approved | dismissed | duplicate
theme_idqueryFilter by theme ID.

Responses

200Paginated insight list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/insights" \
  -H "Authorization: Bearer ff_<your-key>"
get

/themes

List themes

Requires plan:read

Cursor-paginated list of themes for the authenticated team, ordered by insight_score descending then id descending (50 per page). Required ability: plan:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.
statusqueryFilter by status: active | in_progress | archived

Responses

200Paginated theme list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/themes" \
  -H "Authorization: Bearer ff_<your-key>"
get

/themes/{id}

Get theme

Requires plan:read

Fetch a single theme with up to 20 most recent evidence insights. Required ability: plan:read

Parameters

idpathrequiredTheme ID.

Responses

200Theme detail.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/themes/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
post

/themes/{id}/review

Approve or dismiss a theme

Requires plan:write

Approve (transitions to in_progress) or dismiss (transitions to archived) a theme. Required ability: plan:write

Parameters

idpathrequiredTheme ID.

Request body

application/json

FieldTypeRequiredDescription
actionstringyesapprove | dismiss

Examples

Approve the theme

{
  "action": "approve"
}

Dismiss the theme

{
  "action": "dismiss"
}

Responses

200Updated theme status.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/themes/<id>/review" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"action":"approve"}'
get

/prds

List PRDs

Requires plan:read

Cursor-paginated list of root PRDs for the authenticated team, ordered newest first (50 per page). Required ability: plan:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.
lifecyclequeryFilter by lifecycle: draft | review | approved | archived

Responses

200Paginated PRD list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/prds" \
  -H "Authorization: Bearer ff_<your-key>"
post

/prds

Create a PRD

Requires plan:write

Create a new PRD and dispatch AI generation. The PRD is linked to a theme. Generation is asynchronous; poll the list endpoint or check is_generating on the returned object. Required ability: plan:write

Request body

application/json

FieldTypeRequiredDescription
theme_idintegeryesThe theme to base the PRD on.
scopestringnoGeneration depth: full | outline | brief. Default: full.
titlestring | nullnoOverride title (defaults to theme name).
guidancestring | nullnoAdditional instructions for the AI generator.

Examples

Minimal PRD creation

{
  "theme_id": 42
}

PRD with custom scope and guidance

{
  "theme_id": 42,
  "scope": "outline",
  "title": "Redesigned checkout flow",
  "guidance": "Focus on mobile experience and reduce steps."
}

Responses

201PRD created and generation dispatched.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/prds" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"theme_id":42}'
get

/prds/{id}/export

Export PRD as Markdown

Requires plan:read

Download the completed PRD as a Markdown file attachment. Returns 422 when the PRD content is not yet available (generation still in progress). Required ability: plan:read

Parameters

idpathrequiredPRD ID.

Responses

200Markdown file download.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422PRD content not yet available.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/prds/<id>/export" \
  -H "Authorization: Bearer ff_<your-key>"
get

/search

Hybrid plan search

Requires plan:read

Hybrid search (vector ANN + keyword) over plan entities: insights, themes, work items, and PRDs. Results are ranked by similarity and scoped to the authenticated team. Required ability: plan:read

Parameters

qqueryrequiredFree-text search query (max 500 characters).
types[]querySubset of entity types to search: insights | themes | work_items | prds

Responses

200Search results.
401Missing or invalid API key.
403The key does not carry the required ability scope.
422Validation error.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/search" \
  -H "Authorization: Bearer ff_<your-key>"

Intel

get

/intel/accounts

List accounts

Requires intel:read

Cursor-paginated list of accounts for the authenticated team. Required ability: intel:read

Parameters

cursorqueryOpaque cursor from the previous page's next_cursor field.
searchquerySearch by account name or domain.
tier_idqueryFilter by tier ID.

Responses

200Paginated account list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/intel/accounts" \
  -H "Authorization: Bearer ff_<your-key>"
get

/intel/accounts/{id}

Get account

Requires intel:read

Full account detail including health trend (last 10 points), recent activities (last 10), and recent conversations (last 10). Required ability: intel:read

Parameters

idpathrequiredAccount ID.

Responses

200Account detail.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/intel/accounts/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
patch

/intel/accounts/{id}/tier

Update account tier

Requires intel:write

Set or clear the tier for an account. Pass tier_id as null to remove the existing tier assignment. Required ability: intel:write

Parameters

idpathrequiredAccount ID.

Request body

application/json

FieldTypeRequiredDescription
tier_idinteger | nullyesTier ID to assign, or null to clear.

Examples

Assign tier 2

{
  "tier_id": 2
}

Clear tier assignment

{
  "tier_id": null
}

Responses

200Account with updated tier.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X PATCH "https://featurefactory.com/api/v1/intel/accounts/<id>/tier" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"tier_id":2}'

Build

get

/build-runs

List build runs

Requires build:read

List build runs for the authenticated team. Required ability: build:read

Parameters

statusqueryFilter by status: pending | running | complete | failed | cancelled

Responses

200Build run list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/build-runs" \
  -H "Authorization: Bearer ff_<your-key>"
get

/build-runs/{id}

Get build run

Requires build:read

Get a single build run with its steps. Required ability: build:read

Parameters

idpathrequiredBuild run ID.

Responses

200Build run detail.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/build-runs/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
post

/build-runs/{id}/steps/{step}/approve

Approve a build step

Requires build:write

Approve a step that is awaiting human review, allowing the build run to advance to the next step. Required ability: build:write

Parameters

idpathrequiredBuild run ID.
steppathrequiredStep sequence number.

Request body

application/json

FieldTypeRequiredDescription
user_idintegeryesID of the user approving the step.

Responses

200Step approved.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/build-runs/<id>/steps/<id>/approve" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/build-runs/{id}/steps/{step}/reject

Reject a build step

Requires build:write

Reject a step that is awaiting human review, stopping the build run at that point. Required ability: build:write

Parameters

idpathrequiredBuild run ID.
steppathrequiredStep sequence number.

Request body

application/json

FieldTypeRequiredDescription
user_idintegeryesID of the user rejecting the step.
notestring | nullnoOptional rejection note.

Responses

200Step rejected.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/build-runs/<id>/steps/<id>/reject" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/build-runs/{id}/approve

Approve a build run

Requires build:write

Shorthand approval that finds the current awaiting-approval step for the given build run and approves it. Equivalent to calling the per-step approve endpoint when the step number is unknown. Required ability: build:write

Parameters

idpathrequiredBuild run ID.

Request body

application/json

FieldTypeRequiredDescription
user_idintegeryesID of the user approving the step.

Responses

200Step approved.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/build-runs/<id>/approve" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
get

/runners

List runners

Requires build:read

List customer runners for the authenticated team. Platform runners are excluded. Required ability: build:read

Parameters

statusqueryFilter by status: online | offline | idle

Responses

200Runner list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/runners" \
  -H "Authorization: Bearer ff_<your-key>"

Triage

get

/triages

List triages

Requires triage:read

List triage items for the authenticated team. Required ability: triage:read

Parameters

statusqueryFilter by status: pending | approved | dismissed | completed | failed

Responses

200Triage list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/triages" \
  -H "Authorization: Bearer ff_<your-key>"
get

/triages/{id}

Get triage

Requires triage:read

Get a single triage item. Required ability: triage:read

Parameters

idpathrequiredTriage ID.

Responses

200Triage detail.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/triages/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
post

/triages/{id}/approve

Approve a triage

Requires triage:write

Approve a pending triage item, optionally attributing the approval to a specific user. Required ability: triage:write

Parameters

idpathrequiredTriage ID.

Request body

application/json

FieldTypeRequiredDescription
user_idinteger | nullnoID of the approving user (optional).

Responses

200Triage approved.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/triages/<id>/approve" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/triages/{id}/dismiss

Dismiss a triage

Requires triage:write

Dismiss a pending triage item. Required ability: triage:write

Parameters

idpathrequiredTriage ID.

Responses

200Triage dismissed.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/triages/<id>/dismiss" \
  -H "Authorization: Bearer ff_<your-key>"
get

/triage-policies

List triage policies

Requires triage:read

List triage policies for the authenticated team. Required ability: triage:read

Responses

200Triage policy list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/triage-policies" \
  -H "Authorization: Bearer ff_<your-key>"
post

/triage-policies

Create triage policy

Requires triage:write

Create a new triage routing policy. Required ability: triage:write

Request body

application/json

FieldTypeRequiredDescription
integration_idinteger | nullnoIntegration (Jira/Linear/etc.) to listen on.
enabledbooleanyesWhether the policy is active.
filtersobject | nullnoLabel/tag filter rules.
repository_idinteger | nullnoRestrict fixes to a specific repository.
lookback_daysinteger | nullnoHow many days of context to include when triaging.
min_understanding_confidencenumber | nullnoMinimum understanding confidence to auto-approve (0-1).
min_solving_confidencenumber | nullnoMinimum solving confidence to auto-approve (0-1).
modestring | nullnoauto = trigger immediately, manual = require human approval.
daily_capinteger | nullnoMax triages this policy may trigger per calendar day.
per_triage_budget_usdnumber | nullnoMax USD to spend per triage.

Examples

Enable Jira policy in manual mode

{
  "integration_id": 3,
  "enabled": true,
  "mode": "manual",
  "daily_cap": 10,
  "per_triage_budget_usd": 2
}

Responses

201Triage policy created.
401Missing or invalid API key.
403The key does not carry the required ability scope.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/triage-policies" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"integration_id":3,"enabled":true,"mode":"manual","daily_cap":10,"per_triage_budget_usd":2}'
get

/triage-policies/{id}

Get triage policy

Requires triage:read

Get a single triage policy. Required ability: triage:read

Parameters

idpathrequiredTriage policy ID.

Responses

200Triage policy.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/triage-policies/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
put

/triage-policies/{id}

Update triage policy

Requires triage:write

Replace all fields of a triage policy. Required ability: triage:write

Parameters

idpathrequiredTriage policy ID.

Request body

application/json

FieldTypeRequiredDescription
integration_idinteger | nullno
enabledbooleanyes
filtersobject | nullno
repository_idinteger | nullno
lookback_daysinteger | nullno
min_understanding_confidencenumber | nullno
min_solving_confidencenumber | nullno
modestring | nullno
daily_capinteger | nullno
per_triage_budget_usdnumber | nullno

Responses

200Updated triage policy.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X PUT "https://featurefactory.com/api/v1/triage-policies/<id>" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"

Knowledge

get

/knowledge/search

Search knowledge base

Requires knowledge:read

ANN vector search over published knowledge pages for the authenticated team. Returns a synthesized context string suitable for LLM prompting. Required ability: knowledge:read

Parameters

queryqueryrequiredSearch query (max 500 characters).
limitqueryMaximum number of pages to include in context (1-20, default 5).

Responses

200Search result with synthesized context.
401Missing or invalid API key.
403The key does not carry the required ability scope.
422Validation error.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/knowledge/search" \
  -H "Authorization: Bearer ff_<your-key>"
get

/knowledge/pages

List knowledge pages

Requires knowledge:read

List knowledge base pages for the authenticated team. Required ability: knowledge:read

Parameters

statusqueryFilter by status: draft | published

Responses

200Knowledge page list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/knowledge/pages" \
  -H "Authorization: Bearer ff_<your-key>"
get

/knowledge/pages/{id}

Get knowledge page

Requires knowledge:read

Get a single knowledge base page. The embedding vector is omitted from the response. Required ability: knowledge:read

Parameters

idpathrequiredKnowledge page ID.

Responses

200Knowledge page.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/knowledge/pages/<id>" \
  -H "Authorization: Bearer ff_<your-key>"

Outcomes

get

/outcomes/metrics

Get outcome metrics

Requires outcomes:read

High-level agent outcome metrics over a rolling period. Required ability: outcomes:read

Parameters

periodqueryRolling window in days: 7 | 30 | 90. Default: 30.

Responses

200Outcome metrics.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/outcomes/metrics" \
  -H "Authorization: Bearer ff_<your-key>"
get

/outcomes

List outcomes

Requires outcomes:read

List outcome records for the authenticated team. Required ability: outcomes:read

Parameters

statusqueryFilter by status: pending | met | partial | missed

Responses

200Outcome list.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/outcomes" \
  -H "Authorization: Bearer ff_<your-key>"

Usage

get

/usage

Get usage

Requires usage:read

Current month token consumption and cost data for the authenticated team, broken down by feature. Required ability: usage:read

Responses

200Usage summary.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/usage" \
  -H "Authorization: Bearer ff_<your-key>"

Work Orders

get

/work-orders/pending

Peek pending work orders

Requires work-orders:read

Read-only peek at the queue. Lists orders this runner would be eligible to claim, without claiming any of them. Team runners see their own team's queued orders. Platform runners see all queued cloud-eligible orders. Required ability: work-orders:read Rate limit: runner bucket (600 rpm).

Parameters

typequeryFilter by work order type.
repositoryqueryFilter by repository ID.
pagequeryPage number (default 1).
per_pagequeryItems per page (default 20, max 200).

Responses

200Pending work orders with pagination metadata.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/work-orders/pending" \
  -H "Authorization: Bearer ff_<your-key>"
post

/work-orders

Create a work order

Requires work-orders:write

Create a custom work order (type=custom) against one of the team's repositories. Requires the Build pillar (Business tier). Platform keys cannot create work orders. Required ability: work-orders:write Rate limit: runner bucket (600 rpm).

Request body

application/json

FieldTypeRequiredDescription
typestringyesMust be "custom". Other types are internal.
repositoryyesRepository ID (integer) or full_name string (org/repo).
spec_mdstringyesMarkdown specification for the work order.
executorstringnoPreferred runner lane. Default: any.
priorityintegernoPriority 0-100 (higher = claimed sooner). Default 50.

Examples

Create a custom work order

{
  "type": "custom",
  "repository": "acme/api-service",
  "spec_md": "Add rate limiting to the /api/v1/users endpoint.",
  "executor": "local",
  "priority": 70
}

Responses

201Work order created and queued.
401Missing or invalid API key.
403The key does not carry the required ability scope.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/work-orders" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"type":"custom","repository":"acme/api-service","spec_md":"Add rate limiting to the /api/v1/users endpoint.","executor":"local","priority":70}'
post

/work-orders/claim

Claim a work order

Requires work-orders:claim

Claim the next eligible work order for the calling runner. Returns the claimed order payload (204 when nothing is available). Runner is identified via the X-Runner-Key header or runner_key body field. Required ability: work-orders:claim Rate limit: runner bucket (600 rpm).

Request body

application/json

FieldTypeRequiredDescription
runner_keystringnoRunner key (alternative to X-Runner-Key header).
typesarraynoFilter to specific work order types.
work_order_idintegernoClaim this specific order (must be eligible).

Responses

200Work order claimed.
204No eligible work orders available.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/work-orders/claim" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/work-orders/{id}/heartbeat

Extend work order lease

Requires work-orders:claim

Extend the lease on an active work order and transition claimed -> running (recording started_at). Call every 30-60 seconds while executing. Required ability: work-orders:claim Rate limit: runner bucket (600 rpm).

Parameters

idpathrequiredWork order ID.

Responses

200Lease extended.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
409Resource is not in the required state for this operation.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/work-orders/<id>/heartbeat" \
  -H "Authorization: Bearer ff_<your-key>"
post

/work-orders/{id}/release

Release a work order

Requires work-orders:claim

Voluntarily return a claimed/running order to the queue without consuming an attempt. Use when shutting down or running out of budget. Required ability: work-orders:claim Rate limit: runner bucket (600 rpm).

Parameters

idpathrequiredWork order ID.

Responses

200Order released back to queue.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
409Resource is not in the required state for this operation.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/work-orders/<id>/release" \
  -H "Authorization: Bearer ff_<your-key>"
post

/work-orders/{id}/complete

Complete a work order

Requires work-orders:claim

Submit the terminal result of a work order run. - status=completed: validates the result against the order's output schema (when set). First schema failure returns 422 with the lease intact (one repair retry). Second failure fails the order. - status=failed: fails the order immediately. Re-queues while attempts remain. Required ability: work-orders:claim Rate limit: runner bucket (600 rpm).

Parameters

idpathrequiredWork order ID.

Request body

application/json

FieldTypeRequiredDescription
runner_keystringnoRunner key (alternative to X-Runner-Key header).
statusstringyes
resultobject | nullnoStructured result payload (validated against output_schema).
errorstring | nullnoError message when status=failed.
branchstring | nullnoBranch name created during execution.
duration_msinteger | nullnoTotal execution time in milliseconds.
input_tokensinteger | nullnoLLM input tokens consumed.
output_tokensinteger | nullnoLLM output tokens consumed.
modelstring | nullnoModel identifier used for execution.

Responses

200Completion accepted.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
409Resource is not in the required state for this operation.
422Schema validation failure (first strike - repair and resubmit).
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/work-orders/<id>/complete" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
get

/work-orders/{id}/context

Get work order context bundle

Resolve one context bundle for a work order. The runner pulls exactly the context it needs at execution time, one ref at a time. Only the runner currently owning the order may read context. Required ability: any valid key (ownership enforced in handler). Rate limit: runner bucket (600 rpm).

Parameters

idpathrequiredWork order ID.
refqueryrequiredContext reference key to resolve.

Responses

200Context bundle.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/work-orders/<id>/context" \
  -H "Authorization: Bearer ff_<your-key>"
post

/work-orders/{id}/notes

Add a work order note

Append a progress note to a work order's UI timeline. Only the runner currently owning the order may write notes. Required ability: any valid key (ownership enforced in handler). Rate limit: runner bucket (600 rpm).

Parameters

idpathrequiredWork order ID.

Request body

application/json

FieldTypeRequiredDescription
runner_keystringnoRunner key (alternative to X-Runner-Key header).
notestringyesProgress note to append to the timeline.

Responses

201Note created.
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/work-orders/<id>/notes" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"

Runners

post

/runners/register

Register a runner

Requires work-orders:claim

Register (or idempotently re-register) a runner for this team. Returns a runner_key that the runner uses to authenticate subsequent requests via the X-Runner-Key header. Team keys bind the runner to the key's team. Platform keys create platform-fleet runners with no team binding. Required ability: work-orders:claim

Request body

application/json

FieldTypeRequiredDescription
namestringyesHuman-readable runner name.
typestringyesRunner type.

Examples

Register a local runner

{
  "name": "my-machine",
  "type": "local"
}

Responses

200Runner registered (or re-registered). Returns runner_key.
401Missing or invalid API key.
403The key does not carry the required ability scope.
422Validation error.
429Rate limit exceeded.

Example request

curl -X POST "https://featurefactory.com/api/v1/runners/register" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-machine","type":"local"}'
get

/runner/instructions

Get runner instructions

Requires work-orders:claim

Return the polling decision for the runner: stop | wait | claim. Touches last_seen_at. Runner is identified via the X-Runner-Key header or runner_key query parameter. Required ability: work-orders:claim

Parameters

runner_keyqueryRunner key (alternative to X-Runner-Key header).

Responses

200Polling instruction.
401Missing or invalid API key.
403The key does not carry the required ability scope.
429Rate limit exceeded.

Example request

curl "https://featurefactory.com/api/v1/runner/instructions" \
  -H "Authorization: Bearer ff_<your-key>"

Connectors

post

/connectors/{integration}/runs

Start an idempotent connector sync run

Requires connectors:ingest

Parameters

integrationpathrequired
X-Idempotency-Keyheaderrequired

Request body

application/json

FieldTypeRequiredDescription
resourcestringyes
entity_type[ "string", "null" ]no
modeyes
inputobjectno

Responses

200Existing idempotent run returned
201Sync run started
401Missing or invalid API key.
403The key does not carry the required ability scope.
422Validation error

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/runs" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
get

/connectors/{integration}/runs/{run}

Inspect a connector sync run

Requires connectors:ingest

Parameters

integrationpathrequired
runpathrequired

Responses

200Current sync run state and counters
401Missing or invalid API key.
403The key does not carry the required ability scope.
404Resource not found.

Example request

curl "https://featurefactory.com/api/v1/connectors/<id>/runs/<id>" \
  -H "Authorization: Bearer ff_<your-key>"
post

/connectors/{integration}/runs/{run}/batches

Submit an idempotent source-record batch

Requires connectors:ingest

Limited to 500 records and 10 MB. Results identify accepted, duplicate, stale, tombstoned, and rejected records individually.

Parameters

integrationpathrequired
runpathrequired
X-Idempotency-Keyheaderrequired

Request body

application/json

Responses

200Per-record intake results
413Batch exceeds 10 MB
422Batch validation failed

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/runs/<id>/batches" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/connectors/{integration}/runs/{run}/complete

Complete a run and atomically advance its stream cursor

Requires connectors:ingest

A successful full run also tombstones unseen entities. Failed or incomplete full runs never infer deletions.

Parameters

integrationpathrequired
runpathrequired
X-Idempotency-Keyheaderrequired

Request body

application/json

FieldTypeRequiredDescription
cursor[ "string", "null" ]no
resultobjectno

Responses

200Run completed

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/runs/<id>/complete" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/connectors/{integration}/commands/claim

Claim pending outbound commands with row-level skip-locking

Requires connectors:commands

Parameters

integrationpathrequired

Responses

200Commands claimed for delivery

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/commands/claim" \
  -H "Authorization: Bearer ff_<your-key>"
post

/connectors/{integration}/runs/{run}/fail

Mark a connector sync run failed

Requires connectors:ingest

Parameters

integrationpathrequired
runpathrequired
X-Idempotency-Keyheaderrequired

Request body

application/json

FieldTypeRequiredDescription
messagestringyes

Responses

200Run failed idempotently
409Run is already completed

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/runs/<id>/fail" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/connectors/{integration}/runs/{run}/tombstones

Explicitly tombstone source records

Requires connectors:ingest

Parameters

integrationpathrequired
runpathrequired
X-Idempotency-Keyheaderrequired

Responses

200Per-record tombstone results
409Run is no longer accepting records

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/runs/<id>/tombstones" \
  -H "Authorization: Bearer ff_<your-key>"
post

/connectors/{integration}/commands/{command}/complete

Complete a claimed outbound command

Requires connectors:commands

Parameters

integrationpathrequired
commandpathrequired

Responses

200Command completed

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/commands/<id>/complete" \
  -H "Authorization: Bearer ff_<your-key>"
post

/connectors/{integration}/commands/{command}/fail

Fail or mark a claimed outbound command ambiguous

Requires connectors:commands

Parameters

integrationpathrequired
commandpathrequired

Request body

application/json

FieldTypeRequiredDescription
messagestringyes
ambiguousbooleanno

Responses

200Command state recorded

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/commands/<id>/fail" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
post

/connectors/{integration}/health

Report remote connector health and permissions

Requires connectors:health

Parameters

integrationpathrequired

Request body

application/json

FieldTypeRequiredDescription
statusyes
message[ "string", "null" ]no
permissionsarrayno

Responses

200Health report accepted

Example request

curl -X POST "https://featurefactory.com/api/v1/connectors/<id>/health" \
  -H "Authorization: Bearer ff_<your-key>" \
  -H "Content-Type: application/json"
Create the narrowest API key scope required and store the displayed secret securely.

Was this article helpful?

Your response enters the same customer insight queue used by product feedback.