{
  "openapi": "3.0.3",
  "info": {
    "title": "ERPly Pro PSFE API",
    "version": "0.1.0-dev",
    "description": "REST API for the ERPly Pro PSFE (Proveedor de Servicios de Facturación\nElectrónica) covering issuance, polling, reception, certification, and\noperational status. All write operations follow RFC 9457 (`application/problem+json`)\nfor error responses and are idempotent via the `Idempotency-Key` header.\n\nErrors share the `Problem` schema; consult [`/docs/errors`](https://api.erply.pro/docs/errors)\nfor the full DGII error dictionary.\n",
    "contact": {
      "name": "ERPly Pro Support",
      "url": "https://api.erply.pro/docs/support",
      "email": "soporte@erply.pro"
    },
    "license": {
      "name": "Apache-2.0",
      "url": "https://www.apache.org/licenses/LICENSE-2.0"
    }
  },
  "servers": [
    {
      "url": "https://api.erply.pro",
      "description": "Production"
    },
    {
      "url": "https://staging.api.erply.pro",
      "description": "Staging"
    },
    {
      "url": "https://dev.api.erply.pro",
      "description": "Development sandbox"
    },
    {
      "url": "https://sandbox.api.erply.pro",
      "description": "Public sandbox (rate-limited demo key)"
    }
  ],
  "tags": [
    {
      "name": "Companies",
      "description": "Tenant company configuration."
    },
    {
      "name": "Certification",
      "description": "DGII set-de-pruebas certification orchestration."
    },
    {
      "name": "Issuance",
      "description": "e-CF issuance."
    },
    {
      "name": "Status Polling",
      "description": "DGII validation status retrieval."
    },
    {
      "name": "Communication",
      "description": "Email/PDF delivery."
    },
    {
      "name": "Reception",
      "description": "Inbound documents addressed to the tenant."
    },
    {
      "name": "Approvals",
      "description": "Commercial approvals from buyers."
    },
    {
      "name": "Reporting",
      "description": "Historical totals."
    },
    {
      "name": "System Health",
      "description": "DGII operational status."
    }
  ],
  "security": [
    {
      "bearerAuth": [],
      "apiKeyAuth": []
    }
  ],
  "paths": {
    "/v1/company": {
      "post": {
        "operationId": "createCompany",
        "tags": [
          "Companies"
        ],
        "summary": "Register a new tenant company",
        "description": "Registers a new tenant company within the middleware, securely\nconfiguring its RNC and initial environment variables. The RNC must\nbe valid per DGII registry; the response returns the canonical\n`tenantId` used for all subsequent calls.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCompanyRequest"
              },
              "examples": {
                "demo": {
                  "$ref": "#/components/examples/CreateCompanyDemo"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Company created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/company/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "get": {
        "operationId": "getCompany",
        "tags": [
          "Companies"
        ],
        "summary": "Fetch company configuration",
        "description": "Retrieves configuration for an existing tenant.",
        "responses": {
          "200": {
            "description": "Company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "patch": {
        "operationId": "patchCompany",
        "tags": [
          "Companies"
        ],
        "summary": "Update company configuration",
        "description": "Updates webhook URLs, base64 PDF logo, and other tenant config.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchCompanyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/check-set-tests": {
      "post": {
        "operationId": "createCheckSetTests",
        "tags": [
          "Certification"
        ],
        "summary": "Initiate a DGII set-de-pruebas",
        "description": "Initiates the generation of the mandatory test set required by DGII\nto certify a company as an electronic issuer.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCheckSetTestsRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Test set submitted; poll status with `getCheckSetTests`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckSetTests"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/check-set-tests/{id}/idCompany/{idCompany}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SetTestId"
        },
        {
          "$ref": "#/components/parameters/IdCompany"
        }
      ],
      "get": {
        "operationId": "getCheckSetTests",
        "tags": [
          "Certification"
        ],
        "summary": "Query set-de-pruebas status",
        "description": "Queries the validation status of a submitted test set.",
        "responses": {
          "200": {
            "description": "Test-set status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckSetTests"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/provider-info": {
      "get": {
        "operationId": "getProviderInfo",
        "tags": [
          "Certification"
        ],
        "summary": "PSFE provider info",
        "description": "Retrieves the registered information of the certified PSFE provider (ERPly Pro).",
        "responses": {
          "200": {
            "description": "Provider info.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderInfo"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/invoices": {
      "post": {
        "operationId": "postInvoice",
        "tags": [
          "Issuance"
        ],
        "summary": "Emit an e-CF",
        "description": "Emite un comprobante fiscal electrónico (e-CF) firmado XAdES-BES y lo\nentrega a la DGII. Soporta los tipos 31 (Crédito Fiscal), 32 (Consumo),\n33 (Nota de Débito), 34 (Nota de Crédito), 41 (Compras), y 44 (Régimen\nEspecial). Esta operación es **idempotente** vía el header\n`Idempotency-Key`: la misma clave devuelve la respuesta original\ndurante 24 horas.\n\n---\n\nIssues a signed e-CF (XAdES-BES) and submits it to DGII. Supported\ntypes: 31 (Fiscal Credit), 32 (Consumption), 33 (Debit Note),\n34 (Credit Note), 41 (Purchases), 44 (Special Regime).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceRequest"
              },
              "examples": {
                "type31": {
                  "$ref": "#/components/examples/InvoiceType31"
                },
                "type32": {
                  "$ref": "#/components/examples/InvoiceType32"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replay (matches a previous Idempotency-Key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceAccepted"
                }
              }
            }
          },
          "202": {
            "description": "Submitted; poll status via `getFiscalInvoice` or `getInvoiceByCompany`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://sandbox.api.erply.pro/v1/invoices \\\n  -H \"Authorization: Bearer $JWT\" \\\n  -H \"x-api-key: $API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -d @invoice.json\n"
          },
          {
            "lang": "python",
            "label": "Python (httpx)",
            "source": "import os, uuid, httpx\nr = httpx.post(\n    \"https://sandbox.api.erply.pro/v1/invoices\",\n    headers={\n        \"Authorization\": f\"Bearer {os.environ['JWT']}\",\n        \"x-api-key\": os.environ[\"API_KEY\"],\n        \"Idempotency-Key\": str(uuid.uuid4()),\n    },\n    json=payload,\n    timeout=10,\n)\nr.raise_for_status()\n"
          },
          {
            "lang": "javascript",
            "label": "Node (axios)",
            "source": "import axios from \"axios\";\nimport { randomUUID } from \"node:crypto\";\nawait axios.post(\"https://sandbox.api.erply.pro/v1/invoices\", payload, {\n  headers: {\n    Authorization: `Bearer ${process.env.JWT}`,\n    \"x-api-key\": process.env.API_KEY,\n    \"Idempotency-Key\": randomUUID(),\n  },\n  timeout: 10_000,\n});\n"
          },
          {
            "lang": "python",
            "label": "Odoo (requests)",
            "source": "import uuid, requests\nself.env[\"ir.config_parameter\"].sudo().get_param(\"erplypro.endpoint\")\nrequests.post(\n    f\"{endpoint}/v1/invoices\",\n    headers={\n        \"Authorization\": f\"Bearer {jwt}\",\n        \"x-api-key\": api_key,\n        \"Idempotency-Key\": str(uuid.uuid4()),\n    },\n    json=payload,\n    timeout=10,\n).raise_for_status()\n"
          }
        ]
      }
    },
    "/v1/fiscal-invoices/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InvoiceId"
        }
      ],
      "get": {
        "operationId": "getFiscalInvoice",
        "tags": [
          "Status Polling"
        ],
        "summary": "Get fiscal invoice (Type 31) status",
        "description": "Retrieves the DGII validation status of a Fiscal Credit e-CF (Type 31).",
        "responses": {
          "200": {
            "description": "Fiscal invoice status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl https://sandbox.api.erply.pro/v1/fiscal-invoices/$ID \\\n  -H \"Authorization: Bearer $JWT\" -H \"x-api-key: $API_KEY\"\n"
          },
          {
            "lang": "python",
            "label": "Python (httpx)",
            "source": "import httpx\nr = httpx.get(f\"{base}/v1/fiscal-invoices/{id}\", headers=headers, timeout=5)\n"
          },
          {
            "lang": "javascript",
            "label": "Node (axios)",
            "source": "const r = await axios.get(`${base}/v1/fiscal-invoices/${id}`, { headers });\n"
          },
          {
            "lang": "python",
            "label": "Odoo (requests)",
            "source": "requests.get(f\"{base}/v1/fiscal-invoices/{id}\", headers=headers, timeout=5)\n"
          }
        ]
      }
    },
    "/v1/invoices/{id}/idCompany/{idCompany}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InvoiceId"
        },
        {
          "$ref": "#/components/parameters/IdCompany"
        }
      ],
      "get": {
        "operationId": "getInvoiceByCompany",
        "tags": [
          "Status Polling"
        ],
        "summary": "Get consumption invoice (Type 32) status",
        "description": "Retrieves the validation status of standard Consumption e-CFs (Type 32),\nutilising the company ID for precise multi-tenant context routing.\n",
        "responses": {
          "200": {
            "description": "Invoice status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/fiscal-invoices/notify-by-email": {
      "post": {
        "operationId": "notifyFiscalInvoiceByEmail",
        "tags": [
          "Communication"
        ],
        "summary": "Email XML/PDF containers to recipient",
        "description": "Triggers the middleware to generate XML and PDF containers for a\npreviously emitted e-CF and email them to the recipient.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotifyByEmailRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Notification queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/received-documents": {
      "get": {
        "operationId": "listReceivedDocuments",
        "tags": [
          "Reception"
        ],
        "summary": "List inbound e-CFs",
        "description": "Lists inbound documents where the tenant acts as the buyer.",
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Start of date range (inclusive)."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "End of date range (inclusive)."
          },
          {
            "in": "query",
            "name": "rnc",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{9,11}$"
            },
            "description": "Issuer RNC filter."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated received documents.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceivedDocumentsPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/received-documents/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "get": {
        "operationId": "getReceivedDocument",
        "tags": [
          "Reception"
        ],
        "summary": "Fetch a received document",
        "description": "Retrieves details and the XML payload of an individual received e-CF.",
        "responses": {
          "200": {
            "description": "Received document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceivedDocument"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/reception/dgii/receive-document": {
      "post": {
        "operationId": "receiveDgiiDocument",
        "tags": [
          "Reception"
        ],
        "summary": "Inbound DGII reception webhook",
        "description": "Webhook or manual trigger to handle inbound reception of an e-CF directly from DGII.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReceivedDocument"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Document accepted for processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiveAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/received-commercial-approvals/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InvoiceId"
        }
      ],
      "get": {
        "operationId": "getCommercialApproval",
        "tags": [
          "Approvals"
        ],
        "summary": "Query commercial approval status",
        "description": "Queries the status of commercial approvals from buyers for previously issued e-CFs.",
        "responses": {
          "200": {
            "description": "Commercial-approval status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommercialApproval"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/companies/{id}/emitted-documents": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TenantId"
        }
      ],
      "get": {
        "operationId": "getCompanyEmittedDocuments",
        "tags": [
          "Reporting"
        ],
        "summary": "Total emitted documents for a tenant",
        "description": "Retrieves the historical total of e-CFs successfully emitted by a tenant.",
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Emitted-document totals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmittedDocumentsReport"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/check-dgii-status/idCompany/{idCompany}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/IdCompany"
        }
      ],
      "get": {
        "operationId": "getCheckDgiiStatus",
        "tags": [
          "System Health"
        ],
        "summary": "DGII operational status",
        "description": "Queries the current operational status and maintenance windows of the\nDGII web services so callers can pre-emptively handle transmission\ntimeouts.\n",
        "responses": {
          "200": {
            "description": "DGII status snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DgiiStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl https://sandbox.api.erply.pro/v1/check-dgii-status/idCompany/$ID \\\n  -H \"Authorization: Bearer $JWT\" -H \"x-api-key: $API_KEY\"\n"
          },
          {
            "lang": "python",
            "label": "Python (httpx)",
            "source": "import httpx\nr = httpx.get(f\"{base}/v1/check-dgii-status/idCompany/{id}\", headers=headers, timeout=5)\n"
          },
          {
            "lang": "javascript",
            "label": "Node (axios)",
            "source": "const r = await axios.get(`${base}/v1/check-dgii-status/idCompany/${id}`, { headers });\n"
          },
          {
            "lang": "python",
            "label": "Odoo (requests)",
            "source": "requests.get(f\"{base}/v1/check-dgii-status/idCompany/{id}\", headers=headers, timeout=5)\n"
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Cognito-issued access token (custom Lambda authoriser; 60 s API GW result cache)."
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Per-tenant API key. Production callers MUST also send a signed\n`X-ErplyPro-Signature: t=<unix>,v1=<hmac-sha256-hex>` header (±300 s skew);\nsee `x-erplypro-signature-spec` extension and `/docs/concepts#hmac`.\n",
        "x-erplypro-signature-spec": "https://api.erply.pro/docs/concepts#hmac"
      }
    },
    "parameters": {
      "TenantId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9-]{3,64}$"
        },
        "description": "Canonical tenant ID.",
        "example": "tenant-130000000"
      },
      "IdCompany": {
        "name": "idCompany",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9-]{3,64}$"
        },
        "description": "Multi-tenant routing key.",
        "example": "tenant-130000000"
      },
      "InvoiceId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9-]{1,64}$"
        },
        "description": "Internal invoice identifier returned by `postInvoice`.",
        "example": "encf-1a2b3c4d"
      },
      "ReceivedDocumentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9-]{1,64}$"
        }
      },
      "SetTestId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9-]{1,64}$"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Client-generated UUID. The same key replays the original response for\n24 hours; mismatched payloads return `409 Conflict`.\n"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation failed.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid credentials.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflict with existing resource or idempotency mismatch.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Business-rule violation (e.g., DGII rejected, math discrepancy).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "GatewayTimeout": {
        "description": "Upstream DGII unavailable.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Unhandled server error.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "examples": {
      "CreateCompanyDemo": {
        "summary": "Demo company registration",
        "value": {
          "rnc": "130000000",
          "name": "Demo Compañía S.R.L.",
          "webhookUrl": "https://demo.example/erplypro/webhook"
        }
      },
      "InvoiceType31": {
        "summary": "Type 31 — Fiscal credit",
        "value": {
          "tenantId": "tenant-130000000",
          "ecfType": "31",
          "encf": "E310000000001",
          "issueDate": "2026-05-01",
          "buyer": {
            "rnc": "131000002",
            "name": "Cliente Demo S.R.L."
          },
          "lines": [
            {
              "description": "Servicio de consultoría",
              "qty": 1,
              "unitPrice": 5000.0,
              "taxRate": 0.18
            }
          ],
          "subtotal": 5000.0,
          "tax": 900.0,
          "grandTotal": 5900.0
        }
      },
      "InvoiceType32": {
        "summary": "Type 32 — Consumption (no buyer RNC)",
        "value": {
          "tenantId": "tenant-130000000",
          "ecfType": "32",
          "encf": "E320000000001",
          "issueDate": "2026-05-01",
          "lines": [
            {
              "description": "Producto de consumo",
              "qty": 2,
              "unitPrice": 250.0,
              "taxRate": 0.18
            }
          ],
          "subtotal": 500.0,
          "tax": 90.0,
          "grandTotal": 590.0
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. Returned for every 4xx and 5xx response.",
        "additionalProperties": true,
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "example": "https://errors.api.erply.pro/validation-error"
          },
          "title": {
            "type": "string",
            "example": "Validation failed"
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599,
            "example": 400
          },
          "detail": {
            "type": "string",
            "example": "lines[0].qty must be a positive number"
          },
          "instance": {
            "type": "string",
            "format": "uri",
            "example": "/v1/invoices"
          },
          "traceId": {
            "type": "string",
            "example": "1-65fa7c3a-6f9c2d8e0a1b2c3d4e5f6789"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Company": {
        "type": "object",
        "required": [
          "tenantId",
          "rnc",
          "name",
          "status"
        ],
        "properties": {
          "tenantId": {
            "type": "string",
            "example": "tenant-130000000"
          },
          "rnc": {
            "type": "string",
            "example": "130000000"
          },
          "name": {
            "type": "string",
            "example": "Demo Compañía S.R.L."
          },
          "webhookUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "suspended",
              "pending"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateCompanyRequest": {
        "type": "object",
        "required": [
          "rnc",
          "name"
        ],
        "properties": {
          "rnc": {
            "type": "string",
            "pattern": "^[0-9]{9,11}$"
          },
          "name": {
            "type": "string",
            "minLength": 3,
            "maxLength": 200
          },
          "webhookUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PatchCompanyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "maxLength": 200
          },
          "webhookUrl": {
            "type": "string",
            "format": "uri"
          },
          "logoBase64": {
            "type": "string",
            "format": "byte",
            "description": "Optional base64-encoded PNG/JPEG logo, ≤ 200 KB."
          }
        }
      },
      "CreateCheckSetTestsRequest": {
        "type": "object",
        "required": [
          "tenantId"
        ],
        "properties": {
          "tenantId": {
            "type": "string",
            "example": "tenant-130000000"
          }
        }
      },
      "CheckSetTests": {
        "type": "object",
        "required": [
          "id",
          "tenantId",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "settest-1234"
          },
          "tenantId": {
            "type": "string",
            "example": "tenant-130000000"
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted",
              "processing",
              "accepted",
              "rejected"
            ]
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProviderInfo": {
        "type": "object",
        "required": [
          "providerName",
          "providerRnc",
          "certifiedAt"
        ],
        "properties": {
          "providerName": {
            "type": "string",
            "example": "ERPly Pro S.R.L."
          },
          "providerRnc": {
            "type": "string",
            "example": "130000000"
          },
          "certifiedAt": {
            "type": "string",
            "format": "date"
          },
          "certificationNumber": {
            "type": "string",
            "example": "PSFE-2026-001"
          }
        }
      },
      "InvoiceLine": {
        "type": "object",
        "required": [
          "description",
          "qty",
          "unitPrice",
          "taxRate"
        ],
        "properties": {
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "qty": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "InvoiceBuyer": {
        "type": "object",
        "properties": {
          "rnc": {
            "type": "string",
            "pattern": "^[0-9]{9,11}$"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "InvoiceRequest": {
        "type": "object",
        "required": [
          "tenantId",
          "ecfType",
          "encf",
          "issueDate",
          "lines",
          "subtotal",
          "tax",
          "grandTotal"
        ],
        "properties": {
          "tenantId": {
            "type": "string"
          },
          "ecfType": {
            "type": "string",
            "enum": [
              "31",
              "32",
              "33",
              "34",
              "41",
              "44"
            ]
          },
          "encf": {
            "type": "string",
            "pattern": "^E[0-9]{12}$",
            "example": "E310000000001"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "buyer": {
            "$ref": "#/components/schemas/InvoiceBuyer"
          },
          "parentEncf": {
            "type": "string",
            "nullable": true,
            "description": "Required for credit/debit notes (types 33, 34)."
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/InvoiceLine"
            }
          },
          "subtotal": {
            "type": "number",
            "minimum": 0
          },
          "tax": {
            "type": "number",
            "minimum": 0
          },
          "grandTotal": {
            "type": "number",
            "minimum": 0
          }
        }
      },
      "InvoiceAccepted": {
        "type": "object",
        "required": [
          "docId",
          "status"
        ],
        "properties": {
          "docId": {
            "type": "string",
            "example": "encf-1a2b3c4d"
          },
          "trackId": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "accepted",
              "processing",
              "replay"
            ]
          },
          "encf": {
            "type": "string",
            "example": "E310000000001"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InvoiceStatus": {
        "type": "object",
        "required": [
          "docId",
          "status"
        ],
        "properties": {
          "docId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "accepted",
              "rejected",
              "stuck"
            ]
          },
          "dgiiTrackId": {
            "type": "string",
            "nullable": true
          },
          "dgiiResponseAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "NotifyByEmailRequest": {
        "type": "object",
        "required": [
          "docId",
          "recipientEmail"
        ],
        "properties": {
          "docId": {
            "type": "string"
          },
          "recipientEmail": {
            "type": "string",
            "format": "email"
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            }
          }
        }
      },
      "NotificationAccepted": {
        "type": "object",
        "required": [
          "notificationId"
        ],
        "properties": {
          "notificationId": {
            "type": "string"
          },
          "queuedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ReceivedDocument": {
        "type": "object",
        "required": [
          "id",
          "issuerRnc",
          "encf",
          "receivedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "issuerRnc": {
            "type": "string",
            "pattern": "^[0-9]{9,11}$"
          },
          "encf": {
            "type": "string",
            "pattern": "^E[0-9]{12}$"
          },
          "ecfType": {
            "type": "string"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "xmlBase64": {
            "type": "string",
            "format": "byte",
            "nullable": true
          }
        }
      },
      "ReceivedDocumentsPage": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReceivedDocument"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ReceiveAccepted": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CommercialApproval": {
        "type": "object",
        "required": [
          "docId",
          "status"
        ],
        "properties": {
          "docId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected"
            ]
          },
          "decidedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "EmittedDocumentsReport": {
        "type": "object",
        "required": [
          "tenantId",
          "totalEmitted"
        ],
        "properties": {
          "tenantId": {
            "type": "string"
          },
          "totalEmitted": {
            "type": "integer",
            "minimum": 0
          },
          "byType": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          },
          "from": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date",
            "nullable": true
          }
        }
      },
      "DgiiStatus": {
        "type": "object",
        "required": [
          "tenantId",
          "dgiiOperational"
        ],
        "properties": {
          "tenantId": {
            "type": "string"
          },
          "dgiiOperational": {
            "type": "boolean"
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "nextMaintenanceWindow": {
            "type": "object",
            "nullable": true,
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "polledAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}