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.

Documentation

Specifications

Other Resources

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:help@loops.so) 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