Loops API

Loops API v1 manages contacts, contact properties, mailing lists, campaigns, email messages, themes, components, events, transactional emails, dedicated sending IPs, and API key validation.

OpenAPI Specification

loops-openapi.yaml Raw ↑
openapi: 3.1.0
info:
  title: Loops OpenAPI Spec
  description: This is the OpenAPI Spec for the [Loops API](https://loops.so/docs/api).
  version: 1.8.0
servers:
  - url: https://app.loops.so/api/v1
tags:
  - name: API key
  - name: Contacts
    description: Manage contacts in your audience
  - name: Contact properties
    description: Manage contact properties
  - name: Mailing lists
    description: View mailing lists
  - name: Campaigns
    description: Create and manage email campaigns
  - name: Email messages
    description: Manage email message content for campaigns
  - name: Themes
    description: View email themes
  - name: Components
    description: View email components
  - name: Events
    description: Trigger email sending with events
  - name: Transactional emails
    description: Send and view transactional emails
  - name: Dedicated sending IPs
    description: View dedicated sending IP addresses
paths:
  /api-key:
    get:
      tags:
        - API key
      summary: Test your API key
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    examples:
                      - true
                  teamName:
                    type: string
                    description: The name of the team the API key belongs to.
                    examples:
                      - Company name
                required:
                  - success
                  - teamName
        "401":
          description: Invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    examples:
                      - false
                  message:
                    type: string
                    examples:
                      - Invalid API key
                  error:
                    type: string
                    examples:
                      - Invalid API key
      security:
        - apiKey: []
  /contacts/create:
    post:
      tags:
        - Contacts
      summary: Create a contact
      description: Add a contact to your audience.
      requestBody:
        description:
          You can add custom contact properties as keys in this request (of
          type `string`, `number`, `boolean` or `date` ([see available date
          formats](https://loops.so/docs/contacts/properties#dates))).<br>Make
          sure to create the properties in Loops before using them in API calls.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactRequest"
        required: true
      responses:
        "200":
          description: Successful create.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactSuccessResponse"
        "400":
          description: Bad request (e.g. invalid email address).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "405":
          description: Wrong HTTP request method.
        "409":
          description: Email or `userId` already exists.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
      security:
        - apiKey: []
  /contacts/update:
    put:
      tags:
        - Contacts
      summary: Update a contact
      description:
        Update a contact by `email` or `userId`. You must provide one of
        these parameters.<br>If you want to update a contact’s email address,
        the contact will first need a `userId` value. You can then make a
        request containing the userId field along with an updated email address.
      requestBody:
        description:
          You can add custom contact properties as keys in this request (of
          type `string`, `number`, `boolean` or `date` ([see available date
          formats](https://loops.so/docs/contacts/properties#dates))).<br>Make
          sure to create the properties in Loops before using them in API calls.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactUpdateRequest"
        required: true
      responses:
        "200":
          description: Successful update.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactSuccessResponse"
        "400":
          description: Bad request (e.g. `email` or `userId` are missing).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /contacts/find:
    get:
      tags:
        - Contacts
      summary: Find a contact
      description:
        Search for a contact by `email` or `userId`. Only one parameter is
        allowed.
      parameters:
        - name: email
          in: query
          required: false
          description: Email address (URI-encoded)
          schema:
            type: string
        - name: userId
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description:
            List of contacts (or an empty array if no contact was found).
            Contact objects will include any custom properties.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Contact"
        "400":
          description: Bad request (e.g. invalid email address).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /contacts/delete:
    post:
      tags:
        - Contacts
      summary: Delete a contact
      description: Delete a contact by `email` or `userId`.
      requestBody:
        description: Include only one of `email` or `userId`.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactDeleteRequest"
        required: true
      responses:
        "200":
          description: Successful delete.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactDeleteResponse"
        "400":
          description: Bad request (e.g. `email` and `userId` are both provided).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "404":
          description: Contact not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /contacts/suppression:
    get:
      tags:
        - Contacts
      summary: Get suppression status for a contact
      description:
        Retrieve suppression status and removal quota for a contact by
        `email` or `userId`. Include only one query parameter.
      parameters:
        - name: email
          in: query
          required: false
          description: Email address (URI-encoded)
          schema:
            type: string
        - name: userId
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactSuppressionStatusResponse"
        "400":
          description: Bad request (e.g. invalid email address).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "404":
          description: Contact not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
    delete:
      tags:
        - Contacts
      summary: Remove a contact from suppression list
      description:
        Remove a suppressed contact from the suppression list by `email` or
        `userId`. Include only one query parameter.
      parameters:
        - name: email
          in: query
          required: false
          description: Email address (URI-encoded)
          schema:
            type: string
        - name: userId
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Successful removal.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactSuppressionRemoveResponse"
        "400":
          description: Bad request (e.g. contact is not suppressed).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "404":
          description: Contact not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /contacts/properties:
    post:
      tags:
        - Contact properties
      summary: Create a contact property
      description: Add a contact property to your team.
      requestBody:
        description: The name value must be in camelCase, like `planName`.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactPropertyCreateRequest"
        required: true
      responses:
        "200":
          description: Successful create.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactPropertySuccessResponse"
        "400":
          description: Bad request (e.g. invalid type).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactPropertyFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
    get:
      tags:
        - Contact properties
      summary: Get a list of contact properties
      description:
        Retrieve a list of your account's contact properties.<br>Use the
        `list` parameter to query "all" or "custom" properties.
      parameters:
        - name: list
          in: query
          required: false
          description: \"all\" (default) or \"custom\"
          schema:
            type: string
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ContactProperty"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /dedicated-sending-ips:
    get:
      tags:
        - Dedicated sending IPs
      summary: Get dedicated sending IP addresses
      description: Retrieve a list of Loops' dedicated sending IP addresses.
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                type: array
                description: List of dedicated sending IP addresses
                items:
                  type: string
                  description: IP address
                  examples:
                    - 1.2.3.4
        "405":
          description: Wrong HTTP request method.
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                required:
                  - success
                  - message
      security:
        - apiKey: []
  /lists:
    get:
      tags:
        - Mailing lists
      summary: Get a list of mailing lists
      description: Retrieve a list of your account's mailing lists.
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MailingList"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /events/send:
    post:
      tags:
        - Events
      summary: Send an event
      description: Send events to trigger emails in Loops.
      requestBody:
        description:
          Provide either `email` or `userId` to identify the contact ([read
          more](https://loops.so/docs/api-reference/send-event#body)).<br>You
          can add event properties, which will be available in emails sent by
          this event. Values can be of type string, number, boolean or date
          ([see allowed date
          formats](https://loops.so/docs/events/properties#important-information-about-event-properties)).<br>Make
          sure to create the properties in Loops before using them in API
          calls.<br>You can add contact properties as keys in this request (of
          type `string`, `number`, `boolean` or `date` ([see available date
          formats](https://loops.so/docs/contacts/properties#dates))).
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EventRequest"
        required: true
      parameters:
        - in: header
          name: Idempotency-Key
          description:
            Include a unique ID for this request (maximum 100 characters) to
            avoid duplicate events. [More
            info](https://loops.so/docs/api-reference/send-event#param-idempotency-key)
          schema:
            type: string
            maxLength: 100
      responses:
        "200":
          description: Successful send.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventSuccessResponse"
        "400":
          description: Bad request (e.g. `eventName` is missing).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventFailureResponse"
        "405":
          description: Wrong HTTP request method.
        "409":
          description: Idempotency key has been used.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdempotencyKeyFailureResponse"
      security:
        - apiKey: []
  /transactional:
    post:
      tags:
        - Transactional emails
      summary: Send a transactional email
      description: Send a transactional email to a contact.<br>Please [email
        us](mailto:[email protected]) to enable attachments on your account before
        using them with the API.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionalRequest"
        required: true
      parameters:
        - in: header
          name: Idempotency-Key
          description:
            Include a unique ID for this request (maximum 100 characters) to
            avoid duplicate emails. [More
            info](https://loops.so/docs/api-reference/send-transactional-email#param-idempotency-key)
          schema:
            type: string
            maxLength: 100
      responses:
        "200":
          description: Successful send.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionalSuccessResponse"
        "400":
          description: Bad request (e.g. transactional email is not published).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/TransactionalFailureResponse"
                  - $ref: "#/components/schemas/TransactionalFailure2Response"
                  - $ref: "#/components/schemas/TransactionalFailure3Response"
                  - $ref: "#/components/schemas/TransactionalFailure4Response"
                  - $ref: "#/components/schemas/TransactionalFailure5Response"
        "404":
          description: Transactional email not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionalFailure3Response"
        "405":
          description: Wrong HTTP request method.
        "409":
          description: Idempotency key has been used.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdempotencyKeyFailureResponse"
      security:
        - apiKey: []
    get:
      tags:
        - Transactional emails
      summary: List transactional emails
      description: Get a list of published transactional emails.
      parameters:
        - name: perPage
          in: query
          required: false
          description:
            How many results to return in each request. Must be between 10 and
            50. Default is 20.
          schema:
            type: string
        - name: cursor
          in: query
          required: false
          description:
            A cursor, to return a specific page of results. Cursors can be
            found from the `pagination.nextCursor` value in each response.
          schema:
            type: string
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListTransactionalsResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  "/themes/{themeId}":
    parameters:
      - name: themeId
        in: path
        required: true
        description: The ID of the theme.
        schema:
          type: string
    get:
      tags:
        - Themes
      summary: Get a theme
      description: Retrieve a single theme by ID.
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ThemeResponse"
        "400":
          description: Invalid `themeId`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ThemeFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "404":
          description: Theme not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ThemeFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /themes:
    get:
      tags:
        - Themes
      summary: List themes
      description: Retrieve a paginated list of email themes, most recently created first.
      parameters:
        - name: perPage
          in: query
          required: false
          description:
            How many results to return in each request. Must be between 10 and
            50. Default is 20.
          schema:
            type: string
        - name: cursor
          in: query
          required: false
          description:
            A cursor to return a specific page of results. Cursors can be found
            from the `pagination.nextCursor` value in each response.
          schema:
            type: string
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListThemesResponse"
        "400":
          description: Invalid `perPage` value.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ThemeFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  "/components/{componentId}":
    parameters:
      - name: componentId
        in: path
        required: true
        description: The ID of the component.
        schema:
          type: string
    get:
      tags:
        - Components
      summary: Get a component
      description: Retrieve a single component by ID.
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ComponentResponse"
        "400":
          description: Invalid `componentId`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ComponentFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "404":
          description: Component not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ComponentFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /components:
    get:
      tags:
        - Components
      summary: List components
      description: Retrieve a paginated list of email components.
      parameters:
        - name: perPage
          in: query
          required: false
          description:
            How many results to return in each request. Must be between 10 and
            50. Default is 20.
          schema:
            type: string
        - name: cursor
          in: query
          required: false
          description:
            A cursor to return a specific page of results. Cursors can be found
            from the `pagination.nextCursor` value in each response.
          schema:
            type: string
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListComponentsResponse"
        "400":
          description: Invalid `perPage` value.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ComponentFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  /campaigns:
    get:
      tags:
        - Campaigns
      summary: List campaigns
      description: Retrieve a paginated list of campaigns.
      parameters:
        - name: perPage
          in: query
          required: false
          description:
            How many results to return in each request. Must be between 10 and
            50. Default is 20.
          schema:
            type: string
        - name: cursor
          in: query
          required: false
          description:
            A cursor to return a specific page of results. Cursors can be found
            from the `pagination.nextCursor` value in each response.
          schema:
            type: string
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListCampaignsResponse"
        "400":
          description: Invalid `perPage` value.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
    post:
      tags:
        - Campaigns
      summary: Create a campaign
      description:
        Create a new draft campaign. An empty email message is created
        automatically and its `emailMessageId` is returned. Use the
        `/email-messages/{emailMessageId}` endpoint to set subject, sender,
        preview text, and LMX content.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCampaignRequest"
      responses:
        "201":
          description: Campaign created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateCampaignResponse"
        "400":
          description: Invalid request body or no sending domain configured.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
  "/campaigns/{campaignId}":
    parameters:
      - name: campaignId
        in: path
        required: true
        description: The ID of the campaign.
        schema:
          type: string
    get:
      tags:
        - Campaigns
      summary: Get a campaign
      description: Retrieve a single campaign by ID.
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignResponse"
        "400":
          description: Invalid `campaignId`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "404":
          description: Campaign not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
    post:
      tags:
        - Campaigns
      summary: Update a campaign
      description:
        Update a draft campaign's name. Campaigns can only be updated while
        in draft status.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCampaignRequest"
      responses:
        "200":
          description: Campaign updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignResponse"
        "400":
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "404":
          description: Campaign not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
        "405":
          description: Wrong HTTP request method.
        "409":
          description: Campaign is not in draft status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignFailureResponse"
      security:
        - apiKey: []
  "/email-messages/{emailMessageId}":
    parameters:
      - name: emailMessageId
        in: path
        required: true
        description: The ID of the email message.
        schema:
          type: string
    get:
      tags:
        - Email messages
      summary: Get an email message
      description: Retrieve an email message, including its compiled LMX content.
      responses:
        "200":
          description: Successful.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageResponse"
        "400":
          description: Invalid `emailMessageId` or no sending domain configured.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "404":
          description: Email message not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageFailureResponse"
        "405":
          description: Wrong HTTP request method.
      security:
        - apiKey: []
    post:
      tags:
        - Email messages
      summary: Update an email message
      description:
        Update fields on an email message (subject, preview text, sender,
        LMX content). The campaign must be in draft status. Supply
        `expectedRevisionId` matching the current `contentRevisionId` — the
        server rejects mismatched revisions with 409.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateEmailMessageRequest"
      responses:
        "200":
          description: Email message updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageResponse"
        "400":
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageFailureResponse"
        "401":
          description: Invalid API key or content API not enabled for this team.
        "404":
          description: Email message not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageFailureResponse"
        "405":
          description: Wrong HTTP request method.
        "409":
          description:
            Campaign is not in draft status, `contentRevisionId` is stale, or
            content cannot be parsed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageFailureResponse"
        "422":
          description: LMX failed to compile.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailMessageFailureResponse"
      security:
        - apiKey: []
components:
  schemas:
    Contact:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        firstName:
          type:
            - string
            - "null"
        lastName:
          type:
            - string
            - "null"
        source:
          type: string
        subscribed:
          type: boolean
        userGroup:
          type: string
        userId:
          type:
            - string
            - "null"
        mailingLists:
          type: object
          description: An object of mailing list IDs and boolean subscription statuses.
          examples:
            - list_123: true
        optInStatus:
          type:
            - string
            - "null"
          description: Double opt-in status.
          enum:
            - accepted
            - pending
            - rejected
            - null
    ContactRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        subscribed:
          type:

# --- truncated at 32 KB (54 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/loops/refs/heads/main/openapi/loops-openapi.yaml