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.
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: 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