openapi: 3.1.0
info:
  contact:
    email: dev@invopop.com
    name: Invopop Developers
  description: >-
    Set of end-points for the France app, including lookups against the PPF
    Annuaire (French e-invoicing directory).
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  title: France Service API
  version: 0.1.0
servers:
  - description: production
    url: https://api.invopop.com
security:
  - InvopopAuth: []
paths:
  /apps/gov-fr/v1/directory/siren/{siren}:
    get:
      operationId: lookupDirectoryBySIREN
      summary: Look up directory entries by SIREN
      description: |-
        Returns all entries from the PPF Annuaire that
        match the given French SIREN. A company may have several entries 
        so the response is always a list.

        Only available to **live** workspaces. Sandbox workspaces receive
        `403 Forbidden`.
      parameters:
        - name: siren
          in: path
          required: true
          description: 9-digit French SIREN identifier of the company to look up.
          schema:
            type: string
            example: '123456789'
      responses:
        '200':
          description: >-
            Lookup completed. `count` may be `0` if the SIREN is not present in
            the directory.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SIRENLookupResponse'
              examples:
                found:
                  summary: SIREN present in the directory
                  value:
                    siren: '123456789'
                    count: 1
                    results:
                      - id_instance: '1020993'
                        siren: '123456789'
                        siret: ''
                        routing_code: ROUTE123
                        platform_id: '0431'
                        nature: M
                        identifier: '269909452'
                        suffix: ''
                        start_date: '20251227'
                        end_date: ''
                        effective_end_date: '20280402'
                        created_at: '2026-01-15T10:00:00Z'
                        updated_at: '2026-01-15T10:00:00Z'
                not_found:
                  summary: SIREN not in the directory
                  value:
                    siren: '999999999'
                    count: 0
                    results: []
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/directory/identifier/{identifier}:
    get:
      operationId: lookupDirectoryByIdentifier
      summary: Look up directory entries by electronic address
      description: |-
        Returns all entries from the PPF Annuaire that
        match the given electronic address identifier (the value used to
        route invoices to a party in France represented by an inbox in Invopop).

        Only available to **live** workspaces. Sandbox workspaces receive
        `403 Forbidden`.
      parameters:
        - name: identifier
          in: path
          required: true
          description: |-
            Electronic address identifier as registered in the Annuaire,
            typically in `scheme:value` form (e.g. `0009:123456789`).
          schema:
            type: string
            example: '0009:123456789'
      responses:
        '200':
          description: >-
            Lookup completed. `count` may be `0` if no entry uses this
            identifier.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentifierLookupResponse'
              examples:
                found:
                  summary: Identifier present in the directory
                  value:
                    identifier: '0009:123456789'
                    count: 1
                    results:
                      - id_instance: '1020994'
                        siren: '269909452'
                        siret: ''
                        routing_code: ROUTE123
                        platform_id: '0431'
                        nature: M
                        identifier: 269909452_suffixe1
                        suffix: suffixe1
                        start_date: '20251227'
                        end_date: ''
                        effective_end_date: '20280402'
                        created_at: '2026-01-15T10:00:00Z'
                        updated_at: '2026-01-15T10:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/entry/{silo_entry_id}/agreement:
    get:
      operationId: fetchEntryAgreement
      summary: Download the agreement PDF
      description: |-
        Returns the current agreement PDF for the silo entry. If the
        signer has already completed the signing step, the signed
        mandate is returned; otherwise the unsigned template is
        returned pre-filled with the party's details.

        Fails with `422` when any required party or signer field is
        missing from the silo envelope.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
      responses:
        '200':
          description: Agreement PDF, served inline.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      operationId: uploadEntryAgreement
      summary: Upload a pre-signed agreement
      description: |-
        Stores a mandate that the customer has signed outside Invopop.
        Satisfies the `agreement` step on the approval flow.

        Identity verification is still required; call `POST /identity`
        before or after this endpoint, then call `POST /confirm` to
        finalise. For in-app signing, use `POST /sign` instead of this
        endpoint.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgreementSubmit'
      responses:
        '204':
          description: Pre-signed agreement stored.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/entry/{silo_entry_id}/identity:
    post:
      operationId: uploadEntryIdentity
      summary: Upload an identity document image
      description: |-
        Stores one face of the signer's identity document. Required on
        both signing paths before calling `POST /confirm`.

        Call once per `view`:

        - `front` + `back` for an ID card (CNI, titre de séjour).
        - `page` for a passport.

        Re-uploading the same `view` overwrites the previous image.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IdentityUpload'
      responses:
        '204':
          description: Identity image stored.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/entry/{silo_entry_id}/sign:
    post:
      operationId: signEntry
      summary: Sign the agreement mandate
      description: |-
        Stamps the signer's signature onto the generated mandate PDF
        and stores the result as the entry's signed agreement. Satisfies
        the `sign` step on the approval flow.

        Two modes:

        - **Drawn**: pass a PNG or JPEG image (≤ 4 MB) in `signature`.
          The image is inked onto the signature box.
        - **Typed**: omit `signature` or send an empty value. The
          signer's name is rendered as a typed signature.

        Requires the silo entry to have signer fields set (given name,
        surname, and identity number) before calling. Use together with
        `POST /identity`, then call `POST /confirm` to finalise.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SignatureSubmit'
      responses:
        '204':
          description: Signature stamped and stored as the signed agreement.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/entry/{silo_entry_id}/confirm:
    post:
      operationId: confirmEntry
      summary: Confirm the onboarding submission
      description: |-
        Finalises the approval and pokes the queued
        `agreement.wait.approval` task so the onboarding workflow
        continues.

        Identity verification (`POST /identity`) is required on both
        paths. In addition:

        - **In-app signing**: `POST /sign` must have been called.
        - **Pre-signed PDF**: `POST /agreement` must have been called.

        Returns the approval flow status. A `submitted` status means
        the submission is under review; the workflow will advance once
        a decision is made.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
      responses:
        '200':
          description: Submission confirmed. Returns the approval flow status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmResponse'
              examples:
                submitted:
                  summary: Awaiting approval decision
                  value:
                    status: submitted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/reports/{silo_entry_id}:
    get:
      operationId: fetchReportingSummary
      summary: Fetch the e-reporting summary for a party
      description: |-
        Returns the current Flux 10 e-reporting status for the party
        identified by the silo entry, one block per report kind:

        - `tx` — **transactions**.
        - `py` — **payments**.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
      responses:
        '200':
          description: Reporting summary for the party.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportingSummary'
              examples:
                enabled:
                  summary: Reporting enabled (Réel normal mensuel)
                  value:
                    silo_entry_id: 5b45453c-cdd0-11ed-afa1-0242ac120002
                    siren: '123456789'
                    enabled: true
                    regime: real_normal_monthly
                    kinds:
                      - kind: tx
                        cadence: ten_day
                        open_period_start: '2026-07-01'
                        open_period_end: '2026-07-10'
                        next_due_period_start: '2026-06-21'
                        next_due_period_end: '2026-06-30'
                        next_due_deadline: '2026-07-10T23:59:59+02:00'
                        last_period_end: '2026-06-20'
                        last_filed_period:
                          id: 018f9e2a-7c31-7a10-9b44-2f9d1e6c8a01
                          siren: '123456789'
                          flux_kind: tx
                          role: seller
                          period_start: '2026-06-11'
                          period_end: '2026-06-20'
                          sequence: 1
                          status: filed
                          flux_code: PPF206_1025_000123
                          ack_ref: '300'
                          submitted_at: '2026-06-21T08:15:00Z'
                          created_at: '2026-06-21T08:00:00Z'
                          updated_at: '2026-06-21T08:20:00Z'
                      - kind: py
                        cadence: monthly
                        open_period_start: '2026-07-01'
                        open_period_end: '2026-07-31'
                        next_due_period_start: '2026-06-01'
                        next_due_period_end: '2026-06-30'
                        next_due_deadline: '2026-07-10T23:59:59+02:00'
                        last_period_end: ''
                        last_filed_period: null
                disabled:
                  summary: Party exists but reporting is off
                  value:
                    silo_entry_id: 5b45453c-cdd0-11ed-afa1-0242ac120002
                    siren: '123456789'
                    enabled: false
                    kinds: []
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/reports/{silo_entry_id}/periods:
    get:
      operationId: listReportingPeriods
      summary: List e-reporting periods for a party
      description: |-
        Returns the party's Flux 10 report periods, most recent first
        (`created_at` descending). 
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
        - $ref: '#/components/parameters/ReportKind'
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: One page of report periods.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListPeriodsResponse'
              examples:
                page:
                  summary: First page with more results available
                  value:
                    periods:
                      - id: 018f9e2a-7c31-7a10-9b44-2f9d1e6c8a01
                        siren: '123456789'
                        flux_kind: tx
                        role: seller
                        period_start: '2026-06-11'
                        period_end: '2026-06-20'
                        sequence: 1
                        status: filed
                        flux_code: PPF206_1025_000123
                        ack_ref: '300'
                        submitted_at: '2026-06-21T08:15:00Z'
                        created_at: '2026-06-21T08:00:00Z'
                        updated_at: '2026-06-21T08:20:00Z'
                    next_offset: 50
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/reports/{silo_entry_id}/periods/{period_id}:
    get:
      operationId: fetchReportingPeriod
      summary: Fetch a single e-reporting period
      description: |-
        Returns the metadata for one report period, scoped to the party
        identified by the silo entry.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
        - $ref: '#/components/parameters/PeriodID'
      responses:
        '200':
          description: The requested report period.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportingPeriod'
              examples:
                filed:
                  summary: A filed period
                  value:
                    id: 018f9e2a-7c31-7a10-9b44-2f9d1e6c8a01
                    siren: '123456789'
                    flux_kind: tx
                    role: seller
                    period_start: '2026-06-11'
                    period_end: '2026-06-20'
                    sequence: 1
                    status: filed
                    flux_code: PPF206_1025_000123
                    ack_ref: '300'
                    submitted_at: '2026-06-21T08:15:00Z'
                    created_at: '2026-06-21T08:00:00Z'
                    updated_at: '2026-06-21T08:20:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /apps/gov-fr/v1/reports/{silo_entry_id}/periods/{period_id}/xml:
    get:
      operationId: fetchReportingPeriodXML
      summary: Download a period's Flux 10 XML
      description: Streams the raw Flux 10 `Report.xml` body generated for the period.
      parameters:
        - $ref: '#/components/parameters/SiloEntryID'
        - $ref: '#/components/parameters/PeriodID'
      responses:
        '200':
          description: The Flux 10 XML body, served as `application/xml`.
          content:
            application/xml:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  parameters:
    SiloEntryID:
      name: silo_entry_id
      in: path
      required: true
      description: ID of the `org.Party` silo entry being onboarded.
      schema:
        type: string
        example: 5b45453c-cdd0-11ed-afa1-0242ac120002
    PeriodID:
      name: period_id
      in: path
      required: true
      description: ID of the report period.
      schema:
        type: string
        example: 018f9e2a-7c31-7a10-9b44-2f9d1e6c8a01
    ReportKind:
      name: kind
      in: query
      required: false
      description: |-
        Restrict the results to a single report kind:

          - `tx` - Transactions (invoice data).
          - `py` - Payments.

        Omit to return both kinds.
      schema:
        type: string
        enum:
          - tx
          - py
        example: tx
    Offset:
      name: offset
      in: query
      required: false
      description: Number of periods to skip, for pagination. Must be non-negative.
      schema:
        type: integer
        minimum: 0
        default: 0
        example: 0
    Limit:
      name: limit
      in: query
      required: false
      description: |-
        Maximum number of periods to return. Values outside the `1`–`200`
        range fall back to the default of `50`.
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
        example: 50
  schemas:
    DirectoryEntry:
      type: object
      description: |-
        Single entry in the PPF Annuaire. Each entry
        represents one registered electronic address for a SIREN/SIRET.
      properties:
        id_instance:
          type: string
          description: Unique instance identifier from the PPF export.
          example: '1020993'
        siren:
          type: string
          description: 9-digit French SIREN of the company.
          example: '269909452'
        siret:
          type: string
          description: 14-digit French SIRET of the establishment, when applicable.
          example: ''
        routing_code:
          type: string
          description: Routing code used by the PPF to deliver invoices.
          example: ROUTE123
        platform_id:
          type: string
          description: 4-digit identifier of the PA platform that registered the entry.
          example: '0431'
        nature:
          type: string
          description: >-
            Single-letter code for the nature of the entry as classified by the
            Annuaire.
          example: M
        identifier:
          type: string
          description: |-
            Electronic address identifier used for routing. Typically the SIREN,
            optionally with a `_suffix` qualifier when multiple addresses exist
            for the same company.
          example: '269909452'
        suffix:
          type: string
          description: Optional suffix qualifying the identifier.
          example: suffixe1
        start_date:
          type: string
          description: Date from which this entry is active in the Annuaire, as `YYYYMMDD`.
          example: '20251227'
        end_date:
          type: string
          description: Scheduled end date of the entry as `YYYYMMDD`, when set.
          example: ''
        effective_end_date:
          type: string
          description: >-
            Effective end date as `YYYYMMDD`, once the entry has been
            deactivated.
          example: '20280402'
        created_at:
          type: string
          format: date-time
          description: When this entry was first stored in the local mirror.
          example: '2026-01-15T10:00:00Z'
        updated_at:
          type: string
          format: date-time
          description: When this entry was last refreshed from a PPF export.
          example: '2026-01-15T10:00:00Z'
    SIRENLookupResponse:
      type: object
      required:
        - siren
        - count
        - results
      properties:
        siren:
          type: string
          description: SIREN that was queried.
          example: '123456789'
        count:
          type: integer
          description: Number of directory entries returned.
          example: 1
        results:
          type: array
          description: >-
            Matching directory entries. Empty when the SIREN is not in the
            Annuaire.
          items:
            $ref: '#/components/schemas/DirectoryEntry'
    IdentifierLookupResponse:
      type: object
      required:
        - identifier
        - count
        - results
      properties:
        identifier:
          type: string
          description: Electronic address identifier that was queried.
          example: '0009:123456789'
        count:
          type: integer
          description: Number of directory entries returned.
          example: 1
        results:
          type: array
          description: >-
            Matching directory entries. Empty when the identifier is not in the
            Annuaire.
          items:
            $ref: '#/components/schemas/DirectoryEntry'
    ReportingPeriod:
      type: object
      description: |-
        One Flux 10 submission window for a `(kind, role)` tuple. Corrective
        re-submissions of the same window are separate periods sharing the
        window dates but with an incrementing `sequence`.
      required:
        - id
        - siren
        - flux_kind
        - role
        - period_start
        - period_end
        - sequence
        - status
        - created_at
        - updated_at
      properties:
        id:
          type: string
          description: Unique identifier of the period.
          example: 018f9e2a-7c31-7a10-9b44-2f9d1e6c8a01
        siren:
          type: string
          description: 9-digit French SIREN of the reporting party.
          example: '123456789'
        flux_kind:
          type: string
          description: |-
            Report kind:

              - `tx` - Transactions (invoice data).
              - `py` - Payments.
          enum:
            - tx
            - py
          example: tx
        role:
          type: string
          description: |-
            Role the reporting party plays for the transactions in this
            period. Empty for B2C-only periods.

              - `seller` - The party is the supplier.
              - `buyer` - The party is the customer.
          enum:
            - seller
            - buyer
            - ''
          example: seller
        period_start:
          type: string
          description: First day of the reporting window, as `YYYY-MM-DD`.
          example: '2026-06-11'
        period_end:
          type: string
          description: Last day (inclusive) of the reporting window, as `YYYY-MM-DD`.
          example: '2026-06-20'
        sequence:
          type: integer
          description: |-
            Submission sequence for the window. `1` is the initial report;
            higher values are corrective re-submissions.
          example: 1
        previous_period_id:
          type: string
          description: >-
            ID of the prior sequence for the same window. Omitted for sequence
            1.
          example: ''
        status:
          type: string
          description: |-
            Lifecycle status of the period:

              - `generated` - XML built, not yet sent to the PPF.
              - `submitted` - Sent to the PPF, awaiting acknowledgement.
              - `filed` - Accepted by the PPF.
              - `rejected` - Rejected by the PPF.
          enum:
            - generated
            - submitted
            - filed
            - rejected
          example: filed
        flux_code:
          type: string
          description: >-
            PPF flux filename assigned when the report was submitted. Omitted
            before submission.
          example: PPF206_1025_000123
        ack_ref:
          type: string
          description: |-
            PPF acknowledgement status code once received (`300` filed,
            `301` rejected). Omitted while awaiting acknowledgement.
          example: '300'
        submitted_at:
          type: string
          format: date-time
          description: When the report was submitted to the PPF. Omitted before submission.
          example: '2026-06-21T08:15:00Z'
        created_at:
          type: string
          format: date-time
          description: When the period was created.
          example: '2026-06-21T08:00:00Z'
        updated_at:
          type: string
          format: date-time
          description: When the period was last updated.
          example: '2026-06-21T08:20:00Z'
    ReportingKindStatus:
      type: object
      description: |-
        Reporting status for a single kind (`tx` or `py`). Surfaces two
        distinct windows: `open_period_*` (currently being accumulated) and
        `next_due_period_*` (the next window a report will be generated for).
      required:
        - kind
        - cadence
        - open_period_start
        - open_period_end
        - next_due_period_start
        - next_due_period_end
        - next_due_deadline
      properties:
        kind:
          type: string
          description: |-
            Report kind:

              - `tx` - Transactions (invoice data).
              - `py` - Payments.
          enum:
            - tx
            - py
          example: tx
        cadence:
          type: string
          description: |-
            How often reports are filed for this kind, derived from the VAT
            regime:

              - `ten_day` - Every ten days.
              - `monthly` - Once per calendar month.
              - `bimonthly` - Once every two calendar months.
          enum:
            - ten_day
            - monthly
            - bimonthly
          example: ten_day
        open_period_start:
          type: string
          description: >-
            First day of the window currently being accumulated, as
            `YYYY-MM-DD`.
          example: '2026-07-01'
        open_period_end:
          type: string
          description: Last day (inclusive) of the currently open window, as `YYYY-MM-DD`.
          example: '2026-07-10'
        next_due_period_start:
          type: string
          description: >-
            First day of the next window a report will be generated for, as
            `YYYY-MM-DD`.
          example: '2026-06-21'
        next_due_period_end:
          type: string
          description: Last day (inclusive) of the next-due window, as `YYYY-MM-DD`.
          example: '2026-06-30'
        next_due_deadline:
          type: string
          format: date-time
          description: Filing deadline for the next-due window (Europe/Paris).
          example: '2026-07-10T23:59:59+02:00'
        last_period_end:
          type: string
          description: >-
            End date of the most recently processed window, as `YYYY-MM-DD`.
            Omitted when none has been processed.
          example: '2026-06-20'
        last_filed_period:
          description: The most recently filed period for this kind, if any.
          oneOf:
            - $ref: '#/components/schemas/ReportingPeriod'
            - type: 'null'
    ReportingSummary:
      type: object
      description: A party's Flux 10 e-reporting status across all report kinds.
      required:
        - silo_entry_id
        - siren
        - enabled
        - kinds
      properties:
        silo_entry_id:
          type: string
          description: Silo entry (party) the summary is for.
          example: 5b45453c-cdd0-11ed-afa1-0242ac120002
        siren:
          type: string
          description: 9-digit French SIREN of the party.
          example: '123456789'
        enabled:
          type: boolean
          description: Whether e-reporting is currently enabled for the party.
          example: true
        regime:
          type: string
          description: |-
            VAT regime driving the reporting cadences. Omitted when reporting
            is not enabled.

              - `real_normal_monthly` - Réel normal mensuel.
              - `real_normal_quarterly` - Réel normal trimestriel.
              - `simplified` - Réel simplifié.
              - `vat_franchise` - Franchise en base.
          enum:
            - real_normal_monthly
            - real_normal_quarterly
            - simplified
            - vat_franchise
          example: real_normal_monthly
        kinds:
          type: array
          description: Per-kind status blocks. Empty when reporting is not enabled.
          items:
            $ref: '#/components/schemas/ReportingKindStatus'
    ListPeriodsResponse:
      type: object
      description: One page of report periods, most recent first.
      required:
        - periods
      properties:
        periods:
          type: array
          description: The report periods on this page.
          items:
            $ref: '#/components/schemas/ReportingPeriod'
        next_offset:
          type: integer
          description: >-
            Offset to pass on the next call to fetch the following page. Omitted
            on the last page.
          example: 50
    AgreementSubmit:
      type: object
      required:
        - data
      properties:
        data:
          type: string
          format: byte
          description: |-
            Base64-encoded binary of the pre-signed PDF mandate. Must be
            `application/pdf`, maximum 4 MB.
    SignatureSubmit:
      type: object
      properties:
        signature:
          type: string
          format: byte
          description: |-
            Base64-encoded PNG or JPEG image of the drawn signature,
            maximum 4 MB. Omit or leave empty to render the signer's
            name as a typed signature.
    ConfirmResponse:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          description: Current status of the approval flow after finalisation.
          example: submitted
    IdentityUpload:
      type: object
      required:
        - view
        - data
      properties:
        view:
          type: string
          description: |-
            Which face of the identity document this image represents.

              - `front` - Front of an ID card (recto).
              - `back` - Back of an ID card (verso).
              - `page` - Photo page of a passport.
          enum:
            - front
            - back
            - page
        data:
          type: string
          format: byte
          description: |-
            Base64-encoded binary of the image. Must be `image/jpeg` or
            `image/png`, maximum 4 MB. Text on the document should be
            clearly legible.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable description of the error.
          example: missing siren parameter
  responses:
    BadRequest:
      description: The request is malformed or missing required parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: missing siren parameter
    Unauthorized:
      description: Missing or invalid authentication token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: missing enrollment
    Forbidden:
      description: |-
        The directory API is only available to live workspaces. Calls made
        with a sandbox enrollment are rejected.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: directory API is only available for live workspaces
    NotFound:
      description: The referenced silo entry or party could not be found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: silo entry not found
    UnprocessableEntity:
      description: |-
        The request is well-formed but cannot be processed — for example
        the agreement PDF cannot be generated because the draft party is
        incomplete, or the uploaded signature failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: draft party is missing signer details
    InternalServerError:
      description: An unexpected server-side error occurred.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: internal server error
  securitySchemes:
    InvopopAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |-
        Authenticate using a valid Invopop enrollment token in the `Bearer`
        scheme.

        Example: `Authorization: Bearer <token>`
