openapi: 3.1.0
info:
  title: Programmatic Webhooks API
  version: 'v1'
  description: API used for creating, retrieving, updating, and deleting webhooks.
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: Programmatic Webhooks
paths:
  /webhooks:
    get:
      tags:
        - Programmatic Webhooks
      security:
        - oauth2: ['developer_tools']
        - api_key: []
      description: |
        Get all webhook subscriptions. This includes webhooks created
        programmatically and webhooks created in the Loop Admin.
      summary: "Get Webhooks"
      operationId: getWebhooks
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                properties:
                  webhooks:
                    type: array
                    description: An array of webhook subscriptions.
                    items:
                      $ref: '#/components/schemas/Webhook'
    post:
      tags:
        - Programmatic Webhooks
      security:
        - oauth2: ['developer_tools']
        - api_key: []
      description: |
        Create a new webhook subscription.
      summary: "Create Webhook"
      operationId: createWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        "201":
          description: "Created"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResponse'
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedResponse'
        "422":
          description: "Unprocessable"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnprocessableResponse'
  /webhooks/sample:
    get:
      tags:
        - Programmatic Webhooks
      security:
        - oauth2: ['developer_tools']
        - api_key: []
      description: |
        Retrieve a sample webhook payload for a given topic and trigger, populated with the shop's data.
      summary: "Get Sample Webhook"
      operationId: getSampleWebhook
      parameters:
        - in: query
          name: topic
          required: true
          schema:
            $ref: '#/components/schemas/TopicEnum'
        - in: query
          name: trigger
          required: true
          schema:
            $ref: '#/components/schemas/TriggerEnum'
        - in: query
          name: is_zapier
          required: false
          description: When true, the sample payload is returned wrapped in an array (required by Zapier).
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                description: |
                  A sample webhook payload for the requested topic and trigger.
                  When `is_zapier` is true, this object is wrapped in an array.
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedResponse'
        "500":
          description: "Failed to generate sample webhook"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResponse'
  /webhooks/{id}:
    delete:
      tags:
        - Programmatic Webhooks
      security:
        - oauth2: ['developer_tools']
        - api_key: []
      description: |
        Delete a webhook subscription.
      summary: "Delete Webhook"
      operationId: deleteWebhook
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          example: 12345
          description: The webhook's unique identifier.
      responses:
        "204":
          description: "No Content"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedResponse'
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundResponse'
    put:
      tags:
        - Programmatic Webhooks
      security:
        - oauth2: ['developer_tools']
        - api_key: []
      description: |
        Update a webhook subscriptions.
      summary: "Update Webhook"
      operationId: updateWebhook
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          example: 12345
          description: The webhook's unique identifier.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebhookRequest'
      responses:
        "204":
          description: "No Content"
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResponse'
        "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"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnprocessableResponse'
    patch:
      tags:
        - Programmatic Webhooks
      security:
        - oauth2: ['developer_tools']
        - api_key: []
      description: |
        Update a webhook subscription.
      summary: "Update Webhook"
      operationId: patchWebhook
      parameters:
        - in: path
          name: id
          schema:
            type: integer
          required: true
          example: 12345
          description: The webhook's unique identifier.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebhookRequest'
      responses:
        "204":
          description: "No Content"
        "400":
          description: "Bad Request"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResponse'
        "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"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnprocessableResponse'
components:
  schemas:
    Webhook:
      type: object
      properties:
        id:
          type: integer
          examples:
            - 12345
          description: The webhook's unique identifier.
        shop_id:
          type: integer
          examples:
            - 65432
          description: The unique identifier of the shop that created the webhook.
        topic:
          $ref: '#/components/schemas/TopicEnum'
        trigger:
          $ref: '#/components/schemas/TriggerEnum'
        url:
          $ref: '#/components/schemas/WebhookUrl'
        status:
          $ref: '#/components/schemas/StatusEnum'
    CreateWebhookRequest:
      type: object
      required:
        - topic
        - trigger
        - url
      properties:
        topic:
          $ref: '#/components/schemas/TopicEnum'
        trigger:
          $ref: '#/components/schemas/TriggerEnum'
        url:
          $ref: '#/components/schemas/WebhookUrl'
        status:
          default: 'inactive'
          $ref: '#/components/schemas/StatusEnum'
    UpdateWebhookRequest:
      type: object
      required:
        - topic
        - trigger
        - url
        - status
      properties:
        topic:
          $ref: '#/components/schemas/TopicEnum'
        trigger:
          $ref: '#/components/schemas/TriggerEnum'
        url:
          $ref: '#/components/schemas/WebhookUrl'
        status:
          $ref: '#/components/schemas/StatusEnum'
    StatusEnum:
      type: string
      description: The webhook's status.
      enum:
        - active
        - inactive
    TriggerEnum:
      type: string
      description: The condition which triggers the webhook.
      enum:
        - 'return.created'
        - 'return.updated'
        - 'return.closed'
        - 'return.processing.failed'
        - 'label.created'
        - 'label.updated'
        - 'restock.requested'
        - 'label.request.issued'
        - 'label.request.cancelled'
        - 'giftcard.requested'
        - 'shipment.processed'
      examples:
        - 'return.created'
    TopicEnum:
      type: string
      description: The webhook's topic.
      enum:
        - 'return'
        - 'return.processing'
        - 'label'
        - 'restock'
        - 'label.request'
        - 'giftcard'
        - 'happy.returns.shipment'
      examples:
        - 'return'
    UnauthorizedResponse:
      type: object
      required:
        - errors
      properties:
        errors:
          type: string
          examples:
            - 'Unauthorized.'
    NotFoundResponse:
      type: object
      properties:
        errors:
          type: object
          properties:
            code:
              type: string
              examples:
                - 'NOT_FOUND'
    UnprocessableResponse:
      type: object
      properties:
        message:
          type: string
          examples:
            - "Must be one of the following values:return,label,restock"
        errors:
          type: object
    WebhookUrl:
      type: string
      examples:
        - 'https://example.com/webhook'
      description: The webhook's URL.
    BadRequestResponse:
      type: object
      properties:
        errors:
          type: object
          properties:
            code:
              type: string
              examples:
                - 'FAILED_TO_CREATE_WEBHOOK'
            message:
              type: string
              examples:
                - 'The provided webhook URL is invalid.'
  securitySchemes:
    api_key:
      type: 'apiKey'
      name: 'X-Authorization'
      in: 'header'
    oauth2:
      type: oauth2
      description: OAuth 2.0 authorization
      flows:
        authorizationCode:
          authorizationUrl: https://oauth.loopreturns.com/authorize
          tokenUrl: https://oauth.loopreturns.com/oauth/token
          scopes:
            developer_tools: Access to developer tools including webhooks
