---
title: "Get a campaign"
method: GET
path: "/campaigns/{id}"
tags: ["Campaigns"]
---

# Get a campaign

`GET /campaigns/{id}`

## Request

- Base URL: `https://api.privy.com/v1`
- URL: `https://api.privy.com/v1/campaigns/{id}`
- Auth: HTTP bearer

Retrieve a single message campaign by its identifier. Resolves both
newsletters and flow campaigns — no `type` parameter needed. A flow
campaign names the flow that sends it in `flow`; newsletters omit
that field.

A `/v1/campaigns` row already carries this exact shape, so following up
per campaign adds nothing — reach for this when the id came from
somewhere else, such as a flow node's `data.campaign_id` or a report
row's `campaign_id`. Performance is
`/v1/reports/campaigns/{campaign_id}`.

**Required scope:** `campaigns_read`

## Path parameters

- `id` string, required

## Headers

- `Privy-Account-Id` string

## Response `200`

The message campaign.

- object
  - `data` Campaign: A message campaign — configuration only; send statistics belong to the reporting surface. `type` discriminates the two families: `newsletter` (email + SMS broadcasts) and `flow` (the email/SMS messages a flow sends). Newsletter-only fields (`status`, `send_date`, `audience`) are `null` for flow campaigns — the flow's trigger decides who receives them and when.
    - `id` string: Stable, unique identifier for the campaign. Use this value in `/v1/campaigns/{id}` and `/v1/reports/campaigns/{campaign_id}`.
    - `type` 'newsletter' | 'flow': `newsletter` — a broadcast send; `flow` — a message sent by an automation flow.
    - `title` string, nullable
    - `channel` 'email' | 'sms'
    - `status` 'draft' | 'scheduled' | 'preparing' | 'awaiting_winner' | 'sending' | 'sent' | 'partially_sent' | 'unsent' | 'canceled' | 'null', nullable: The newsletter's send lifecycle. `null` for flow campaigns. Two kinds of send reach beyond the plain draft → scheduled → sending → sent path, and they share some states. A newsletter scheduled to land at a recipient-local wall-clock time ("send in recipient's timezone") rolls delivery out over roughly 24 hours as each timezone reaches that time. `preparing` is audience generation, and it is the last point at which the send can be unscheduled; after that a merchant can only cancel the remaining delivery windows, leaving `partially_sent` when some recipients were already sent to and `canceled` when none were. An A/B test runs `preparing` → `awaiting_winner` → `sending` → `sent`: it generates its audience, sends to the test cohort, then holds at `awaiting_winner` while the decision window runs, and only sends the winning version to the remaining audience once a winner is committed. A test cancelled mid-flight also reports `canceled`. `awaiting_winner` occurs on A/B tests only.
    - `subject` string, nullable: Email subject line. `null` for SMS campaigns.
    - `send_date` string, date-time, nullable: When the newsletter is scheduled to send (or sent). `null` for flow campaigns.
    - `flow` CampaignFlowRef: The automation flow that sends this campaign. Pass `id` to `GET /v1/flows/{id}` for the flow's trigger, filters, and node graph.
      - `id` string: The flow's identifier, as returned by `GET /v1/flows`.
      - `title` string, nullable
    - `audience` object, nullable: Audience segment references. `null` for flow campaigns — the flow's trigger decides the audience. Each entry is `{id, name, prebuilt}`; saved segments carry an id you can look up via `GET /v1/segments/{id}` for counts and members. `prebuilt: true` marks Privy's built-in smart segments (Potential Customers, Engaged Contacts, …) — when one hasn't been saved to the account, its `id` is the stable slug rather than a segments-API id.
      - `included_segments` AudienceSegmentRef[]
        - `id` string: For saved segments, the segment id — pass it to `GET /v1/segments/{id}` (counts) or `GET /v1/segments/{id}/contacts` (members). A smart segment that has not been saved to the account has no segment record; its `id` is the built-in segment's stable slug (e.g. `potential_customers`), with no segments-API detail read.
        - `name` string: Display name of the segment.
        - `prebuilt` boolean: Whether this is one of Privy's built-in smart segments (Potential Customers, Engaged Contacts, …) — the same flag the segments API returns, so refs match `GET /v1/segments` rows. `false` for segments you created.
      - `excluded_segments` AudienceSegmentRef[]
        - `id` string: For saved segments, the segment id — pass it to `GET /v1/segments/{id}` (counts) or `GET /v1/segments/{id}/contacts` (members). A smart segment that has not been saved to the account has no segment record; its `id` is the built-in segment's stable slug (e.g. `potential_customers`), with no segments-API detail read.
        - `name` string: Display name of the segment.
        - `prebuilt` boolean: Whether this is one of Privy's built-in smart segments (Potential Customers, Engaged Contacts, …) — the same flag the segments API returns, so refs match `GET /v1/segments` rows. `false` for segments you created.
    - `coupon` CouponRef: The coupon pool attached to a message or display — a `{{ coupon_code }}` tag in the content only renders when one is attached. Pass `id` to `GET /v1/coupons/{id}` for the pool's inventory and health; `kind` and `platform` match that endpoint's rows.
      - `id` string: The pool's identifier, as returned by `GET /v1/coupons`.
      - `title` string
      - `kind` 'master' | 'unique': `master` is a single shared code; `unique` is a pool of individually generated codes.
      - `platform` 'manual' | 'shopify' | 'bigcommerce' | 'woocommerce'
    - `ab_test` CampaignAbTest: How the newsletter's A/B test is set up.
      - `winning_metric` 'open_rate' | 'click_rate' | 'order_rate' | 'null', nullable: The metric the winner is chosen on. `open_rate` only appears on older email tests and never on SMS.
      - `test_duration_seconds` integer, nullable: How long the test cohort runs before a winner is chosen.
      - `versions` CampaignAbTestVersion[]: The versions in labelled order (A, B, C…); the control is first.
        - `id` string: Opaque, stable version identifier, prefixed `cpv_`. Matches the version's results on `GET /v1/reports/campaigns/{campaign_id}`.
        - `name` string
        - `is_control` boolean
        - `is_winner` boolean: Whether this version won. `false` on every version until a winner is committed.
        - `audience_share` number, nullable: Share of the eligible audience (0–1) this version is sent to during the test. `null` until set on a draft. Shares sum to the test size; the remainder goes to the winner.
        - `subject` string, nullable: This version's subject line, or its message body for SMS.
        - `preheader` string, nullable: This version's preheader. `null` for SMS or when none was set.
        - `coupon` CouponRef: The coupon pool attached to a message or display — a `{{ coupon_code }}` tag in the content only renders when one is attached. Pass `id` to `GET /v1/coupons/{id}` for the pool's inventory and health; `kind` and `platform` match that endpoint's rows.
          - `id` string: The pool's identifier, as returned by `GET /v1/coupons`.
          - `title` string
          - `kind` 'master' | 'unique': `master` is a single shared code; `unique` is a pool of individually generated codes.
          - `platform` 'manual' | 'shopify' | 'bigcommerce' | 'woocommerce'
    - `created_at` string, date-time
    - `updated_at` string, date-time

## Other responses

- `401`: Missing or invalid bearer token.
- `403`: `insufficient_scope` when the token lacks the required scope for this endpoint.
- `404`: The requested resource does not exist or does not belong to your account.
- `429`: Rate limit exceeded. Retry after the specified time.

## Changes

- **2026-11-05** `cf0885e90d5a`: 8 warning
  - added the new `forbidden` enum value to the `error/code` response property for the response status `401`
  - added the new `forbidden` enum value to the `error/code` response property for the response status `403`
  - added the new `forbidden` enum value to the `error/code` response property for the response status `404`
  - added the new `forbidden` enum value to the `error/code` response property for the response status `429`
  - …4 more
- **2026-11-03** `fe500ab59b3b`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/privy/apis/privy-api/changes/campaigns/:id/get.md)

---

[API](https://skmtc.dev/privy/apis/privy-api.md) - [All operations](https://skmtc.dev/privy/apis/privy-api/llms.txt) - [OpenAPI document](https://skmtc.dev/privy/apis/privy-api/revisions/bc29b48dcdd2?raw)
