> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a session

> Returns one session with its status, model in use, token usage totals and cost.



## OpenAPI

````yaml /en/openapi/v3-current/narya.yaml get /v1/sessions/{sessionId}
openapi: 3.1.0
info:
  title: Narya Host API
  version: 1.0.0
  description: >-
    The contract between the Narya host and every client. One long-lived host
    runs on your machine and serves this API over a Unix socket in your Narya
    home. Narya creates the socket owner-only, and file permissions are the
    whole authorization. There is no password, no token and no TLS. The terminal
    client and the one-shot command that Lerian ships drive this API, and a
    client you write drives the same one.


    Results never arrive on the response of the request that caused them.
    Submitting a message returns 202 with a turn id. Everything the turn
    produces streams over GET /v1/events as server-sent events with a typed
    envelope, and a stream resumes from a Last-Event-ID header.


    One error envelope: code, title, message, and fields on 422. Codes are NRY-
    followed by four digits. A paged list answers items, limit and a nextCursor
    when more remains. Cursors are opaque.
servers: []
security: []
tags:
  - name: host
    description: The host process itself — version, uptime, mode, store.
  - name: sessions
    description: Durable conversation containers. Archive, never destroy.
  - name: messages
    description: Submitting work into a session and interrupting it.
  - name: events
    description: The server-sent event stream every client consumes.
  - name: lanes
    description: Parallel tracks inside a session — main, subagent, side.
  - name: agents
    description: Named recipes — instructions, tools, model, policy. Read-only in v1.
  - name: ladder
    description: >-
      What a person can type: the skills and command files in force for one
      repository, and expanding one into text. Five origins merged, nearest
      winning a name, the repository's own rungs gated on trust.
  - name: permissions
    description: Pending permission asks, decisions, and the decision audit.
  - name: intercom
    description: Sessions on one machine finding and messaging each other.
  - name: packages
    description: The one thing a user installs — resources, Go code, or both.
  - name: workflows
    description: Deterministic multi-agent orchestration runs.
  - name: providers
    description: Model suppliers, their auth state, and the model catalogue.
  - name: monitors
    description: >-
      Long-running watchers a session keeps beside its conversation — a test
      runner in watch mode, a build, a log being followed. Started by the model
      or by the person, always listed, always killable.
  - name: records
    description: The queryable local record of everything that happened.
  - name: schedules
    description: >-
      Work the host's own clock starts with nobody present — a repository, a
      prompt, a rule and what one fire may spend. Cancel, never destroy.
