openapi: 3.0.3
info:
  title: MeanderViz REST API
  version: "1"
  description: |
    Structural variants, alignment sites, assembly checks, binned tracks and space-filling-curve coordinates,
    computed by the same engine as the MeanderViz web app. Files are uploaded, referenced by URL, or taken from the
    packaged examples. Analyses run as jobs; poll the job or pass `"wait": true` to get the result in one call.
    Uploaded files and results are deleted automatically (default: after one hour). No login, no cookies.
servers:
  - url: https://meanderviz.org/api/v1
paths:
  /:
    get: { summary: List endpoints, responses: { "200": { description: OK } } }
  /health:
    get: { summary: Service health, responses: { "200": { description: OK } } }
  /examples:
    get: { summary: List example datasets, responses: { "200": { description: OK } } }
  /examples/{id}:
    get: { summary: Files of an example (usable as "examples/<name>" references), parameters: [ { name: id, in: path, required: true, schema: { type: string } } ], responses: { "200": { description: OK }, "404": { description: Unknown example } } }
  /curve:
    get:
      summary: Convert between a curve index and pixel coordinates (2D)
      parameters:
        - { name: order, in: query, schema: { type: integer, default: 9 }, description: side = 2^order pixels }
        - { name: curve, in: query, schema: { type: string, enum: [hilbert, moore, morton, gray, snake], default: hilbert } }
        - { name: d, in: query, schema: { type: integer }, description: index along the curve (gives x, y) }
        - { name: x, in: query, schema: { type: integer } }
        - { name: y, in: query, schema: { type: integer }, description: with x, gives d }
      responses: { "200": { description: "{order, curve, d, x, y}" } }
  /curve3d:
    get:
      summary: 3D curve index to voxel coordinates
      parameters: [ { name: bits, in: query, schema: { type: integer, default: 6 } }, { name: curve, in: query, schema: { type: string, enum: [hilbert, morton] } }, { name: d, in: query, required: true, schema: { type: integer } } ]
      responses: { "200": { description: "{bits, curve, d, x, y, z}" } }
  /files:
    put:
      summary: Upload one file as the raw request body
      parameters: [ { name: X-Filename, in: header, required: true, schema: { type: string } } ]
      requestBody: { content: { application/octet-stream: { schema: { type: string, format: binary } } } }
      responses: { "201": { description: "{id, name, size, expires}" }, "413": { description: Too large } }
    post:
      summary: Upload files as multipart/form-data
      requestBody: { content: { multipart/form-data: { schema: { type: object } } } }
      responses: { "201": { description: "{files: [{id, name, size, field}], expires}" } }
  /files/{id}:
    delete: { summary: Delete an uploaded file, parameters: [ { name: id, in: path, required: true, schema: { type: string } } ], responses: { "200": { description: Deleted } } }
  /findings:
    post:
      summary: Structural-variant candidates from read depth and read pairs
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [sample]
              properties:
                sample: { type: string, description: file id, URL or examples/<name> (BAM or bedGraph/depth) }
                reference: { type: string, description: same; without it the sample is compared with its own median }
                pairs: { type: string, description: BEDPE of discordant pairs (BAM input yields pairs itself) }
                chrom: { type: string, default: all }
                support: { type: integer, default: 3 }
                rd: { type: number, default: 0.4, description: log2 threshold for read-depth changes }
                mapq: { type: integer, default: 20 }
                wait: { type: boolean, default: false, description: return the result in this call }
      responses: { "202": { description: Job accepted (see Location) }, "200": { description: Result when wait is true } }
  /sites:
    post:
      summary: Variable sites of an alignment, or accessory genes of a presence/absence matrix
      requestBody: { content: { application/json: { schema: { type: object, required: [alignment], properties: { alignment: { type: string }, reference: { type: string, description: sequence name or consensus }, min: { type: integer, default: 2 }, wait: { type: boolean } } } } } }
      responses: { "202": { description: Job accepted }, "200": { description: Result when wait is true } }
  /assembly:
    post:
      summary: Assembly statistics, gaps and read-depth problems
      requestBody: { content: { application/json: { schema: { type: object, required: [fasta], properties: { fasta: { type: string }, depth: { type: string, description: BAM or bedGraph of reads mapped to the assembly }, rd: { type: number, default: 0.4 }, wait: { type: boolean } } } } } }
      responses: { "202": { description: Job accepted }, "200": { description: Result when wait is true } }
  /bins:
    post:
      summary: Mean value of a track over n bins of a region (read depth, or GC/skew/CpG of a FASTA)
      requestBody: { content: { application/json: { schema: { type: object, required: [file, chrom], properties: { file: { type: string }, chrom: { type: string }, start: { type: integer }, end: { type: integer }, n: { type: integer, default: 4096, maximum: 1048576 }, metric: { type: string, enum: [gc, skew, cpg, cumskew] }, mapq: { type: integer }, wait: { type: boolean } } } } } }
      responses: { "202": { description: Job accepted }, "200": { description: Result when wait is true } }
  /chromosomes:
    post:
      summary: Chromosomes or contigs and their lengths in a file
      requestBody: { content: { application/json: { schema: { type: object, required: [file], properties: { file: { type: string }, wait: { type: boolean } } } } } }
      responses: { "202": { description: Job accepted }, "200": { description: Result when wait is true } }
  /jobs/{id}:
    get: { summary: Job status, parameters: [ { name: id, in: path, required: true, schema: { type: string } } ], responses: { "200": { description: "{id, type, status: queued|running|done|failed, progress, params, error, result_url}" }, "404": { description: Unknown or expired } } }
    delete: { summary: Forget a job, parameters: [ { name: id, in: path, required: true, schema: { type: string } } ], responses: { "200": { description: Deleted } } }
  /jobs/{id}/result:
    get:
      summary: Result of a finished job
      parameters: [ { name: id, in: path, required: true, schema: { type: string } }, { name: format, in: query, schema: { type: string, enum: [json, csv], default: json } } ]
      responses: { "200": { description: JSON or CSV }, "409": { description: Not finished }, "422": { description: Failed } }
