openapi: 3.1.0
info:
  title: Locations API
  description: |-
    API for managing locations

    ### Resource identifier format

    Resource identifiers (the `id` field and related global-identifier fields) are returned as JSON
    integers by default. To receive them as strings instead, send the
    `X-Loop-Global-Identifier-Type: string` request header.
  version: "v1"
tags:
  - name: Locations
    description: Operations related to managing locations
servers:
  - url: https://api.loopreturns.com/api/2026-10
    description: 2026-10 (Stable)
  - url: https://api.loopreturns.com/api/2026-07
    description: 2026-07
  - url: https://api.loopreturns.com/api/unstable
    description: Unstable (Preview)
paths:
  /locations:
    post:
      summary: Create Location
      description: Create a new location.
      operationId: createLocation
      tags:
        - Locations
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateLocationRequest"
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    put:
      summary: Upsert Location
      description: Upsert a location, identified using the location's `external_id`.
      operationId: upsertLocation
      tags:
        - Locations
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertLocationRequest"
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    get:
      summary: List Locations
      description: Retrieve a list of locations. The list can be filtered, sorted, and paginated
        using the parameters below.
      operationId: listLocations
      tags:
        - Locations
      parameters:
        - in: query
          name: external_id
          schema:
            type: string
            maxLength: 64
          example: 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          description: "An `external_id` used to filter the locations."
        - in: query
          name: limit
          schema:
            type: integer
            maximum: 250
            default: 50
          example: 20
          description: "The maximum number of locations returned."
        - in: query
          name: sort_order
          schema:
            type: string
            enum:
              - asc
              - desc
            default: asc
          example: desc
          description: "The sort order for the results."
        - in: query
          name: cursor
          schema:
            type: string
          description: "The cursor to return the next page of results."
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                type: object
                required:
                  - locations
                properties:
                  locations:
                    type: array
                    items:
                      $ref: "#/components/schemas/LocationResponse"
                  next_page_url:
                    type: [string, "null"]
                  previous_page_url:
                    type: [string, "null"]
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
      security:
        - read: []
  /locations/{id}:
    get:
      summary: Get Location
      description: Get the details about a single location.
      operationId: getLocation
      tags:
        - Locations
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          description: The location's unique identifier.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - read: []
    put:
      summary: Update Location
      description: Update a location.
      operationId: updateLocation
      tags:
        - Locations
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          description: The location's unique identifier.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateLocationRequest"
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    patch:
      summary: Update Location
      description: Update a location.
      operationId: patchLocation
      tags:
        - Locations
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          description: The location's unique identifier.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateLocationRequest"
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    delete:
      summary: Delete Location
      description: Delete a location.
      operationId: deleteLocation
      tags:
        - Locations
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          description: The location's unique identifier.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationDeleteResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
  /locations/external/{externalId}:
    get:
      summary: Get Location by External ID
      description: >-
        Get the details about a single location by its commerce `external_id`.
      operationId: getLocationByExternalId
      tags:
        - Locations
      parameters:
        - in: path
          name: externalId
          schema:
            type: string
            maxLength: 64
          required: true
          description: The identifier used by an external source to identify the location.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - read: []
    delete:
      summary: Delete Location by External ID
      description: >-
        Delete a location by its commerce `external_id`.
      operationId: deleteLocationByExternalId
      tags:
        - Locations
      parameters:
        - in: path
          name: externalId
          schema:
            type: string
            maxLength: 64
          required: true
          description: The identifier used by an external source to identify the location.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LocationDeleteByExternalIdResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