paths:
  /v1/sessions/{sessionId}:
    parameters:
      - $ref: '#/components/parameters/SessionIdParam'
    get:
      tags:
        - sessions
      summary: Get a session
      description: >-
        Returns one session with its status, model in use, token usage totals
        and cost.
      operationId: getSession
      responses:
        '200':
          description: The session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    SessionIdParam:
      name: sessionId
      in: path
      required: true
      description: The session's id.
      schema:
        type: string
        format: uuid
  schemas:
    Session:
      type: object
      description: >-
        A durable conversation container. Survives the process that created it;
        archiving hides it from active lists but its transcript remains
        queryable and exportable forever.
      required:
        - id
        - repository
        - status
        - clientKind
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          format: uuid
          description: The session's id.
        title:
          type: string
          maxLength: 512
          description: Human-readable title.
        repository:
          type: string
          maxLength: 4096
          description: Absolute path of the repository the session works in.
        status:
          type: string
          enum:
            - idle
            - running
            - waiting
            - archived
          description: >-
            Lifecycle: running while a turn executes or normal follow-ups chain;
            waiting after interrupt when admitted steering or follow-up inputs
            remain queued; idle only when neither execution nor queued input
            exists; archived accepts no new work.
        provider:
          type: string
          description: The model provider currently in use.
        model:
          type: string
          description: The model currently in use.
        variant:
          type: string
          description: >-
            The preset of request options this session's turns run under — the
            name, not the options it stands for, since what a name means is the
            catalogue's answer and can change while the session's own choice
            cannot. Absent on a session that never chose one, which is every
            session running its model at the model's own defaults.
        style:
          type: string
          maxLength: 64
          description: >-
            The named response style this session's answers are shaped by — the
            name, not the prompt it appends, since what a name means is
            configuration's answer and changes when somebody edits that file
            while the session's own choice does not. Absent on a session that
            never chose one, which is every session answering in narya's own
            voice. Unlike `variant` it survives a model switch: a style is a
            fact about how the conversation reads rather than about the model
            answering it. A client renders this and derives nothing — the names
            that exist are the host's answer (setSessionStyle refuses the rest).
        clientKind:
          $ref: '#/components/schemas/ClientKind'
          description: >-
            Which kind of client opened this session, recorded when it was
            created and never rewritten. Always present: `unknown` for a session
            created before this host recorded the datum, which is the honest
            answer rather than a guessed frontend.
        modelNotice:
          type: string
          maxLength: 1024
          description: >-
            One sentence stating what a client could not otherwise know about
            the model this session opened on or was just moved to, present on
            the responses to createSession and setSessionModel. Two facts earn
            it. The first is a model narya chose itself rather than being told
            to use: no default_model configured and no model previously chosen,
            so the first provider holding a usable credential answered — or a
            named model the catalogue no longer carries, for which the provider
            default was substituted. It names the alternatives that also
            qualified and how to choose another, because a choice nobody made
            has to be stated to be a choice at all rather than something that
            merely happened. The second is a level this session will run
            without: a model reference may carry one (see the variant field),
            and an endpoint that does not take the field runs at its own default
            instead — so a level named at creation or at a switch, or configured
            beside default_model, is said here rather than quoted in a picker
            while every request drops it. A client that renders the live event
            stream rather than the session's transcript has no other road to it.
            Otherwise absent, including for a model that was simply configured,
            chosen or named; absent on getSession and listSessions, which report
            state rather than how it came about.
        usage:
          $ref: '#/components/schemas/TokenUsage'
          description: >-
            What this session has consumed and cost across every turn it has run
            — a running total, not the last turn's figure. Carried by getSession
            and by every row of listSessions, so a client drawing a list of
            sessions can say what each one has cost without asking per row.
            Absent on a session that has never completed a turn, and its costUsd
            is absent whenever any one of that session's turns could not be
            priced: the sum over only the priced turns is smaller than what the
            session actually cost, and a figure that quietly understates money
            is worse than an admitted unknown.
        lastTurnUsage:
          $ref: '#/components/schemas/TokenUsage'
          description: >-
            What the newest round of this session's own conversation sent and
            produced — the LAST turn's figure, not the running total above.
            Carried by getSession only.

            It exists because the two are not interchangeable for the one
            question a reader reopening a long conversation asks: how much room
            is left before this compacts. That answer is the size of the prompt
            last sent, and usage above is a sum over every turn the session ever
            ran, which is a larger number growing without bound and means
            nothing as a fraction of a context window. The live event stream
            carries this figure on turn-finished, so a client watching a session
            has always had it; a client that READ the session had no source for
            it at all and drew no context figure until it spent a turn of its
            own. Restoring a recorded reading rather than deriving one is the
            whole of it — nothing here is computed from the transcript.

            The newest round of the MAIN lane: a delegation's rounds are another
            conversation's prompts, and the figure is about the reader's. Absent
            on a session that has never completed a round, and on one whose
            rounds all ran on delegates. Its costUsd is that one round's price,
            absent when the round could not be priced.
        pendingInputs:
          type: array
          description: >-
            Pending steering and follow-up references in durable operation
            order. Content is intentionally absent; fetch the transcript by
            entryId.
          items:
            $ref: '#/components/schemas/PendingInputReference'
        parentSessionId:
          type: string
          format: uuid
          description: When the session was branched, the session it branched from.
        summary:
          type: string
          maxLength: 4096
          description: >-
            What a model said this conversation tried and concluded. Absent
            until somebody asks for it (summarizeSession) — nothing summarises a
            branch because a rewind moved away from it. A model that answers
            past this length is trimmed to it, with a marker saying so rather
            than a sentence that just stops.
        summaryAt:
          type: string
          format: date-time
          description: >-
            When the summary was taken. Read against updatedAt it answers
            whether the summary predates the last thing that happened here.
        posture:
          $ref: '#/components/schemas/Posture'
          description: >-
            Whether somebody is there to answer a permission prompt raised here.
            Absent on a session nobody ever said either way about, which reads
            as `attended` — the posture is only ever recorded because somebody
            stated it (setSessionPosture), never inferred from whether a client
            happens to be connected.
        warming:
          $ref: '#/components/schemas/Warming'
          description: >-
            Whether this session pays to keep its provider cache alive while
            nobody is using it, and the most it may ever spend doing so. Absent
            on a session nobody ever turned it on for, which is every session
            until somebody does — warming is off unless somebody asked
            (setSessionWarming).
        viewedAt:
          type: string
          format: date-time
          description: >-
            When a reader last had this session in front of them
            (acknowledgeSessionViewed). Read against updatedAt it answers
            whether anything has happened here since somebody looked, which is
            what lets a client mark a background conversation as carrying news —
            and, because the fact is held here rather than in a window, what
            makes two clients agree about it and makes the answer survive a
            relaunch. ABSENT on a session nobody has ever viewed, which is not
            the same as viewed long ago: one has nothing to report, the other
            has everything that ever happened in it.
        lastOutcome:
          type: string
          enum:
            - finished
            - failed
          description: >-
            How the last turn here ended. It is what lets a client that was not
            running at the time tell a conversation that finished from one that
            failed. Absent on a session that has never run a turn. An interrupt
            is not among the values: somebody was there and stopped it
            themselves, so it is not news to bring back to them.
        cacheSpend:
          $ref: '#/components/schemas/CacheSpend'
          description: >-
            What this session's provider prompt cache has cost it, from both
            sides: what letting it expire cost, and what keeping it alive cost.
            Present only on getSession, and only once one of the two has
            happened — a session that has never paid for either carries nothing
            here rather than a pair of zeros.
        tasks:
          type: array
          description: >-
            The agent's own task list for this session — every item it has
            written, in the order it wrote them, whatever is in the store right
            now.

            It is here rather than behind a listing of its own because the list
            is a stored row and every client already reads the session when it
            attaches, so one optional field costs no round trip and no second
            operation. A change to the list is announced as `task-list`, which
            carries the same whole list for the same reason; this is where a
            client that was not attached at the time gets it.

            Present on getSession only, like `lastTurnUsage` and `cacheSpend`
            and for their reason: a listing that carried it would read every
            row's tasks to draw a page nobody asked for.

            ABSENT MEANS EMPTY, and it cannot mean anything narrower: the store
            holds rows, so a list nobody ever wrote and a list somebody cleared
            are the same read. The live stream is where those two differ — a
            clearing write announces `task-list` carrying an empty array — and a
            read that returned `[]` would be claiming to know which of the two
            it was looking at.
          items:
            $ref: '#/components/schemas/Task'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      examples:
        - id: 6b9f6d2e-1c3a-4f5b-9d7e-2a8c4e6f0b1d
          title: Fix flaky ledger test
          repository: /Users/dev/repos/midaz
          status: running
          provider: anthropic
          model: claude-sonnet-4-5
          clientKind: inline
          usage:
            inputTokens: 152000
            outputTokens: 8400
            costUsd: 1.87
          createdAt: '2026-08-08T12:00:00.000Z'
          updatedAt: '2026-08-08T12:34:56.000Z'
    ClientKind:
      type: string
      enum:
        - inline
        - tui
        - oneshot
        - api
        - unknown
      x-enum-varnames:
        - ClientKindInline
        - ClientKindTui
        - ClientKindOneshot
        - ClientKindApi
        - ClientKindUnknown
      description: >-
        Which kind of client opened a session — the coarse classification a
        client filters and mixes on when it lists sessions it did not itself
        open. inline — the terminal client (`narya`). tui — the full-screen
        client (`narya --tui`). oneshot — a one-shot command (`narya -p`). api —
        a caller of this contract that named no kind; every narya frontend names
        itself, so this is what speaking the contract directly is. unknown —
        never recorded, which is every session created before this host carried
        the datum; a read answers `unknown` rather than picking a kind, and a
        create request naming it records nothing. Presentation metadata only:
        nothing in the host behaves differently because of its value.
    TokenUsage:
      type: object
      description: >-
        Token consumption totals and their cost. costUsd is priced from the
        catalogue's published rates for the model that ran, and is absent
        whenever no rate is known for it — a model configured by hand against a
        private endpoint, say. Absent means unknown; a false zero would mean
        free. On a session total it is absent unless every turn in the session
        could be priced.
      required:
        - inputTokens
        - outputTokens
      properties:
        inputTokens:
          type: integer
          minimum: 0
          description: Input tokens consumed.
        outputTokens:
          type: integer
          minimum: 0
          description: Output tokens produced.
        cacheReadTokens:
          type: integer
          minimum: 0
          description: Tokens served from the provider's prompt cache.
        cacheWriteTokens:
          type: integer
          minimum: 0
          description: Tokens written to the provider's prompt cache.
        costUsd:
          type: number
          minimum: 0
          description: Total cost in US dollars.
      examples:
        - inputTokens: 152000
          outputTokens: 8400
          cacheReadTokens: 120000
          cacheWriteTokens: 9000
          costUsd: 1.87
    PendingInputReference:
      type: object
      description: >-
        A content-free durable queued-input reference. Message content remains
        only in the transcript.
      required:
        - entryId
        - turnId
        - kind
        - sequence
        - enqueuedAt
      properties:
        entryId:
          type: string
          format: uuid
        turnId:
          type: string
          format: uuid
        kind:
          type: string
          enum:
            - steer
            - follow-up
        sequence:
          type: integer
          format: int64
          minimum: 1
        enqueuedAt:
          type: string
          format: date-time
      examples:
        - entryId: 4d8e2f6a-0b1c-4d3e-9f5a-7b9c1d3e5f0a
          turnId: 4d8e2f6a-0b1c-4d3e-9f5a-7b9c1d3e5f0a
          kind: follow-up
          sequence: 7
          enqueuedAt: '2026-08-10T15:04:05.000Z'
    Posture:
      type: string
      enum:
        - attended
        - detached
      x-enum-varnames:
        - PostureAttended
        - PostureDetached
      description: >-
        Whether somebody is there to answer a permission prompt raised in a
        session. attended — a prompt is shown and waited on, which is what every
        session starts as. detached — nobody is watching, so a prompt is refused
        and recorded with its reason rather than blocking the turn forever on a
        question nothing can answer.

        Orthogonal to clientKind, and deliberately not a value of it: clientKind
        is stamped once at creation and never rewritten, whereas a session
        BECOMES detached when somebody says so. It is a recorded fact with
        exactly one transition (setSessionPosture) and is never inferred from
        the presence or absence of a connection — a client that crashed, a lid
        that closed and a pipe that dropped all leave it exactly as it was.
    Warming:
      type: object
      description: >-
        One session's standing instruction about its provider cache while nobody
        is using it: whether to pay to hold the cached prefix past the five
        minutes a provider keeps it for free, and the lifetime dollars this
        session may spend doing so.

        Both fields travel together because they are one decision. Turning
        warming on without saying what it may cost is not a decision anybody
        makes, and there is no default narya could supply that is not a guess
        about somebody else's bill.

        The ceiling is a LIFETIME figure for this session, not a daily one: a
        session is a bounded thing with an end, and "this conversation may cost
        me at most fifty cents to keep warm" is the sentence a person means. A
        second, separate ceiling bounds the whole store's spending per UTC day
        and is operator configuration rather than part of any session — either
        one being reached stops warming rather than warning about it.
      required:
        - enabled
        - ceilingUsd
      properties:
        enabled:
          type: boolean
          description: >-
            Whether this session is warmed. False with a ceiling recorded is a
            session somebody turned off, which keeps the number they chose for
            the day they turn it back on.
        ceilingUsd:
          type: number
          format: double
          minimum: 0
          description: >-
            The most this session may ever spend on warming, in US dollars,
            counted across its whole life. Reaching it stops warming this
            session and nothing else; every other session keeps its own
            allowance. Zero is a legitimate value and means the same as off.
      examples:
        - enabled: true
          ceilingUsd: 0.5
    CacheSpend:
      type: object
      description: >-
        What this session's provider prompt cache has cost it, read from both
        sides of one decision: what letting the cached prefix expire cost, and
        what refusing to let it expire cost.

        The two numbers are reported together because they are the same decision
        seen from either end. Somebody weighing whether to turn warming on for
        this session is comparing exactly these figures, and either one alone
        answers half the question.

        The headline is the re-billed prompt: rounds that paid full input rates
        to send again a prefix the provider had been holding, because the
        session sat idle past the cache's lifetime. A duplicated cache WRITE is
        a real event too and is deliberately not reported here — it is an
        internal metric, not what a person reads on a bill.

        Absent on a session where neither has ever happened, which is every
        session until one of them does. Never a zero-filled object standing in
        for "nothing yet".
      required:
        - rebilledRounds
        - rebilledTokens
        - rebilledUsd
        - warmingUsd
      properties:
        rebilledRounds:
          type: integer
          minimum: 0
          description: >-
            How many rounds paid to re-send an expired prefix. Zero here with a
            warming figure present is a session that has been kept warm and has
            therefore never paid for its own idleness — which is warming
            working.
        rebilledTokens:
          type: integer
          minimum: 0
          description: >-
            The prefix re-sent across those rounds: the part of each round's
            cache write that had genuinely been cached before, never the whole
            write. A resumed conversation also writes the message that ended the
            wait, and that content would have been written with the prefix alive
            too — charging it to the expiry would blame idleness for the weight
            of the message that ended it.
        rebilledUsd:
          type: number
          format: double
          minimum: 0
          description: >-
            What that expiry cost, in US dollars: for each round, the difference
            between what its prompt was billed and what the same prompt would
            have cost with the prefix still cached.

            It DECOMPOSES money already reported in `usage.costUsd` rather than
            adding to it. A re-billed round's whole price is in the session
            total like every other round's, and this says how much of that price
            the expiry was; adding the two together counts the same dollars
            twice.
        rebilledUsdComplete:
          type: boolean
          description: >-
            Absent means true. Present and false makes `rebilledUsd` a FLOOR —
            "at least this much" — because at least one re-billed round ran on a
            model this host cannot price, contributing a real expiry and no
            dollars. The same honesty `costComplete` carries on a usage report,
            for the same reason: a total that quietly skipped the rounds it
            could not price is a number a reader will believe.
        warmingUsd:
          type: number
          format: double
          minimum: 0
          description: >-
            What this session has spent keeping its cache alive, in US dollars,
            counted across its whole life and against the ceiling in
            `warming.ceilingUsd`. Reservations are included: a warm that was
            paid for and whose outcome was never established is money that may
            already be gone, and leaving it out would let it be spent twice.
      examples:
        - rebilledRounds: 4
          rebilledTokens: 18240
          rebilledUsd: 0.0312
          warmingUsd: 0.045
    Task:
      type: object
      description: >-
        One item of the agent's task list: what the work is, and where it
        stands.

        Two fields and no more. There is no id, because the list is replaced
        whole on every write and position is the only order there is — an
        identifier would be a second thing two surfaces could disagree about.
        There is no priority, because nothing renders or orders by one.
      required:
        - content
        - status
      properties:
        content:
          type: string
          description: What the task is, in the agent's own words.
        status:
          type: string
          enum:
            - pending
            - in_progress
            - completed
            - cancelled
          description: >-
            Where the item stands. `cancelled` counts toward the total and not
            toward what is done — an abandoned step is still a step the list
            once held. The engine does not enforce "exactly one in_progress":
            that is guidance in the tool's own description, and a list holding
            two is drawn honestly rather than refused.
          x-enum-varnames:
            - TaskStatusPending
            - TaskStatusInProgress
            - TaskStatusCompleted
            - TaskStatusCancelled
      examples:
        - content: Reproduce the flaky ledger test
          status: completed
    Error:
      type: object
      description: >-
        The single error envelope every operation returns. Codes are NRY-
        followed by four digits and are catalogued in the top-level
        x-error-catalog extension. fields appears only on 422 validation errors,
        mapping each offending property to its problem.
      required:
        - code
        - title
        - message
      properties:
        code:
          type: string
          pattern: ^NRY-[0-9]{4}$
          description: Machine-readable error code from the NRY catalogue.
        title:
          type: string
          maxLength: 256
          description: Short human-readable summary of the error class.
        message:
          type: string
          maxLength: 4096
          description: Specific, actionable description of what went wrong.
        fields:
          type: object
          description: Per-field validation problems. Present on 422 only.
          additionalProperties:
            type: string
      examples:
        - code: NRY-0002
          title: Session not found
          message: >-
            No session with id 6b9f6d2e-1c3a-4f5b-9d7e-2a8c4e6f0b1d exists on
            this host.
  responses:
    BadRequest:
      description: Malformed request — invalid parameter, cursor, or JSON body.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: The addressed resource does not exist on this host.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InternalServerError:
      description: The host failed — including a store that refuses writes (NRY-0012).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

````