openapi: 3.1.0
info:
  title: FKTG Customer OMS Open API
  version: 1.2.0
  summary: Rate API, Ship API and Tracking API for customer OMS integration
  description: >-
    Customer OMS 공개 계약. 2026-10-19 운영 DB와 앱에 반영했습니다. 고객사별 권한과 실제 운송사 수락 여부는 별도 확인 대상입니다. shipment_id는 내부 UUID와
    별개의 opaque 공개 ID입니다. 중복 범위는 company_id + external_shipment_ref이며 credential 간 공유됩니다. 별도 샌드박스는 제공되지 않습니다.
    Idempotency-Key, outbound webhook, 공개 재고/피킹 API를 지원하지 않습니다.

    2026-11-05: Rate returns stable FKTG service_code values. Select one quote, then create one shipment with
    that code. Legacy carrier + service_name requests remain supported. Examples use synthetic codes; copy a
    code returned for your account instead.
servers:
  - url: https://www.fktg.co.kr
    description: Production (not a sandbox; verify deployed contract before use)
tags:
  - name: Auth
    description: Client credential based token issuance
  - name: Rate API
    description: Customer-facing multi-carrier service quote APIs
  - name: Ship API
    description: Shipment lifecycle, agent AWB lookup and FKTG house label PDF
  - name: Tracking API
    description: Stored shipment status and event history
security:
  - bearerAuth: []
