Ledger Analytics API

The Ledger Analytics API (operated by Korra, a Ledger Investing company) provides remote compute access to Bayesian actuarial models over insurance loss triangles. Triangles are uploaded and managed as first-class resources, then development, tail, forecast and cashflow models are fit against them asynchronously and polled as remote tasks, with predictions written back as new triangles. Model families include Chain Ladder, Traditional Chain Ladder, ManualATA, MeyersCRC and GMCL for loss development; Generalized Bondy, Sherman and Classical Power Transform for tail development; and AR1, SSM and Generalized Cape Cod for forecasting. The API is in beta and is consumed primarily through the open-source ledger-analytics Python client.

OpenAPI Specification

ledger-investing-analytics-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Ledger Analytics API
  version: 0.0.30
  summary: Remote Bayesian actuarial compute over insurance loss triangles.
  description: |
    The Ledger Analytics API provides remote compute access to Bayesian actuarial models
    for insurance loss triangles. It is operated by Korra Tech, LLC (d/b/a Korra), a wholly
    owned SaaS subsidiary of Ledger Investing, Inc., and is currently in beta.

    Loss triangles are uploaded and managed as first-class `triangle` resources. Development,
    tail, forecast and cashflow models are then fit against a named triangle. Fitting and
    prediction are executed as asynchronous remote tasks: the fit/predict call returns a task
    handle which is polled at `/tasks/{task_id}` until it completes; predictions are written
    back as new triangle resources.

    IMPORTANT PROVENANCE NOTE: Ledger/Korra do not publish an OpenAPI description. This
    document was DERIVED by API Evangelist from the request construction in the first-party,
    open-source `ledger-analytics` Python client (github.com/LedgerInvesting/ledger-analytics,
    MIT-licensed, Copyright 2025 Korra, LLC) and from the published documentation. Paths,
    methods, auth, request-body field names, pagination parameters and error semantics are
    taken verbatim from that client. Response schemas are partial — only the fields the
    client actually reads are described. It is not an authoritative provider artifact.
  contact:
    name: Ledger Analytics
    email: analytics@ledgerinvesting.com
    url: https://ledger-investing-ledger-analytics.readthedocs-hosted.com/en/stable/index.html
  license:
    name: MIT
    url: https://github.com/LedgerInvesting/ledger-analytics/blob/main/LICENSE.txt
externalDocs:
  description: LedgerAnalytics Python documentation
  url: https://ledger-investing-ledger-analytics.readthedocs-hosted.com/en/stable/index.html
servers:
- url: https://api.korra.com/analytics
  description: Production
tags:
- name: Triangles
  description: Upload, list, retrieve and delete insurance loss triangles.
- name: Development Models
  description: Bayesian loss development models (ChainLadder, TraditionalChainLadder, ManualATA, MeyersCRC, GMCL).
- name: Tail Models
  description: Tail development models (GeneralizedBondy, Sherman, ClassicalPowerTransformTail).
- name: Forecast Models
  description: Forecasting models (AR1, SSM, TraditionalGCC).
- name: Cashflow Models
  description: Cashflow models composed from a fitted development model and tail model.
- name: Tasks
  description: Poll the status of asynchronous fit and predict tasks.
