openapi: 3.1.0
info:
  title: VIP Post API
  version: 2.0.0
  summary: API за креирање, пребарување, печатење и бришење пратки.
  description: |
    VIP Post API V2 им овозможува на автентицираните клиенти да креираат поединечни или групни пратки,
    да преземаат информации за пратка, да генерираат товарен лист како Base64 PDF и да избришат пратка.

    ## Начин на враќање одговори
    API-то моментално враќа HTTP 200 и за успешни одговори и за грешки на апликациско ниво.
    Клиентите МОРА да го проверат полето `error` во JSON одговорот:
    - `0` означува успешна операција.
    - `1` означува грешка на апликациско ниво.

    ## Кодирање на знаци
    Податоците во барањата треба да се испраќаат како UTF-8.

    ## Однесување на identifier
    `identifier` е број на нарачка или референца обезбеден од клиентот. Повторно испраќање на претходно внесен
    identifier за истиот активен корисник вообичаено го враќа постојниот `consignment_id`, наместо
    да креира нов запис.

servers:
  - url: https://vipposta.mk:3030/v2
    description: Тест околина
  - url: https://vipposta.mk:6060/v2
    description: Продукциска околина

tags:
  - name: Consignments
    description: Креирање и управување со пратки.
  - name: Documents
    description: Генерирање документи за пратки.

security:
  - bearerAuth: []

