Skip to main content

Create a campaign

This creates a draft campaign and a related email message in one request. Only a name value is required.
Save the returned emailMessageContentRevisionId. Pass it as expectedRevisionId when updating an email message to avoid 409 Conflict errors caused by stale revisions.
API reference
CLI reference
loops campaigns create --name "Spring product announcement" -o json

Query themes and components for your LMX

You can fetch your available themes and reusable components before building the lmx payload. List themes API reference CLI reference
List components API reference CLI reference
loops themes list -o json
loops components list -o json
Use emailMessageId from when you created the campaign as the path parameter, and pass the emailMessageContentRevisionId as expectedRevisionId. Apply styles or a theme in <Style />, and create an email using LMX elements. Themes and components you queried in step 2 can be referenced by their IDs.
Save the returned contentRevisionId after each update. Pass it as expectedRevisionId on the next update to avoid 409 Conflict errors caused by stale revisions.
API reference
CLI reference
loops email-messages update $emailMessageId \
  --expected-revision-id $emailMessageContentRevisionId \
  --subject "Big spring updates" \
  --preview-text "A quick look at what's new" \
  --from-name "Loops" \
  --from-email hello \
  --reply-to support@example.com \
  --lmx-file ./email.lmx

Upload an image asset

If your LMX includes <Image /> tags, upload image files with the Upload API and use the returned finalUrl as the image src. Create upload API reference
Complete upload API reference
Upload CLI reference
loops uploads create ./header.png -o json

Target an audience segment

List your audience segments to find a segment ID, then apply it to a draft campaign with Update a campaign. Setting audienceSegmentId clears any existing audienceFilter on the campaign. API reference
CLI reference
const segmentsResponse = await fetch(
  "https://app.loops.so/api/v1/audience-segments",
  {
    method: "GET",
    headers: {
      "Authorization": "Bearer <your-api-key>",
    },
  },
);

const { data: segments } = await segmentsResponse.json();
const audienceSegmentId = segments[0].id;

const updateResponse = await fetch(
  `https://app.loops.so/api/v1/campaigns/${campaignId}`,
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer <your-api-key>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      audienceSegmentId,
    }),
  },
);

const updatedCampaign = await updateResponse.json();
You can also set the segment when creating the campaign:
const response = await fetch("https://app.loops.so/api/v1/campaigns", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <your-api-key>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Spring product announcement",
    audienceSegmentId: "clr4c8t0v008yl70x3y4z5a6b",
  }),
});

Send a campaign preview

After updating the campaign’s email message, send a test preview to one or more addresses. Campaign previews accept contactProperties for personalization. Use emailMessageId from when you created the campaign as the path parameter. API reference
const previewResponse = await fetch(
  `https://app.loops.so/api/v1/email-messages/${emailMessageId}/preview`,
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer <your-api-key>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      emails: ["you@example.com"],
      contactProperties: {
        firstName: "Alex",
        plan: "Pro",
      },
    }),
  },
);

const preview = await previewResponse.json();
Last modified on July 1, 2026