security:
- ApiKeyAuth: []
paths:
  /triangle:
    get:
      tags: [Triangles]
      operationId: listTriangles
      summary: List triangles
      description: Returns a paginated list of loss triangles belonging to the authenticated account.
      parameters:
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated list of triangles.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaginatedTriangleList' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/InternalServerError' }
    post:
      tags: [Triangles]
      operationId: createTriangle
      summary: Create a triangle
      description: |
        Uploads a loss triangle. `triangle_data` is the Bermuda triangle dictionary
        representation (`bermuda.Triangle.to_dict()`). Set `overwrite` to replace an
        existing triangle of the same name — the upsert semantic used by the client's
        `get_or_create` / `get_or_update` helpers.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TriangleCreateRequest' }
      responses:
        '201':
          description: Triangle created.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Triangle' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /triangle/{id}:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    get:
      tags: [Triangles]
      operationId: getTriangle
      summary: Retrieve a triangle
      description: |
        Retrieves a triangle by id. For large triangles the response carries a `url`
        field with a pre-signed download location instead of an inline `triangle_data`
        payload; the client follows that URL and streams the content.
      responses:
        '200':
          description: The triangle.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Triangle' }
        '404': { $ref: '#/components/responses/NotFound' }
        '403': { $ref: '#/components/responses/Forbidden' }
    delete:
      tags: [Triangles]
      operationId: deleteTriangle
      summary: Delete a triangle
      responses:
        '204': { description: Triangle deleted. }
        '404': { $ref: '#/components/responses/NotFound' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /development-model:
    get:
      tags: [Development Models]
      operationId: listDevelopmentModels
      summary: List development models
      parameters:
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated list of development models.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaginatedModelList' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      tags: [Development Models]
      operationId: fitDevelopmentModel
      summary: Fit a development model
      description: Fits a Bayesian loss development model against a named triangle. Returns a task handle to poll.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModelFitRequest' }
      responses:
        '201':
          description: Fit task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTaskResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /development-model-type:
    get:
      tags: [Development Models]
      operationId: listDevelopmentModelTypes
      summary: List available development model types
      responses:
        '200':
          description: Available model types.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTypeList' }
  /development-model/{id}:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    get:
      tags: [Development Models]
      operationId: getDevelopmentModel
      summary: Retrieve a development model
      responses:
        '200':
          description: The model.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Model' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Development Models]
      operationId: deleteDevelopmentModel
      summary: Delete a development model
      responses:
        '204': { description: Model deleted. }
        '404': { $ref: '#/components/responses/NotFound' }
  /development-model/{id}/predict:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Development Models]
      operationId: predictDevelopmentModel
      summary: Predict from a development model
      description: Runs prediction for a fitted development model against a triangle; results are written back as a new triangle.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModelPredictRequest' }
      responses:
        '201':
          description: Predict task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PredictTaskResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
  /development-model/{id}/terminate:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Development Models]
      operationId: terminateDevelopmentModel
      summary: Terminate a running development model task
      responses:
        '200':
          description: Termination requested.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Task' }
  /tail-model:
    get:
      tags: [Tail Models]
      operationId: listTailModels
      summary: List tail models
      parameters:
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated list of tail models.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaginatedModelList' }
    post:
      tags: [Tail Models]
      operationId: fitTailModel
      summary: Fit a tail model
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModelFitRequest' }
      responses:
        '201':
          description: Fit task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTaskResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
  /tail-model-type:
    get:
      tags: [Tail Models]
      operationId: listTailModelTypes
      summary: List available tail model types
      responses:
        '200':
          description: Available model types.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTypeList' }
  /tail-model/{id}:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    get:
      tags: [Tail Models]
      operationId: getTailModel
      summary: Retrieve a tail model
      responses:
        '200':
          description: The model.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Model' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Tail Models]
      operationId: deleteTailModel
      summary: Delete a tail model
      responses:
        '204': { description: Model deleted. }
        '404': { $ref: '#/components/responses/NotFound' }
  /tail-model/{id}/predict:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Tail Models]
      operationId: predictTailModel
      summary: Predict from a tail model
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModelPredictRequest' }
      responses:
        '201':
          description: Predict task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PredictTaskResponse' }
  /tail-model/{id}/terminate:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Tail Models]
      operationId: terminateTailModel
      summary: Terminate a running tail model task
      responses:
        '200':
          description: Termination requested.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Task' }
  /forecast-model:
    get:
      tags: [Forecast Models]
      operationId: listForecastModels
      summary: List forecast models
      parameters:
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated list of forecast models.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaginatedModelList' }
    post:
      tags: [Forecast Models]
      operationId: fitForecastModel
      summary: Fit a forecast model
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModelFitRequest' }
      responses:
        '201':
          description: Fit task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTaskResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
  /forecast-model-type:
    get:
      tags: [Forecast Models]
      operationId: listForecastModelTypes
      summary: List available forecast model types
      responses:
        '200':
          description: Available model types.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTypeList' }
  /forecast-model/{id}:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    get:
      tags: [Forecast Models]
      operationId: getForecastModel
      summary: Retrieve a forecast model
      responses:
        '200':
          description: The model.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Model' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Forecast Models]
      operationId: deleteForecastModel
      summary: Delete a forecast model
      responses:
        '204': { description: Model deleted. }
        '404': { $ref: '#/components/responses/NotFound' }
  /forecast-model/{id}/predict:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Forecast Models]
      operationId: predictForecastModel
      summary: Predict from a forecast model
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModelPredictRequest' }
      responses:
        '201':
          description: Predict task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PredictTaskResponse' }
  /forecast-model/{id}/terminate:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Forecast Models]
      operationId: terminateForecastModel
      summary: Terminate a running forecast model task
      responses:
        '200':
          description: Termination requested.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Task' }
  /cashflow-model:
    get:
      tags: [Cashflow Models]
      operationId: listCashflowModels
      summary: List cashflow models
      parameters:
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated list of cashflow models.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaginatedModelList' }
    post:
      tags: [Cashflow Models]
      operationId: createCashflowModel
      summary: Create a cashflow model
      description: Composes a cashflow model from a previously fitted development model and tail model, referenced by name.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CashflowCreateRequest' }
      responses:
        '201':
          description: Cashflow model task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTaskResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
  /cashflow-model-type:
    get:
      tags: [Cashflow Models]
      operationId: listCashflowModelTypes
      summary: List available cashflow model types
      responses:
        '200':
          description: Available model types.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelTypeList' }
  /cashflow-model/{id}:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    get:
      tags: [Cashflow Models]
      operationId: getCashflowModel
      summary: Retrieve a cashflow model
      responses:
        '200':
          description: The cashflow model.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CashflowModel' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Cashflow Models]
      operationId: deleteCashflowModel
      summary: Delete a cashflow model
      responses:
        '204': { description: Cashflow model deleted. }
        '404': { $ref: '#/components/responses/NotFound' }
  /cashflow-model/{id}/predict:
    parameters:
    - $ref: '#/components/parameters/ResourceId'
    post:
      tags: [Cashflow Models]
      operationId: predictCashflowModel
      summary: Predict from a cashflow model
      description: |
        Runs cashflow prediction. `predict_config.initial_loss_name` optionally names an
        initial loss triangle to seed the projection.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CashflowPredictRequest' }
      responses:
        '201':
          description: Predict task accepted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PredictTaskResponse' }
  /tasks/{task_id}:
    parameters:
    - name: task_id
      in: path
      required: true
      description: Identifier of the asynchronous remote task.
      schema: { type: string }
    get:
      tags: [Tasks]
      operationId: getTask
      summary: Poll an asynchronous task
      description: |
        Polls a fit or predict task. While `task_response` is `null` the task is still
        pending; once populated, `task_response.status` is `success` or carries an `error`.
      responses:
        '200':
          description: Task state.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Task' }
        '404': { $ref: '#/components/responses/NotFound' }
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: |
        API key credential sent as `Authorization: Api-Key <API_KEY>`. Keys are requested by
        emailing analytics@ledgerinvesting.com during the beta and managed at https://ldgr.app/api-keys.
        The Python client reads the key from the `LEDGER_ANALYTICS_API_KEY` environment variable.
  parameters:
    Limit:
      name: limit
      in: query
      required: false
      description: Maximum number of results to return per page.
      schema:
        type: integer
        default: 25
        minimum: 1
    ResourceId:
      name: id
      in: path
      required: true
      description: Identifier of the resource.
      schema: { type: string }
  responses:
    BadRequest:
      description: Bad request — the payload was rejected.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Forbidden:
      description: Forbidden — the API key does not have permission to perform this action.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Not found — the endpoint or resource does not exist.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    InternalServerError:
      description: Internal server error.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
  schemas:
    Error:
      type: object
      description: |
        Error envelope. The client surfaces the decoded JSON body verbatim in the raised
        HTTPError message; the exact field names are not documented by the provider.
      additionalProperties: true
    Pagination:
      type: object
      properties:
        count:
          type: integer
          description: Total number of matching objects.
        next:
          type: [string, 'null']
          description: URL of the next page, when present.
        previous:
          type: [string, 'null']
          description: URL of the previous page, when present.
    Triangle:
      type: object
      description: An insurance loss triangle resource.
      properties:
        id:
          type: string
          description: Triangle identifier.
        name:
          type: string
          description: Triangle name, unique per account.
        triangle_data:
          type: object
          description: Bermuda triangle dictionary representation.
          additionalProperties: true
        url:
          type: [string, 'null']
          format: uri
          description: Pre-signed download URL returned in place of inline data for large triangles.
    PaginatedTriangleList:
      allOf:
      - $ref: '#/components/schemas/Pagination'
      - type: object
        properties:
          results:
            type: array
            items: { $ref: '#/components/schemas/Triangle' }
    TriangleCreateRequest:
      type: object
      required: [triangle_name, triangle_data]
      properties:
        triangle_name:
          type: string
          description: Name to store the triangle under.
        triangle_data:
          type: object
          description: Bermuda triangle dictionary representation (`bermuda.Triangle.to_dict()`).
          additionalProperties: true
        overwrite:
          type: boolean
          default: false
          description: Replace an existing triangle of the same name.
    Model:
      type: object
      description: A fitted model resource.
      properties:
        id: { type: string }
        name: { type: string }
        triangle:
          type: object
          description: The triangle the model was fit against.
          properties:
            name: { type: string }
        modal_task_info:
          type: object
          description: Remote task metadata, including the arguments the model was fit with.
          properties:
            task_args:
              type: object
              properties:
                model_type: { type: string }
                model_config:
                  type: object
                  additionalProperties: true
    CashflowModel:
      type: object
      description: A cashflow model composed from a development model and a tail model.
      properties:
        id: { type: string }
        name: { type: string }
        development_model:
          type: string
          description: Identifier of the source development model.
        tail_model:
          type: string
          description: Identifier of the source tail model.
    PaginatedModelList:
      allOf:
      - $ref: '#/components/schemas/Pagination'
      - type: object
        properties:
          results:
            type: array
            items: { $ref: '#/components/schemas/Model' }
    ModelTypeList:
      type: object
      description: Available model types for a model class.
      additionalProperties: true
    ModelFitRequest:
      type: object
      required: [triangle_name, model_name, model_type]
      properties:
        triangle_name:
          type: string
          description: Name of the triangle to fit against.
        model_name:
          type: string
          description: Name to store the fitted model under.
        model_type:
          type: string
          description: Model type to fit.
          examples:
          - ChainLadder
          - TraditionalChainLadder
          - ManualATA
          - MeyersCRC
          - GMCL
          - GeneralizedBondy
          - Sherman
          - ClassicalPowerTransformTail
          - AR1
          - SSM
          - TraditionalGCC
        overwrite:
          type: boolean
          default: false
          description: Replace an existing model of the same name.
        model_config:
          type: object
          description: |
            Model-specific configuration. Common members include `loss_family`, `seed`,
            `use_multivariate`, `informed_priors_version`, `priors`, and `autofit_override`
            (MCMC autofit controls). See the per-model reference in the documentation.
          additionalProperties: true
    ModelPredictRequest:
      type: object
      required: [triangle_name]
      properties:
        triangle_name:
          type: string
          description: Name of the triangle to predict on.
        prediction_name:
          type: string
          description: Optional name to store the resulting prediction triangle under.
        overwrite:
          type: boolean
          default: false
        predict_config:
          type: object
          description: Prediction configuration; may include `target_triangle` naming a triangle to predict onto.
          additionalProperties: true
          properties:
            target_triangle:
              type: string
              description: Name of the triangle to project predictions onto.
    CashflowCreateRequest:
      type: object
      required: [name, dev_model_name, tail_model_name]
      properties:
        name:
          type: string
          description: Name to store the cashflow model under.
        dev_model_name:
          type: string
          description: Name of the fitted development model to compose from.
        tail_model_name:
          type: string
          description: Name of the fitted tail model to compose from.
        model_config:
          type: object
          additionalProperties: true
    CashflowPredictRequest:
      allOf:
      - $ref: '#/components/schemas/ModelPredictRequest'
      - type: object
        properties:
          predict_config:
            type: object
            additionalProperties: true
            properties:
              initial_loss_name:
                type: string
                description: Name of an initial loss triangle to seed the cashflow projection.
    ModelTaskResponse:
      type: object
      description: Response returned when a fit is accepted.
      properties:
        model:
          type: object
          properties:
            id: { type: string }
        modal_task:
          type: object
          properties:
            id:
              type: string
              description: Task identifier to poll at `/tasks/{task_id}`.
    PredictTaskResponse:
      type: object
      description: Response returned when a prediction is accepted.
      properties:
        predictions:
          type: string
          description: Identifier of the triangle the predictions will be written to.
        modal_task:
          type: object
          properties:
            id:
              type: string
              description: Task identifier to poll at `/tasks/{task_id}`.
    Task:
      type: object
      description: Asynchronous remote task state.
      properties:
        id: { type: string }
        status:
          type: string
          description: Task status, e.g. `created`, `pending`, `terminated`.
        task_response:
          type: [object, 'null']
          description: Null while the task is pending; populated on completion.
          additionalProperties: true
          properties:
            status:
              type: string
              description: '`success` when the task completed successfully.'
            error:
              type: [string, 'null']
              description: Failure detail when the task did not succeed.