paths:
  /consignment/create:
    post:
      tags: [Consignments]
      operationId: createConsignment
      summary: Креирај пратка
      description: |
        Креира една пратка.

        When the same `identifier` has already been inserted for the active user, the API normally
        returns the existing `consignment_id` with the message
        `The data has already been inserted.`

        The endpoint accepts `multipart/form-data`.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ConsignmentCreateRequest'
            encoding:
              type:
                style: form
              price:
                style: form
            examples:
              standard:
                summary: Standard parcel
                value:
                  name: Name/Company
                  phone: "071123456"
                  city: Skopje
                  street: "Улица 123 бр.23"
                  note: "забелешка"
                  payer: "И-Ф"
                  type: 7
                  price: "0"
                  identifier: VIP3000350000012392MK
                  identifier2: ""
                  user_id: "183"
      responses:
        '200':
          description: Успешен одговор или грешка на апликациско ниво. Проверете го полето `error`.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CreateSuccessResponse'
                  - $ref: '#/components/schemas/CreateDuplicateResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
              examples:
                created:
                  value:
                    error: 0
                    message: Data Inserted Successfully.
                    consignment_id: "103079"
                duplicate:
                  value:
                    error: 0
                    message: The data has already been inserted.
                    consignment_id: "103079"
                invalidName:
                  value:
                    error: 1
                    message: Invalid Name!!
                invalidPhone:
                  value:
                    error: 1
                    message: Invalid Phone!!
                invalidPayer:
                  value:
                    error: 1
                    message: Invalid Payer!!
                invalidType:
                  value:
                    error: 1
                    message: Invalid Type!!
                invalidPrice:
                  value:
                    error: 1
                    message: Invalid Price!!
                invalidUser:
                  value:
                    error: 1
                    message: Invalid user_id!!
        default:
          $ref: '#/components/responses/UnexpectedResponse'

  /consignment/bulkcreate:
    post:
      tags: [Consignments]
      operationId: bulkCreateConsignments
      summary: Креирај повеќе пратки
      description: |
        Креира повеќе пратки во едно JSON барање.

        The response contains one result object per submitted item. New and duplicate identifiers
        may be mixed in the same request and are processed independently.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              minItems: 1
              items:
                $ref: '#/components/schemas/ConsignmentCreateRequest'
            examples:
              standard:
                value:
                  - name: Velice Kumanovka
                    phone: "075924384"
                    city: Kumanovo
                    street: Braniteli 12
                    note: "-"
                    payer: "И-Ф"
                    type: 7
                    price: "1500"
                    identifier: VIP3000350000012393MK
                    user_id: "183"
                  - name: Velice Ohrid
                    phone: "075924384"
                    city: Ohrid
                    street: Makedonski 12
                    note: "-"
                    payer: "И-Ф"
                    type: 7
                    price: "1300"
                    identifier: VIP3000350000012394MK
                    user_id: "183"
      responses:
        '200':
          description: |
            Per-item results, or a general error object when the request body cannot be parsed.
          content:
            application/json:
              schema:
                oneOf:
                  - type: array
                    items:
                      $ref: '#/components/schemas/BulkItemResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
              examples:
                created:
                  value:
                    - error: 0
                      message: Data Inserted Successfully.
                      consignment_id: "103080"
                      identifier: VIP3000350000012393MK
                    - error: 0
                      message: Data Inserted Successfully.
                      consignment_id: "103081"
                      identifier: VIP3000350000012394MK
                invalidJson:
                  value:
                    error: 1
                    message: Syntax error
        default:
          $ref: '#/components/responses/UnexpectedResponse'

  /consignment/id/{user_id}/{consignment_id}:
    get:
      tags: [Consignments]
      operationId: getConsignmentById
      summary: Преземи пратка по ID
      parameters:
        - $ref: '#/components/parameters/UserId'
        - $ref: '#/components/parameters/ConsignmentId'
      responses:
        '200':
          description: Пронајдена пратка или грешка на апликациско ниво.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ConsignmentResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
              examples:
                found:
                  value:
                    error: 0
                    consignment:
                      name: API Test Recipient
                      phone: "071123456"
                      city: "Скопје"
                      street: API Test Address 1
                      note: Automated integration test.
                      type: "7"
                      payer: "И-Ф"
                      price: "0"
                      delivery_price: "90"
                      status: not picked up
                      created_at: "2026-07-21 00:09:21"
                      consignment_id: "103079"
                      identifier: API-SINGLE-EXAMPLE
                notFound:
                  value:
                    error: 1
                    message: Consignment does not exist!
        default:
          $ref: '#/components/responses/UnexpectedResponse'

  /consignment/identifier/{user_id}/{identifier}:
    get:
      tags: [Consignments]
      operationId: getConsignmentByIdentifier
      summary: Преземи пратка по клиентски identifier
      parameters:
        - $ref: '#/components/parameters/UserId'
        - name: identifier
          in: path
          required: true
          description: Клиентски број на нарачка или референца.
          schema:
            type: string
          example: VIP3000350000012393MK
      responses:
        '200':
          description: Пронајдена пратка или грешка на апликациско ниво.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ConsignmentResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
        default:
          $ref: '#/components/responses/UnexpectedResponse'

  /consignment/waybill/{user_id}/{consignment_id}:
    get:
      tags: [Documents]
      operationId: getConsignmentWaybill
      summary: Преземи товарен лист како Base64 PDF
      description: |
        Враќа PDF товарен лист кодиран како Base64 во полето `waybill`.
        Декодирајте ја Base64 вредноста и зачувајте ги добиените bytes како `.pdf` датотека.
      parameters:
        - $ref: '#/components/parameters/UserId'
        - $ref: '#/components/parameters/ConsignmentId'
      responses:
        '200':
          description: Товарен лист како Base64 PDF или грешка на апликациско ниво.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/WaybillResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
              examples:
                success:
                  value:
                    error: 0
                    waybill: JVBERi0xLjQK...
        default:
          $ref: '#/components/responses/UnexpectedResponse'

  /consignment/delete/{user_id}/{consignment_id}:
    get:
      tags: [Consignments]
      operationId: deleteConsignment
      summary: Избриши пратка
      description: |
        Ја брише избраната пратка.

        API-то моментално го извршува бришењето преку HTTP GET. Клиентите не треба автоматски
        да ја отвораат или prefetch-ираат оваа URL адреса бидејќи ја менува состојбата на серверот.
      parameters:
        - $ref: '#/components/parameters/UserId'
        - $ref: '#/components/parameters/ConsignmentId'
      responses:
        '200':
          description: Резултат од бришење или грешка на апликациско ниво.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/DeleteSuccessResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
              examples:
                success:
                  value:
                    error: 0
                    message: Consignment Deleted Successfully
        default:
          $ref: '#/components/responses/UnexpectedResponse'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Bearer token supplied by VIP Post.

  parameters:
    UserId:
      name: user_id
      in: path
      required: true
      description: Идентификатор на VIP Post корисник.
      schema:
        type: string
      example: "183"
    ConsignmentId:
      name: consignment_id
      in: path
      required: true
      description: VIP Post идентификатор на пратка вратен по креирањето.
      schema:
        type: string
      example: "103079"

  responses:
    UnexpectedResponse:
      description: Неочекуван transport или server одговор.

  schemas:
    ConsignmentCreateRequest:
      type: object
      additionalProperties: false
      required:
        - name
        - phone
        - city
        - street
        - payer
        - type
        - price
        - identifier
        - user_id
      properties:
        name:
          type: string
          minLength: 1
          description: Име на примач или компанија.
          example: Name/Company
        phone:
          type: string
          minLength: 1
          description: Телефонски број на примачот.
          example: "071123456"
        city:
          type: string
          minLength: 1
          description: |
            Град на примачот. Полето е задолжително.
          example: Skopje
        street:
          type: string
          minLength: 1
          description: Улица, број и други адресни детали.
          example: "Улица 123 бр.23"
        note:
          type: string
          description: Опционална забелешка за достава.
          default: ""
          example: "забелешка"
        payer:
          type: string
          enum: ["И-Ф", "И-Г", "П-Г", "П-Ф"]
          description: |
            Payment arrangement. Confirmed examples include:
            - `П-Г`: payment on delivery.
            - `И-Ф`: payment by invoice/card arrangement; invoicing is handled periodically.
          example: "И-Ф"
        type:
          oneOf:
            - type: integer
            - type: string
              pattern: '^[0-9]+$'
          description: |
            Shipment type:
            1. Обично писмо
            2. Препорачано писмо
            3. Писмо лично на рака
            4. Писмо Експрес
            5. Писмено Нотар лично на рака
            6. Извршител лично на рака
            7. 0–5 kg
            8. 5–10 kg
            9. 10–20 kg
            10. 20–30 kg
            11. Пакет Експрес
          example: 7
        price:
          oneOf:
            - type: string
              pattern: '^[0-9]+(?:\.[0-9]+)?$'
            - type: number
              minimum: 0
          description: Износ за откуп или наплата при достава во MKD.
          example: "1500"
        identifier:
          type: string
          minLength: 1
          description: |
            Клиентски број на нарачка или референца, на пример NPN, IBR или број на нарачка.
            Користете стабилна уникатна вредност за секоја нарачка.
          example: VIP3000350000012392MK
        identifier2:
          type: string
          description: Опционален секундарен клиентски identifier.
          default: ""
          example: "1001695159"
        user_id:
          type: string
          minLength: 1
          description: VIP Post user identifier assigned to the integration.
          example: "183"

    CreateSuccessResponse:
      type: object
      required: [error, message, consignment_id]
      properties:
        error:
          type: integer
          const: 0
        message:
          type: string
          const: Data Inserted Successfully.
        consignment_id:
          type: string

    CreateDuplicateResponse:
      type: object
      required: [error, message, consignment_id]
      properties:
        error:
          type: integer
          const: 0
        message:
          type: string
          const: The data has already been inserted.
        consignment_id:
          type: string

    BulkItemResponse:
      type: object
      required: [error, message, consignment_id, identifier]
      properties:
        error:
          type: integer
          enum: [0, 1]
        message:
          type: string
        consignment_id:
          type: string
        identifier:
          type: string

    ErrorResponse:
      type: object
      required: [error, message]
      properties:
        error:
          type: integer
          const: 1
        message:
          type: string
          examples:
            - Token is not valid
            - Invalid Name!!
            - Invalid Phone!!
            - Invalid Payer!!
            - Invalid Type!!
            - Invalid Price!!
            - Invalid user_id!!
            - Syntax error
            - Consignment does not exist!

    ConsignmentResponse:
      type: object
      required: [error, consignment]
      properties:
        error:
          type: integer
          const: 0
        consignment:
          $ref: '#/components/schemas/Consignment'

    Consignment:
      type: object
      required:
        - name
        - phone
        - city
        - street
        - note
        - type
        - payer
        - price
        - delivery_price
        - status
        - created_at
        - consignment_id
        - identifier
      properties:
        name:
          type: string
        phone:
          type: string
        city:
          type: string
        street:
          type: string
        note:
          type: string
        type:
          type: string
        payer:
          type: string
        price:
          type: string
        delivery_price:
          type: string
        status:
          type: string
          enum:
            - not picked up
            - in transit
            - delivered
            - canceled
            - deleted
        created_at:
          type: string
          description: Локално време на серверот во формат `YYYY-MM-DD HH:mm:ss`.
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
        consignment_id:
          type: string
        identifier:
          type: string

    WaybillResponse:
      type: object
      required: [error, waybill]
      properties:
        error:
          type: integer
          const: 0
        waybill:
          type: string
          contentEncoding: base64
          contentMediaType: application/pdf
          description: PDF документ кодиран во Base64.

    DeleteSuccessResponse:
      type: object
      required: [error, message]
      properties:
        error:
          type: integer
          const: 0
        message:
          type: string
          const: Consignment Deleted Successfully
