---
openapi: 3.0.3
info:
  title: Customers API
  version: v1
servers:
- url: https://my.parcel4you.com
  description: Production server
components:
  securitySchemes:
    Bearer:
      bearerFormat: JWT Token - formated as "eyJhbGciUkXY....2fMU"
      description: Customer API Token. Availalbe in the customer details in myParcel4You.
        Navigate to Account -> Secrets -> Show Secrets -> Copy secret (the 2nd option)
      type: apiKey
      name: authorization-token
      in: header
  schemas:
    address:
      type: object
      properties:
        name:
          type: string
          example: Thomas Bloch
          maxLength: 40
          pattern: "(?-mix:^[^\\d]+$|^$)"
        company_name:
          type: string
          example: Parcel4You
        address_first_line:
          type: string
          example: Poul Larsens Vej 8C
          minLength: 1
          maxLength: 50
        address_second_line:
          type: string
          example: Example street
          maxLength: 50
        city:
          type: string
          example: Silkeborg
          minLength: 1
          maxLength: 40
          pattern: "(?-mix:^[^\\d]+$)"
        state:
          type: string
          example: Manhatan
          maxLength: 40
          pattern: "(?-mix:\\A[\\p{L}a-zA-Z \\-']+\\z|^$)"
        zip_code:
          type: string
          example: 23-233
          minLength: 1
          maxLength: 12
          pattern: "(?-mix:\\A[A-Za-z0-9\\-_`,. ]{3,12}\\z|^$)"
        country_id:
          type: integer
          description: If country_code provided, the country_id is no longer required.
            If country_id is provided, country_code is ignored.
        country_code:
          type: string
          example: DK
          description: If country_id provided, country_code is ignored.
          minLength: 1
        phone:
          type: string
          example: "+48321321321"
          maxLength: 20
          pattern: "(?-mix:^\\+?[0-9\\s]+[0-9]+$|^$)"
        email:
          type: string
          example: example@example.org
          maxLength: 50
          pattern: "(?-mix:\\A[^@\\s]+@([^@\\s]+\\.)+[^@\\s]+\\z|^$)"
      required:
      - address_first_line
      - city
      - zip_code
      - country_id
    shipper_address:
      allOf:
      - "$ref": "#/components/schemas/address"
      - type: object
        properties:
          company_name:
            type: string
            example: Parcel4You
            pattern: "^\\S+(\\s\\S+)*$"
          eori:
            type: string
            example: '123456789012345678'
          vat:
            type: string
            example: PL1234567890
      required:
      - company_name
    receiver_address:
      allOf:
      - "$ref": "#/components/schemas/address"
    address_response:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Thomas Bloch
        company_name:
          type: string
          example: Parcel4You
          pattern: "^\\S+(\\s\\S+)*$"
        address_first_line:
          type: string
          example: Poul Larsens Vej 8C
        address_second_line:
          type: string
          example: Example street
        city:
          type: string
          example: Silkeborg
        state:
          type: string
          example: Manhattan
        zip_code:
          type: string
          example: 23-233
        country:
          "$ref": "#/components/schemas/country"
        phone:
          type: string
          example: "+48321321321"
        email:
          type: string
          example: example@example.org
        eori:
          type: string
          example: '123456789012345678'
        vat:
          type: string
          example: PL1234567890
    create_parcel:
      type: object
      required:
      - product_id
      - receiver_address
      - shipper_address
      parameters:
      - name: include_label
        in: query
        required: false
        schema:
          type: boolean
        example: true
        description: Include the generated label in field 'label' in the response.
      properties:
        product_id:
          type: integer
          example: 1
          description: Product ID - you can fetch it from the Products endpoint
        price_value:
          "$ref": "#/components/schemas/price_value"
          minLength: 1
          pattern: "(?-mix:\\d+(\\.\\d+)?)"
        price_currency:
          type: string
          example: PLN
          minLength: 0
        shipper_address:
          "$ref": "#/components/schemas/shipper_address"
        receiver_address:
          "$ref": "#/components/schemas/receiver_address"
        pickup_point_id:
          type: string
          example: AM 342
          minLength: 1
          maxLength: 20
        weight:
          "$ref": "#/components/schemas/measurement"
          pattern: "(?-mix:\\d+(\\.\\d+)?)"
        length:
          "$ref": "#/components/schemas/measurement"
          pattern: "(?-mix:\\d+(\\.\\d+)?)"
        width:
          "$ref": "#/components/schemas/measurement"
          pattern: "(?-mix:\\d+(\\.\\d+)?)"
        height:
          "$ref": "#/components/schemas/measurement"
          pattern: "(?-mix:\\d+(\\.\\d+)?)"
        shipper_reference:
          type: string
          example: CR345567
          maxLength: 100
        products:
          type: array
          items:
            "$ref": "#/components/schemas/product"
    parcel:
      type: object
      required:
      - id
      - hub
      - supplier_system_number
      - supplier_order_number
      parameters:
      - name: include_label
        in: query
        required: false
        schema:
          type: boolean
        example: true
        description: Include the generated label in field 'label' in the response.
      properties:
        id:
          type: number
          example: 2434678
        hub:
          type: string
          example: EIN
        supplier_system_number:
          type: string
          example: 20430-460
        supplier_order_number:
          type: string
          example: '345645666888'
        tracking_number:
          type: string
          example: JJD000033509000461687787
        tracking_url:
          type: string
          example: https://example.com/tracking/JJD000033509000461687787
        status:
          "$ref": "#/components/schemas/parcel_status"
        created_at:
          "$ref": "#/components/schemas/date"
        updated_at:
          "$ref": "#/components/schemas/date"
        weight:
          "$ref": "#/components/schemas/measurement"
        is_label_attached:
          type: boolean
          example: true
        label:
          type: string
          nullable: true
          format: byte
          example: base64 encoded label
        height:
          "$ref": "#/components/schemas/measurement"
        width:
          "$ref": "#/components/schemas/measurement"
        length:
          "$ref": "#/components/schemas/measurement"
        shipper_reference:
          type: string
          example: shipper reference
        price_currency:
          type: string
          example: GBP
        price_value:
          "$ref": "#/components/schemas/price_value"
        accepted_at:
          allOf:
          - "$ref": "#/components/schemas/date"
          nullable: true
        sent_to_transit_at:
          allOf:
          - "$ref": "#/components/schemas/date"
          nullable: true
        delivered_at:
          nullable: true
          allOf:
          - "$ref": "#/components/schemas/date"
        pickup_point_id:
          type: string
          nullable: true
          example: '1234567890'
        bulk_parcel_id:
          type: string
          nullable: true
          example: '1234567890'
        products:
          type: array
          items:
            "$ref": "#/components/schemas/product"
        receiver_address:
          "$ref": "#/components/schemas/receiver_address"
        shipper_address:
          "$ref": "#/components/schemas/shipper_address"
        label_created_at:
          "$ref": "#/components/schemas/date"
        supplier:
          "$ref": "#/components/schemas/supplier"
        product:
          "$ref": "#/components/schemas/parcel_product"
    parcel_product:
      type: object
      required:
      - name
      - supplier_id
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Parcel Plus
        supplier_id:
          type: integer
          example: 1
        supplier_name:
          type: string
          example: B2cEurope
        countries:
          type: array
          items:
            type: string
          example:
          - DE
          - FR
          - IT
    parcel_product_without_countries:
      type: object
      required:
      - name
      - supplier_id
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Parcel Plus
        supplier_id:
          type: integer
          example: 1
        supplier_name:
          type: string
          example: B2cEurope
    pudo_point:
      type: object
      required:
      - name
      - code
      properties:
        name:
          type: string
          description: The name of the PUDO point
          example: Pakketautomaat
        code:
          type: string
          description: The unique identifier of the PUDO point
          example: '261534'
        address_first_line:
          type: string
          example: Werf
        address_second_line:
          type: string
          example: 15 PBA
        zip_code:
          type: string
          example: 1435KP
        city:
          type: string
          example: Rijsenhout
        country_id:
          type: integer
          description: MyParcel4You Country ids
          example: 217
        country_code:
          type: string
          description: ISO 3166-1 alpha-2 country code
          example: NL
        state:
          type: string
        email:
          type: string
          description: The email address of the PUDO point. Not available for all
            suppliers, nor all PUDO points
        phone:
          type: string
          description: The phone number of the PUDO point. Not available for all suppliers,
            nor all PUDO points
        pickup_point:
          type: object
          properties:
            latitude:
              type: number
              example: 52.25857847
              description: Not available for all suppliers, nor all PUDO points
            longitude:
              type: number
              example: 4.71502639
              description: Not available for all suppliers, nor all PUDO points
            distance:
              type: number
              example: 1
              description: The distance in meters between the pickup point and the
                customer receiver address
            description:
              type: string
              example: Dit is een Pakketautomaat met een brievenbus. Hier kunt u pakketten
                voorzien van een barcodelabel versturen. Pakketten en brieven die
                u op werkdagen vóór de lichtingstijd afgeeft worden binnen Nederland
                de volgende dag bezorgd.
              description: The description of the PUDO point. Additional Pudo details
                (holidays, notifications, etc.) can be added here depending on the
                supplier data.
            owner_party:
              type: string
              example: PostNL
              description: A field that indicates the owner of the PUDO point. Not
                available for all suppliers, nor all PUDO points. In general the owner
                would be the supplier of the PUDO point..
            code:
              type: string
              example: 261534
              description: The unique identifier of the PUDO point. Same as the PUDO
                point code in the parent object.
            additional_codes:
              type: object
              description: Additional codes that can be used to identify the PUDO
                point. The object can vary, but the structure will always be a key-value
                pair.
            metadata:
              type: object
              description: Additional metadata that can be used to identify the PUDO
                point. The object can vary, but the structure will always be a key-value
                pair.
            opening_hours:
              type:
              - array
              - 'null'
              required: false
              items:
                type: object
                properties:
                  day:
                    type: string
                    example: Monday
                  open:
                    type: string
                    example: '07:00'
                  close:
                    type: string
                    example: '20:00'
    product:
      type: object
      required:
      - description
      - sku
      - hs_code
      - quantity
      - item_composition
      - price_value
      - price_currency
      properties:
        description:
          type: string
          example: Lorem ipsum dolor sit amet
          minLength: 1
          maxLength: 2000
        country_name:
          type: string
          example: Poland
        country:
          type: string
          example: PL
          maxLength: 4
        sku:
          type: string
          example: KS944RUR
          minLength: 1
          maxLength: 30
        purchase_web_address:
          type: string
          example: https://example-store.pl
          minLength: 1
          pattern: |-
            (?x-mi:(?=(?-mix:http|https):)
                    ([a-zA-Z][\-+.a-zA-Z\d]*):                           (?# 1: scheme)
                    (?:
                       ((?:[\-_.!~*'()a-zA-Z\d;?:@&=+$,]|%[a-fA-F\d]{2})(?:[\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*)                    (?# 2: opaque)
                    |
                       (?:(?:
                         \/\/(?:
                             (?:(?:((?:[\-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)?        (?# 3: userinfo)
                               (?:((?:(?:[a-zA-Z0-9\-.]|%\h\h)+|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))(?::(\d*))?))? (?# 4: host, 5: port)
                           |
                             ((?:[\-_.!~*'()a-zA-Z\d$,;:@&=+]|%[a-fA-F\d]{2})+)                 (?# 6: registry)
                           )
                         |
                         (?!\/\/))                           (?# XXX: '\/\/' is the mark for hostport)
                         (\/(?:[\-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[\-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:\/(?:[\-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[\-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)?                    (?# 7: path)
                       )(?:\?((?:[\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))?                 (?# 8: query)
                    )
                    (?:\#((?:[\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))?                  (?# 9: fragment)
                  )
        hs_code:
          type: string
          example: '8501.10'
          minLength: 1
          maxLength: 10
        item_composition:
          type: string
          example: Lorem ipsum
          minLength: 1
          maxLength: 2000
        quantity:
          type: integer
          example: 10
        cpc_code:
          type: string
          example: 4407 12 90 90
          minLength: 1
          maxLength: 30
        price_value:
          description: Price for the product, representing the cost of a single unit
            of the product.
          type: string
          example: '1000.0'
          minLength: 1
          pattern: "(?-mix:\\d+(\\.\\d+)?)"
        price_currency:
          description: Currency of the price_value. If nothing is specified, the default
            currency is EUR.
          type: string
          example: PLN
          minLength: 0
        warehouse_number:
          type: string
          example: 01-01-1-A
          minLength: 1
          maxLength: 30
    supplier:
      type: object
      required:
      - id
      - full_name
      - full_name_myp4u
      - contact_name
      - contact_position
      - contact_email
      - phone
      - address
      - website
      - tax_id
      properties:
        id:
          type: integer
          example: 1
        full_name:
          type: string
          example: Example Supplier
        full_name_myp4u:
          type: string
          example: Example Supplier
        contact_name:
          type: string
          example: Example Contact Name
        contact_position:
          type: string
          example: Example Contact Position
        contact_email:
          type: string
          example: Example Contact Email
        phone:
          type: string
          example: '123456789'
        address:
          type: string
          example: Apt. 623 33017 Loma Roads, Jacobsonbury, NV 70326-8244
        website:
          type: string
          example: Example Website
        tax_id:
          type: string
          example: Example Tax ID
    id:
      type: number
      maxLength: 10
      example: '42'
    date:
      type: string
      pattern: "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} UTC"
      example: 2021-09-12 20:20:20 UTC
    country:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: United States
        code:
          type: string
          example: US
        continent_name:
          type: string
          example: North America
    measurement:
      type: number
      format: float
      example: 21.37
    price_value:
      type: string
      description: Price Value, comma separated, 2 decimal places
      pattern: "\\d{1,}(\\.\\d{1,2})?"
    parcel_status:
      type: string
      enum:
      - created
      - information_received
      - in_transit
      - out_for_delivery
      - customs_clearance_delayed
      - attempted_delivery
      - ready_for_pickup
      - delivered_to_pickup
      - delivered
      - returned
      - undeliverable
      - unknown
      example: created
    parcel_response:
      type: object
      properties:
        id:
          type: number
          example: 2434678
        price_value:
          "$ref": "#/components/schemas/price_value"
        price_currency:
          type: string
          example: PLN
        shipper_address:
          "$ref": "#/components/schemas/address_response"
        receiver_address:
          "$ref": "#/components/schemas/address_response"
        pickup_point_address:
          nullable: true
          "$ref": "#/components/schemas/address_response"
        pickup_point_id:
          type: string
          nullable: true
          example: '1234567890'
        weight:
          "$ref": "#/components/schemas/measurement"
        length:
          "$ref": "#/components/schemas/measurement"
        width:
          "$ref": "#/components/schemas/measurement"
        height:
          "$ref": "#/components/schemas/measurement"
        shipper_reference:
          type: string
          example: CR345567
        delivered_at:
          nullable: true
          allOf:
          - "$ref": "#/components/schemas/date"
        bulk_parcel_id:
          type: string
          nullable: true
          example: '1234567890'
        products:
          type: array
          items:
            "$ref": "#/components/schemas/product"
        label_created_at:
          allOf:
          - "$ref": "#/components/schemas/date"
          nullable: true
        returned_to_customer_at:
          allOf:
          - "$ref": "#/components/schemas/date"
          nullable: true
        product:
          "$ref": "#/components/schemas/parcel_product_without_countries"
        hub:
          type: string
          example: EIN
        supplier_system_number:
          type: string
          example: 20430-460
        supplier_order_number:
          type: string
          example: '345645666888'
        tracking_number:
          type: string
          example: JJD000033509000461687787
        tracking_url:
          type: string
          example: https://example.com/tracking/JJD000033509000461687787
        status:
          "$ref": "#/components/schemas/parcel_status"
        created_at:
          "$ref": "#/components/schemas/date"
        updated_at:
          "$ref": "#/components/schemas/date"
        is_label_attached:
          type: boolean
          example: true
        label:
          nullable: true
          type: string
          format: byte
          example: base64 encoded string
        accepted_at:
          allOf:
          - "$ref": "#/components/schemas/date"
          nullable: true
        sent_to_transit_at:
          allOf:
          - "$ref": "#/components/schemas/date"
          nullable: true
    products:
      type: object
      properties:
        products:
          type: array
          items:
            "$ref": "#/components/schemas/parcel_product"
    pudo_points:
      type: array
      items:
        "$ref": "#/components/schemas/pudo_point"
    not_found_response:
      type: object
      properties:
        error:
          type: string
          example: Could not find object by 'id'=8
    unauthorized_response:
      type: object
      properties:
        error:
          type: string
          example: You need to sign in or sign up before continuing.
    bad_request_response:
      type: object
      properties:
        error:
          type: string
          example: Bad Request
    download_file_response:
      type: string
      format: binary
paths:
  "/customer_api/v1/parcels/{id}/label":
    get:
      summary: Get a Parcel Label
      tags:
      - Labels
      security:
      - Bearer: []
      parameters:
      - name: id
        in: path
        required: true
        description: Parcel ID
        schema:
          type: string
      responses:
        '200':
          description: Label found
          content:
            application/pdf:
              schema:
                "$ref": "#/components/schemas/download_file_response"
        '404':
          description: Parcel not found
          content:
            application/pdf:
              schema:
                "$ref": "#/components/schemas/not_found_response"
        '401':
          description: Unauthorized
          content:
            application/pdf:
              schema:
                "$ref": "#/components/schemas/unauthorized_response"
  "/customer_api/v1/parcels":
    post:
      summary: Create a Parcel
      tags:
      - Parcel
      security:
      - Bearer: []
      parameters:
      - name: include_label
        in: query
        required: true
        schema:
          type: boolean
        description: Include the generated label in field 'label' in the response.
      responses:
        '201':
          description: Parcel created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/parcel_response"
        '422':
          description: Invalid request
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/bad_request_response"
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/unauthorized_response"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/create_parcel"
  "/customer_api/v1/parcels/{id}":
    get:
      summary: Get Parcel details
      tags:
      - Parcel
      security:
      - Bearer: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: include_label
        in: query
        required: false
        schema:
          type: boolean
        example: true
        description: Include the generated label in field 'label' in the response.
      responses:
        '200':
          description: parcel found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/parcel_response"
        '404':
          description: Parcel not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/not_found_response"
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/unauthorized_response"
  "/customer_api/v1/products/":
    get:
      summary: Retrieve available products for country/supplier
      tags:
      - Products
      security:
      - Bearer: []
      parameters:
      - name: country_ids[]
        in: query
        required: false
        schema:
          type: array
          items:
            type: integer
        example:
        - 217
        - 65
        - 16
        description: Country IDs
      - name: supplier_ids[]
        in: query
        required: false
        schema:
          type: array
          items:
            type: integer
        example:
        - 3
        - 4
        description: Supplier IDs
      responses:
        '200':
          description: Successful obtain a list of products
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/products"
        '404':
          description: Parcel not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/not_found_response"
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/unauthorized_response"
  "/customer_api/v1/pudo_points/":
    get:
      summary: Retrieve available Pudo Points for country/supplier
      tags:
      - Pudo Points
      security:
      - Bearer: []
      parameters:
      - name: supplier
        in: query
        required: true
        schema:
          type: integer
        example: 3
        description: Supplier ID
      - name: product_id
        in: query
        required: false
        schema:
          type: integer
        example: 307
        description: Product ID - ID available in products endpoint
      - name: country_code
        in: query
        required: true
        schema:
          type: string
        example: NL
        description: Country Code
      - name: city
        in: query
        required: false
        schema:
          type: string
        example: Rijsenhout
        description: City
      - name: zip_code
        in: query
        required: true
        schema:
          type: string
        example: 1435KP
        description: Zip Code
      - name: address_first_line
        in: query
        required: true
        schema:
          type: string
        example: Werf 15
        description: Address First Line
      - name: address_second_line
        in: query
        required: false
        schema:
          type: string
        example: ''
        description: Address Second Line
      - name: state
        in: query
        required: false
        schema:
          type: string
        example: ''
        description: State
      - name: number_of_pudos
        in: query
        required: false
        schema:
          type: integer
        example: 5
        description: Number of Pudo Points to retrieve. Default is 5.
      - name: max_distance
        in: query
        required: false
        schema:
          type: integer
        example: 10000
        description: Max distance from the address in meters
      responses:
        '200':
          description: Successful obtain a list of 5 pudo points for GLS DK
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/pudo_points"
