Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Start here
Concepts
Release notes
What's new
Elyra
Working from your editor

Working from your editor

Setting up an IDE or a coding agent to read issues and log time against Félagi, end to end, with the commands to paste.


Why this exists

A developer already has an editor open. Switching to a browser to find out what an issue says, and switching back at the end of the day to remember how long it took, is the friction that makes time tracking stop happening after a fortnight.

A personal token closes that. It is not gated to admins — anybody who works here can make one, because the point is that the tool on your desk needs nobody's permission to read an issue.


1. Make a token

Settings → API tokens. Give it a name you will recognise in six months, choose Read or Read and write, and optionally an expiry.

You get the secret once. Félagi stores a hash of it, so it cannot be recovered — only replaced.

Underneath it is a block ready to paste:

FELAGI_URL=https://felagi.example.com
FELAGI_TOKEN=fat_…
FELAGI_WORKSPACE=Acme Industries

A token is the password, and a better one

Félagi will not let an integration authenticate with your account password, and that is a decision rather than an omission:

Token Account password
Scope One workspace Everything you can reach
Read-only possible Yes No
Expires If you say so No
Revoked On its own, without touching anything else By changing your password everywhere
Can change your password No Yes

The last row is the whole argument. A password in a file on a laptop unlocks the ability to change that password.

A token acts as you, in one workspace, and can never do more than you can.


2. Check it works

curl -s -H "Authorization: Bearer $FELAGI_TOKEN" "$FELAGI_URL/api/v1/me" | jq
{
  "data": {
    "user": { "type": "user", "id": 3, "name": "Knut W. Horne" },
    "workspace": { "id": 1, "name": "Acme Industries" },
    "abilities": ["read", "write"]
  }
}

If that returns 401, the token is wrong, revoked, expired, or you have left the workspace. Félagi answers all four the same way on purpose — a different message for each tells whoever is guessing which part of the guess was right.

A shell function worth keeping

Everything below assumes this:

felagi() {
  local method=$1 path=$2; shift 2
  curl -sS -X "$method" "$FELAGI_URL/api/v1$path" \
    -H "Authorization: Bearer $FELAGI_TOKEN" \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' "$@"
}

Reading work

What is on my plate

felagi GET '/issues?assignee_type=user&assignee_id=3&status=in_progress'

Find something by words

felagi GET '/issues?q=redirect%20loop'

q matches the title and the description. To reach one issue you already know:

felagi GET '/issues/231'

The id in a URL is the database key; the id in the response is the identifier a person types, ACM-231. They are deliberately different things: one is stable and internal, the other is what somebody says out loud.

Narrow it down

Parameter
status, type, priority Enum values — in_progress, bug, high
project_id
assignee_type + assignee_id user or agent, and the id
label Repeatable. Two labels mean the overlap, not either
cycle The cycle number a person says, or none for unplanned work
updated_since For a client polling: everything touched since it last asked
per_page Up to 100

The thread and what runs delivered

felagi GET '/issues/231/comments'
felagi GET '/issues/231/artifacts'    # pull requests, branches, documents

Creating work

A project

felagi POST /projects -d '{
  "name": "Billing overhaul",
  "description": "Rework invoicing before the VAT change.",
  "priority": "high",
  "issue_types": ["epic", "task", "bug"],
  "lead": "user:3"
}'

201. Only name is required. lead takes an actor key — user:3 or agent:1 — and is checked against your workspace rather than trusted.

Changing one later sends only what moved:

felagi PATCH /projects/4 -d '{"status": "in_progress", "target_date": "2026-12-01"}'

A PATCH leaves out what it does not mention. It will not quietly reset a date because you were changing a status.

An issue

felagi POST /issues -d '{
  "title": "Rate limit the export endpoint",
  "description": "Anyone can pull the whole database.",
  "type": "bug",
  "priority": "high",
  "project_id": 4,
  "estimate_minutes": 240
}'

201. Only title is required.

A comment

felagi POST /issues/231/comments -d '{"body": "Reproduced on staging."}'

Mentioning an agent by name in the body hands it the thread, exactly as it would from the interface.


Logging time

The way you would say it

felagi POST /issues/231/time -d '{
  "duration": "2h 30m",
  "note": "Traced the redirect loop"
}'

1d, 90m and 2h 30m all work. {"minutes": 150} is accepted for a tool that has already done the arithmetic, but nothing makes you convert first.

spent_on defaults to today. An hour worked on Monday and written down on Friday should say Monday:

felagi POST /issues/231/time -d '{"duration": "1h", "spent_on": "2026-08-03"}'

The hours are always yours

There is no actor field. Sending one does nothing. A credential that could log time against a colleague is a credential that could rewrite their timesheet, and hours are somebody's statement about their own week.

Reading it back

felagi GET '/time-entries?mine=1&from=2026-08-01'
Parameter
mine Only yours. Without it you see the whole workspace
from, to On spent_on, the day the work happened
issue_id
source manual, timer or agent_run

