openapi: 3.1.0
info:
  title: Customers API
  description: |-
    API for managing customers

    ### 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"
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)
tags:
  - name: Customers
paths:
  /customers:
    post:
      summary: Create Customer
      description: Create a new customer.
      operationId: createCustomer
      tags:
        - Customers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCustomerRequest"
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerWrappedResponse"
        "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 Customers
      description: Retrieve a list of customers. The list can be filtered, sorted, and
        paginated using the parameters below.
      operationId: listCustomers
      tags:
        - Customers
      parameters:
        - name: external_id
          in: query
          description: "An `external_id` used to filter the customers."
          required: false
          schema:
            type: string
            maxLength: 64
            examples:
              - 28hs67udfh-213csss-12d31ja
        - name: limit
          in: query
          description: "The maximum number of customers returned."
          required: false
          schema:
            type: integer
            maximum: 250
            default: 50
            examples:
              - 20
        - name: sort_order
          in: query
          description: "The sort order for the results."
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: asc
            examples:
              - asc
        - name: cursor
          in: query
          description: "The cursor to return the next page of results."
          required: false
          schema:
            type: string
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                type: object
                required:
                  - customers
                properties:
                  customers:
                    type: array
                    items:
                      $ref: "#/components/schemas/CustomerResponse"
                  next_page_url:
                    type: [string, "null"]
                  previous_page_url:
                    type: [string, "null"]
        "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: Upsert Customer
      description: Upsert a customer, identified using the customer's `external_id`.
      operationId: upsertCustomer
      tags:
        - Customers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertCustomerRequest"
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerWrappedResponse"
        "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: []
  /customers/{id}:
    get:
      summary: Get Customer
      description: Get details about a single customer.
      operationId: getCustomer
      tags:
        - Customers
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 67329889100573
          required: true
          description: The unique identifier for the customer, created by Loop.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerWrappedResponse"
        "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 Customer
      description: Update a customer.
      operationId: updateCustomer
      tags:
        - Customers
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 67329889100573
          required: true
          description: The unique identifier for the customer, created by Loop.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCustomerRequest"
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerWrappedResponse"
        "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 Customer
      description: Update a customer.
      operationId: patchCustomer
      tags:
        - Customers
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 67329889100573
          required: true
          description: The unique identifier for the customer, created by Loop.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCustomerRequest"
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerWrappedResponse"
        "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 Customer
      description: Delete a customer.
      operationId: deleteCustomer
      tags:
        - Customers
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 67329889100573
          required: true
          description: The unique identifier for the customer, created by Loop.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerDeleteResponse"
        "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: []
  /customers/external/{externalId}:
    get:
      summary: Get Customer by External ID
      description: >-
        Get details about a single customer by its commerce `external_id`.
      operationId: getCustomerByExternalId
      tags:
        - Customers
      parameters:
        - in: path
          name: externalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 71263csss-12uhsdfh-2138dja
          required: true
          description: The identifier used by an external source to identify the customer.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerWrappedResponse"
        "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 Customer by External ID
      description: >-
        Delete a customer by its commerce `external_id`.
      operationId: deleteCustomerByExternalId
      tags:
        - Customers
      parameters:
        - in: path
          name: externalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 71263csss-12uhsdfh-2138dja
          required: true
          description: The identifier used by an external source to identify the customer.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerDeleteByExternalIdResponse"
        "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 external_id field is required."
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          examples:
            - external_id:
                - "The external_id field is required."
    CreateCustomerRequest:
      type: object
      properties:
        external_id:
          type: [string, "null"]
          maxLength: 100
          examples:
            - 71263csss-12uhsdfh-2138dja
          description: "The provided `external_id` must be unique. If a non-unique
            `external_id` is provided, the API will respond with an error."
        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."
        first_name:
          type: [string, "null"]
          maxLength: 50
          examples:
            - Edgar
          description: The customer's given name.
        last_name:
          type: [string, "null"]
          maxLength: 50
          examples:
            - Martinez
          description: The customer's surname.
        email:
          type: [string, "null"]
          maxLength: 255
          description: The customer's email address.
          format: email
          examples:
            - sample@example.com
        phone:
          type: [string, "null"]
          maxLength: 30
          examples:
            - 678-999-8212
          description: The customer's phone number.
        tags:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/TagRequestData"
          examples:
            - [FirstPurchase, ReturnCustomer]
          description: "An array of tags used to classify and group customers."
      required:
        - sales_channel
    UpdateCustomerRequest:
      type: object
      properties:
        external_id:
          type: [string, "null"]
          maxLength: 100
          examples:
            - 71263csss-12uhsdfh-2138dja
          description: "The identifier used by an external source to identify the customer."
        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."
        first_name:
          type: [string, "null"]
          maxLength: 50
          examples:
            - Edgar
          description: The customer's given name.
        last_name:
          type: [string, "null"]
          maxLength: 50
          examples:
            - Martinez
          description: The customer's surname.
        email:
          type: [string, "null"]
          maxLength: 255
          description: The customer's email address.
          format: email
          examples:
            - sample@example.com
        phone:
          type: [string, "null"]
          maxLength: 30
          examples:
            - 678-999-8212
          description: The customer's phone number.
        tags:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/TagRequestData"
          examples:
            - [FirstPurchase, ReturnCustomer]
          description: "An array of tags used to classify and group customers."
      required:
        - sales_channel
    UpsertCustomerRequest:
      type: object
      properties:
        external_id:
          type: string
          maxLength: 100
          description: "The identifier used by an external source to identify the customer."
          examples:
            - 71263csss-12uhsdfh-2138dja
        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."
        first_name:
          type: [string, "null"]
          maxLength: 50
          examples:
            - Edgar
          description: The customer's given name.
        last_name:
          type: [string, "null"]
          maxLength: 50
          examples:
            - Martinez
          description: The customer's surname.
        email:
          type: [string, "null"]
          maxLength: 255
          description: The customer's email address.
          format: email
          examples:
            - sample@example.com
        phone:
          type: [string, "null"]
          maxLength: 30
          examples:
            - 678-999-8212
          description: The customer's phone number.
        tags:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/TagRequestData"
          examples:
            - [FirstPurchase, ReturnCustomer]
          description: "An array of tags used to classify and group customers."
      required:
        - external_id
        - sales_channel
    TagRequestData:
      description: A tag, accepted either as a plain string or as an object with a `name`.
      oneOf:
        - type: string
          minLength: 1
          maxLength: 255
          description: The tag name, provided as a plain string.
        - type: object
          properties:
            name:
              type: string
              minLength: 1
              maxLength: 255
              description: Name of the tag.
          required:
            - name
    CustomerResponse:
      type: object
      required:
        - id
      properties:
        id:
          type: integer
          format: int64
          description: The unique identifier for the customer, created by Loop.
          examples:
            - 9007199254740991
        external_id:
          type: [string, "null"]
          description: The identifier used by an external source to identify the customer.
          examples:
            - 71263csss-12uhsdfh-2138dja
        sales_channel:
          type: [string, "null"]
          examples:
            - shopify
          description: "The name of the channel associated with the customer."
        first_name:
          type: [string, "null"]
          description: The customer's given name.
          examples:
            - Edgar
        last_name:
          type: [string, "null"]
          description: The customer's surname.
          examples:
            - Martinez
        email:
          type: [string, "null"]
          description: The customer's email address.
          format: email
          examples:
            - sample@example.com
        phone:
          type: [string, "null"]
          description: The customer's phone number.
          examples:
            - 678-999-8212
        tags:
          type: array
          items:
            type: string
          examples:
            - [FirstPurchase, ReturnCustomer]
          description: An array of tags associated with the customer.
    CustomerWrappedResponse:
      type: object
      required:
        - customer
      properties:
        customer:
          $ref: "#/components/schemas/CustomerResponse"
    CustomerDeleteResponse:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: The unique identifier for the customer, created by Loop.
          examples:
            - 9007199254740991
        success:
          type: boolean
          description: Whether the customer was successfully deleted.
          examples:
            - true
    CustomerDeleteByExternalIdResponse:
      type: object
      required:
        - id
        - external_id
        - success
      properties:
        id:
          type: integer
          format: int64
          description: The unique identifier for the customer, created by Loop.
          examples:
            - 9007199254740991
        external_id:
          type: string
          description: The identifier used by an external source to identify the customer.
          examples:
            - 71263csss-12uhsdfh-2138dja
        success:
          type: boolean
          description: Whether the customer was successfully deleted.
          examples:
            - true
    NotFoundResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          examples:
            - "Resource not found."
    UnauthorizedResponse:
      type: object
      required:
        - errors
      properties:
        errors:
          type: string
          examples:
            - "Unauthorized."
  securitySchemes:
    read:
      type: apiKey
      in: header
      name: X-Authorization
      description: 'API Scope: "Customers (read)"'
      x-scope: Customers (Read)
    write:
      name: X-Authorization
      type: apiKey
      in: header
      description: 'API Scope: "Customers (write)"'
