{
  "openapi": "3.0.3",
  "info": {
    "title": "BOCP REST API",
    "version": "1.14.0",
    "description": "BOCP ERP integration API. Read and write documents, products, payments, AWBs and more.\n\n**Note on list endpoints:** filters are passed as URL *path segments*, not query strings. The format is `/key:value/key2:value2/`, not `?key=value&key2=value2`. See [Conventions](./conventions/) for the full list of standard filters.",
    "contact": {
      "name": "BOCP Support",
      "url": "https://kb.bocp.eu/"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "/app/rest/v1/{company_id}",
      "description": "Your BOCP account",
      "variables": {
        "company_id": {
          "default": "0",
          "description": "Your BOCP account ID (visible in account settings)."
        }
      }
    }
  ],
  "security": [
    {
      "basicAuth": []
    }
  ],
  "tags": [
    {"name": "Sales",                 "description": "Bonuri de vânzare (sale receipts). The document that moves stock out."},
    {"name": "Invoices",              "description": "Facturi fiscale (fiscal invoices)."},
    {"name": "Proformas",             "description": "Facturi proforme (proforma invoices)."},
    {"name": "Fiscal Receipts",       "description": "Bonuri fiscale (cash register receipts)."},
    {"name": "Payments",              "description": "Încasări și plăți (receipts and payments)."},
    {"name": "Offers",                "description": "Ofertă / comandă produse."},
    {"name": "Receptions (GRN)",      "description": "Facturi de recepționat (goods receipt notes)."},
    {"name": "Stock Transfers",       "description": "Bonuri de transfer între gestiuni."},
    {"name": "Consumptions",          "description": "Bonuri de consum."},
    {"name": "Inventory",             "description": "Inventarieri."},
    {"name": "Products",              "description": "Nomenclator produse — read, patch, delete, stocks, images."},
    {"name": "Product Web Categories","description": "Categorii web pentru produse."},
    {"name": "AWB",                   "description": "Air WayBill — attach couriers' AWBs to sales."},
    {"name": "Courier Pickup Points", "description": "Puncte de ridicare curier."},
    {"name": "Workmanager",           "description": "Fișe service, sesizări și cereri de trimitere în service."},
    {"name": "Pricelists",             "description": "Liste de prețuri — enumerate available pricelists and look up their IDs for use in the product list filter."},
    {"name": "Contacts",              "description": "Agenda de contacte / clienți."},
    {"name": "Subscriptions",         "description": "Șabloane de abonament (subscription templates)."},
    {"name": "Workpoints",            "description": "Puncte de lucru."},
    {"name": "Postcodes",             "description": "Cod poștal (Romania) lookup."},
    {"name": "Attachments",           "description": "Fișiere atașate — upload files and attach them to any document, read attachment metadata, delete attachments. Files are stored on the account's external FTP storage (Setări cont → Setări Avansate → External FTP storage), which must be configured."},
    {"name": "eCommerce Orders",      "description": "Order connector for eCommerce and Dropship integrations. Both connector types use this same endpoint and payload. See the Sending Orders guide for the full integration flow."},
    {"name": "Marketplace",           "description": "Read-only, account-wide view of the webshops and marketplaces orders are imported from. Lists the account's connectors and their IDs, and lists imported orders across ALL of them with the sales receipts, invoices and AWBs generated for each. Use this instead of the per-connector order list when you want one feed of every order regardless of its source."}
  ],
  "components": {
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic Auth with the API user and password issued in the BOCP admin panel (Integrări → BOCP REST API)."
      }
    },
    "schemas": {
      "ResponseEnvelope": {
        "type": "object",
        "description": "All BOCP API responses share this envelope.",
        "properties": {
          "is_error":       {"type": "boolean"},
          "http_code":      {"type": "integer"},
          "data":           {"description": "Endpoint-specific payload (object, array, or null)."},
          "messages":       {"type": "array", "items": {"type": "string"}},
          "request_route":  {"type": "string"},
          "your_ip":        {"type": "string"},
          "your_user":      {"type": "string"},
          "your_method":    {"type": "string", "enum": ["GET","POST","PATCH","PUT","DELETE"]},
          "data_count": {"type": "integer", "description": "Number of items in `data`."},
          "data_page": {"type": "integer", "description": "Present on paginated list responses — the page you are looking at."},
          "data_next_page_exists": {"type": "integer", "description": "Present on paginated list responses — 1 when at least one more page exists, 0 when this is the last page."},
          "data_next_page": {"type": "integer", "description": "Present on paginated list responses — the next page number."},
          "data_next_page_url": {"type": "string", "description": "Present on paginated list responses — the ready-made URL of the next page."}
        }
      },
      "ClientBlock": {
        "type": "object",
        "description": "Included on documents linked to a client.",
        "properties": {
          "client_id":     {"type": "integer"},
          "client_name":   {"type": "string"},
          "client_cui":    {"type": "string"},
          "client_reg_com":{"type": "string"}
        }
      },
      "PaymentRequest": {
        "type": "object",
        "required": ["amount", "payment_type"],
        "properties": {
          "amount":         {"type": "number", "format": "float", "description": "Amount paid. Must be greater than 0."},
          "payment_type":   {"type": "string", "description": "Payment method. See allowed values under `/payments/types/`.", "example": "bank_transfer"},
          "date":           {"type": "string", "format": "date", "description": "Payment date (YYYY-MM-DD). Defaults to today."},
          "currency":       {"type": "string", "description": "ISO currency code. Defaults to the sale's currency.", "example": "RON"},
          "exchange_rate":  {"type": "number", "description": "Rate against RON. Only needed if `currency` differs from RON and you want to override the automatic rate."},
          "bank_account_id":{"type": "integer", "description": "Internal BOCP bank account ID. Relevant for `bank_transfer`."},
          "mentions":       {"type": "string", "description": "Free-form note stored with the payment."},
          "external_ref":   {"type": "string", "description": "Your own reference (order ID, transaction ID). Appended to the payment note."}
        }
      },
      "PaymentResponse": {
        "type": "object",
        "properties": {
          "incasare_id": {"type": "integer"},
          "bv_id":       {"type": "integer"},
          "amount":      {"type": "number"},
          "currency":    {"type": "string"},
          "payment_type":{"type": "string"},
          "date":        {"type": "string", "format": "date"}
        }
      },
      "FiscalReceiptImportItem": {
        "type": "object",
        "required": ["pos_receipt_id", "bv_id", "status_id"],
        "properties": {
          "pos_receipt_id":       {"type": "integer", "description": "Unique receipt ID in your external POS system."},
          "bv_id":                {"type": "integer", "description": "BOCP sale (bon de vânzare) ID."},
          "status_id":            {"type": "integer", "enum": [0,1,2,3], "description": "0=cancelled, 1=not printed, 2=in process, 3=printed."},
          "bf_content":           {"type": "string", "description": "Raw text command sent to the fiscal printer."},
          "printer_response":     {"type": "string", "description": "Raw text response from the fiscal printer."},
          "print_confirmed_ts":   {"type": "string", "format": "date-time"},
          "bf_date":              {"type": "string", "format": "date"},
          "bon_nr":               {"type": "integer", "description": "Fiscal receipt number printed by the ECR."},
          "z_nr":                 {"type": "integer", "description": "Z-report number."},
          "paid_cash":            {"type": "number"},
          "paid_card":            {"type": "number"},
          "paid_foodstamp":       {"type": "number"},
          "paid_voucher":         {"type": "number"},
          "paid_by_voucher_code": {"type": "string"},
          "paid_modern_payment":  {"type": "number"},
          "paid_other_payment":   {"type": "number"},
          "reimport_existing_bf": {"type": "integer", "enum": [0,1], "description": "0 = do not reimport, 1 = reimport by pos_receipt_id."}
        }
      },
      "AWBItem": {
        "type": "object",
        "required": ["sale_id", "awb_nr", "awb_type"],
        "properties": {
          "sale_id":     {"type": "integer", "description": "BOCP sale (bon de vânzare) ID."},
          "awb_nr":      {"type": "string",  "description": "AWB tracking number."},
          "awb_type":    {"type": "string",  "description": "Courier code (e.g. `fancurier`, `innoship`, `dpd`, `gls`, `sameday`, `urgentcargus`)."},
          "awb_subtype": {"type": "string",  "description": "Sub-courier when `awb_type` is a broker (e.g. `innoship` + sub `fancurier`)."},
          "awb_url":     {"type": "string",  "format": "uri", "description": "URL to the AWB PDF or courier tracking page."}
        }
      },
      "SendInServiceRequest": {
        "type": "object",
        "required": ["item_code", "item_name", "client_rec_id", "client_addres_id", "defect_description"],
        "properties": {
          "item_code":                {"type": "string"},
          "item_series":              {"type": "string"},
          "item_name":                {"type": "string"},
          "client_rec_id":            {"type": "integer"},
          "client_addres_id":         {"type": "integer"},
          "defect_description":       {"type": "string"},
          "acquisition_invoice_nr":   {"type": "string"},
          "acquisition_invoice_date": {"type": "string", "format": "date"},
          "supplier_name":            {"type": "string"},
          "invoice_address_id":       {"type": "integer"},
          "aparat_producator_id":     {"type": "integer"},
          "aparat_serie_id":          {"type": "integer"},
          "aparat_model_id":          {"type": "integer"},
          "equipment_type_id":        {"type": "integer"},
          "mentions":                 {"type": "string"},
          "requested_pickup_date":    {"type": "string", "format": "date"},
          "from_b2b":                 {"type": "integer", "enum": [0,1], "description": "1 = triggers pickup AWB generation via B2B path."}
        }
      },
      "PostcodeLookup": {
        "type": "object",
        "properties": {
          "postcode": {"type": "string"}
        }
      },

      "ConnectorInvoiceAddress": {
        "type": "object",
        "required": ["city", "county", "country", "street"],
        "properties": {
          "city":      {"type": "string"},
          "county":    {"type": "string"},
          "country":   {"type": "string", "example": "Romania"},
          "zip":       {"type": "string", "description": "Postal code. May be blank — BOCP will attempt to resolve it."},
          "street":    {"type": "string", "description": "Street name. Put the full address here if you cannot split it into separate fields."},
          "number":    {"type": "string"},
          "building":  {"type": "string"},
          "stair":     {"type": "string"},
          "floor":     {"type": "string"},
          "apartment": {"type": "string"},
          "GLN":       {"type": "string"}
        }
      },

      "ConnectorDeliveryAddress": {
        "type": "object",
        "required": ["city", "county", "country", "street"],
        "properties": {
          "city":                   {"type": "string", "description": "City. For Bucharest, include the sector (e.g. `sector 3`)."},
          "county":                 {"type": "string"},
          "country":                {"type": "string", "example": "Romania"},
          "zip":                    {"type": "string"},
          "street":                 {"type": "string"},
          "number":                 {"type": "string"},
          "building":               {"type": "string"},
          "stair":                  {"type": "string"},
          "floor":                  {"type": "string"},
          "apartment":              {"type": "string"},
          "shipping_contact_name":  {"type": "string"},
          "shipping_contact_phone": {"type": "string"},
          "GLN":                    {"type": "string"}
        }
      },

      "ConnectorOrderClient": {
        "type": "object",
        "required": ["name", "invoice_address", "delivery_address"],
        "properties": {
          "name":                {"type": "string", "description": "Company name, or full name for individuals."},
          "representative":      {"type": "string", "description": "Contact person name when `name` is a company. May be blank."},
          "email":               {"type": "string"},
          "phone":               {"type": "string"},
          "vat_id":              {"type": "string", "description": "VAT / CUI number. Used to look up an existing BOCP contact by fiscal code."},
          "registration_number": {"type": "string", "description": "Trade register number."},
          "client_unique_id":    {"type": "integer", "description": "Partner-side client ID. Recommended — lets BOCP recognise returning clients and update their record."},
          "invoice_address":     {"$ref": "#/components/schemas/ConnectorInvoiceAddress"},
          "delivery_address":    {"$ref": "#/components/schemas/ConnectorDeliveryAddress"}
        }
      },

      "ConnectorOrderItem": {
        "type": "object",
        "required": ["type", "code", "item_name", "item_quantity", "item_price", "item_vat_percent", "item_price_with_vat", "line_value_with_vat"],
        "properties": {
          "type":                         {"type": "string", "enum": ["product", "discount", "service"], "description": "`product` — code must match a product in BOCP. `discount` — negative-price line. `service` — maps to a service code (`SHIPPING` for shipping, `SERVICE` for anything else)."},
          "code":                         {"type": "string", "description": "SKU / product or service code."},
          "ean":                          {"type": "string"},
          "item_name":                    {"type": "string", "description": "Alias `name` also accepted."},
          "item_comments":                {"type": "string", "description": "Per-item notes, personalisation requests, etc."},
          "item_requires_customization":  {"type": "integer", "enum": [0, 1]},
          "item_quantity":                {"type": "number", "description": "Alias `quantity` also accepted."},
          "item_price":                   {"type": "number", "description": "Unit price without VAT. Alias `price` also accepted."},
          "item_vat_percent":             {"type": "number", "description": "VAT rate, e.g. `19`."},
          "item_price_with_vat":          {"type": "number"},
          "line_value_with_vat":          {"type": "number", "description": "`item_quantity × item_price_with_vat`."},
          "discounted_price_with_vat":    {"type": "number", "description": "Optional. Effective unit price after discount. Triggers discount application when less than `item_price_with_vat` and `line_discount_value_with_vat` > 0."},
          "line_discount_value_with_vat": {"type": "number", "description": "Optional. Total line discount (with VAT)."}
        }
      },

      "ConnectorOrderPayment": {
        "type": "object",
        "required": ["payment_method", "payment_status", "payment_date", "payment_amount", "payment_currency", "payment_reference"],
        "properties": {
          "payment_method":    {"type": "string", "enum": ["bank_transfer", "card", "paypal", "credit"], "description": "The legacy field name `type` is still accepted."},
          "payment_status":    {"type": "string", "enum": ["confirmed", "pending", "refunded"], "description": "`pending` payments are NOT recorded as money on the order — no transaction is created and they do not count toward `paid_amount`. A pending entry is still read as evidence of the chosen payment method when the order-level `payment_method` is absent. Resend the order with `confirmed` once the money actually arrives."},
          "payment_date":      {"type": "string", "description": "Date-time of the payment, e.g. `2024-01-03 13:12:02`."},
          "payment_amount":    {"type": "number"},
          "payment_currency":  {"type": "string", "description": "ISO currency code, e.g. `RON`, `EUR`, `USD`."},
          "payment_reference": {"type": "string", "description": "Unique identifier per payment within this order. Prevents duplicate recording on PUT resend."},
          "payment_comments":  {"type": "string"}
        }
      },

      "ConnectorOrderRequest": {
        "type": "object",
        "required": ["order_unique_id", "order_status", "shipping_method", "order_currency_code", "client", "items"],
        "properties": {
          "order_unique_id":     {"type": "string", "description": "Partner-side order ID. **Must match the `{order_id}` URL segment.**"},
          "order_reference":     {"type": "string", "description": "Human-readable reference. May be blank."},
          "order_date":          {"type": "string", "format": "date", "example": "2024-01-01"},
          "order_updated_at":    {"type": "string", "description": "Last-modified timestamp of the order in your system. Used to update `last_changed_ts` in BOCP."},
          "order_status":        {"type": "string", "description": "Updateable. Free-text status from your system (e.g. `new`, `processing`, `shipped`)."},
          "order_cancelled":     {"type": "integer", "enum": [0, 1], "description": "Updateable. Set to `1` to cancel the order in BOCP."},
          "order_mentions":      {"type": "string"},
          "order_currency_code": {"type": "string", "description": "ISO currency code for all item prices. Must be configured in the BOCP account."},
          "shipping_method":     {"type": "string", "enum": ["courier", "locker", "post", "own_fleet", "digital", "personal_pickup"], "description": "How the order reaches the customer. Drives AWB auto-generation: `personal_pickup` suppresses it, everything else is treated as a shipment. Previously accepted but ignored, so a pickup order still got a courier AWB. An unrecognised value falls back to `courier` and is reported back in the response notices."},
          "cod_amount":          {"type": "number", "description": "Cash-on-delivery amount collected by the partner. Required for correct AWB generation when COD applies. Send `0` if no COD."},
          "cod_currency":        {"type": "string", "example": "RON"},
          "payment_method":      {"type": "string", "enum": ["bank_transfer", "card", "paypal", "credit"], "description": "The payment method the customer SELECTED, independent of whether any money has arrived. Always send it, on every order, paid or not — `payments` carries the actual money, this carries the intent, and they are different facts. It sets the order's payment type in BOCP, which is what the payment-keyed automations read (notably proforma auto-issue). A bank-transfer order awaiting payment has an empty `payments` array, so this is the only field that can express it; `card` with no payment means the card attempt did not go through. Only when this is omitted AND the order has no payment method yet does BOCP fall back to the first `payments` entry."},
          "client":              {"$ref": "#/components/schemas/ConnectorOrderClient"},
          "items":               {"type": "array", "items": {"$ref": "#/components/schemas/ConnectorOrderItem"}},
          "payments":            {"type": "array", "items": {"$ref": "#/components/schemas/ConnectorOrderPayment"}, "description": "Payments forwarded through the partner. Omit or send empty if none yet — resend the full order when payment is confirmed. Only `confirmed` payments count toward `paid_amount` in BOCP."}
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid Basic Auth credentials.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResponseEnvelope"}}}
      },
      "BadRequest": {
        "description": "Request is malformed or a required field is missing / invalid.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResponseEnvelope"}}}
      },
      "NotFound": {
        "description": "The requested resource does not exist.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResponseEnvelope"}}}
      },
      "SuccessEnvelope": {
        "description": "OK — response uses the shared envelope.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResponseEnvelope"}}}
      }
    }
  },
  "paths": {

    "/sales/list/": {
      "get": {
        "tags": ["Sales"],
        "summary": "List sale receipts (bonuri de vânzare)",
        "description": "Returns a paginated list of sale receipts. In BOCP the sale receipt is the document that moves stock out. It can be issued with or without an invoice, with or without a fiscal receipt.\n\n**Filters are passed as URL path segments.** Example: `/sales/list/year:2024/modifiedafter:2024-06-01%2000:00:00/page:2/`.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `skipcancelled`, `page`.\n\n**Include:** `items`, `transactions`, `invoiced`.\n\n**Exclude:** `added-by-api`, `invoiced`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },
    "/sales/{bv_id}/payment/": {
      "get": {
        "tags": ["Sales", "Payments"],
        "summary": "Payment schema for a sale",
        "description": "Returns the JSON schema and allowed payment types for creating a payment on the given sale. Useful for building your integration UI.",
        "parameters": [
          {"in": "path", "name": "bv_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP ID of the sale."}
        ],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "post": {
        "tags": ["Sales", "Payments"],
        "summary": "Register a payment on a sale",
        "description": "Records an incasso (or payment) against an existing sale receipt.\n\n**Notes:**\n- You can register multiple payments on the same sale (partial payments, installments).\n- `incasare_id` in the response identifies the payment inside `/payments/list/`.\n- `payment_type` accepts the string codes returned by `/payments/types/`.",
        "parameters": [
          {"in": "path", "name": "bv_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP ID of the sale."}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/PaymentRequest"},
              "example": {"amount": 250.0, "payment_type": "bank_transfer", "date": "2026-05-20", "currency": "RON", "mentions": "OP 1234 BT", "external_ref": "TXN-98765"}
            }
          }
        },
        "responses": {
          "200": {"description": "Payment registered.", "content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/ResponseEnvelope"}, {"type": "object", "properties": {"data": {"$ref": "#/components/schemas/PaymentResponse"}}}]}}}},
          "400": {"description": "Missing/invalid fields, or the sale is cancelled.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResponseEnvelope"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/invoices/list/": {
      "get": {
        "tags": ["Invoices"],
        "summary": "List fiscal invoices",
        "description": "Returns a paginated list of fiscal invoices.\n\n**Filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `skipcancelled`, `documentnr`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/proformas/list/": {
      "get": {
        "tags": ["Proformas"],
        "summary": "List proforma invoices",
        "description": "Returns a paginated list of proforma invoices.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.\n\n**Custom filters:**\n- `for_abon_tpl_id` — proformas issued from a specific subscription template.\n\n**Include:** `payment_link`.\n\n**Exclude:** `cancelled` (exclude cancelled proformas).",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/fiscalreceipts/list/": {
      "get": {
        "tags": ["Fiscal Receipts"],
        "summary": "List fiscal receipts (bonuri fiscale)",
        "description": "Returns a paginated list of fiscal receipts issued from the ECR (electronic cash register).\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.\n\n**Custom filters:**\n- `bv_id` — receipts attached to one sale\n- `raport_z_nr` — receipts in one Z report\n- `pl_id` — receipts issued at one workpoint\n- `statie_id` — receipts from one cash register station\n- `status_id` — `0`=cancelled, `1`=not printed, `2`=in process, `3`=printed\n\n**Include:** `items`, `printer_responses`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/payments/list/": {
      "get": {
        "tags": ["Payments"],
        "summary": "List payments and receipts",
        "description": "Returns a paginated list of payments and receipts (încasări și plăți).\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.\n\n**Custom filter — `allocation_status`:**\n- `any` — all payments\n- `not_allocated` — only unallocated\n- `some_allocated` — has some allocation, not fully\n- `fully_allocated` — fully allocated\n- `has_allocated` — has at least one allocation\n\n**Include:** `allocations`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },
    "/payments/types/": {
      "get": {
        "tags": ["Payments"],
        "summary": "List payment methods",
        "description": "Enumerates the predefined payment method codes accepted by `payment_type` fields in other endpoints (e.g. `POST /sales/{bv_id}/payment/`). Includes `bank_transfer`, `cash`, `card`, `voucher`, `foodstamps`, `modern_payment`, and any custom types defined in your account.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },
    "/payments/fiscalreceipt/": {
      "get": {
        "tags": ["Payments", "Fiscal Receipts"],
        "summary": "Schema for POSTing fiscal receipts",
        "description": "Returns a plain-text schema showing the expected shape of the POST payload for attaching one or more fiscal receipts to sales.",
        "responses": {
          "200": {"description": "Schema description (text/html).", "content": {"text/html": {}}}
        }
      },
      "post": {
        "tags": ["Payments", "Fiscal Receipts"],
        "summary": "Attach fiscal receipts to sales (batch)",
        "description": "Registers one or more fiscal receipts and their payments against existing sales.\n\n**Effect on the sale:**\n- Deletes all existing payments (`incasari`) on the sale.\n- Creates new payments based on the fiscal receipt totals (cash, card, voucher, food stamps, modern, other).\n- If an invoice exists on the sale, it must have the same date as the fiscal receipt.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["receipts"],
                "properties": {
                  "receipts": {
                    "type": "array",
                    "items": {"$ref": "#/components/schemas/FiscalReceiptImportItem"}
                  }
                }
              },
              "example": {
                "receipts": [
                  {
                    "pos_receipt_id": 1,
                    "bv_id": 198,
                    "status_id": 3,
                    "bf_date": "2025-05-05",
                    "bon_nr": 145,
                    "z_nr": 2,
                    "paid_cash": 10.0,
                    "paid_card": 5.0,
                    "paid_foodstamp": 14.0,
                    "paid_voucher": 44.0,
                    "paid_modern_payment": 30.0,
                    "paid_other_payment": 0.0,
                    "print_confirmed_ts": "2025-05-05 12:00:00",
                    "reimport_existing_bf": 0
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/offers/list/": {
      "get": {
        "tags": ["Offers"],
        "summary": "List product orders / offers (comenzi produse)",
        "description": "Returns a paginated list of `comenzi_produse` — the offer / product order table.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },

    "/grn/list/": {
      "get": {
        "tags": ["Receptions (GRN)"],
        "summary": "List Goods Receipt Notes (recepții)",
        "description": "Returns a paginated list of GRN (recepții). Each row includes the linked client (supplier) block.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `skipcancelled`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },
    "/grn/add/": {
      "post": {
        "tags": ["Receptions (GRN)"],
        "summary": "Add a received invoice for processing",
        "description": "**Not fully implemented yet.** Reserved endpoint for pushing a received invoice into the receptions queue.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },

    "/stocktransfers/list/": {
      "get": {
        "tags": ["Stock Transfers"],
        "summary": "List stock transfer receipts (bonuri de transfer)",
        "description": "Returns a paginated list of inter-workpoint stock transfers.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },

    "/consumptions/list/": {
      "get": {
        "tags": ["Consumptions"],
        "summary": "List consumption receipts (bonuri de consum)",
        "description": "Returns a paginated list of consumption receipts.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },

    "/inventory/list/": {
      "get": {
        "tags": ["Inventory"],
        "summary": "List inventories (inventarieri)",
        "description": "Returns a paginated list of inventory documents.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },
    "/inventory/view/{inventory_id}/": {
      "get": {
        "tags": ["Inventory"],
        "summary": "View inventory contents",
        "description": "Returns the stock rows scoped to a single inventory document (workpoint and, if set, sub-management).",
        "parameters": [
          {"in": "path", "name": "inventory_id", "required": true, "schema": {"type": "integer"}}
        ],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/product/list/": {
      "get": {
        "tags": ["Products"],
        "summary": "List products (product variants)",
        "description": "Returns a paginated list of product variants from the catalogue.\n\n**Filters:**\n- Standard filters (`year`, `page`, `modifiedafter`, ...).\n- `format` — `json` (default), `xml`, `csv`.\n- `code` — product code or barcode (URL-encoded). Also `codeb64:...` (base64).\n- `manufacturer` — filter by manufacturer name.\n- `visibility` — `all` (default), `partner`, `published`.\n- `show_deleted` — `0` or `1`.\n- `magazia_id` — workpoint ID for stock context.\n- `cont` — `sellable` (default) or `all` (subgestiune scope).\n- `product_id` — BOCP variant ID.\n- `fm_id` — fișă de magazie ID.\n- `pricelist_id` — filter to only products that have a rule in this pricelist, and return pricelist prices. **Requires `magazia_id`.** Price fields in the response depend on the workpoint type: en-gros workpoints return the pricelist price in `pret_vanzare` / `pret_vanzare_cu_tva` (discounted fields become `null`); en-detail workpoints return the normal retail price in `pret_vanzare` and the pricelist price in `pret_vanzare_discounted` / `pret_vanzare_cu_tva_discounted`. Use `/pricelists/list/` to enumerate available pricelist IDs.\n\n**Include:** `images`, `properties`, `last_aquisition_price_ron`, `last_purchase_price_currency`.\n\n**Exclude:** `deleted`, `archived`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },
    "/product/stocks/": {
      "get": {
        "tags": ["Products"],
        "summary": "List product stocks",
        "description": "Same filter set as `/product/list/`, but the response focus is on stock levels rather than catalogue metadata.\n\n**Include:** `last_purchase_price_currency`, `onstock`, `aquisition_value_ron`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },
    "/product/{product_id}/": {
      "get": {
        "tags": ["Products"],
        "summary": "Get product variant details",
        "description": "Returns the full detail record for one product variant.",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "patch": {
        "tags": ["Products"],
        "summary": "Partial update on a product variant",
        "description": "Send only the fields you want to change. Field names match those returned by the GET call. Combined fields (e.g. price + currency) must be sent together.",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "requestBody": {
          "required": true,
          "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true}}}
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "put": {
        "tags": ["Products"],
        "summary": "Replace product variant fields",
        "description": "PUT variant of the product update. Prefer PATCH for partial updates.",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "requestBody": {
          "required": true,
          "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true}}}
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "delete": {
        "tags": ["Products"],
        "summary": "Delete a product variant",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },
    "/product/{product_id}/images/": {
      "get": {
        "tags": ["Products"],
        "summary": "List attached images for a product",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "delete": {
        "tags": ["Products"],
        "summary": "Delete ALL images from a product",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },
    "/product/{product_id}/image/": {
      "post": {
        "tags": ["Products"],
        "summary": "Attach an image from an uploaded file",
        "description": "Uploads and attaches a new image to the product. Multipart form-data body.\n\nFields: `file` (binary), `title`, `description`, `alt_text`.",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file":        {"type": "string", "format": "binary"},
                  "title":       {"type": "string"},
                  "description": {"type": "string"},
                  "alt_text":    {"type": "string"}
                }
              }
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "put": {
        "tags": ["Products"],
        "summary": "Attach an image from a URL",
        "description": "Attaches an image to the product from a remote URL. Depending on the catalog setting the URL is either stored as-is or downloaded to the local CDN.",
        "parameters": [{"in": "path", "name": "product_id", "required": true, "schema": {"type": "integer"}}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["URL"],
                "properties": {
                  "URL":         {"type": "string", "format": "uri"},
                  "title":       {"type": "string"},
                  "description": {"type": "string"},
                  "alt_text":    {"type": "string"}
                }
              },
              "example": {"URL": "https://example.com/img.jpg", "title": "Hero", "alt_text": "Product front"}
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },
    "/product/add/": {
      "post": {
        "tags": ["Products"],
        "summary": "Create a new product (not implemented yet)",
        "description": "Reserved. This endpoint is documented for planning purposes; it is not implemented in the current release.",
        "responses": {
          "501": {"description": "Not implemented."}
        }
      }
    },

    "/productwebcategories/{category_id}/image/": {
      "post": {
        "tags": ["Product Web Categories"],
        "summary": "Attach/replace an image on a web category",
        "description": "Uploads an image and sets it on the given web category. Replaces the existing image if present.\n\nFields: `file` (binary), `title`, `description`, `alt_text`.",
        "parameters": [{"in": "path", "name": "category_id", "required": true, "schema": {"type": "integer"}}],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file":        {"type": "string", "format": "binary"},
                  "title":       {"type": "string"},
                  "description": {"type": "string"},
                  "alt_text":    {"type": "string"}
                }
              }
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },
    "/productwebcategories/{category_id}/": {
      "delete": {
        "tags": ["Product Web Categories"],
        "summary": "Delete a web category",
        "parameters": [{"in": "path", "name": "category_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/awb/list/": {
      "get": {
        "tags": ["AWB"],
        "summary": "List AWBs",
        "description": "Returns a paginated list of AWBs.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.\n\n**Custom filter:**\n- `documentnr` — filter by AWB tracking number.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },
    "/awb/add/": {
      "get": {
        "tags": ["AWB"],
        "summary": "Sample body for POST /awb/add/",
        "description": "Returns a schema/example payload for the POST call and lists the accepted courier codes.",
        "responses": {"200": {"description": "Schema/example (text/html)."}}
      },
      "post": {
        "tags": ["AWB"],
        "summary": "Register / attach AWBs to sales (batch)",
        "description": "Attaches one or more already-issued AWBs to existing sales. Use when the AWB was generated in a third-party system (broker, courier portal) and you want BOCP to know about it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["awbs"],
                "properties": {
                  "awbs": {"type": "array", "items": {"$ref": "#/components/schemas/AWBItem"}}
                }
              },
              "example": {
                "awbs": [
                  {"sale_id": 3, "awb_nr": "11111",  "awb_type": "fancurier", "awb_subtype": "",         "awb_url": "https://domain.ro/url"},
                  {"sale_id": 4, "awb_nr": "222222", "awb_type": "innoship",  "awb_subtype": "fancurier","awb_url": "https://domain.ro/url"}
                ]
              }
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },
    "/awb/{awb_id}/": {
      "delete": {
        "tags": ["AWB"],
        "summary": "Delete an AWB attached to a sale",
        "parameters": [{"in": "path", "name": "awb_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },
    "/awb/issue/": {
      "get": {
        "tags": ["AWB"],
        "summary": "Placeholder (not implemented)",
        "description": "Planned endpoint for issuing an AWB from BOCP based on a sale. Not implemented yet.",
        "responses": {"501": {"description": "Not implemented."}}
      }
    },

    "/courierpickuppoints/list/{courier_id}/": {
      "get": {
        "tags": ["Courier Pickup Points"],
        "summary": "List pickup points for one courier",
        "description": "Returns the imported pickup point directory for a specific courier connector (DPD, FanCourier, SameDay, etc.). Values are cached locally in BOCP; refreshed from the courier via internal jobs.",
        "parameters": [
          {"in": "path", "name": "courier_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP courier connector ID."}
        ],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },

    "/workmanager/servicefiles/": {
      "get": {
        "tags": ["Workmanager"],
        "summary": "List service files (fișe service)",
        "description": "Returns a paginated list of service files.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.\n\n**Include:** `clientrecommendations`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "401": {"description": "Workmanager module is not active in your account."}
        }
      }
    },
    "/workmanager/servicefile/{fisa_id}/": {
      "get": {
        "tags": ["Workmanager"],
        "summary": "View a single service file",
        "description": "Returns the full record for one service file, including its associated documents.",
        "parameters": [{"in": "path", "name": "fisa_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },
    "/workmanager/servicerequests/": {
      "get": {
        "tags": ["Workmanager"],
        "summary": "List service requests (sesizări)",
        "description": "Returns a paginated list of service requests raised by clients.\n\n**Filters are passed as URL path segments.** Example: `/workmanager/servicerequests/datefrom:2026-08-01/page:2/`.\n\n**Standard filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`. Date filters apply to the date the request was registered.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },
    "/workmanager/sendinrequest/": {
      "post": {
        "tags": ["Workmanager"],
        "summary": "Create a send-in service request",
        "description": "Creates a `cerere trimitere în service` — a client-side request to send an item in for service. Optionally triggers pickup AWB generation via the B2B path.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/SendInServiceRequest"},
              "example": {
                "item_code": "ABC123",
                "item_series": "ZXCV56789",
                "item_name": "TV Set",
                "client_rec_id": 1,
                "client_addres_id": 2,
                "defect_description": "Blank screen",
                "acquisition_invoice_nr": "FA23",
                "acquisition_invoice_date": "2024-01-01",
                "supplier_name": "SC Furnizor SRL",
                "invoice_address_id": -1,
                "aparat_producator_id": -1,
                "equipment_type_id": -1,
                "mentions": "Please pick up between 09:00-17:00",
                "requested_pickup_date": "2026-07-15",
                "from_b2b": 1
              }
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },

    "/contacts/list/": {
      "get": {
        "tags": ["Contacts"],
        "summary": "List clients",
        "description": "Returns a paginated list of clients (`agendă firme` rows where `is_client = 1`).\n\n**Standard filters:** `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page` (date filters use `lastupdated`).\n\n**Include:**\n- `pricelist` — adds `pricelist_id` and `pricelist_name` to each row. Falls back to the client's group pricelist when the contact has no direct pricelist.\n- `address` — adds `addresses[]` array (all non-deleted delivery/invoice addresses).\n- `contacts` — adds `contacts[]` array (linked contact persons).\n- `banks` — adds `bank_accounts[]` array (linked bank accounts with currency).\n\nExample: `/contacts/list/modifiedafter:2024-01-01/include:address,pricelist/`",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/contacts/{contact_id}/": {
      "get": {
        "tags": ["Contacts"],
        "summary": "Get contact details",
        "description": "Returns the details of one contact (`agendă firme` row), formatted using the shared client-data helper.\n\n**Include:**\n- `pricelist` — adds `pricelist_id` and `pricelist_name` to the response. Falls back to the client's group pricelist.",
        "parameters": [{"in": "path", "name": "contact_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/subscriptions/list/": {
      "get": {
        "tags": ["Subscriptions"],
        "summary": "List subscription templates with prices",
        "description": "Returns the account's subscription templates (deleted ones are never listed), ordered by the template's display order, 250 per page.\n\nEach row includes: `abon_id`, `name` (the name used on invoices), `public_name` (the public label; equals `name` when no public name is set), `description`, `price_ex_tva`, `price_w_tva`, `vat_percent`, `currency`, `period_in_months`, `um`, `group`, `is_published` (1 = published for online ordering). With `raises_limit`, each row also carries `limit_increase`.\n\n**Filters (URL path segments):**\n- `published:1` only published templates, `published:0` only unpublished ones. Omit it to list both.\n- `raises_limit:{limit_key}` only published templates that raise the given account limit and are offered for it. Implies `published:1`.\n- `cached:1` serve from a 1 hour server-side cache. Honoured only together with `raises_limit` alone on page 1; ignored otherwise. The `activations` include is never served from cache.\n- `include:activations` (BOCP vendor account only; ignored on any other account) adds an `activations` object to each row: `modules` [{`id`, `code`, `title`}], `submodules` [{`id`, `code`, `name`}], `pilots` [{`id`, `text_id`, `name`, `is_free`, `job_description`}] and `limits` {limit_key: integer}.\n- Standard: `id`, `minid`, `page`.\n\nAn empty result returns 404.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/subscriptions/alignment/": {
      "get": {
        "tags": ["Subscriptions"],
        "summary": "Compute an aligned first period for a subscription template",
        "description": "BOCP vendor account only. Computes what a client would pay to start a subscription template now and have it renew together with the client's existing subscription.\n\nThe reference subscription is the client's active, auto-renewed, non-expired, paid subscription for the given BOCP account, in the template's currency, with the earliest next invoicing date on or after today. The first period runs from today to that date (`end_date`, exclusive): as many whole periods of `period_months` as fit, counted backwards from `end_date`, plus the remaining days, priced per day as monthly price × discount / days in the current month. Discount: 6 months 5%, 12 months 10%.\n\n**Filters (URL path segments):**\n- `abon_tpl_id:{id}` required, the template.\n- `for_client_id:{id}` required, the client contact; must be the billing contact of `for_account_id`.\n- `for_account_id:{id}` required, the client's BOCP account.\n- `abon_units:{n}` optional, 1 to 100, default 1.\n\nResponse fields: `available` (0/1), `end_date`, `period_months`, `reference_name`, `days`, `whole_periods`, `partial_amount_ex_tva`, `amount_ex_tva`, `amount_w_vat`, `unit_price_ex_tva` (discounted monthly price for future renewals). When `available` is 0 the reason is in `messages`. Templates invoiced on a fixed day of the month are never aligned.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/subscriptions/{abon_tpl_id}/": {
      "get": {
        "tags": ["Subscriptions"],
        "summary": "Get subscription template details",
        "description": "Returns the details of one subscription template: name, public_name (falls back to name), description, price with/without VAT, period in months, currency, unit, group, VAT %.\n\n`include:activations` (BOCP vendor account only) adds the same `activations` object as the list.\n\n`include:detailed_description` adds `detailed_description`: the HTML offer presentation of the template (tags p, ul, ol, li, strong, em, a, h3). It is filled only for orderable templates (published, or on the BOCP vendor account the activation template of a submodule or pilot); for any other template it is an empty string. Never cached. Treat it as untrusted HTML and sanitize before rendering.",
        "parameters": [{"in": "path", "name": "abon_tpl_id", "required": true, "schema": {"type": "integer"}}],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/workpoints/list/": {
      "get": {
        "tags": ["Workpoints"],
        "summary": "List workpoints (puncte de lucru)",
        "description": "Returns all workpoints defined in the account.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"}
        }
      }
    },

    "/pricelists/list/": {
      "get": {
        "tags": ["Pricelists"],
        "summary": "List pricelists",
        "description": "Returns a paginated list of pricelists defined in the account.\n\nEach row includes: `bocp_id`, `name`, `deleted`, `item_count` (number of product rules), `currency`, `currency_id`, `flag_used_for_sites`, `last_changed_ts`, `rec_ts`.\n\nUse the returned `bocp_id` as the `pricelist_id` filter value on `/product/list/`.\n\n**Standard filters:** `modifiedafter`, `modifiedthrough`, `id`, `minid`, `page`.",
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/postcodes/": {
      "get": {
        "tags": ["Postcodes"],
        "summary": "Look up a Romanian postal code",
        "description": "Looks up a Romanian postal code by county / city / address. Filters are passed as URL path segments and are **all required** except `country`.\n\nIf the city is `Bucuresti`, the address should include the sector (e.g. `str Nume Nr 2 sector 3`).\n\nSpaces may be replaced with `_` or `-`. Diacritics are optional.",
        "parameters": [
          {"in": "path", "name": "country", "required": false, "schema": {"type": "string", "default": "Romania"}, "description": "Filter `country:Romania` (or `country:RO`). Defaults to Romania."},
          {"in": "path", "name": "county",  "required": true,  "schema": {"type": "string"}, "description": "Filter `county:...` — county name or short code (MS, B, CJ, CT, ...)."},
          {"in": "path", "name": "city",    "required": true,  "schema": {"type": "string"}, "description": "Filter `city:...` — city name."},
          {"in": "path", "name": "address", "required": true,  "schema": {"type": "string"}, "description": "Filter `address:...` — street name and number."}
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/ResponseEnvelope"}, {"type": "object", "properties": {"data": {"$ref": "#/components/schemas/PostcodeLookup"}}}]}}}
          },
          "400": {"$ref": "#/components/responses/BadRequest"}
        }
      }
    },

    "/attachments/upload/": {
      "post": {
        "tags": ["Attachments"],
        "summary": "Upload a file and attach it to a document",
        "description": "Uploads a file and attaches it to an existing BOCP document. Multipart form-data body.\n\nThe file is stored on the account's **external FTP storage** (Setări cont → Setări Avansate → External FTP storage); a `503` is returned when that storage is not configured or unreachable.\n\n**Notes:**\n- server-executable file extensions (php, cgi, sh, asp, ...) are rejected with `400`\n- large images are automatically resized and some document types are compressed by the storage layer — always download the file through its BOCP access URL, never directly from the FTP host\n- `GET /attachments/upload/` returns this documentation inline",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file", "doctype", "doc_id"],
                "properties": {
                  "file":               {"type": "string", "format": "binary", "description": "The file to upload."},
                  "doctype":            {"type": "integer", "description": "BOCP doctype of the document to attach to (must be > 0)."},
                  "doc_id":             {"type": "integer", "description": "`bocp_id` of the document to attach to (must be > 0)."},
                  "client_id":          {"type": "integer", "description": "BOCP contact id the file belongs to. Default -1.", "default": -1},
                  "title":              {"type": "string",  "description": "Display title. Defaults to the sanitized file name."},
                  "is_public":          {"type": "integer", "enum": [0, 1], "description": "1 = public download link, 0 = logged-in access only. Default 1.", "default": 1},
                  "attachment_type_id": {"type": "integer", "description": "Attachment type id. Default -1.", "default": -1}
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK — `data.attached_file_rec_ids` holds the new attachment id(s).",
            "content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/ResponseEnvelope"}, {"type": "object", "properties": {"data": {"type": "object", "properties": {"attached_file_rec_ids": {"type": "array", "items": {"type": "integer"}}}}}}]}}}
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "503": {"description": "The account's external FTP storage is not configured or is unreachable — retry later."}
        }
      }
    },

    "/attachments/{attachment_id}/": {
      "get": {
        "tags": ["Attachments"],
        "summary": "Attachment metadata",
        "description": "Returns the attachment's metadata: `bocp_id`, `doctype`, `doc_id`, `client_id`, `title`, `original_filename`, `filesize`, `is_public`, `attachment_type_id`, `uploaded_ts`.",
        "parameters": [
          {"in": "path", "name": "attachment_id", "required": true, "schema": {"type": "integer"}}
        ],
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      },
      "delete": {
        "tags": ["Attachments"],
        "summary": "Delete an attachment",
        "description": "Deletes the attachment: the file on the external FTP storage, the metadata row and its download secret key.\n\nWhen the external FTP storage is unreachable the attachment is kept and a `503` is returned — retry later. Requires the `DELETE` HTTP method to be allowed for your API user.",
        "parameters": [
          {"in": "path", "name": "attachment_id", "required": true, "schema": {"type": "integer"}}
        ],
        "responses": {
          "200": {"description": "Deleted — `data.deleted_attachment_id` confirms the removed id."},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "503": {"description": "The account's external FTP storage is unreachable — the attachment was NOT deleted, retry later."}
        }
      }
    },

    "/marketplace/connectors/list/": {
      "get": {
        "tags": ["Marketplace"],
        "summary": "List the account's order connectors",
        "description": "Returns the order connectors configured on the account — the webshops and marketplaces orders are imported from — together with their BOCP IDs.\n\n**Call this first.** `connector_id` is the value the `/marketplace/orders/list/` filter expects, and there is no other way to discover it: the per-connector routes require you to already know the ID.\n\nInactive and deleted connectors are returned too, flagged by `active` and `deleted`, because historical orders still point at them. Pass `skipcancelled` to omit the deleted ones.\n\n**Supported filters:** `id`, `minid`, `skipcancelled`, `page`. Connectors are returned oldest ID first. Date filters do not apply to this endpoint and return 400.\n\n**Note for connector-bound API keys:** a key issued for one specific connector only ever sees that one connector in this list.\n\nCredentials and internal endpoints configured on a connector (usernames, API URLs, push URLs, secrets) are deliberately **not** returned.",
        "responses": {
          "200": {
            "description": "The account's order connectors.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ResponseEnvelope"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "connector_id":     {"type": "integer", "description": "Pass this as `connector_id` to the order list filter."},
                              "name":             {"type": "string",  "description": "Connector name as configured in the ERP."},
                              "type":             {"type": "string",  "description": "Connector type, e.g. GOMAG, BOCPRAPI, EMAG, SHOPIFY."},
                              "active":           {"type": "integer", "description": "1 when the connector is currently importing."},
                              "deleted":          {"type": "integer", "description": "1 when the connector was removed — kept here because past orders still reference it."},
                              "last_imported_ts": {"type": "string",  "description": "Last time BOCP imported orders from this connector. Empty when it never has."}
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"description": "No connectors matched — the account has none configured, or the filters excluded them all."}
        }
      }
    },

    "/marketplace/orders/list/": {
      "get": {
        "tags": ["Marketplace"],
        "summary": "List orders from every connector",
        "description": "Returns a paginated, account-wide list of imported webshop / marketplace orders, together with the sales receipts, fiscal invoices and AWBs generated for each one.\n\nThis is the global counterpart of `GET /connector/{connector_id}/order/list/`: same item shape, but it is not bound to a single connector, and each item carries a `connector` block telling you where the order came from.\n\n**Filters are passed as URL path segments.** Example: `/marketplace/orders/list/connector_id:7/datefrom:2026-08-01/datethrough:2026-08-31/page:2/`.\n\n**Supported filters:**\n\n- `connector_id` — only orders imported through this connector. Get the IDs from `GET /marketplace/connectors/list/`.\n- `datefrom` / `datethrough` — order-date period, `YYYY-MM-DD`, both **inclusive**.\n- `year` — a whole year, e.g. `year:2026`.\n- `yearmonth` — a whole month, e.g. `yearmonth:202608`.\n- `date` — a single day, e.g. `date:2026-08-14`.\n- `modifiedafter` / `modifiedthrough` — filter on the last-change timestamp, `YYYY-MM-DD HH:MM:SS`. Use `modifiedafter` for incremental syncs.\n- `skipcancelled` — omit deleted orders.\n\nCancelled sale receipts and cancelled AWBs are returned too, each flagged by its own `cancelled` field, so you can show that a shipment or receipt is no longer valid instead of it silently disappearing from your copy of the data.\n- `id` — a single BOCP order ID.\n- `minid` — orders from this BOCP order ID upwards.\n- `page` — page number, 250 items per page.\n\nOrders are returned newest first (`rec_id` descending). `data_next_page_exists` in the envelope tells you whether to request the next page, and `data_next_page_url` is the ready-made URL for it. A page with no matching orders returns **404**, not an empty 200.\n\n**Note for connector-bound API keys:** a key issued for one specific connector is silently restricted to that connector's orders, and passing a different `connector_id` returns 401.",
        "responses": {
          "200": {
            "description": "Paginated list of orders. Each item includes its connector, linked sales receipts, invoices, and AWBs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ResponseEnvelope"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "rec_id":          {"type": "integer", "description": "BOCP internal order ID."},
                              "external_id":     {"type": "string",  "description": "Order number as received from the source platform."},
                              "status":          {"type": "string",  "description": "Current order status."},
                              "order_date":      {"type": "string",  "description": "Order date/time as received from the source platform."},
                              "last_changed_ts": {"type": "string",  "description": "Last time the order was changed in BOCP — use with the `modifiedafter` filter for incremental syncs."},
                              "deleted":         {"type": "integer", "description": "1 when the order was deleted in BOCP."},
                              "currency":        {"type": "string",  "description": "Order currency code."},
                              "grand_total":     {"type": "string",  "description": "Order total with VAT, in the order currency."},
                              "grand_total_ron": {"type": "string",  "description": "Order total with VAT, in RON."},
                              "paid_amount":     {"type": "string",  "description": "Amount already collected for this order."},
                              "connector": {
                                "type": "object",
                                "description": "Which connector the order was imported through.",
                                "properties": {
                                  "connector_id": {"type": "integer"},
                                  "name":         {"type": "string", "description": "Connector name as configured in the ERP."},
                                  "type":         {"type": "string", "description": "Connector type, e.g. GOMAG, BOCPRAPI, EMAG."}
                                }
                              },
                              "sales": {
                                "type": "array",
                                "description": "Sale receipts issued for this order.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "series":    {"type": "string"},
                                    "nr":        {"type": "integer"},
                                    "date":      {"type": "string", "format": "date"},
                                    "cancelled": {"type": "integer", "description": "1 when the sale receipt was cancelled."}
                                  }
                                }
                              },
                              "invoices": {
                                "type": "array",
                                "description": "Fiscal invoices linked to the order.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "seria": {"type": "string"},
                                    "nr":    {"type": "integer"},
                                    "date":  {"type": "string", "format": "date"}
                                  }
                                }
                              },
                              "awbs": {
                                "type": "array",
                                "description": "AWBs generated for this order, including ones that were subsequently cancelled — check `cancelled` before treating an AWB as the live shipment. Active AWBs come first.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "nr_awb":    {"type": "string"},
                                    "pdf_url":   {"type": "string", "format": "uri"},
                                    "cancelled": {"type": "integer", "description": "1 when this AWB was cancelled and is no longer valid for tracking or handover."}
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/connector/{connector_id}/order/list/": {
      "get": {
        "tags": ["eCommerce Orders"],
        "summary": "List orders",
        "description": "Returns a paginated list of orders imported through a connector.\n\n**Filters are passed as URL path segments.** Example: `/connector/7/order/list/datefrom:2026-08-01/page:2/`.\n\n**Supported filters:** `year`, `yearmonth`, `yearmonthday`, `date`, `datefrom`, `datethrough`, `modifiedafter`, `modifiedthrough`, `skipcancelled`, `id`, `minid`, `page`. Date filters apply to the order date; `modifiedafter` / `modifiedthrough` apply to the last time the order changed in BOCP.\n\nFor an account-wide list across every connector, and one that also returns order totals and each order's source, use `GET /marketplace/orders/list/` instead — the per-order shape is the same.",
        "parameters": [
          {"in": "path", "name": "connector_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP connector ID."}
        ],
        "responses": {
          "200": {
            "description": "Paginated list of orders. Each item includes linked sales receipts, invoices, and AWBs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ResponseEnvelope"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "rec_id":      {"type": "integer", "description": "BOCP internal order ID."},
                              "external_id": {"type": "string",  "description": "Partner-side order identifier."},
                              "status":      {"type": "string",  "description": "Current order status."},
                              "sales": {
                                "type": "array",
                                "description": "Sale receipts issued for this order, including cancelled ones — check `cancelled`.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "series":    {"type": "string"},
                                    "nr":        {"type": "integer"},
                                    "date":      {"type": "string", "format": "date"},
                                    "cancelled": {"type": "integer", "description": "1 when the sale receipt was cancelled."}
                                  }
                                }
                              },
                              "invoices": {
                                "type": "array",
                                "description": "Fiscal invoices linked to the order.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "seria": {"type": "string"},
                                    "nr":    {"type": "integer"},
                                    "date":  {"type": "string", "format": "date"}
                                  }
                                }
                              },
                              "awbs": {
                                "type": "array",
                                "description": "AWBs generated for this order, including ones that were subsequently cancelled — check `cancelled` before treating an AWB as the live shipment. Active AWBs come first.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "nr_awb":    {"type": "string"},
                                    "pdf_url":   {"type": "string", "format": "uri"},
                                    "cancelled": {"type": "integer", "description": "1 when this AWB was cancelled and is no longer valid for tracking or handover."}
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    },

    "/connector/{connector_id}/order/{order_id}/": {
      "post": {
        "tags": ["eCommerce Orders"],
        "summary": "Submit a new order",
        "description": "Creates a new order in BOCP from the connector. The `{order_id}` URL segment must equal `order_unique_id` in the body — a mismatch returns `400`.\n\n**DropShip vs eCommerce:** Both connector types use this same endpoint and payload. The difference is in invoice issuance: for DropShip connectors the invoice is issued to the dropshipper's preconfigured BOCP contact; for eCommerce connectors the invoice goes directly to the `client` supplied in the order.\n\n**Authentication:** HTTP Basic with credentials tied to this specific connector. The API user cannot access other connectors.\n\n**Webhook notifications (BOCP → partner):** BOCP POSTs JSON to your configured notification URL on three events. Your endpoint must return HTTP 200 to acknowledge; HTTP 4xx closes the notification permanently; any other code retries periodically.\n\n*Status change:* `{\"order_unique_id\":\"...\",\"notification_type\":\"status\",\"status_text\":\"...\"}`\n\n*Invoice issued:* `{\"notification_type\":\"invoice\",\"invoice_number\":\"...\",\"invoice_date\":\"...\",\"invoice_series\":\"...\",\"invoice_total\":0.0,\"invoice_currency\":\"RON\",\"invoice_total_RON\":0.0,\"invoice_url_html\":\"...\",\"invoice_url_pdf\":\"...\"}`\n\n*AWB issued:* `{\"notification_type\":\"awb\",\"awb_number\":\"...\",\"awb_packet_count\":1,\"weight\":1.5,\"shipping_payer\":\"...\",\"cash_on_delivery\":0.0,\"cash_on_delivery_currency\":\"RON\",\"delivery_country\":\"Romania\",\"declared_value\":0.0,\"awb_url_pdf\":\"...\"}`\n\n**Product push (same URL):** the identical notification URL also receives product-catalog pushes — BOCPRAPI is wired as a regular push destination (like Shopify, GoMag, ...), not a passive feed, so `GET .../products/` no longer needs to be polled on a schedule. Every push carries the CURRENT full product state (not a diff), using the same field names as `GET .../products/`. See the *Sending Orders* guide, section \"Product push (catalog changes)\", for the full field list.\n\n*New / republished:* `notification_type=\"product_published\"`. *Field, stock or price change:* `notification_type=\"product_update\"`. *Disconnected / deleted:* `notification_type=\"product_unpublished\"` — `{\"notification_type\":\"product_unpublished\",\"cod_produs\":\"...\"}`. Single packet per product for now.",
        "parameters": [
          {"in": "path", "name": "connector_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP connector ID assigned when the connector was created."},
          {"in": "path", "name": "order_id",     "required": true, "schema": {"type": "string"},  "description": "Partner-side order identifier. Must match `order_unique_id` in the body."}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/ConnectorOrderRequest"}
            }
          }
        },
        "responses": {
          "200": {"description": "Order accepted. Response `data` contains `BOCP_order_id`, `notices` and `errors` arrays, the `received_payload` echoed back, and an `import_summary` object: `mode` (create|update), `bocp_order_id`, `items_received` vs `items_imported`, `payments_received` vs `payments_registered`, `payments_skipped_pending`, `paid_amount_registered`, `payment_method_applied`, and `item_field_aliases_used`. Compare `items_received` with `items_imported` to catch a payload BOCP could not fully read.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResponseEnvelope"}}}},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      },
      "put": {
        "tags": ["eCommerce Orders"],
        "summary": "Update an existing order",
        "description": "Updates an order previously submitted via POST. All body fields are optional except `order_unique_id`. Only fields present in the body are updated:\n\n- `items` — if present, **fully replaces** all existing order items.\n- `client` — if present, updates client data.\n- `order_status`, `order_cancelled` — always updateable individually without sending the full payload.",
        "parameters": [
          {"in": "path", "name": "connector_id", "required": true, "schema": {"type": "integer"}},
          {"in": "path", "name": "order_id",     "required": true, "schema": {"type": "string"}}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/ConnectorOrderRequest"}
            }
          }
        },
        "responses": {
          "200": {"$ref": "#/components/responses/SuccessEnvelope"},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/connector/{connector_id}/products/": {
      "get": {
        "tags": ["eCommerce Orders"],
        "summary": "Read the product catalog (pull feed)",
        "description": "Returns the products published to this connector, so you can obtain the product `code` used in order `items` and keep stock/prices in sync.\n\n**Only products published to this connector** (from *Product Catalogue* in BOCP) are returned — there is no push; you pull the current list on demand. The response has the **same shape as `/product/list/`**, so a single parser handles both. Prices and stock reflect the connector's configured working point.\n\n**Filters are passed as URL path segments.** Example: `/connector/7/products/include:images/page:2/`.\n\n**Supported filters:** `page`, `format` (`json`|`xml`|`csv`), `code` (single product by code/barcode), `modifiedafter:YYYY-MM-DD HH:MM:SS` (incremental sync), `include:images,properties`, `magazia_id` (override pricing/stock working point).\n\nUse `cod_produs` from each item as the `code` when building orders.",
        "parameters": [
          {"in": "path", "name": "connector_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP connector ID assigned when the connector was created."}
        ],
        "responses": {
          "200": {
            "description": "Paginated list of the products published to this connector.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ResponseEnvelope"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "product_id":           {"type": "integer", "description": "BOCP internal product variant ID."},
                              "cod_varianta":         {"type": "string",  "description": "BOCP variant group code."},
                              "cod_produs":           {"type": "string",  "description": "Product code — use as the order item `code`."},
                              "barcode":              {"type": "string"},
                              "product_name":         {"type": "string"},
                              "category_name":        {"type": "string"},
                              "stoc_global":          {"type": "number",  "description": "Total stock across all warehouses (not reduced by reservations)."},
                              "stock_reserved":       {"type": "number",  "description": "Quantity currently reserved for other orders, across all warehouses."},
                              "stock_available":      {"type": "number",  "description": "Sellable quantity: `stoc_global - stock_reserved`, floored at 0."},
                              "unitate_masura":       {"type": "string"},
                              "pret_vanzare":         {"type": "number",  "description": "Selling price without VAT."},
                              "pret_vanzare_cu_tva":  {"type": "number",  "description": "Selling price with VAT."},
                              "cota_tva_vanzare":     {"type": "number",  "description": "VAT percentage."},
                              "price_applies_to":     {"type": "string",  "enum": ["mu1", "mu2"], "description": "Measurement unit the selling price refers to: mu1 = primary unit (unitate_masura), mu2 = secondary unit. Stock is always in the primary unit."},
                              "selling_currency_txt": {"type": "string"},
                              "website_published":    {"type": "integer"},
                              "producator":           {"type": "string"},
                              "last_changed_ts":      {"type": "string", "description": "Last modification timestamp — filter with `modifiedafter`."}
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    },

    "/connector/{connector_id}/config/": {
      "get": {
        "tags": ["eCommerce Orders"],
        "summary": "Read the connector configuration",
        "description": "Returns the connector's current configuration so your integration knows **what to expect** when it sends orders: which automations will run (sale issue, invoicing, proforma, AWB), how products sync in both directions, and how order events map to statuses.\n\n**Read-only** — a GET never changes any setting.\n\n**Option fields** are exported as an object with three keys so you can also see what values are possible:\n\n`{\"value\": \"a\", \"label\": \"AUTOMAT: ...\", \"options\": {\"a\": \"AUTOMAT: ...\", \"m\": \"MANUAL: ...\"}}`\n\nSettings that fall back to an account-wide default when not set per connector also carry a `source` key (`connector` or `global`).\n\n**Response groups (`data`):**\n\n- `connector` — id, site domain, type, active flag, order API URL, configured `order_event_notification_url`.\n- `products` — auto-import into the ERP (`autoimport_into_erp`, `import_mode`: `all` | `ordered_only` | `only_categories_with_autoimport`), update options (update product code in ERP, disconnect multiple connections, group products, fields updated from the site), the sub-inventory where new products are created, and `push` (product push active, `products_feed_url`, and `product_change_notify_url` — the same URL as `order_event_notification_url`; BOCP pushes the full current product state there on publish/update/unpublish, same as any other push destination).\n- `order_processing` — order confirmation method (automatic/manual), the working point where orders are auto-processed, action on order import (reserve stock / issue sale / ...), next-day scheduling hour, weekdays skipped for sale issue, cancel/return actions, ignored validation errors and statuses, repeated-order block threshold.\n- `invoicing` — preferred invoice series, auto-issue trigger and stock condition, payment methods excluded from auto-invoicing, only-if-paid flag, e-mail auto-send.\n- `proforma` — proforma series and the payment methods / status that trigger it.\n- `awb` — AWB auto-issue mode and stock condition, latest issue hour, skipped weekdays.\n- `status_sync` — the event → external status mapping used when BOCP pushes status changes back to your site.",
        "parameters": [
          {"in": "path", "name": "connector_id", "required": true, "schema": {"type": "integer"}, "description": "BOCP connector ID assigned when the connector was created."}
        ],
        "responses": {
          "200": {
            "description": "The connector configuration, grouped by area.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {"$ref": "#/components/schemas/ResponseEnvelope"},
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "connector":        {"type": "object", "description": "Identity: id, site_domain, type, active, order_api_url, order_event_notification_url."},
                            "products":         {"type": "object", "description": "Product auto-import options, update options, target sub-inventory, and push/feed settings."},
                            "order_processing": {"type": "object", "description": "Confirmation method, auto-process working point, on-import action, scheduling and automation guards."},
                            "invoicing":        {"type": "object", "description": "Invoice series and auto-issue rules."},
                            "proforma":         {"type": "object", "description": "Proforma series and auto-issue rules."},
                            "awb":              {"type": "object", "description": "AWB auto-issue rules and scheduling."},
                            "status_sync":      {"type": "object", "description": "Event to external-status mapping (events_statuses_mapping)."}
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "success": true,
                  "data": {
                    "connector": {
                      "id": 1,
                      "site_domain": "shop.example.ro",
                      "type": "BOCPRAPI",
                      "active": true,
                      "order_api_url": "https://secure.bocp.eu/app/rest/v1/2469/connector/1/order/{order_unique_id}/",
                      "order_event_notification_url": "https://shop.example.ro/bocp/notify"
                    },
                    "products": {
                      "autoimport_into_erp": true,
                      "import_mode": {"value": "import_only_ordered", "label": "ordered_only", "options": {"import_all": "all", "import_only_ordered": "ordered_only", "import_only_category_products": "only_categories_with_autoimport"}},
                      "update_options": {"update_product_code_in_erp": false, "disconnect_multiple_connections": true, "group_imported_products": true, "fields_updated_from_site": []},
                      "create_new_products_in_subinventory": {"id": 1, "name": "Marfa"},
                      "push": {"active": true, "products_feed_url": "https://secure.bocp.eu/app/rest/v1/2469/connector/1/products/", "product_change_notify_url": "https://shop.example.ro/bocp/notify"}
                    },
                    "order_processing": {
                      "order_confirm_method": {"value": "a", "label": "AUTOMAT: ...", "options": {"a": "AUTOMAT: ...", "m": "MANUAL: ..."}, "source": "global"},
                      "auto_process_workpoint": {"id": 3, "name": "Depozit central"},
                      "skip_bv_issue_on_iso_weekdays": [6, 7]
                    },
                    "invoicing": {"issue_invoice_only_if_paid": true, "autosend_invoice_via_email": false},
                    "status_sync": {"events_statuses_mapping": {"on_bv_issued": "in_processing", "on_awb_issued": "shipped"}}
                  }
                }
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"}
        }
      }
    }
  }
}
