{
  "openapi": "3.0.4",
  "info": {
    "title": "SOFTIP Digitálny poštár API",
    "description": "Machine-to-machine REST API of SOFTIP Digitálny poštár for integrating customer software: sending and receiving Peppol business documents.\n\nAuthentication: JWT (Keycloak) or API Key.\nKeycloak base URL: https://keycloak.test.softipdigitalnypostar.sk/\nRealm: postar\nToken endpoint (password / client credentials): https://keycloak.test.softipdigitalnypostar.sk/realms/postar/protocol/openid-connect/token\n\nClient credentials example:\ncurl -X POST \"https://keycloak.test.softipdigitalnypostar.sk/realms/postar/protocol/openid-connect/token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=client_credentials&client_id=<client-id>&client_secret=<secret>\"\n\nAfter obtaining the access token, click Authorize -> bearer and paste it as 'Bearer <token>'.\nFor API key access, provide header X-API-KEY in the Authorize dialog.",
    "version": "2.0"
  },
  "paths": {
    "/sapi/v2/auth/token": {
      "post": {
        "tags": [
          "SapiAuth"
        ],
        "summary": "Obtain an access token using client credentials.",
        "description": "Authenticates a SAPI client by proxying the supplied credentials to the Keycloak identity provider.\nOnly the `client_credentials` grant type is supported.\nThe returned access token must be supplied as `Authorization: Bearer {access_token}` on all\nsubsequent SAPI requests. Tokens are short-lived (approximately 15 minutes).",
        "operationId": "SapiObtainToken",
        "requestBody": {
          "description": "Client credentials and grant type.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenRequest"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "423": {
            "description": "Locked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/sapi/v2/auth/token/status": {
      "get": {
        "tags": [
          "SapiAuth"
        ],
        "summary": "Check the validity and metadata of the current access token.",
        "description": "Returns whether the supplied access token is active, along with its expiry and associated scopes.",
        "operationId": "SapiGetTokenStatus",
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenStatus"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/sapi/v2/auth/renew": {
      "post": {
        "tags": [
          "SapiAuth"
        ],
        "summary": "Renew an access token using a refresh token.",
        "description": "Exchanges a previously issued refresh token for a new access token (and optionally a new refresh token)\nwithout requiring the client to re-supply its credentials.\nThe renewal request is forwarded to the configured Keycloak identity provider.",
        "operationId": "SapiRenewToken",
        "requestBody": {
          "description": "Refresh token to exchange.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiRenewRequest"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenResponse"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/sapi/v2/auth/revoke": {
      "post": {
        "tags": [
          "SapiAuth"
        ],
        "summary": "Revoke an access or refresh token.",
        "description": "Immediately invalidates the supplied token so it cannot be used for further authentication.",
        "operationId": "SapiRevokeToken",
        "requestBody": {
          "description": "Token revocation request.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiRevokeRequest"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiRevokeResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/sapi/v2/document/send": {
      "post": {
        "tags": [
          "SapiDocument"
        ],
        "summary": "Submit a document for Peppol delivery",
        "description": "Submit a single electronic business document for processing and delivery via Peppol network.",
        "operationId": "SapiSendDocument",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Unique key for idempotent document submission. Same key + same payload returns the original response. Same key + different payload returns 409 Conflict. Keys expire after 24 hours.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          {
            "name": "X-Peppol-Participant-Id",
            "in": "header",
            "description": "Sender's Peppol Participant ID (organization context). Must match metadata.senderParticipantId in the request body.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "0245:1234567890"
          }
        ],
        "requestBody": {
          "description": "Document send request containing metadata and payload.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiSendDocumentRequest"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorResponse"
                }
              }
            }
          },
          "202": {
            "description": "Document accepted for processing. The returned `providerDocumentId` is the transaction ID that can be used in the `api/v1/out-transaction` endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiSendDocumentResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authorization error (no org access)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - Idempotency-Key has already been used with a different request payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/sapi/v2/document/receive": {
      "get": {
        "tags": [
          "SapiDocument"
        ],
        "summary": "List received documents.",
        "description": "Retrieve a cursor-paginated list of documents received via Peppol network for your organization.\nDocuments are returned in ascending order by received date (oldest first) per SAPI specification.\nUse pageToken for cursor-based pagination. This operation does not imply acknowledgement or state transition.",
        "operationId": "SapiListReceivedDocuments",
        "parameters": [
          {
            "name": "X-Peppol-Participant-Id",
            "in": "header",
            "description": "Receiver's Peppol Participant ID (organization context).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pageToken",
            "in": "query",
            "description": "Opaque cursor token for the next page. Obtained from the nextPageToken field of a previous response.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of records to return per page (1-100, default 20).",
            "schema": {
              "maximum": 100,
              "minimum": 1,
              "type": "integer",
              "format": "int32",
              "default": 20
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by document status. Use RECEIVED for documents awaiting acknowledgement, or ACKNOWLEDGED for confirmed documents.",
            "schema": {
              "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiDocumentStatus"
            }
          }
        ],
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiReceivedDocumentListResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/sapi/v2/document/receive/{documentId}": {
      "get": {
        "tags": [
          "SapiDocument"
        ],
        "summary": "Retrieve the full content of a specific inbound document.",
        "description": "Returns the document metadata and its base64-encoded content.\nClients should acknowledge the document after successful processing.",
        "operationId": "SapiGetReceivedDocument",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "description": "Unique SAPI document identifier returned by `GET /document/receive`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Peppol-Participant-Id",
            "in": "header",
            "description": "Receiver's Peppol Participant ID (organization context).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "payloadFormat",
            "in": "header",
            "description": "Optional payload encoding: \"XML\" (default) or \"Base64\", case-insensitive. \"XML\" returns the\ndocument as raw XML text; \"Base64\" returns the full StandardBusinessDocument, base64-encoded.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sbd",
            "in": "header",
            "description": "Optional. When `true`, returns the full StandardBusinessDocument instead of just the inner\nbusiness document. Has no effect when payloadFormat is \"Base64\".",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiReceivedDocumentDetailResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    },
    "/sapi/v2/document/receive/{documentId}/acknowledge": {
      "post": {
        "tags": [
          "SapiDocument"
        ],
        "summary": "Acknowledge receipt and processing of an inbound document.",
        "description": "Confirms that the client has successfully retrieved and processed the document.\nAcknowledged documents are marked with ACKNOWLEDGED status and will no longer appear\nin subsequent `GET /document/receive` responses when filtering by RECEIVED status.",
        "operationId": "SapiAcknowledgeDocument",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "description": "Unique SAPI document identifier to acknowledge.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Peppol-Participant-Id",
            "in": "header",
            "description": "Receiver's Peppol Participant ID (organization context).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiAcknowledgeResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiAcknowledgeResponse": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "description": "The acknowledged document identifier."
          },
          "status": {
            "type": "string",
            "description": "Acknowledgement status (always \"ACKNOWLEDGED\")."
          },
          "acknowledgedDateTime": {
            "type": "string",
            "description": "Timestamp when the document was acknowledged.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "Response returned by the SAPI `POST /document/receive/{documentId}/acknowledge` endpoint.\nConfirms that a received document has been successfully acknowledged by the client."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiDocumentMetadata": {
        "required": [
          "creationDateTime",
          "documentId",
          "documentTypeId",
          "processId",
          "receiverParticipantId",
          "senderParticipantId"
        ],
        "type": "object",
        "properties": {
          "documentId": {
            "minLength": 1,
            "type": "string",
            "description": "Client-assigned unique document identifier.",
            "example": "INV-2026-0001"
          },
          "documentTypeId": {
            "minLength": 1,
            "type": "string",
            "description": "Peppol Document Type Identifier.",
            "example": "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1"
          },
          "processId": {
            "minLength": 1,
            "type": "string",
            "description": "Peppol Process Identifier.",
            "example": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"
          },
          "senderParticipantId": {
            "minLength": 1,
            "type": "string",
            "description": "Sender's Peppol Participant ID.",
            "example": "0245:1234567890"
          },
          "receiverParticipantId": {
            "minLength": 1,
            "type": "string",
            "description": "Receiver's Peppol Participant ID.",
            "example": "0245:9876543210"
          },
          "creationDateTime": {
            "type": "string",
            "description": "Document creation timestamp in ISO 8601 format.",
            "format": "date-time",
            "example": "2026-02-12T10:30:00Z"
          }
        },
        "additionalProperties": false,
        "description": "Metadata describing a SAPI document (inbound or outbound) per SAPI v1.0 specification.\nUsed in both send requests and receive responses."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiDocumentStatus": {
        "enum": [
          "RECEIVED",
          "ACKNOWLEDGED"
        ],
        "type": "string",
        "description": "SAPI document status values for filtering received documents.\nUsed in GET /document/receive endpoint to filter documents by their processing state."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiError": {
        "required": [
          "category",
          "code",
          "correlation_id",
          "message",
          "retryable"
        ],
        "type": "object",
        "properties": {
          "category": {
            "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorCategory"
          },
          "code": {
            "minLength": 1,
            "type": "string",
            "description": "Machine-readable error code that uniquely identifies the error type within its category\n(e.g., \"invalid_client\", \"document_not_found\")."
          },
          "message": {
            "minLength": 1,
            "type": "string",
            "description": "Human-readable description of the error suitable for logging and diagnostics."
          },
          "details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorDetail"
            },
            "description": "Optional field-level or contextual information to aid diagnostics.\nEach entry describes a specific field that caused the error.",
            "nullable": true
          },
          "retryable": {
            "type": "boolean",
            "description": "Indicates whether the client may retry the request. True for TEMPORARY category errors;\nfalse for all other categories where retry would not change the outcome."
          },
          "correlation_id": {
            "minLength": 1,
            "type": "string",
            "description": "Correlation identifier that links this error to the originating request in server-side logs.\nClients should include this value when reporting issues for support."
          }
        },
        "additionalProperties": false,
        "description": "Represents a single structured error returned by the SAPI interface."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorCategory": {
        "enum": [
          "AUTH",
          "VALIDATION",
          "PROCESSING",
          "TEMPORARY",
          "PERMANENT"
        ],
        "type": "string",
        "description": "Categorizes the type of error returned by the SAPI interface.\nMaps directly to the SAPI protocol error category enum values."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorDetail": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "The field that caused the error.",
            "nullable": true
          },
          "issue": {
            "type": "string",
            "description": "Description of the issue with the field.",
            "nullable": true
          },
          "value": {
            "type": "string",
            "description": "The invalid value that was provided.",
            "nullable": true
          },
          "errorId": {
            "type": "string",
            "description": "PEPPOL rule identifier that failed, e.g. \"PEPPOL-EN16931-R010\".\nPopulated for Schematron validation errors; null otherwise.",
            "nullable": true
          },
          "artifactPath": {
            "type": "string",
            "description": "Path of the validation artifact (XSD or XSLT) that produced this error.\nPopulated for Schematron validation errors; null otherwise.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Field-level error detail within a ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiError."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiErrorResponse": {
        "required": [
          "error"
        ],
        "type": "object",
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiError"
          }
        },
        "additionalProperties": false,
        "description": "Top-level error envelope returned by all SAPI endpoints when a request fails.\nAll non-2xx responses from SAPI endpoints use this structure."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiReceivedDocumentDetailResponse": {
        "type": "object",
        "properties": {
          "metadata": {
            "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiDocumentMetadata"
          },
          "payload": {
            "type": "string",
            "description": "The document content. With the default \"XML\" payloadFormat, this is the raw XML of the inner\nbusiness document (or the full StandardBusinessDocument when the `sbd` header is true).\nWith payloadFormat \"Base64\", this is the full StandardBusinessDocument, base64-encoded."
          },
          "payloadFormat": {
            "type": "string",
            "description": "Format of the payload content: \"XML\" (default) or \"Base64\"."
          }
        },
        "additionalProperties": false,
        "description": "Response returned by the SAPI `GET /document/receive/{documentId}` endpoint per SAPI v1.0 section 8.2.\nContains the full metadata and content of a single inbound document.\nBy default, ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiReceivedDocumentDetailResponse.Payload contains only the inner business document (as identified by\n`metadata.documentTypeId`), extracted from its StandardBusinessDocument envelope. Controlled by the\noptional `payloadFormat` and `sbd` request headers."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiReceivedDocumentListResponse": {
        "type": "object",
        "properties": {
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiDocumentMetadata"
            },
            "description": "List of document metadata (sorted oldest first)."
          },
          "nextPageToken": {
            "type": "string",
            "description": "Opaque cursor token for the next page. Pass as ?pageToken= to fetch the next page. Absent when no more pages.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Response returned by the SAPI `GET /document/receive` endpoint per SAPI v1.0 section 8.1.\nContains a cursor-paginated list of documents received via Peppol network.\nListing does not imply retrieval, processing, or business acceptance."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiRenewRequest": {
        "required": [
          "refresh_token"
        ],
        "type": "object",
        "properties": {
          "refresh_token": {
            "minLength": 1,
            "type": "string",
            "description": "Refresh token previously issued by `POST /auth/token` or a prior `POST /auth/renew` call.\nThe token is validated against the configured Keycloak identity provider."
          }
        },
        "additionalProperties": false,
        "description": "Request body for the SAPI `POST /auth/renew` endpoint.\nExchanges a valid refresh token for a new access (and optionally refresh) token."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiRevokeRequest": {
        "required": [
          "token"
        ],
        "type": "object",
        "properties": {
          "token": {
            "minLength": 1,
            "type": "string",
            "description": "The token string to be revoked."
          },
          "token_type_hint": {
            "type": "string",
            "description": "Optional hint about the type of token being revoked.\nThe only accepted value per the SAPI spec is `refresh_token`.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Request body for the SAPI `POST /auth/revoke` endpoint.\nRequests invalidation of a previously issued access or refresh token."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiRevokeResponse": {
        "required": [
          "success"
        ],
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Indicates whether the token was successfully revoked."
          },
          "message": {
            "type": "string",
            "description": "Human-readable confirmation message.",
            "nullable": true
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp of the revocation in ISO 8601 format.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Response returned by the SAPI `POST /auth/revoke` endpoint on successful token revocation."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiSendDocumentRequest": {
        "required": [
          "metadata",
          "payload",
          "payloadFormat"
        ],
        "type": "object",
        "properties": {
          "metadata": {
            "$ref": "#/components/schemas/ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiDocumentMetadata"
          },
          "payload": {
            "minLength": 1,
            "type": "string",
            "description": "Peppol-conformant business document content (UTF-8 encoded UBL XML). Maximum size: 10 MB.",
            "example": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\"><cbc:ID>INV-2026-0001</cbc:ID></Invoice>"
          },
          "payloadFormat": {
            "minLength": 1,
            "enum": [
              "XML"
            ],
            "type": "string",
            "description": "Format of the payload. Fixed to XML in SAPI v1.0.",
            "example": "XML"
          },
          "payloadEncoding": {
            "type": "string",
            "description": "Optional character encoding of the payload. Defaults to UTF-8.",
            "nullable": true,
            "example": "UTF-8"
          },
          "checksum": {
            "pattern": "^[a-f0-9]{64}$",
            "type": "string",
            "description": "Optional integrity checksum. If provided, MUST be SHA-256 calculated over the exact byte representation\nof the payload before transmission, encoded as lowercase hexadecimal (64 characters).",
            "nullable": true,
            "example": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
          }
        },
        "additionalProperties": false,
        "description": "SAPI v1.0 document submission payload (per section 7.1)."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiSendDocumentResponse": {
        "required": [
          "providerDocumentId",
          "status",
          "timestamp"
        ],
        "type": "object",
        "properties": {
          "providerDocumentId": {
            "minLength": 1,
            "type": "string",
            "description": "Unique document identifier assigned by the Access Point provider.\nIt is the transaction ID that can be used in the `api/v1/out-transaction` endpoints.",
            "example": "123456"
          },
          "status": {
            "minLength": 1,
            "enum": [
              "ACCEPTED",
              "REJECTED"
            ],
            "type": "string",
            "description": "ACCEPTED = technical receipt confirmed. REJECTED = document was not accepted.",
            "example": "ACCEPTED"
          },
          "receivedAt": {
            "type": "string",
            "description": "Timestamp when the document was received. Required when status = ACCEPTED.",
            "format": "date-time",
            "nullable": true
          },
          "timestamp": {
            "type": "string",
            "description": "Response generation timestamp",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "Send Document response (per section 7.2). Synchronous acceptance confirms technical receipt only.\nIt does not imply semantic validation, successful Peppol delivery, or legal effect."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenRequest": {
        "required": [
          "client_id",
          "client_secret",
          "grant_type"
        ],
        "type": "object",
        "properties": {
          "client_id": {
            "minLength": 1,
            "type": "string",
            "description": "Client identifier issued during SAPI registration.\nCorresponds to the Keycloak client_id used for authentication."
          },
          "client_secret": {
            "minLength": 1,
            "type": "string",
            "description": "Client secret issued during SAPI registration.\nCorresponds to the Keycloak client_secret used for authentication."
          },
          "grant_type": {
            "minLength": 1,
            "type": "string",
            "description": "OAuth 2.0 grant type. Must be `client_credentials`.\nOther grant types are not supported by the SAPI protocol.",
            "example": "client_credentials"
          },
          "scope": {
            "type": "string",
            "description": "Optional space-delimited list of requested OAuth 2.0 scopes.\nWhen omitted, the default scopes configured for the client in Keycloak are granted.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Request body for the SAPI `POST /auth/token` endpoint.\nThe SAPI protocol supports the `client_credentials` grant type only.\nCredentials are validated by proxying to the configured Keycloak identity provider."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenResponse": {
        "required": [
          "access_token",
          "expires_in",
          "token_type"
        ],
        "type": "object",
        "properties": {
          "access_token": {
            "minLength": 1,
            "type": "string",
            "description": "JWT access token to be included in the `Authorization: Bearer` header of subsequent SAPI requests.\nTokens are short-lived (typically 15 minutes as per the SAPI specification)."
          },
          "token_type": {
            "minLength": 1,
            "type": "string",
            "description": "Token type. Always `Bearer` for the SAPI protocol.",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Access token lifetime in seconds from the time of issuance.\nClients should use this value to schedule proactive token renewal before expiry.",
            "format": "int32",
            "example": 900
          },
          "scope": {
            "type": "string",
            "description": "Space-delimited list of scopes granted with this token.\nMay be absent when the granted scope matches the requested scope.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Response returned by the SAPI `POST /auth/token` and `POST /auth/renew` endpoints\nupon successful authentication or token renewal."
      },
      "ElektronickyPostar.PublicAPI.Models.Customs.Api.V1.Sapi.SapiTokenStatus": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "Whether the token is currently valid."
          },
          "token_type": {
            "type": "string",
            "description": "Token type. Always `access` for access tokens.",
            "nullable": true
          },
          "client_id": {
            "type": "string",
            "description": "The client ID this token belongs to (`azp` claim).",
            "nullable": true
          },
          "issued_at": {
            "type": "string",
            "description": "When the token was issued (`iat` claim), ISO 8601.",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "description": "When the token will expire (`exp` claim), ISO 8601.",
            "nullable": true
          },
          "expires_in_seconds": {
            "type": "integer",
            "description": "Seconds remaining until expiration.",
            "format": "int32",
            "nullable": true
          },
          "should_refresh": {
            "type": "boolean",
            "description": "True if the token should be refreshed soon (less than 3 minutes remaining).",
            "nullable": true
          },
          "refresh_recommended_at": {
            "type": "string",
            "description": "Recommended time to refresh the token, ISO 8601.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Response returned by the SAPI `GET /auth/token/status` endpoint.\nIndicates the current validity state and metadata of the supplied access token."
      },
      "ElektronickyPostar.Shared.Models.Customs.Error": {
        "required": [
          "category",
          "code",
          "message"
        ],
        "type": "object",
        "properties": {
          "code": {
            "minLength": 1,
            "type": "string",
            "description": "Unique error code"
          },
          "category": {
            "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorCategory"
          },
          "message": {
            "minLength": 1,
            "type": "string",
            "description": "Human-readable description"
          },
          "details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.ErrorDetail"
            },
            "description": "Detail error information."
          }
        },
        "additionalProperties": false,
        "description": "Structure of error message"
      },
      "ElektronickyPostar.Shared.Models.Customs.ErrorCategory": {
        "enum": [
          "AUTH",
          "VALIDATION",
          "PROCESSING",
          "TEMPORARY",
          "PERMANENT"
        ],
        "type": "string"
      },
      "ElektronickyPostar.Shared.Models.Customs.ErrorDetail": {
        "required": [
          "issue"
        ],
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "The field that caused the error",
            "nullable": true
          },
          "issue": {
            "minLength": 1,
            "type": "string",
            "description": "Description of the issue"
          },
          "value": {
            "type": "string",
            "description": "The invalid value that was provided",
            "nullable": true
          },
          "errorId": {
            "type": "string",
            "description": "PEPPOL rule identifier that failed, e.g. \"PEPPOL-EN16931-R010\".\nPopulated for Schematron validation errors; null otherwise.",
            "nullable": true
          },
          "artifactPath": {
            "type": "string",
            "description": "Path of the validation artifact (XSD or XSLT) that produced this error.\nPopulated for Schematron validation errors; null otherwise.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Structure of detail error message"
      },
      "ElektronickyPostar.Shared.Models.Customs.ErrorResponse": {
        "required": [
          "errors",
          "responseId",
          "status",
          "timestamp"
        ],
        "type": "object",
        "properties": {
          "responseId": {
            "type": "string",
            "description": "Response unique identifier",
            "format": "uuid"
          },
          "timestamp": {
            "type": "string",
            "description": "When the error occurred in UTC",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.StatusErrorResponse"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ElektronickyPostar.Shared.Models.Customs.Error"
            },
            "description": "List of errors ocured"
          }
        },
        "additionalProperties": false,
        "description": "Error response for errors with structured informations."
      },
      "ElektronickyPostar.Shared.Models.Customs.StatusErrorResponse": {
        "enum": [
          "REJECTED",
          "ERROR"
        ],
        "type": "string"
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "description": "Retrieve a Keycloak access token (token endpoint: https://keycloak.test.softipdigitalnypostar.sk/realms/postar/protocol/openid-connect/token) and paste it into Swagger Authorize as 'Bearer <token>'.",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "apikey": {
        "type": "apiKey",
        "description": "API Key header",
        "name": "X-API-KEY",
        "in": "header"
      }
    }
  },
  "security": [
    {
      "bearer": [ ]
    },
    {
      "apikey": [ ]
    }
  ],
  "tags": [
    {
      "name": "SapiAuth",
      "description": "SAPI Auth"
    },
    {
      "name": "SapiDocument",
      "description": "SAPI Document"
    }
  ]
}