API reference

Every endpoint is authenticated with a bearer API key and lives under https://www.flowrelay.it/api/v1. Rate limit: 60 requests / minute / key.

Download OpenAPIImport openapi.json into Postman or Insomnia to generate a client.

Finding IDs

Every id is a UUID you discover from a list endpoint – you never need to know one up front.

You needGet it from
project_id (and {id} in the path)GET /projects
handoff_idGET /handoffs
insight_idGET /projects/{id}/insights
channel_idGET /discord/channels
filter values (repos, branches, etc.)GET /handoffs/filters?project_id=...
jobIdreturned in the 202 response when you start a generation

List accessible projects

GET/projects

Returns the tenant context for the key owner: account type, organization memberships, and every personal or organization project the key can access (role-scoped).

Request

curl "https://www.flowrelay.it/api/v1/projects" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "account_type": "business",
  "organizations": [
    {
      "id": "8c2b1a90-1234-4abc-9def-0123456789ab",
      "name": "Acme",
      "slug": "acme",
      "role": "admin",
      "is_temporary_admin": false
    }
  ],
  "projects": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Payments",
      "slug": "payments",
      "description": "Billing and checkout",
      "organization_id": "8c2b1a90-1234-4abc-9def-0123456789ab",
      "organization_name": "Acme",
      "organization_slug": "acme",
      "project_type": "organization",
      "access_role": "admin",
      "created_at": "2026-05-01T09:12:00Z",
      "updated_at": "2026-06-20T14:03:00Z"
    }
  ]
}
StatusWhen
200Success
401Missing or invalid API key.
429Rate limit exceeded (60/min/key).

List handoffs

GET/handoffs

Lists handoffs across accessible projects, newest first. Without project_id it spans every project the key can access.

Query parameters

NameTypeDescription
statusoptionalstringFilter by status.One of: active, archived, allDefault: active
limitoptionalintegerMaximum rows to return (1–100).Default: 20
project_idoptionalstringRestrict to a single project.

Request

curl "https://www.flowrelay.it/api/v1/handoffs" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "handoffs": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "kind": "project",
      "title": "Checkout refactor handoff",
      "summary": "Migrated the checkout flow to the new pricing engine.",
      "status": "active",
      "sources": [
        "github",
        "linear"
      ],
      "key_changes": [
        "Replaced legacy tax calc"
      ],
      "decisions": [
        "Use flat per-artifact pricing"
      ],
      "open_questions": [
        "Backfill historical invoices?"
      ],
      "next_steps": [
        "Wire the new webhook"
      ],
      "completion_percentage": null,
      "related_event_ids": [
        "d4e5f6a7-b8c9-0123-def4-23456789013a"
      ],
      "scope_type": "project",
      "project_name": "Payments",
      "created_at": "2026-06-20T14:03:00Z",
      "updated_at": "2026-06-20T14:03:00Z",
      "markdown": "# Checkout refactor handoff\n\n> Migrated the checkout flow…"
    }
  ]
}
StatusWhen
200Success
400The body or a query parameter is invalid: an unknown field, an unknown source, an unsupported filter dimension, or an out-of-range number. The response names the offending field and lists the allowed values.
401Missing or invalid API key.
404Project not found or not accessible by this key.

Generate a handoff

POST/handoffs

Asynchronous – returns 202 with a jobId. Poll GET /jobs/{jobId} until the job completes.

Enqueues a project handoff generation and returns a job id. Poll GET /jobs/{jobId} until status is completed. When sources/filters are omitted the project saved scope preferences are used.

Body parameters

NameTypeDescription
project_idrequiredstringProject UUID to generate the handoff for (from GET /projects).
sourcesoptionalstring[]Restrict generation to these sources. Each must be a known source id; an unknown value is rejected with 400 (not silently ignored). Omit to use every connected source.One of: github, slack, discord, linear, notion, jira, gitlab, bitbucket, azure_devops, figma, confluence, microsoft_outlook, microsoft_teams, sentry, datadog, pagerduty, asana, gmail, buildkite, circleci, vercel
filtersoptionalSourceFilterPer-source projects / eventTypes / branches / priorities (AND-combined). Keys must be source ids (unknown keys are rejected with 400); the values are provider-driven and matched leniently – an unmatched value simply returns no events. Fetch GET /handoffs/filters for the set selectable on a project.