components:
  schemas:
    ValidationErrorResponse:
      type: object
      description: >-
        Returned when the request body fails validation. Uses Laravel's standard
        validation error shape.
      properties:
        message:
          type: string
          examples:
            - "The name field is required."
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          examples:
            - name:
                - "The name field is required."
    CreateLocationRequest:
      type: object
      properties:
        external_id:
          type: [string, "null"]
          maxLength: 100
          description: "The provided `external_id` must be unique. If a non-unique `external_id` is
            provided, the API will respond with an error."
          examples:
            - 62291d16-9b61-4fbb-b868-21c2d32dc155
        sales_channel:
          type: string
          maxLength: 255
          examples:
            - shopify
          description: "If a channel with this name is not already associated with the shop, it will
            be used to create a new channel."
        name:
          type: string
          maxLength: 100
          description: "The name of the location."
          examples:
            - Main Warehouse
        status:
          type: string
          maxLength: 100
          description: "The status of the location."
          enum:
            - active
            - inactive
        address:
          $ref: "#/components/schemas/AddressRequestData"
          description: "The address of the location."
      required:
        - name
        - status
        - address
        - sales_channel
    AddressRequestData:
      type: object
      properties:
        name:
          type: [string, "null"]
          maxLength: 100
          description: "The name of the address."
          examples:
            - Main Warehouse
        company:
          type: [string, "null"]
          maxLength: 100
          description: "The name of the company residing at the address."
          examples:
            - Universal Exports
        address1:
          type: string
          maxLength: 125
          description: "Line 1 of the address."
          examples:
            - 1234 Example Street
        address2:
          type: [string, "null"]
          maxLength: 125
          description: "Line 2 of the address."
          examples:
            - Unit 19
        city:
          type: string
          maxLength: 100
          description: "The city in which the address is located."
          examples:
            - Townsville
        region:
          type: [string, "null"]
          maxLength: 100
          description: "The state or region in which the address is located."
          examples:
            - Louisiana
        postal_code:
          type: [string, "null"]
          maxLength: 16
          description: "The postal code associated with the address."
          examples:
            - 90210
        country_code:
          type: string
          maxLength: 2
          description: "The country code associated with the address."
          examples:
            - US
      required:
        - address1
        - city
        - country_code
    AddressResponseData:
      type: object
      properties:
        name:
          type: [string, "null"]
          maxLength: 100
          description: "The name of the address."
          examples:
            - Main Warehouse
        company:
          type: [string, "null"]
          maxLength: 100
          description: "The name of the company residing at the address."
          examples:
            - Universal Exports
        address1:
          type: [string, "null"]
          maxLength: 125
          description: "Line 1 of the address."
          examples:
            - 1234 Example Street
        address2:
          type: [string, "null"]
          maxLength: 125
          description: "Line 2 of the address."
          examples:
            - Unit 19
        city:
          type: [string, "null"]
          maxLength: 100
          description: "The city in which the address is located."
          examples:
            - Townsville
        region:
          type: [string, "null"]
          maxLength: 100
          description: "The state or region in which the address is located."
          examples:
            - Louisiana
        postal_code:
          type: [string, "null"]
          maxLength: 16
          description: "The postal code associated with the address."
          examples:
            - 90210
        country_code:
          type: [string, "null"]
          maxLength: 2
          description: "The country code associated with the address."
          examples:
            - US
    UpdateLocationRequest:
      type: object
      properties:
        external_id:
          type: [string, "null"]
          maxLength: 100
          description: "The provided `external_id` must be unique. If a non-unique `external_id` is
            provided, the API will respond with an error."
          examples:
            - 62291d16-9b61-4fbb-b868-21c2d32dc155
        sales_channel:
          type: string
          maxLength: 255
          examples:
            - shopify
          description: "If a channel with this name is not already associated with the shop, it will
            be used to create a new channel."
        name:
          type: string
          maxLength: 100
          description: "The name of the location."
          examples:
            - Main Warehouse
        status:
          type: string
          maxLength: 100
          description: "The status of the location."
          enum:
            - active
            - inactive
        address:
          $ref: "#/components/schemas/AddressRequestData"
          description: "The address of the location."
      required:
        - name
        - status
        - sales_channel
    UpsertLocationRequest:
      type: object
      properties:
        external_id:
          type: string
          maxLength: 100
          description: "The identifier used by an external source to identify the location."
          examples:
            - 62291d16-9b61-4fbb-b868-21c2d32dc155
        sales_channel:
          type: string
          maxLength: 255
          examples:
            - shopify
          description: "If a channel with this name is not already associated with the shop, it will
            be used to create a new channel."
        name:
          type: string
          maxLength: 100
          description: "The name of the location."
          examples:
            - Main Warehouse
        status:
          type: string
          maxLength: 100
          description: "The status of the location."
          enum:
            - active
            - inactive
        address:
          $ref: "#/components/schemas/AddressRequestData"
          description: "The address of the location."
      required:
        - external_id
        - name
        - status
        - sales_channel
    LocationResponse:
      type: object
      required:
        - id
        - sales_channel
        - name
        - status
        - address
      properties:
        id:
          type: integer
          format: int64
          description: "The unique integer identifier for the location, created by Loop."
          examples:
            - 9007199254740991
        external_id:
          type: [string, "null"]
          description: "The identifier used by an external source to identify the location."
          examples:
            - 62291d16-9b61-4fbb-b868-21c2d32dc155
        sales_channel:
          type: string
          description: "The name of the channel."
          examples:
            - shopify
        name:
          type: string
          maxLength: 100
          description: "The name of the location."
          examples:
            - Main Warehouse
        status:
          type: string
          description: "The status of the location."
          enum:
            - active
            - inactive
        address:
          $ref: "#/components/schemas/AddressResponseData"
          description: The address of the location.
    LocationWrappedResponse:
      type: object
      required:
        - location
      properties:
        location:
          $ref: "#/components/schemas/LocationResponse"
    LocationDeleteResponse:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: "The unique integer identifier for the location, created by Loop."
          examples:
            - 9007199254740991
        success:
          type: boolean
          description: Whether the location was successfully deleted.
          examples:
            - true
    LocationDeleteByExternalIdResponse:
      type: object
      required:
        - id
        - external_id
        - success
      properties:
        id:
          type: integer
          format: int64
          description: "The unique integer identifier for the location, created by Loop."
          examples:
            - 9007199254740991
        external_id:
          type: string
          description: The identifier used by an external source to identify the location.
        success:
          type: boolean
          description: Whether the location was successfully deleted.
          examples:
            - true
    UnauthorizedResponse:
      type: object
      required:
        - errors
      properties:
        errors:
          type: string
          examples:
            - "Unauthorized."
    NotFoundResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          examples:
            - "Resource not found."
  securitySchemes:
    read:
      type: apiKey
      in: header
      name: X-Authorization
      description: 'API Scope: "Locations (read)"'
      x-scope: Locations (Read)
    write:
      name: X-Authorization
      type: apiKey
      in: header
      description: 'API Scope: "Locations (write)"'
