openapi: 3.1.0
info:
  title: EuroTrust EURS API
  version: 1.0.0
  description: |
    REST API for the EURS Euro stablecoin.
    Two surfaces: the **Bank API** (KYB-gated, API-key auth) for minting, redemption,
    transfers and webhooks; and the **Public Transparency API** (no auth) exposing
    supply, reserves and attestations.

    Money is expressed in EUR as decimal numbers (`amount_eur`). Internally all
    values are integer cents.
  contact: { email: developers@eurotrust.eu }
servers:
  - url: https://api.eurotrust.eu/v1
    description: Production (after EMI authorisation)
  - url: http://localhost:4280/api/v1
    description: Local prototype
security:
  - apiKey: []
tags:
  - { name: Public, description: No authentication required }
  - { name: Account }
  - { name: Minting }
  - { name: Redemption }
  - { name: Transfers }
  - { name: Webhooks }

paths:
  /public/supply:
    get:
      tags: [Public]
      security: []
      summary: Current EURS supply, reserves and backing ratio
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Supply" }
  /public/reserves:
    get:
      tags: [Public]
      security: []
      summary: Reserve custody breakdown by credit institution
      responses:
        "200": { description: OK }
  /public/history:
    get:
      tags: [Public]
      security: []
      summary: Daily supply/reserve snapshots
      parameters:
        - { name: days, in: query, schema: { type: integer, default: 180, maximum: 365 } }
      responses:
        "200": { description: OK }
  /public/attestations:
    get:
      tags: [Public]
      security: []
      summary: Monthly independent attestations
      responses:
        "200": { description: OK }
  /public/transactions/recent:
    get:
      tags: [Public]
      security: []
      summary: Recent on-chain mint/burn/transfer events
      parameters:
        - { name: limit, in: query, schema: { type: integer, default: 25, maximum: 100 } }
      responses:
        "200": { description: OK }

  /institution:
    get:
      tags: [Account]
      summary: Institution profile, KYB status and on-ledger accounts
      responses:
        "200": { description: OK }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /balance:
    get:
      tags: [Account]
      summary: Aggregate EURS balance
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  token: { type: string, example: EURS }
                  balance_eur: { type: number, example: 4820000 }
                  as_of: { type: string, format: date-time }

  /mint-requests:
    post:
      tags: [Minting]
      summary: Create a mint request (fiat in → EURS out)
      description: |
        Returns SEPA funding instructions with a unique wire reference.
        EURS is minted after funds clear and compliance approves (4-eyes).
        Supports `Idempotency-Key` header — retries return the original request.
      parameters:
        - { name: Idempotency-Key, in: header, schema: { type: string, maxLength: 64 } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount_eur]
              properties:
                amount_eur: { type: number, minimum: 1000, example: 250000 }
                dest_address: { type: string, pattern: "^0x[0-9a-fA-F]{40}$" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/MintRequest" }
        "422": { $ref: "#/components/responses/Invalid" }
    get:
      tags: [Minting]
      summary: List mint requests
      responses:
        "200": { description: OK }
  /mint-requests/{ref}:
    get:
      tags: [Minting]
      summary: Get a mint request by reference
      parameters:
        - { name: ref, in: path, required: true, schema: { type: string, example: MR-2026-000123 } }
      responses:
        "200": { description: OK }
        "404": { description: Not found }

  /redemptions:
    post:
      tags: [Redemption]
      summary: Redeem EURS (burn → EUR to IBAN at 1:1)
      parameters:
        - { name: Idempotency-Key, in: header, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount_eur, payout_iban]
              properties:
                amount_eur: { type: number, example: 100000 }
                payout_iban: { type: string, example: EE382200221020145685 }
      responses:
        "201": { description: Created }
        "422": { $ref: "#/components/responses/Invalid" }
    get:
      tags: [Redemption]
      summary: List redemptions
      responses:
        "200": { description: OK }

  /transfers:
    post:
      tags: [Transfers]
      summary: Transfer EURS on-ledger
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to_address, amount_eur]
              properties:
                to_address: { type: string, pattern: "^0x[0-9a-fA-F]{40}$" }
                amount_eur: { type: number }
      responses:
        "201": { description: Executed }

  /transactions:
    get:
      tags: [Account]
      summary: Ledger history for your institution
      parameters:
        - { name: limit, in: query, schema: { type: integer, default: 50, maximum: 200 } }
      responses:
        "200": { description: OK }

  /webhooks:
    post:
      tags: [Webhooks]
      summary: Register an HTTPS webhook endpoint
      description: Response contains the HMAC signing secret — shown once.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string, format: uri }
                events:
                  type: array
                  items: { type: string, enum: [mint.completed, redemption.burned, redemption.settled] }
      responses:
        "201": { description: Created }
    get:
      tags: [Webhooks]
      summary: List webhook endpoints
      responses:
        "200": { description: OK }
  /webhooks/{id}:
    delete:
      tags: [Webhooks]
      summary: Disable a webhook endpoint
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200": { description: Disabled }

components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: "etk_live_…"
  responses:
    Unauthorized:
      description: Missing/invalid API key
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Invalid:
      description: Validation error
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Error:
      type: object
      properties:
        error: { type: string, example: invalid_amount }
        message: { type: string }
    Supply:
      type: object
      properties:
        token: { type: string, example: EURS }
        total_supply_eur: { type: number, example: 18425000 }
        reserves_eur: { type: number, example: 18545000 }
        backing_ratio: { type: number, example: 1.0065 }
        contracts:
          type: object
          properties:
            ethereum: { type: string }
            polygon: { type: string }
    MintRequest:
      type: object
      properties:
        ref: { type: string, example: MR-2026-000123 }
        amount_eur: { type: number }
        status:
          type: string
          enum: [awaiting_funds, funds_received, approved, minted, rejected, expired]
        sepa_reference: { type: string, example: EURSMINT84913 }
        tx_hash: { type: string, nullable: true }
        funding_instructions:
          type: object
          properties:
            iban: { type: string }
            bic: { type: string }
            reference: { type: string }

webhooks:
  mint.completed:
    post:
      summary: EURS minted for your institution
      description: |
        Signed with `X-EURS-Signature: t=<unix>,v1=<hex>` where
        `v1 = HMAC_SHA256(secret, t + "." + raw_body)`. Reject if |now − t| > 300s.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: string, example: evt_5f3a9c1b2e4d6f80 }
                event: { type: string, example: mint.completed }
                created: { type: string, format: date-time }
                data:
                  type: object
                  properties:
                    ref: { type: string }
                    amount_eur: { type: number }
                    tx_hash: { type: string }
      responses:
        "200": { description: Acknowledged }