Request

curl "https://www.flowrelay.it/api/v1/handoffs" \
  -H "Authorization: Bearer fr_your_api_key" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{ "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "sources": [ "github", "linear" ], "filters": { "github": { "branches": [ "main" ] } } }'

Request body

JSON
{
  "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "sources": [
    "github",
    "linear"
  ],
  "filters": {
    "github": {
      "branches": [
        "main"
      ]
    }
  }
}

Response

JSON
{
  "jobId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "status": "pending"
}
StatusWhen
202Job enqueued. Poll GET /jobs/{jobId} for the result.
400project_id is missing or invalid, or the body failed validation (unknown field / source / filter value). The response names the field and lists allowed values.
401Missing or invalid API key.
404Project not found or not accessible by this key.
429Rate limit exceeded (60/min/key).

List selectable filter options

GET/handoffs/filters

Returns the real selectable filter values per connected source for a project: resources (repos / channels / boards / Figma files), branches, event types, and priorities. Use these to build a valid SourceFilter instead of guessing. The response also carries a "figma" availability block: Figma can be selected only on projects in the Global processing region, and selecting it attaches visual context (frame renders) at a flat 1-credit surcharge.

Query parameters

NameTypeDescription
project_idrequiredstringProject UUID (from GET /projects) to resolve filter options for.

Request

curl "https://www.flowrelay.it/api/v1/handoffs/filters?project_id=PROJECT_ID" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "filters": {
    "github": {
      "projects": [
        {
          "id": "acme/payments",
          "label": "acme/payments"
        }
      ],
      "eventTypes": [
        {
          "value": "push",
          "label": "Push"
        }
      ],
      "priorities": [],
      "branches": [
        {
          "value": "main",
          "label": "main"
        }
      ]
    },
    "linear": {
      "projects": [
        {
          "id": "f1e2d3c4-b5a6-7890-1234-567890abcdef",
          "label": "Core"
        }
      ],
      "eventTypes": [
        {
          "value": "issue",
          "label": "Issue"
        }
      ],
      "priorities": [
        {
          "value": "high",
          "label": "High"
        }
      ]
    }
  },
  "figma": {
    "selectable": true,
    "region": "default",
    "scope": "personal",
    "canManageResidency": true,
    "residencyHref": "/projects/3fa85f64-5717-4562-b3fc-2c963f66afa6#data-residency"
  }
}
StatusWhen
200Success
400project_id is missing or not a valid UUID.
401Missing or invalid API key.
404Project not found or not accessible by this key.

List project insights

GET/projects/{id}/insights

Lists AI insights for a project, newest first. Optionally filter by kind and status.

Path parameters

NameTypeDescription
idrequiredstringProject UUID (from GET /projects).

Query parameters

NameTypeDescription
kindoptionalstringFilter by insight kind.One of: cross_source_correlation, onboarding_brief, architecture_insight
statusoptionalstringFilter by status.One of: active, archived, allDefault: active
limitoptionalintegerMaximum rows to return (1–100).Default: 20

Request

curl "https://www.flowrelay.it/api/v1/projects/3fa85f64-5717-4562-b3fc-2c963f66afa6/insights" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "insights": [
    {
      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "kind": "architecture_insight",
      "title": "Pricing engine trade-offs",
      "summary": "Flat per-artifact pricing simplifies billing at the cost of margin variance.",
      "data": {
        "tradeOffs": [],
        "risks": [],
        "patterns": [],
        "recommendations": []
      },
      "model_used": "gemini-2.5-flash",
      "status": "active",
      "related_event_ids": [
        "d4e5f6a7-b8c9-0123-def4-23456789013a"
      ],
      "sources": [
        "github"
      ],
      "created_at": "2026-06-21T08:00:00Z",
      "updated_at": "2026-06-21T08:00:00Z",
      "markdown": "# Pricing engine trade-offs\n\n> Flat per-artifact pricing…"
    }
  ]
}
StatusWhen
200Success
400The body or a query parameter is invalid: an unknown field, an unknown source, an unsupported filter dimension, or an out-of-range number. The response names the offending field and lists the allowed values.
401Missing or invalid API key.
404Project not found or not accessible by this key.

Generate an insight

POST/projects/{id}/insights/{kind}

