openapi: 3.1.0
info:
  title: ResiPlan exit interface
  version: "1.0.0"
  description: |
    Starts and fetches the full export of an organisation, so that a program or
    the destination provider can retrieve its data (Regulation (EU) 2023/2854,
    Article 30(2)). Free on every plan, including after a cancellation or the
    end of a trial.

    An administrator creates the exit key on the Reversibility page of the
    application. A key is shown once, is valid for 90 days and gives access to
    nothing else: the MCP server, the OData feed and the other REST routes refuse
    it. The organisation comes from the key, never from the request. A key stops
    working (403) when the person who created it is no longer an administrator of
    the organisation; an administrator can create a new one. Each request that
    returns download links is recorded in the organisation's audit log.

    Limits: 60 requests per minute per key, 5 exports per 24 hours per
    organisation, one export at a time. An export stays downloadable for 7 days.

    Structures and formats of the export: /legal/resiplan-export-register.json.
    Procedure and contract: /legal/switching.
servers:
  - url: https://app.resiplan.eu
security:
  - BearerAuth: []
  - ApiKeyHeader: []
paths:
  /api/v1/switching/exports:
    get:
      summary: The organisation's latest exports (20 at most, newest first)
      operationId: listExports
      responses:
        "200":
          description: Exports
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Export"
        "401":
          $ref: "#/components/responses/Refused"
        "403":
          $ref: "#/components/responses/Refused"
        "429":
          $ref: "#/components/responses/Refused"
    post:
      summary: Start a full export
      description: >-
        The answer comes at once; the export runs in the background. An e-mail
        tells the administrator who created the key when it is ready. Poll the
        export to get its download links.
      operationId: startExport
      parameters:
        - name: locale
          in: query
          description: Language of the README and workbook.
          schema:
            type: string
            enum: [fr, en, nl]
            default: en
      responses:
        "202":
          description: Export queued
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: "#/components/schemas/Export"
        "401":
          $ref: "#/components/responses/Refused"
        "403":
          $ref: "#/components/responses/Refused"
        "409":
          description: An export is already running, or the organisation is being deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Too many requests, or the limit of exports per 24 hours is reached.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/switching/exports/{exportId}:
    get:
      summary: One export and, once it is ready, the download links of its volumes
      operationId: getExport
      parameters:
        - name: exportId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Export
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    allOf:
                      - $ref: "#/components/schemas/Export"
                      - type: object
                        required: [volumes]
                        properties:
                          volumes:
                            type: array
                            items:
                              $ref: "#/components/schemas/Volume"
        "401":
          $ref: "#/components/responses/Refused"
        "403":
          $ref: "#/components/responses/Refused"
        "404":
          description: No such export in this organisation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
  responses:
    Refused:
      description: Missing, invalid, revoked or expired key, wrong scope, IP not allowed, or rate limit.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
    Export:
      type: object
      required: [id, status, stage, trigger, createdAt, rows, files]
      properties:
        id:
          type: string
        status:
          type: string
          enum: [queued, running, completed, failed, expired]
        stage:
          type: string
        trigger:
          type: string
          enum: [manual, closure]
        createdAt:
          type: number
          description: Milliseconds since the Unix epoch.
        completedAt:
          type: [number, "null"]
        expiresAt:
          type: [number, "null"]
          description: The volumes are deleted after this moment.
        rows:
          type: number
        dataBytes:
          type: number
        files:
          type: number
        fileBytes:
          type: number
        filesLinkOnly:
          type: number
          description: Files too large for the archive; files/index.json gives their link.
        filesMissing:
          type: number
        redactedValues:
          type: number
          description: Secret values replaced by [REDACTED].
        manifestSha256:
          type: [string, "null"]
    Volume:
      type: object
      required: [index, fileName, bytes, sha256]
      properties:
        index:
          type: number
        fileName:
          type: string
        bytes:
          type: number
        sha256:
          type: string
        url:
          type: [string, "null"]
          description: Download link of the ZIP volume, valid until the export expires.