That last one is the distinction worth knowing: timer is measured, manual is remembered, and a total that adds them without saying so is a guess.

Taking one back

felagi DELETE /time-entries/91          # 204

Only hours you logged yourself, and never an agent run's — the run is in the timeline with its own duration, and editing the entry would let the report disagree with the history it came from.


The stopwatch

This is the part that pays for the setup. An editor that starts a clock when you open a file:

felagi POST /issues/231/timer -d '{"note": "Pairing on the redirect"}'
{ "data": { "issue": "ACM-231", "issue_id": 231,
            "started_at": "2026-08-03T09:12:00+00:00", "max_minutes": 480 },
  "stopped": null }

started_at, never an elapsed count. A number of seconds is stale the instant it is serialised; a timestamp stays right however long the response sat in a pipe, and your editor counts on its own.

Starting a second clock stops the first and logs it. That is not an error — switching tasks is the normal case — but the response says what it banked:

{ "data": { "issue": "ACM-240", "…": "…" },
  "stopped": { "id": 91, "issue": "ACM-231", "minutes": 45 } }
felagi GET /timer          # what is running, or null
felagi POST /timer/stop    # 201, returns the entry it wrote
felagi DELETE /timer       # 204, records nothing

GET /timer answers 200 with null when nothing is running. "Nothing is running" is an answer; a client made to treat it as an error will treat a real failure the same way.

A clock left running is capped at eight hours and the note says it was capped, so an editor that forgets to stop one cannot put an indefensible number into a report.


Reading the team's documentation

The endpoint worth having if you are pointing a coding agent at Félagi. Skills are what an agent is given; this is what it can look up.

felagi GET '/articles?q=deploy'
felagi GET /articles/01k9abc…          # one article, with its body

A list is a table of contents — titles, depth and a 200-character excerpt, no bodies. Fetch the one you want.

Writing works too:

felagi POST /articles -d '{
  "title": "Restoring the database",
  "body": "<h2>First</h2><p>Stop the queue workers.</p>",
  "published": true,
  "parent": "01k8xyz…"
}'

felagi PATCH /articles/01k9abc… -d '{"body": "<p>Corrected.</p>"}'

The body is HTML, sanitised on the way to disk through the same allowlist the editor's output goes through. A PATCH that omits body leaves the document alone — it will not blank a page to rename it. Every overwrite keeps a revision, restorable from the interface.

Somebody else's draft never appears. Half a page found through an API is worse than not finding it, because something will act on it without a person reading it first.

Putting a thought on a whiteboard

felagi GET /whiteboards
felagi GET /whiteboards/01k9abc…       # the board, with everything on it
felagi POST /whiteboards/01k9abc…/notes -d '{"text": "Rate limiting came up again"}'

Notes and boxes only — an arrow needs two points you cannot see. Anybody with the board open watches it appear.

Running an agent

felagi GET /agents
felagi POST /issues/231/runs -d '{"agent_id": 2}'

202, not 201. The run is queued; whether a machine picks it up depends on a daemon that may not be running, and "created" would promise something the response cannot know.

felagi GET '/runs?status=running'
felagi GET /runs/01k9…

When it goes wrong

401 The credential is no good. Wrong, revoked, expired, or you left the workspace — all four read the same
403 A read-only token tried to write
404 It does not exist, or it belongs to another workspace. Telling you which would confirm an id and a customer in one response
422 Validation. The body names the fields
429 Too fast. Retry-After says how long

Limits are 300 requests a minute per token — far above anything an editor does, and there to stop a loop rather than to pace work.

Every response carries X-RateLimit-Remaining.


A worked morning

# What am I meant to be doing
felagi GET '/issues?assignee_type=user&assignee_id=3&status=todo' \
  | jq -r '.data[] | "\(.id)  \(.title)"'

# Start on one
felagi POST /issues/231/timer
felagi PATCH /issues/231 -d '{"status": "in_progress"}'

# … work …

# Stop, and say what happened
felagi POST /timer/stop | jq -r '.data | "logged \(.minutes)m on \(.issue)"'
felagi POST /issues/231/comments -d '{"body": "Fixed; intended() ran before the session regenerated."}'
felagi PATCH /issues/231 -d '{"status": "in_review"}'

Not there yet

  • No agents, runtimes or skills over the API. Read-only, deliberately: naming a runtime and a provider wrongly over an API is much harder to see than getting it wrong in a form.
  • No labels or cycles written over the API. Both are readable and filterable; setting them is done in the interface.
  • No file uploads. Attachments come back on comments and cannot be created.
  • No moving or drawing on a whiteboard. Read it, and add a note.
  • No article history over the API. Revisions are kept; restoring is done in the interface.
  • No OpenAPI document. The reference is the specification.
  • No cursors for pagination. Page numbers, which drift if rows are inserted while you page.
  • Nothing calls you. Webhooks exist but are configured in the interface, not through this API. updated_since is there so polling stays cheap.