Asynchronous – returns 202 with a jobId. Poll GET /jobs/{jobId} until the job completes.

Enqueues an insight generation for the project and returns a job id. The accepted body fields depend on kind. Poll GET /jobs/{jobId} for the result.

Path parameters

NameTypeDescription
idrequiredstringProject UUID (from GET /projects).
kindrequiredstringInsight kind.One of: correlation, onboarding, architecture

Body parameters

NameTypeDescription
lookbackHoursoptionalintegercorrelation only – window in hours (1–2160, default 168). Rejected on other kinds.
lookbackDaysoptionalintegeronboarding / architecture – window in days (1–365, default 30 / 14). Rejected on correlation.
newMemberRoleoptionalstringonboarding only – role of the person being onboarded.
focusAreaoptionalstringonboarding only – area to emphasise.
focusQuestionoptionalstringarchitecture only – the question to investigate.
sourcesoptionalstring[]Restrict to these sources. Unknown values are rejected with 400.One of: github, slack, discord, linear, notion, jira, gitlab, bitbucket, azure_devops, figma, confluence, microsoft_outlook, microsoft_teams, sentry, datadog, pagerduty, asana, gmail, buildkite, circleci, vercel
filtersoptionalSourceFilterPer-source filters (AND-combined). Keys must be source ids (unknown keys are rejected with 400); values are matched leniently (see GET /handoffs/filters).
maxEventsoptionalintegerCap the events fed to the model (1–1000).

Request

curl "https://www.flowrelay.it/api/v1/projects/3fa85f64-5717-4562-b3fc-2c963f66afa6/insights/architecture" \
  -H "Authorization: Bearer fr_your_api_key" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{ "focusQuestion": "Is the pricing engine coupling billing to providers?", "lookbackDays": 14 }'

Request body

JSON
{
  "focusQuestion": "Is the pricing engine coupling billing to providers?",
  "lookbackDays": 14
}

Response

JSON
{
  "jobId": "e7f8a9b0-1c2d-3e4f-5a6b-7c8d9e0f1a2b",
  "status": "pending"
}
StatusWhen
202Job enqueued. Poll GET /jobs/{jobId} for the result.
400Unsupported insight kind, or the body failed validation (unknown field / source / filter value, or a field that does not apply to this kind). The response names the field and lists allowed values.
401Missing or invalid API key.
404Project not found or not accessible by this key.
429Rate limit exceeded (60/min/key).

List context events

GET/events

Lists recent context events, newest first. With project_id it spans the project members and scoped sources; without it returns the key owner personal-stream events.

Query parameters

NameTypeDescription
sourceoptionalstringFilter by source. Unknown values are rejected with 400.One of: github, slack, discord, linear, notion, jira, gitlab, bitbucket, azure_devops, figma, confluence, microsoft_outlook, microsoft_teams, sentry, datadog, pagerduty, asana, gmail, buildkite, circleci, vercel
limitoptionalintegerMaximum rows to return (1–200).Default: 50
project_idoptionalstringRestrict to a project scope.

Request

curl "https://www.flowrelay.it/api/v1/events" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "events": [
    {
      "id": "d4e5f6a7-b8c9-0123-def4-23456789013a",
      "user_id": "0a9b8c7d-6e5f-4a3b-2c1d-0e9f8a7b6c5d",
      "source": "github",
      "event_type": "push",
      "title": "Push to acme/payments",
      "content": "3 commits on main",
      "created_at": "2026-06-20T13:55:00Z"
    }
  ]
}
StatusWhen
200Success
400The body or a query parameter is invalid: an unknown field, an unknown source, an unsupported filter dimension, or an out-of-range number. The response names the offending field and lists the allowed values.
401Missing or invalid API key.
404Project not found or not accessible by this key.

List integrations

GET/integrations

Lists connected integrations. With project_id it returns the project-scoped resources with health and provider coverage; without it returns the key owner connected sources.

Query parameters

NameTypeDescription
project_idoptionalstringReturn project-scoped integrations instead of personal ones.

Request

curl "https://www.flowrelay.it/api/v1/integrations" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "integrations": [
    {
      "source": "github",
      "workspace_id": "acme",
      "workspace_name": "Acme",
      "connected_at": "2026-05-01T09:00:00Z",
      "scope": "project",
      "resource_type": "repository",
      "connection_status": "active",
      "providers_connected": 2,
      "last_validated_at": "2026-06-20T10:00:00Z"
    }
  ]
}
StatusWhen
200Success
401Missing or invalid API key.
404Project not found or not accessible by this key.