paths:
  /api/open/v1/auth/token:
    post:
      tags:
        - Auth
      summary: Issue access token
      description: Issue a Bearer access token using client_id and client_secret.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenIssueRequest'
            examples:
              default:
                value:
                  client_id: hugo_client
                  client_secret: secret_example_value
      responses:
        '200':
          description: Token issued successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenIssueResponse'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Revoked credential
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: 404 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/open/v1/rates/quotes:
    post:
      tags:
        - Rate API
      summary: Quote customer-facing rates across eligible carrier services
      description: >-
        Returns all applicable carrier/service quotes for the authenticated company. Omitted
        declared_packaging_type defaults to BOX. For one handling unit, use the top-level field; when packages
        is present, each array entry takes precedence and its omitted declaration defaults to BOX, without
        inheriting the top-level declaration. PALLET/SKID requires the actual declared_pallet_piece_count for
        every unit. UPS BOX/CRATE is not eligible for Worldwide Express Freight; PALLET/SKID is not eligible
        for UPS parcel services.

        Destination-country filtering, active flags, internal service inclusion rules,

        company carrier pricing settings, margins/discounts, and peak surcharge rules are applied.

        If one carrier quote fails, successful quotes are still returned and failures are listed in
        `unavailable_quotes`.

        A YunExpress destination missing from an allowlist-only local coverage table can return a
        contracted-rate quote

        with optional `requires_carrier_confirmation: true`. Display a carrier-confirmation notice alongside
        the price;

        quote success is not proof of carrier acceptance. Acceptance is resolved when the label is issued.

        Explicit blocked destinations and other country, service, weight and customs restrictions still apply.

        Quoting never creates a YunExpress order or calls its price-trial API.


        Required credential scope: rates.read.

        Return eligible services without creating a shipment, FKTG AWB or carrier label. Compare quotes,
        select one service_code and submit one Ship create request. A quote does not reserve a price or
        guarantee carrier acceptance. Optional service_code filters to that one eligible service; unknown,
        inactive or ineligible selections return 422 CARRIER_SERVICE_NOT_CUSTOMER_SELECTABLE without falling
        back. Malformed codes return 400 INVALID_REQUEST.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - weight
                - dimensions
                - recipient
              properties:
                declared_packaging_type:
                  $ref: '#/components/schemas/DeclaredPackagingType'
                declared_pallet_piece_count:
                  $ref: '#/components/schemas/DeclaredPalletPieceCount'
                weight:
                  type: number
                  minimum: 0.001
                dimensions:
                  type: object
                  required:
                    - length
                    - width
                    - height
                  properties:
                    length:
                      type: number
                      minimum: 0.001
                    width:
                      type: number
                      minimum: 0.001
                    height:
                      type: number
                      minimum: 0.001
                recipient:
                  type: object
                  required:
                    - city
                    - country
                    - postal_code
                  properties:
                    city:
                      type: string
                    state:
                      type:
                        - string
                        - 'null'
                    country:
                      type: string
                      minLength: 2
                      maxLength: 2
                    postal_code:
                      type: string
                    company_name:
                      type:
                        - string
                        - 'null'
                packages:
                  type: array
                  description: >-
                    Per-unit declared_packaging_type (BOX, CRATE, PALLET, SKID or null) takes precedence over
                    the top-level declaration. Omission on each unit defaults to BOX; PALLET/SKID requires
                    declared_pallet_piece_count. Top-level declarations are not copied to the array.
                  items:
                    type: object
                    required:
                      - weight
                    properties:
                      declared_packaging_type:
                        $ref: '#/components/schemas/DeclaredPackagingType'
                      declared_pallet_piece_count:
                        $ref: '#/components/schemas/DeclaredPalletPieceCount'
                      package_index:
                        type: integer
                        minimum: 1
                      weight:
                        type: number
                        minimum: 0.001
                      length:
                        type: number
                        minimum: 0.001
                      width:
                        type: number
                        minimum: 0.001
                      height:
                        type: number
                        minimum: 0.001
                item_count:
                  type: integer
                  minimum: 1
                  maximum: 1000
                  description: >-
                    Required for EU27. Declared Item count: group matching HS first 6 digits + product name +
                    origin; quantity does not multiply the count. Yun local quote policy before 2026-11-01
                    KST: KRW5400/item; after: KRW9000/item (EUR3 duty + EUR2 handling at KRW1800/EUR). VAT
                    advance fees billed separately later. Existing acceptance limits unchanged; deployment and
                    account billing must be verified.
                incoterms:
                  $ref: '#/components/schemas/Incoterms'
                service_code:
                  $ref: '#/components/schemas/PublicServiceCode'
            examples:
              default:
                value:
                  weight: 1.2
                  dimensions:
                    length: 25
                    width: 15
                    height: 10
                  recipient:
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                summary: 'Ordinary box: omitted declaration defaults to BOX'
              box:
                summary: 'BOX: explicit final packaging'
                value:
                  weight: 1.2
                  dimensions:
                    length: 25
                    width: 15
                    height: 10
                  recipient:
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  incoterms: DAP
                  declared_packaging_type: BOX
              crate:
                summary: 'CRATE: explicit final packaging'
                value:
                  weight: 10
                  dimensions:
                    length: 50
                    width: 40
                    height: 30
                  recipient:
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  incoterms: DAP
                  declared_packaging_type: CRATE
              pallet:
                summary: 'PALLET: 12 individual packages on one handling unit'
                value:
                  weight: 80
                  dimensions:
                    length: 120
                    width: 100
                    height: 80
                  recipient:
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  incoterms: DAP
                  packages:
                    - package_index: 1
                      weight: 80
                      length: 120
                      width: 100
                      height: 80
                      declared_packaging_type: PALLET
                      declared_pallet_piece_count: 12
              skid:
                summary: 'SKID: 12 individual packages on one handling unit'
                value:
                  weight: 80
                  dimensions:
                    length: 120
                    width: 100
                    height: 80
                  recipient:
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  incoterms: DAP
                  packages:
                    - package_index: 1
                      weight: 80
                      length: 120
                      width: 100
                      height: 80
                      declared_packaging_type: SKID
                      declared_pallet_piece_count: 12
              selected_service:
                summary: Requote one selected service (synthetic code)
                value:
                  weight: 1.2
                  dimensions:
                    length: 25
                    width: 15
                    height: 10
                  recipient:
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  service_code: FKTG-SVC-0123456789ABCDEF0123456789ABCDEF
      responses:
        '200':
          description: Quotes resolved successfully
          content:
            application/json:
              examples:
                default:
                  value:
                    success: true
                    request:
                      weight: 1.2
                      dimensions:
                        length: 25
                        width: 15
                        height: 10
                      recipient:
                        city: New York
                        state: NY
                        country: US
                        postal_code: '10001'
                    quotes:
                      - carrier_id: example-ups
                        carrier_code: UPS
                        carrier_name: UPS
                        service_name: Worldwide Expedited
                        currency: KRW
                        total_cost: 9900
                        charged_weight: 1.2
                        volume_weight_divisor: 5000
                        service_code: FKTG-SVC-0123456789ABCDEF0123456789ABCDEF
                    unavailable_quotes:
                      - carrier_id: example-fedex
                        carrier_code: FEDEX
                        carrier_name: FedEx
                        service_name: International Priority
                        reason: Rate quote unavailable
                        service_code: FKTG-SVC-FEDCBA9876543210FEDCBA9876543210
              schema:
                type: object
                required:
                  - success
                  - request
                  - quotes
                  - unavailable_quotes
                properties:
                  success:
                    const: true
                  request:
                    type: object
                    required:
                      - weight
                      - dimensions
                      - recipient
                    properties:
                      weight:
                        type: number
                        minimum: 0.001
                      dimensions:
                        type: object
                        required:
                          - length
                          - width
                          - height
                        properties:
                          length:
                            type: number
                            minimum: 0.001
                          width:
                            type: number
                            minimum: 0.001
                          height:
                            type: number
                            minimum: 0.001
                      recipient:
                        type: object
                        required:
                          - city
                          - country
                          - postal_code
                        properties:
                          city:
                            type: string
                          state:
                            type:
                              - string
                              - 'null'
                          country:
                            type: string
                            minLength: 2
                            maxLength: 2
                          postal_code:
                            type: string
                          company_name:
                            type:
                              - string
                              - 'null'
                      packages:
                        type: array
                        items:
                          type: object
                          required:
                            - weight
                          properties:
                            package_index:
                              type: integer
                              minimum: 1
                            weight:
                              type: number
                              minimum: 0.001
                            length:
                              type: number
                              minimum: 0.001
                            width:
                              type: number
                              minimum: 0.001
                            height:
                              type: number
                              minimum: 0.001
                      item_count:
                        type: integer
                        minimum: 1
                        maximum: 1000
                        description: >-
                          Required for EU27. Declared item count, not sum of quantity; provide actual
                          declaration count.
                      incoterms:
                        $ref: '#/components/schemas/Incoterms'
                      service_code:
                        $ref: '#/components/schemas/PublicServiceCode'
                  quotes:
                    type: array
                    items:
                      $ref: '#/components/schemas/RateQuote'
                  unavailable_quotes:
                    type: array
                    items:
                      type: object
                      properties:
                        carrier_id:
                          type: string
                        carrier_code:
                          type: string
                        carrier_name:
                          type: string
                        service_name:
                          type: string
                        reason:
                          type: string
                        service_code:
                          $ref: '#/components/schemas/PublicServiceCode'
                      required:
                        - service_code
        '400':
          description: Invalid JSON or schema validation failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: 404 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: No eligible services available for the requested destination
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: rates.read
  /api/open/v1/shipments:
    post:
      tags:
        - Ship API
      summary: Create shipment
      description: >-
        Create a shipment in FKTG OMS. Omitted packages[].declared_packaging_type defaults to BOX, preserving
        existing ordinary-box requests. Declare CRATE, PALLET or SKID when applicable. UPS Worldwide Express
        Freight requires eligible actual packaging and declared_pallet_piece_count; default BOX compatibility
        does not make an ordinary box eligible for Freight.


        Duplicate prevention:

        - The request is treated as unique by company_id + external_shipment_ref.

        - Re-using the same external_shipment_ref returns 409 conflict.


        Policy notes:

        - packages must contain at least one package.

        - items must contain at least one item.

        - HS Code and origin country are optional.

        - sender_ioss is optional.

        - EU destinations require export_reason; EU sales to individual recipients require merchantProductId
        and nonStandardManufacturerProductId on every item.

        - standardManufacturerProductId is optional. PID fields are strings and preserve leading zeroes.

        - Unsupported carrier/service/incoterms/destination combinations return 422.


        Required credential scope: shipments.write. Use an FKTG service_code from Rate. Without a code, legacy
        carrier + service_name remains supported. If a supplied carrier or name disagrees with the code,
        return 422 SERVICE_SELECTION_CONFLICT; never silently select another service. Unknown/disabled codes
        return 422 CARRIER_SERVICE_NOT_ACTIVE, ambiguous legacy names return 422 CARRIER_SERVICE_AMBIGUOUS,
        and destination/packaging restrictions still apply. Malformed code strings return 400 INVALID_REQUEST.
        The server calculates the selected service rate from the submitted package weights/dimensions,
        recipient, incoterms and declaration items under the authenticated company pricing, then saves freight
        and its snapshot atomically with the shipment. No client-supplied amount or quote token is required.
        Internal calculation requires no extra rates.read scope. Calculation failure returns 422
        RATE_UNAVAILABLE before shipment creation; no zero-fare registration is accepted. This saves an
        estimate, not payment or release approval. Yun supports one package per shipment; multiple packages
        return422 CARRIER_MULTI_PIECE_UNSUPPORTED before any write. Older registration acceptance did not
        imply rating or label support.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShipmentRequest'
            examples:
              selected_service:
                summary: Create once using the selected Rate service_code (synthetic code)
                value:
                  external_order_id: ORD-20260422-1001
                  external_shipment_ref: ORD-20260422-1001-1
                  external_order_ref: MALL-10000421
                  external_channel: hugo-shop
                  order_datetime: '2026-04-22T10:20:00+09:00'
                  incoterms: DAP
                  sender:
                    company_name: HUGO SHOP
                    name: 홍길동
                    phone: '01012345678'
                    email: ops@hugoshop.com
                    address_line1: Seoul, Korea
                  sender_ioss: null
                  recipient:
                    name: John Smith
                    phone: +1-302-555-1234
                    email: john@example.com
                    address_line1: 350 5th Ave
                    address_line2: Floor 10
                    address_line3: Suite 1001
                    city: New York
                    state: NY
                    postal_code: '10001'
                    country: US
                    tax_id: null
                  packages:
                    - package_index: 1
                      weight: 1.2
                      length: 25
                      width: 15
                      height: 10
                    - package_index: 2
                      weight: 2.1
                      length: 40
                      width: 20
                      height: 10
                  items:
                    - sku: SKU-001
                      name_kr: 테스트 상품
                      name_en: Test Product
                      quantity: 2
                      unit_price: 12.5
                      unit_weight: 0.5
                      origin_country: null
                      hs_code: null
                  memo: Gift wrap requested
                  service_code: FKTG-SVC-0123456789ABCDEF0123456789ABCDEF
              fedex_dap:
                value:
                  external_order_id: ORD-20260422-1001
                  external_shipment_ref: ORD-20260422-1001-1
                  external_order_ref: MALL-10000421
                  external_channel: hugo-shop
                  order_datetime: '2026-04-22T10:20:00+09:00'
                  carrier: FEDEX
                  incoterms: DAP
                  sender:
                    company_name: HUGO SHOP
                    name: 홍길동
                    phone: '01012345678'
                    email: ops@hugoshop.com
                    address_line1: Seoul, Korea
                  sender_ioss: null
                  recipient:
                    name: John Smith
                    phone: +1-302-555-1234
                    email: john@example.com
                    address_line1: 350 5th Ave
                    address_line2: Floor 10
                    address_line3: Suite 1001
                    city: New York
                    state: NY
                    postal_code: '10001'
                    country: US
                    tax_id: null
                  packages:
                    - package_index: 1
                      weight: 1.2
                      length: 25
                      width: 15
                      height: 10
                    - package_index: 2
                      weight: 2.1
                      length: 40
                      width: 20
                      height: 10
                  items:
                    - sku: SKU-001
                      name_kr: 테스트 상품
                      name_en: Test Product
                      quantity: 2
                      unit_price: 12.5
                      unit_weight: 0.5
                      origin_country: null
                      hs_code: null
                  memo: Gift wrap requested
                  service_name: International Priority
                summary: Legacy carrier + service_name request (still supported)
              eu_pid:
                value:
                  external_order_id: SYNTHETIC-20261018-1
                  external_shipment_ref: SYNTHETIC-20261018-1-P1
                  carrier: FEDEX
                  service_name: International Priority
                  incoterms: DAP
                  export_reason: SALE
                  sender:
                    company_name: Example Shop
                    name: 테스트 발송인
                    phone: '01000000000'
                    address_line1: Synthetic Seoul address
                  recipient:
                    name: Test Recipient
                    phone: '+490000000000'
                    address_line1: Synthetic Strasse 1
                    address_line2: Test floor
                    address_line3: Test room
                    city: Berlin
                    postal_code: '10115'
                    country: DE
                  packages:
                    - package_index: 1
                      weight: 1.2
                      length: 25
                      width: 15
                      height: 10
                  items:
                    - sku: TEST-SKU-1
                      name_kr: 테스트 상품
                      name_en: Test product
                      quantity: 1
                      unit_price: 10
                      invoice_currency: EUR
                      origin_country: KR
                      merchantProductId: SHOP-TEST-1
                      nonStandardManufacturerProductId: MODEL-TEST-1
                      standardManufacturerProductId: '0012345678905'
              box:
                summary: 'BOX: synthetic shipment; use an available quoted carrier/service'
                value:
                  external_order_id: SYNTHETIC-BOX-1
                  external_shipment_ref: SYNTHETIC-BOX-1-P1
                  carrier: FEDEX
                  service_name: International Priority
                  incoterms: DAP
                  export_reason: SALE
                  sender:
                    company_name: Example Shop
                    name: Test Sender
                    phone: '01000000000'
                    address_line1: Synthetic Seoul address
                  recipient:
                    name: Test Recipient
                    phone: '+10000000000'
                    address_line1: Synthetic address
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  packages:
                    - package_index: 1
                      weight: 1.2
                      length: 25
                      width: 15
                      height: 10
                      declared_packaging_type: BOX
                  items:
                    - sku: TEST-SKU-1
                      name_kr: 테스트 상품
                      name_en: Test product
                      quantity: 1
                      unit_price: 10
                      invoice_currency: USD
                      origin_country: KR
              crate:
                summary: 'CRATE: synthetic shipment; use an available quoted carrier/service'
                value:
                  external_order_id: SYNTHETIC-CRATE-1
                  external_shipment_ref: SYNTHETIC-CRATE-1-P1
                  carrier: FEDEX
                  service_name: International Priority
                  incoterms: DAP
                  export_reason: SALE
                  sender:
                    company_name: Example Shop
                    name: Test Sender
                    phone: '01000000000'
                    address_line1: Synthetic Seoul address
                  recipient:
                    name: Test Recipient
                    phone: '+10000000000'
                    address_line1: Synthetic address
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  packages:
                    - package_index: 1
                      weight: 10
                      length: 50
                      width: 40
                      height: 30
                      declared_packaging_type: CRATE
                  items:
                    - sku: TEST-SKU-1
                      name_kr: 테스트 상품
                      name_en: Test product
                      quantity: 1
                      unit_price: 10
                      invoice_currency: USD
                      origin_country: KR
              pallet:
                summary: 'PALLET: synthetic shipment; use an available quoted carrier/service'
                value:
                  external_order_id: SYNTHETIC-PALLET-1
                  external_shipment_ref: SYNTHETIC-PALLET-1-P1
                  carrier: UPS
                  service_name: Worldwide Express Freight
                  incoterms: DAP
                  export_reason: SALE
                  sender:
                    company_name: Example Shop
                    name: Test Sender
                    phone: '01000000000'
                    address_line1: Synthetic Seoul address
                  recipient:
                    name: Test Recipient
                    phone: '+10000000000'
                    address_line1: Synthetic address
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  packages:
                    - package_index: 1
                      weight: 80
                      length: 120
                      width: 100
                      height: 80
                      declared_packaging_type: PALLET
                      declared_pallet_piece_count: 12
                  items:
                    - sku: TEST-SKU-1
                      name_kr: 테스트 상품
                      name_en: Test product
                      quantity: 24
                      unit_price: 10
                      invoice_currency: USD
                      origin_country: KR
              skid:
                summary: 'SKID: synthetic shipment; use an available quoted carrier/service'
                value:
                  external_order_id: SYNTHETIC-SKID-1
                  external_shipment_ref: SYNTHETIC-SKID-1-P1
                  carrier: UPS
                  service_name: Worldwide Express Freight
                  incoterms: DAP
                  export_reason: SALE
                  sender:
                    company_name: Example Shop
                    name: Test Sender
                    phone: '01000000000'
                    address_line1: Synthetic Seoul address
                  recipient:
                    name: Test Recipient
                    phone: '+10000000000'
                    address_line1: Synthetic address
                    city: New York
                    state: NY
                    country: US
                    postal_code: '10001'
                  packages:
                    - package_index: 1
                      weight: 80
                      length: 120
                      width: 100
                      height: 80
                      declared_packaging_type: SKID
                      declared_pallet_piece_count: 12
                  items:
                    - sku: TEST-SKU-1
                      name_kr: 테스트 상품
                      name_en: Test product
                      quantity: 24
                      unit_price: 10
                      invoice_currency: USD
                      origin_country: KR
      responses:
        '201':
          description: Shipment created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateShipmentResponse'
              examples:
                default:
                  summary: Synthetic saved declared-input estimate
                  value:
                    success: true
                    shipment_id: shp_0123456789abcdef0123456789abcdef
                    fktg_awb: '483920174625'
                    external_order_id: SYNTHETIC-ORDER-1
                    external_shipment_ref: SYNTHETIC-ORDER-1-P1
                    status: created
                    message: 발송물이 성공적으로 생성되었습니다.
                    service_code: FKTG-SVC-0123456789ABCDEF0123456789ABCDEF
                    estimated_rate:
                      currency: KRW
                      total_cost: 42000
                      charged_weight: 3
                      requires_carrier_confirmation: false
                      dimensions_complete: true
                      calculated_at: '2026-11-05T00:00:00.000Z'
        '400':
          description: Invalid JSON or schema validation failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: 404 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Duplicate external_shipment_ref
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                duplicate_external_shipment_ref:
                  value:
                    success: false
                    error:
                      code: DUPLICATE_EXTERNAL_SHIPMENT_REF
                      message: The external shipment reference already exists.
                      retryable: false
                      guide: 기존 발송물을 조회하거나 external_shipment_ref를 새로운 값으로 변경해주세요.
                      field: external_shipment_ref
                      details:
                        shipment_id: shp_0123456789abcdef0123456789abcdef
                        fktg_awb: '483920174625'
                        external_order_id: ORD-20260422-1001
                        external_shipment_ref: ORD-20260422-1001-1
                        status: created
        '422':
          description: Unsupported carrier/service/incoterms policy or unknown carrier service
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rate_unavailable:
                  summary: Selected service cannot be priced; no shipment change is saved
                  value:
                    success: false
                    error:
                      code: RATE_UNAVAILABLE
                      message: >-
                        A rate for the selected service could not be calculated. Retry the rate request before
                        saving the shipment.
                      retryable: false
                      correlation_id: 00000000-0000-4000-8000-000000000001
                yun_multi_piece:
                  summary: Yun multi-piece registration is unsupported
                  value:
                    success: false
                    error:
                      code: CARRIER_MULTI_PIECE_UNSUPPORTED
                      message: Yun supports one package per shipment.
                      retryable: false
                      correlation_id: 00000000-0000-4000-8000-000000000001
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                shipment_persistence_failed:
                  summary: The shipment could not be saved; retrying the same payload will not help
                  value:
                    success: false
                    error:
                      correlation_id: 2b7c1a90-5e4d-4b3a-8f61-0d9c4e2a7b53
                      code: SHIPMENT_PERSISTENCE_FAILED
                      message: Failed to save the shipment.
                      retryable: false
                internal_error:
                  summary: Unexpected server failure; keep the correlation ID for support
                  value:
                    success: false
                    error:
                      correlation_id: 6a0d8c31-2f9b-4e57-a1c8-3d7f5b9e0a24
                      code: INTERNAL_ERROR
                      message: Failed to create shipment.
                      retryable: false
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                awb_generation_failed:
                  summary: FKTG AWB generation failed; the request itself is valid, retry after the advertised delay
                  value:
                    success: false
                    error:
                      correlation_id: 4c9d3b1e-8f7a-4c2d-9e51-6b0a7f2d3c88
                      code: AWB_GENERATION_FAILED
                      message: Failed to generate FKTG AWB. Please retry.
                      retryable: true
                service_unavailable:
                  summary: Transient database/connection failure; safe to retry with backoff
                  value:
                    success: false
                    error:
                      correlation_id: 9e2f6a54-1d3b-4f80-8c47-2a5b9e0d7f16
                      code: SERVICE_UNAVAILABLE
                      message: The service is temporarily unavailable. Please retry.
                      retryable: true
      x-required-scope: shipments.write
  /api/open/v1/shipments/{shipmentId}:
    get:
      tags:
        - Ship API
      summary: Get shipment by shipment ID
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Shipment found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShipmentDetailResponse'
              examples:
                default:
                  value:
                    success: true
                    shipment_id: shp_0123456789abcdef0123456789abcdef
                    fktg_awb: '123456789012'
                    carrier_awb: null
                    external_order_id: ORDER
                    external_shipment_ref: REF
                    status: created
                    carrier: null
                    carrier_name: null
                    service_name: null
                    export_reason: SALE
                    recipient:
                      name: Synthetic Name
                      company_name: null
                      phone: '000'
                      email: null
                      address_line1: Synthetic address
                      address_line2: null
                      address_line3: null
                      city: Berlin
                      state: null
                      postal_code: '10115'
                      country: DE
                    package_summary:
                      package_count: 1
                      actual_weight: 1
                      billable_weight: 1
                      packages:
                        - package_index: 1
                          declared_packaging_type: null
                          declared_pallet_piece_count: null
                          packaging_type: null
                          pallet_piece_count: null
                          weight: 1
                          billable_weight: 1
                          length: 10
                          width: 10
                          height: 10
                    items:
                      - sku: SKU
                        name_kr: 테스트
                        name_en: Test
                        quantity: 1
                        unit_price: 1
                        invoice_currency: EUR
                        unit_weight: 1
                        origin_country: KR
                        origin_country_code: KR
                        hs_code: '610910'
                        merchantProductId: SHOP-001
                        nonStandardManufacturerProductId: MODEL-001
                        standardManufacturerProductId: '0012345678905'
                    timestamps:
                      created_at: null
                      updated_at: null
                      status_updated_at: null
                      external_updated_at: null
                    service_code: null
                    estimated_rate: null
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.read
      description: |-

        Required credential scope: shipments.read.
    patch:
      tags:
        - Ship API
      summary: Update shipment by shipment ID
      description: >-
        Shipment update is allowed only before the shipment reaches FKTG warehouse inbound scan. Packages is a
        full replacement. For a matching package_index, an omitted declared_packaging_type preserves the saved
        declaration; for a new package, omission defaults to BOX. Explicit null clears the declaration.
        Warehouse-confirmed packaging_type and pallet_piece_count are read-only.

        Once the shipment enters `received_at_warehouse` or later equivalent state, updates are rejected.


        Required credential scope: shipments.write. Use an FKTG service_code from Rate. Without a code, legacy
        carrier + service_name remains supported. If a supplied carrier or name disagrees with the code,
        return 422 SERVICE_SELECTION_CONFLICT; never silently select another service. Unknown/disabled codes
        return 422 CARRIER_SERVICE_NOT_ACTIVE, ambiguous legacy names return 422 CARRIER_SERVICE_AMBIGUOUS,
        and destination/packaging restrictions still apply. Malformed code strings return 400 INVALID_REQUEST.
        Omitted selection fields retain the existing selection behavior. State, PID, packaging and incoterms
        validation still apply. Price-affecting changes recalculate freight from effective saved/new inputs.
        Shipment changes, price and snapshot commit together after locking and checking updated_at; concurrent
        edits return 409 and require re-fetch. Rate failure leaves previous data and freight unchanged.
        Metadata-only changes retain the saved estimate. Yun supports one package per shipment; multiple
        packages return422 CARRIER_MULTI_PIECE_UNSUPPORTED before any write. Older registration acceptance did
        not imply rating or label support.
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateShipmentRequest'
            examples:
              recipient_and_packages:
                value:
                  incoterms: DDP
                  sender_ioss: IM2760000742
                  recipient:
                    phone: +1-302-555-5678
                    address_line1: 11 Madison Ave
                    city: New York
                    state: NY
                    postal_code: '10010'
                    country: US
                  packages:
                    - package_index: 1
                      weight: 1.4
                      length: 30
                      width: 18
                      height: 12
                  items:
                    - sku: SKU-001
                      name_kr: 테스트 상품
                      name_en: Test Product
                      quantity: 2
                      unit_price: 12.5
                      unit_weight: 0.5
                      origin_country: null
                      hs_code: null
              eu_pid_full_replacement:
                value:
                  export_reason: SALE
                  items:
                    - sku: TEST-SKU-1
                      name_kr: 테스트 상품
                      name_en: Test product
                      quantity: 2
                      unit_price: 10
                      invoice_currency: EUR
                      merchantProductId: SHOP-TEST-1
                      nonStandardManufacturerProductId: MODEL-TEST-1
                      standardManufacturerProductId: '0012345678905'
              selected_service:
                summary: Change service before inbound/carrier label lock (synthetic code)
                value:
                  service_code: FKTG-SVC-0123456789ABCDEF0123456789ABCDEF
      responses:
        '200':
          description: Shipment updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShipmentDetailResponse'
        '400':
          description: Invalid JSON or schema validation failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Shipment can no longer be updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                not_editable:
                  value:
                    success: false
                    error:
                      code: SHIPMENT_NOT_EDITABLE
                      message: Shipment update is not allowed after warehouse inbound scan.
                      guide: FKTG 센터 입고스캔 전까지만 수정 가능합니다.
        '422':
          description: Unsupported carrier/service/incoterms policy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rate_unavailable:
                  summary: Selected service cannot be priced; no shipment change is saved
                  value:
                    success: false
                    error:
                      code: RATE_UNAVAILABLE
                      message: >-
                        A rate for the selected service could not be calculated. Retry the rate request before
                        saving the shipment.
                      retryable: false
                      correlation_id: 00000000-0000-4000-8000-000000000001
                yun_multi_piece:
                  summary: Yun multi-piece registration is unsupported
                  value:
                    success: false
                    error:
                      code: CARRIER_MULTI_PIECE_UNSUPPORTED
                      message: Yun supports one package per shipment.
                      retryable: false
                      correlation_id: 00000000-0000-4000-8000-000000000001
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.write
  /api/open/v1/shipments/external/{externalShipmentRef}:
    get:
      tags:
        - Ship API
      summary: Get shipment by external shipment reference
      parameters:
        - $ref: '#/components/parameters/ExternalShipmentRef'
      responses:
        '200':
          description: Shipment found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShipmentDetailResponse'
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.read
      description: |-

        Required credential scope: shipments.read.
  /api/open/v1/shipments/fktg-awb/{fktgAwb}:
    get:
      tags:
        - Ship API
      summary: Get shipment by FKTG AWB
      parameters:
        - $ref: '#/components/parameters/FktgAwb'
      responses:
        '200':
          description: Shipment found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShipmentDetailResponse'
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.read
      description: |-

        Required credential scope: shipments.read.
  /api/open/v1/shipments/{shipmentId}/cancel:
    post:
      tags:
        - Ship API
      summary: Cancel shipment
      description: |-
        Shipment cancel is allowed only before the shipment reaches FKTG warehouse inbound scan.

        Required credential scope: shipments.write.
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Shipment cancelled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelShipmentResponse'
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Shipment can no longer be cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                not_cancellable:
                  value:
                    success: false
                    error:
                      code: SHIPMENT_NOT_CANCELLABLE
                      message: Shipment cancel is not allowed after warehouse inbound scan.
                      guide: FKTG 센터 입고스캔 전까지만 취소 가능합니다.
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.write
  /api/open/v1/shipments/{shipmentId}/tracking:
    get:
      tags:
        - Tracking API
      summary: Get tracking events
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Tracking payload returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrackingResponse'
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.read
      description: |-

        Required credential scope: shipments.read.
  /api/open/v1/shipments/{shipmentId}/awb:
    get:
      tags:
        - Ship API
      summary: Get FKTG and agent waybill numbers
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: AWB data returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AwbResponse'
              examples:
                assigned:
                  value:
                    success: true
                    shipment_id: shp_0123456789abcdef0123456789abcdef
                    fktg_awb: '123456789012'
                    carrier_awb: '001234567890'
                    external_order_id: SYNTHETIC-ORDER-1
                    external_shipment_ref: SYNTHETIC-ORDER-1-P1
                    status: carrier_label_printed
                awaiting_assignment:
                  value:
                    success: true
                    shipment_id: shp_0123456789abcdef0123456789abcdef
                    fktg_awb: '123456789012'
                    carrier_awb: null
                    external_order_id: SYNTHETIC-ORDER-1
                    external_shipment_ref: SYNTHETIC-ORDER-1-P1
                    status: created
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.read
      description: >-
        Ship API returns the saved FKTG number (fktg_awb) and agent/carrier number (carrier_awb).

        carrier_awb is null until assigned and saved; refetch this endpoint to obtain it later.

        Preserve both values as strings, including leading zeros. This GET does not issue a carrier label.

        Required credential scope: shipments.read. Only shipments belonging to the credential's company are
        accessible.
  /api/open/v1/shipments/{shipmentId}/label:
    get:
      tags:
        - Ship API
      summary: Download FKTG label PDF
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: PDF blob response
          headers:
            Content-Type:
              schema:
                type: string
                example: application/pdf
            Content-Disposition:
              schema:
                type: string
                example: inline; filename="483920174625.pdf"
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '400':
          description: 400 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 403 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Shipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 409 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 422 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 429 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 500 error; see error.code and correlation_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            503 error; retryable=true. Honour Retry-After, then retry the same payload with backoff. See
            error.code for the fixed server code and correlation_id for support
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-required-scope: shipments.read
      description: |-

        Required credential scope: shipments.read.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Opaque token; never decode it or infer permissions. Use the issued scope string.
  parameters:
    ShipmentId:
      name: shipmentId
      in: path
      required: true
      description: Stable opaque public shipment ID, never an internal database UUID
      schema:
        type: string
        minLength: 1
        pattern: ^shp_[a-f0-9]{32}$
      example: shp_0123456789abcdef0123456789abcdef
    ExternalShipmentRef:
      name: externalShipmentRef
      in: path
      required: true
      description: Company-scoped external reference (shared across credentials)
      schema:
        type: string
        minLength: 1
      example: ORD-20260422-1001-1
    FktgAwb:
      name: fktgAwb
      in: path
      required: true
      description: 12-digit FKTG AWB
      schema:
        type: string
        pattern: ^[0-9]{12}$
      example: '483920174625'
  schemas:
    TokenIssueRequest:
      type: object
      required:
        - client_id
        - client_secret
      properties:
        client_id:
          type: string
          minLength: 1
        client_secret:
          type: string
          minLength: 1
    TokenIssueResponse:
      type: object
      required:
        - access_token
        - token_type
        - expires_in
        - scope
      properties:
        access_token:
          type: string
        token_type:
          type: string
          enum:
            - Bearer
        expires_in:
          type: integer
          minimum: 1
          example: 3600
        scope:
          type: string
          description: >-
            Space-separated permissions actually granted to this credential. Empty means no scopes; not all
            credentials have all permissions.
    Sender:
      type: object
      required:
        - company_name
        - name
        - phone
        - address_line1
      properties:
        company_name:
          type: string
        name:
          type: string
        phone:
          type: string
        email:
          type:
            - string
            - 'null'
          format: email
        address_line1:
          type: string
    Recipient:
      type: object
      required:
        - name
        - phone
        - address_line1
        - city
        - postal_code
        - country
      properties:
        name:
          type: string
        phone:
          type: string
        email:
          type:
            - string
            - 'null'
          format: email
        address_line1:
          type: string
        address_line2:
          type:
            - string
            - 'null'
        address_line3:
          type:
            - string
            - 'null'
        city:
          type: string
        state:
          type:
            - string
            - 'null'
        postal_code:
          type: string
        country:
          type: string
          minLength: 2
          maxLength: 2
          description: ISO 3166-1 alpha-2 recommended
        tax_id:
          type:
            - string
            - 'null'
        company_name:
          type:
            - string
            - 'null'
          description: >-
            Current software uses a nonempty company name for B2B classification; this is not a legal
            classification.
    DeclaredPackagingType:
      type:
        - string
        - 'null'
      enum:
        - BOX
        - CRATE
        - PALLET
        - SKID
        - null
      description: >-
        Sender-declared final handling unit. BOX means a paper carton or customer packaging (MY PACKAGE);
        CRATE means a wooden box; PALLET means a pallet; SKID means a skid. MY_PACKAGE is not an input value.
        Omission defaults to BOX on creation and rate quotes; existing ordinary-box requests need no new
        field. For rate quotes, packages entries take precedence over top-level declarations and do not
        inherit them. On PATCH, omission preserves the saved declaration for the same package_index and
        defaults to BOX for a new package; explicit null clears it. Legacy or explicitly cleared declarations
        can read back as null. Separate from warehouse-confirmed packaging_type. UPS BOX/CRATE cannot use
        Worldwide Express Freight; PALLET/SKID cannot use UPS parcel services. Each PALLET/SKID needs
        declared_pallet_piece_count. An incompatible service returns HTTP 422
        CARRIER_SERVICE_NOT_CUSTOMER_SELECTABLE or INVALID_PACKAGING_SERVICE depending on the validation
        stage; a provider may return no quote.
    DeclaredPalletPieceCount:
      type:
        - integer
        - 'null'
      minimum: 1
      maximum: 99999
      description: >-
        Number of individual packages loaded on this pallet or skid, not the number of pallets or product
        units. Required for PALLET/SKID and UPS Worldwide Express Freight, including legacy rows without a
        declared type. Each handling unit requires its own count; no automatic default is applied. The total
        cannot exceed 99999. On package PATCH, omission preserves the saved count; null clears it only when
        the effective service and packaging do not require it.
    ShipmentPackage:
      type: object
      required:
        - package_index
        - weight
      properties:
        declared_packaging_type:
          $ref: '#/components/schemas/DeclaredPackagingType'
        declared_pallet_piece_count:
          $ref: '#/components/schemas/DeclaredPalletPieceCount'
        package_index:
          type: integer
          minimum: 1
        weight:
          type: number
          exclusiveMinimum: 0
        length:
          type:
            - number
            - 'null'
          minimum: 0
        width:
          type:
            - number
            - 'null'
          minimum: 0
        height:
          type:
            - number
            - 'null'
          minimum: 0
    ShipmentItem:
      type: object
      required:
        - name_kr
        - name_en
        - quantity
        - unit_price
      properties:
        sku:
          type:
            - string
            - 'null'
        name_kr:
          type: string
        name_en:
          type: string
        quantity:
          type: integer
          minimum: 1
        unit_price:
          type: number
          minimum: 0
        unit_weight:
          type:
            - number
            - 'null'
          minimum: 0
        origin_country:
          type:
            - string
            - 'null'
          description: >-
            Optional ISO alpha-2. Same-value aliases accepted; conflicting origin_country_code rejected. Both
            readback aliases represent the stored origin.
        hs_code:
          type:
            - string
            - 'null'
          description: Optional. Additional customs info may be requested later.
        invoice_currency:
          type: string
          enum:
            - USD
            - KRW
            - JPY
            - CNY
            - GBP
            - EUR
          default: USD
        origin_country_code:
          type:
            - string
            - 'null'
          description: >-
            Alias for origin_country; when both nonempty values are supplied they must agree
            (case-insensitive).
    ProductIdentifier:
      type:
        - string
        - 'null'
      maxLength: 200
      description: >-
        Printable ASCII identifier or comma-separated identifiers. Each identifier must contain 2-70
        characters; the entire field is limited to 200 characters. Leading zeroes are preserved. Duplicate
        identifiers and placeholders such as N/A, NA, NULL, NONE, UNKNOWN, all-zero or all-nine values are
        rejected.
      example: 001234,MODEL-02
    CreateShipmentItem:
      allOf:
        - $ref: '#/components/schemas/ShipmentItem'
        - type: object
          properties:
            merchantProductId:
              allOf:
                - $ref: '#/components/schemas/ProductIdentifier'
              description: >-
                M-PID. Required for EU SALE shipments to individual recipients. Shipper-assigned product
                identifier; use Amazon ASIN/FNSKU or the platform/store product-management SKU. May equal
                NS-PID; supply both fields explicitly.
            nonStandardManufacturerProductId:
              allOf:
                - $ref: '#/components/schemas/ProductIdentifier'
              description: >-
                NS-PID. Required for EU SALE shipments to individual recipients. Shipper-assigned product
                identifier; use Amazon ASIN/FNSKU or the platform/store product-management SKU. May equal
                M-PID; never auto-filled from it.
            standardManufacturerProductId:
              allOf:
                - $ref: '#/components/schemas/ProductIdentifier'
              description: >-
                S-PID. Optional international-standard barcode assigned by the manufacturer or brand, such as
                GTIN, EAN, UPC or ISBN. Preserve leading zeroes as a string.
    CreateShipmentRequest:
      type: object
      required:
        - external_order_id
        - external_shipment_ref
        - incoterms
        - sender
        - recipient
        - packages
        - items
      properties:
        external_order_id:
          type: string
        external_shipment_ref:
          type: string
          description: Unique per company scope
        external_order_ref:
          type:
            - string
            - 'null'
        external_channel:
          type:
            - string
            - 'null'
        order_datetime:
          type:
            - string
            - 'null'
          format: date-time
        carrier:
          type: string
          example: FEDEX
        incoterms:
          $ref: '#/components/schemas/Incoterms'
        sender:
          $ref: '#/components/schemas/Sender'
        sender_ioss:
          type:
            - string
            - 'null'
          description: Optional. Useful for EU destinations.
        export_reason:
          type: string
          enum:
            - SALE
            - GIFT
            - SAMPLE
            - RETURN
            - REPAIR
            - INTERCOMPANYDATA
            - PERSONAL_EFFECTS
            - OTHER
          description: >-
            Required for EU destinations. Use the actual purpose; missing EU purpose returns 422
            INVALID_EU_EXPORT_REASON.
        recipient:
          $ref: '#/components/schemas/Recipient'
        packages:
          type: array
          minItems: 1
          description: >-
            Each final handling unit accepts declared_packaging_type: BOX (paper carton/customer packaging, MY
            PACKAGE), CRATE (wooden box), PALLET or SKID; explicit null is allowed. Omission defaults to BOX.
            PALLET/SKID requires declared_pallet_piece_count.
          items:
            $ref: '#/components/schemas/ShipmentPackage'
        items:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/CreateShipmentItem'
        memo:
          type:
            - string
            - 'null'
        service_name:
          type: string
          description: >-
            Legacy selection: exact Rate service_name together with carrier. Optional when service_code is
            provided; if supplied, it must match that code.
          example: International Priority
        service_code:
          $ref: '#/components/schemas/PublicServiceCode'
          description: >-
            Use the selected Rate quote service_code. Without a code, both carrier and service_name are
            required. Supplied carrier/name must agree with the code.
      anyOf:
        - required:
            - service_code
        - required:
            - carrier
            - service_name
    PartialSender:
      type: object
      minProperties: 1
      properties:
        company_name:
          type: string
        name:
          type: string
        phone:
          type: string
        email:
          type:
            - string
            - 'null'
          format: email
        address_line1:
          type: string
    PartialRecipient:
      type: object
      minProperties: 1
      properties:
        name:
          type: string
        phone:
          type: string
        email:
          type:
            - string
            - 'null'
          format: email
        address_line1:
          type: string
        address_line2:
          type:
            - string
            - 'null'
        address_line3:
          type:
            - string
            - 'null'
        city:
          type: string
        state:
          type:
            - string
            - 'null'
        postal_code:
          type: string
        country:
          type: string
          minLength: 2
          maxLength: 2
        tax_id:
          type:
            - string
            - 'null'
        company_name:
          type:
            - string
            - 'null'
          description: >-
            Current software uses a nonempty company name for B2B classification; this is not a legal
            classification.
    UpdateShipmentRequest:
      type: object
      minProperties: 1
      description: >-
        Partial shipment update; items and packages, when supplied, replace the complete arrays. Send every
        item including its PIDs. SKU or array order never identifies an old item. DB transaction locks parent,
        rechecks editable state, replaces children and binds PID snapshot to new identities. Omitted fields
        remain unchanged. Issued carrier label or warehouse scan blocks update.
      properties:
        external_order_ref:
          type:
            - string
            - 'null'
        external_channel:
          type:
            - string
            - 'null'
        carrier:
          type: string
        incoterms:
          $ref: '#/components/schemas/Incoterms'
        memo:
          type:
            - string
            - 'null'
        sender_ioss:
          type:
            - string
            - 'null'
        sender:
          $ref: '#/components/schemas/PartialSender'
        recipient:
          $ref: '#/components/schemas/PartialRecipient'
        packages:
          type: array
          minItems: 1
          description: >-
            Full replacement. declared_packaging_type accepts BOX, CRATE, PALLET, SKID or null. At the same
            package_index, omission preserves the saved declaration; omission on a new unit defaults to BOX.
            Explicit null clears the declaration.
          items:
            $ref: '#/components/schemas/ShipmentPackage'
        items:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/CreateShipmentItem'
        service_name:
          type: string
        export_reason:
          type: string
          enum:
            - SALE
            - GIFT
            - SAMPLE
            - RETURN
            - REPAIR
            - INTERCOMPANYDATA
            - PERSONAL_EFFECTS
            - OTHER
          description: >-
            Required for EU destinations. Use the actual purpose; missing EU purpose returns 422
            INVALID_EU_EXPORT_REASON.
        service_code:
          $ref: '#/components/schemas/PublicServiceCode'
          description: >-
            Select another eligible service before the shipment becomes immutable. Omission preserves existing
            selection behavior. Supplied carrier/name must agree with the code.
    CreateShipmentResponse:
      type: object
      required:
        - success
        - shipment_id
        - fktg_awb
        - external_order_id
        - external_shipment_ref
        - status
        - message
        - service_code
        - estimated_rate
      properties:
        success:
          type: boolean
          const: true
        shipment_id:
          type: string
          pattern: ^shp_[a-f0-9]{32}$
        fktg_awb:
          type: string
          pattern: ^[0-9]{12}$
        external_order_id:
          type: string
        external_shipment_ref:
          type: string
        status:
          type: string
          example: created
        message:
          type: string
          example: Shipment created successfully.
        service_code:
          $ref: '#/components/schemas/PublicServiceCode'
        estimated_rate:
          $ref: '#/components/schemas/ShipmentEstimatedRate'
    CancelShipmentResponse:
      type: object
      required:
        - success
        - shipment_id
        - fktg_awb
        - external_order_id
        - external_shipment_ref
        - status
        - message
      properties:
        success:
          type: boolean
          const: true
        shipment_id:
          type: string
          pattern: ^shp_[a-f0-9]{32}$
        fktg_awb:
          type: string
          pattern: ^[0-9]{12}$
        external_order_id:
          type: string
        external_shipment_ref:
          type: string
        status:
          type: string
          const: cancelled
        message:
          type: string
          example: Shipment cancelled successfully.
    ShipmentStatus:
      type: string
      enum:
        - created
        - pending_classification
        - ready_for_label
        - label_printed
        - carrier_label_printed
        - received_at_warehouse
        - shipped
        - in_transit
        - customs
        - delivered
        - returned
        - cancelled
        - exception
        - delivery_failed
    Incoterms:
      type: string
      enum:
        - DAP
        - DDP
      description: >-
        FedEx DAP/DDP; UPS DAP/DDP; YUN BR DAP only, all other destinations DDP; SF US DDP only, flexible
        services DAP/DDP elsewhere, E-Commerce Express CD DAP only for TH/TW/HK/CN/SG/VN/MO/PH and DDP
        otherwise. Invalid combinations rejected; no silent payer change. Existing create compatibility: Yun
        non-BR and some SF-CD DAP requests normalize to DDP with explicit INCOTERMS_NORMALIZED_TO_DDP warning.
        Obtain payer consent and request DDP explicitly. Quotes do not normalize. PATCH rejects invalid
        incoterms with 422; it does not normalize DAP to DDP.
    PackageSummaryPackage:
      type: object
      properties:
        declared_packaging_type:
          $ref: '#/components/schemas/DeclaredPackagingType'
        declared_pallet_piece_count:
          $ref: '#/components/schemas/DeclaredPalletPieceCount'
        pallet_piece_count:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 99999
          readOnly: true
          description: >-
            Warehouse-confirmed count of packages loaded on this pallet or skid. Customers cannot set this
            field.
        packaging_type:
          type:
            - string
            - 'null'
          enum:
            - BOX
            - CRATE
            - PALLET
            - SKID
            - null
          readOnly: true
          description: Warehouse-confirmed packaging. Customers cannot set this field.
        package_index:
          type: integer
        weight:
          type:
            - number
            - 'null'
        billable_weight:
          type:
            - number
            - 'null'
        length:
          type:
            - number
            - 'null'
        width:
          type:
            - number
            - 'null'
        height:
          type:
            - number
            - 'null'
    PackageSummary:
      type: object
      properties:
        package_count:
          type:
            - integer
            - 'null'
        actual_weight:
          type:
            - number
            - 'null'
        billable_weight:
          type:
            - number
            - 'null'
        packages:
          type: array
          items:
            $ref: '#/components/schemas/PackageSummaryPackage'
    ShipmentTimestamps:
      type: object
      properties:
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
        status_updated_at:
          type:
            - string
            - 'null'
          format: date-time
        external_updated_at:
          type:
            - string
            - 'null'
          format: date-time
    ShipmentDetailData:
      type: object
      properties:
        shipment_id:
          type: string
          pattern: ^shp_[a-f0-9]{32}$
        fktg_awb:
          type:
            - string
            - 'null'
          pattern: ^[0-9]{12}$
        carrier_awb:
          type:
            - string
            - 'null'
        external_order_id:
          type:
            - string
            - 'null'
        external_shipment_ref:
          type:
            - string
            - 'null'
        status:
          $ref: '#/components/schemas/ShipmentStatus'
        carrier:
          type:
            - string
            - 'null'
        carrier_name:
          type:
            - string
            - 'null'
        service_name:
          type:
            - string
            - 'null'
        recipient:
          type: object
          properties:
            name:
              type: string
            phone:
              type: string
            email:
              type:
                - string
                - 'null'
              format: email
            address_line1:
              type: string
            address_line2:
              type:
                - string
                - 'null'
            address_line3:
              type:
                - string
                - 'null'
            city:
              type: string
            state:
              type:
                - string
                - 'null'
            postal_code:
              type: string
            country:
              type: string
              minLength: 2
              maxLength: 2
              description: ISO 3166-1 alpha-2 recommended
            company_name:
              type:
                - string
                - 'null'
              description: >-
                Current software uses a nonempty company name for B2B classification; this is not a legal
                classification.
        package_summary:
          $ref: '#/components/schemas/PackageSummary'
        items:
          type: array
          items:
            $ref: '#/components/schemas/CreateShipmentItem'
        timestamps:
          $ref: '#/components/schemas/ShipmentTimestamps'
        export_reason:
          type:
            - string
            - 'null'
        service_code:
          anyOf:
            - $ref: '#/components/schemas/PublicServiceCode'
            - type: 'null'
          description: >-
            FKTG code of the associated service; null for historical records with no service relation. Also
            returned by external-reference and FKTG-AWB detail lookup.
        estimated_rate:
          anyOf:
            - $ref: '#/components/schemas/ShipmentEstimatedRate'
            - type: 'null'
          description: >-
            Latest saved declared-input API estimate. Null for historical shipments with no such snapshot. GET
            does not recalculate or charge.
    ShipmentDetailResponse:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean
          const: true
      allOf:
        - $ref: '#/components/schemas/ShipmentDetailData'
    AwbResponse:
      type: object
      required:
        - success
        - shipment_id
        - fktg_awb
        - carrier_awb
        - status
      properties:
        success:
          type: boolean
          const: true
        shipment_id:
          type: string
          pattern: ^shp_[a-f0-9]{32}$
        fktg_awb:
          type:
            - string
            - 'null'
          pattern: ^[0-9]{12}$
        carrier_awb:
          description: >-
            Saved agent/carrier waybill number shown as Agent AWB in the portal; null until assigned. Preserve
            leading zeros.
          type:
            - string
            - 'null'
        external_order_id:
          type:
            - string
            - 'null'
        external_shipment_ref:
          type:
            - string
            - 'null'
        status:
          $ref: '#/components/schemas/ShipmentStatus'
    TrackingEvent:
      type: object
      properties:
        time:
          type:
            - string
            - 'null'
          format: date-time
        code:
          type:
            - string
            - 'null'
        description:
          type:
            - string
            - 'null'
        location:
          type:
            - string
            - 'null'
        status:
          type:
            - string
            - 'null'
    TrackingResponse:
      type: object
      required:
        - success
        - shipment_id
        - tracking_status
        - events
      properties:
        success:
          type: boolean
          const: true
        shipment_id:
          type: string
          pattern: ^shp_[a-f0-9]{32}$
        fktg_awb:
          type:
            - string
            - 'null'
          pattern: ^[0-9]{12}$
        carrier_awb:
          type:
            - string
            - 'null'
        tracking_status:
          $ref: '#/components/schemas/ShipmentStatus'
        events:
          type: array
          items:
            $ref: '#/components/schemas/TrackingEvent'
    ErrorBody:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          example: INVALID_REQUEST
        message:
          type: string
        guide:
          type: string
        field:
          type: string
        details:
          description: Optional error details object
        correlation_id:
          type: string
          format: uuid
          description: Server-generated support correlation ID, also returned in x-correlation-id.
        retryable:
          type: boolean
          description: >-
            Always present on error responses. true only for 429 and 503, which may succeed after the
            Retry-After window; false means the request must be changed before it can succeed. Server 5xx
            codes are a fixed enum: AWB_GENERATION_FAILED / SERVICE_UNAVAILABLE (503, retryable) and
            SHIPMENT_PERSISTENCE_FAILED / INTERNAL_ERROR (500, not retryable).
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: '#/components/schemas/ErrorBody'
    RateQuote:
      type: object
      additionalProperties: false
      required:
        - carrier_id
        - carrier_code
        - carrier_name
        - service_name
        - currency
        - total_cost
        - charged_weight
        - volume_weight_divisor
        - service_code
      properties:
        carrier_id:
          type: string
        carrier_code:
          type: string
        carrier_name:
          type: string
        service_name:
          type: string
        currency:
          type: string
        total_cost:
          type: number
        charged_weight:
          type: number
        volume_weight_divisor:
          type: number
        requires_carrier_confirmation:
          type: boolean
          description: 'Optional true: rate exists but carrier acceptance must still be confirmed.'
        service_code:
          $ref: '#/components/schemas/PublicServiceCode'
    PublicServiceCode:
      type: string
      pattern: ^FKTG-SVC-[0-9A-F]{32}$
      description: >-
        Stable opaque FKTG service identifier returned by Rate API. Not a provider-native service code. Use
        the returned value verbatim; do not derive it from carrier/name or an internal ID.
      example: FKTG-SVC-0123456789ABCDEF0123456789ABCDEF
    ShipmentEstimatedRate:
      type: object
      additionalProperties: false
      description: >-
        Saved estimate calculated by the server from declared Ship inputs and company pricing. Not a settled
        charge, deposit deduction or price reservation. calculated_at identifies this historical estimate;
        final measurement and additional fees may change billing.
      required:
        - currency
        - total_cost
        - charged_weight
        - requires_carrier_confirmation
        - dimensions_complete
        - calculated_at
      properties:
        currency:
          type: string
          pattern: ^[A-Z]{3}$
        total_cost:
          type: number
          exclusiveMinimum: 0
        charged_weight:
          type: number
          exclusiveMinimum: 0
        requires_carrier_confirmation:
          type: boolean
        dimensions_complete:
          type: boolean
          description: >-
            False when any package lacks complete dimensions. The input remains compatible; volumetric
            remeasurement can increase the fare.
        calculated_at:
          type: string
          format: date-time
x-fktg-policy:
  duplicate_prevention:
    key: company_id + external_shipment_ref
  operation_window:
    update: Allowed before warehouse inbound scan
    cancel: Allowed before warehouse inbound scan
  customs_optional_fields:
    hs_code: optional
    origin_country: optional
    sender_ioss: optional
  incoterms_matrix:
    FEDEX:
      allowed:
        - DAP
        - DDP
    UPS:
      allowed:
        - DAP
    YUN:
      allowed_default:
        - DDP
      exceptions:
        - country: BR
          note: Brazil handled by exception policy; unsupported combinations are rejected.
    SF:
      INTERNATIONAL_STANDARD_EXPRESS:
        allowed:
          - DAP
          - DDP
      GLOBAL_STANDARD_PLUS:
        allowed:
          - DAP
          - DDP
      INTERNATIONAL_ECONOMY_EXPRESS:
        allowed:
          - DAP
          - DDP
      E_COMMERCE_EXPRESS_CD:
        allowed:
          - DDP