List untracked resources

GET/integrations/untracked

Returns active resources (last 30 days) that produced events but are not yet assigned to any project. The response is a bare array, sorted by most recent activity.

Request

curl "https://www.flowrelay.it/api/v1/integrations/untracked" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
[
  {
    "source": "slack",
    "resource_id": "C0123",
    "resource_name": "#incidents",
    "resource_type": "channel",
    "event_count": 42,
    "last_event_at": "2026-06-20T12:00:00Z"
  }
]
StatusWhen
200Success
401Missing or invalid API key.
500Discovery failed for a connected source.

Poll an async job

GET/jobs/{jobId}

Returns the job record and, once completed, the generated artifact (handoff or insight). The artifact carries a `markdown` field with the canonical rendering – identical to the dashboard copy button. Poll this after any 202 response until status is completed or failed.

Path parameters

NameTypeDescription
jobIdrequiredstringUUID of the job returned in a prior 202 (POST /handoffs or POST /projects/{id}/insights/{kind}). Project-scoped: only readable if you can access the job's project.

Request

curl "https://www.flowrelay.it/api/v1/jobs/c3d4e5f6-a7b8-9012-cdef-123456789012" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "job": {
    "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "kind": "project_handoff",
    "status": "completed",
    "result_kind": "handoff",
    "result_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "error": null,
    "error_code": null,
    "created_at": "2026-06-20T14:02:00Z",
    "updated_at": "2026-06-20T14:03:00Z"
  },
  "result": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "title": "Checkout refactor handoff",
    "markdown": "# Checkout refactor handoff\n\n> Migrated the checkout flow…"
  }
}
StatusWhen
200Success
400jobId is not a valid UUID.
401Missing or invalid API key.
404Job not found or not accessible.

List Discord channels

GET/discord/channels

Lists the text channels in the Discord guild connected to the key owner.

Request

curl "https://www.flowrelay.it/api/v1/discord/channels" \
  -H "Authorization: Bearer fr_your_api_key"

Response

JSON
{
  "channels": [
    {
      "id": "987654",
      "name": "general",
      "type": 0
    }
  ]
}
StatusWhen
200Success
401Missing or invalid API key.
404Discord not connected.
400No guild associated with this integration.

Send a Discord message

POST/discord/send

Sends to a channel in the connected guild. Provide exactly one of: content (inline text); handoff_id or insight_id (renders that artifact to Markdown and attaches it as a .md file); or artifact (last_handoff / last_correlation / last_onboarding / last_architecture, with project_id) to send the latest active artifact of that kind. Rendered artifacts use the same Markdown as the dashboard copy button and are always delivered as a .md attachment. The channel must belong to the connected guild (cross-tenant posts are rejected).

Body parameters

NameTypeDescription
channel_idrequiredstringTarget channel id – a Discord snowflake from GET /discord/channels.
contentoptionalstringInline message text. Mutually exclusive with handoff_id / insight_id / artifact.
handoff_idoptionalstringUUID of a handoff (from GET /handoffs). Rendered to Markdown and sent as a .md attachment.
insight_idoptionalstringUUID of an insight (from GET /projects/{id}/insights). Sent as a .md attachment.
artifactoptionalstringSend the latest active artifact of a kind. Requires project_id.One of: last_handoff, last_correlation, last_onboarding, last_architecture
project_idoptionalstringProject UUID. Required only when artifact is set.

Request

curl "https://www.flowrelay.it/api/v1/discord/send" \
  -H "Authorization: Bearer fr_your_api_key" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{ "channel_id": "987654321098765432", "artifact": "last_handoff", "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }'

Request body

JSON
{
  "channel_id": "987654321098765432",
  "artifact": "last_handoff",
  "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

Response

JSON
{
  "ok": true,
  "message_id": "111222333444555666"
}
StatusWhen
200Success
400channel_id missing, or no content / artifact provided.
401Missing or invalid API key.
403Channel does not belong to the connected guild.
404Discord not connected, or the requested artifact does not exist / is not accessible.
502Discord rejected the message.