---
title: "Administrator only: pre-enrol outreach products. Each row gets an account under the alias outreach-<slug>@guest.afterlaunch.io, a snapshot and a paused enrollment, one provisioning event for a NEW row only, and a 30-day entry link. An alias that already has an enrollment answers 'exists' with its link and nothing is written; if that moves list never started building, the provisioning event is sent once more (resent: true). A product already pre-enrolled under another address (its owner switched to their own email, or another slug) answers 'exists' with no link and an error saying so. Scope 'write'; at most 25 rows a call; dry_run reads and writes nothing."
method: POST
path: "/api/v1/outreach/preenrol"
---

# Administrator only: pre-enrol outreach products. Each row gets an account under the alias outreach-<slug>@guest.afterlaunch.io, a snapshot and a paused enrollment, one provisioning event for a NEW row only, and a 30-day entry link. An alias that already has an enrollment answers 'exists' with its link and nothing is written; if that moves list never started building, the provisioning event is sent once more (resent: true). A product already pre-enrolled under another address (its owner switched to their own email, or another slug) answers 'exists' with no link and an error saying so. Scope 'write'; at most 25 rows a call; dry_run reads and writes nothing.

`POST /api/v1/outreach/preenrol`

## Request

- Base URL: `https://afterlaunch.io`
- URL: `https://afterlaunch.io/api/v1/outreach/preenrol`
- Auth: HTTP bearer

## Request body

- object
  - `rows` object[], required
    - `url` string, uri, required: The product, http(s), with a dotted host. Refused: a user or port in the address, localhost, an IP address, a platform nobody owns (github.com and the like), and afterlaunch.io or any subdomain of it.
    - `label` string, required
    - `slug` string: Optional. The alias slug; read from the host when absent (dyad.sh is dyad). "afterlaunch" is reserved.
  - `campaign` string, required: Stamped on each new row as the cohort outreach:<campaign>.
  - `dry_run` boolean: Report what a real call would do; no write, no event.

## Response `200`

One result per row, in order. status is created, exists, would_create (dry run) or failed; link is null for would_create, a failure, or an existing row that is not an outreach row.

- object
  - `data` object, required
    - `dry_run` boolean, required
    - `rows` object[], required
      - `label` string, required
      - `alias` string, nullable, required
      - `url` string, required
      - `status` 'created' | 'exists' | 'would_create' | 'failed', required
      - `link` string, nullable, required
      - `resent` boolean: On 'exists' only: true when the moves list had never started building and the provisioning event was sent again. Never true on a dry run.
      - `error` string
  - `meta` object, required
    - `enrollment_id` string, uuid, required
    - `generated_at` string, date-time, required

## Other responses

- `400`: Invalid body: a bad url, more than 25 rows, no campaign, or two rows on one slug. Nothing was written.
- `401`: Missing, invalid or revoked API key.
- `403`: The key does not carry the scope this endpoint requires.
- `429`: Per-key rate limit exceeded. Honour Retry-After.
- `500`: The deployment has no public site url, so no link could be made. Nothing was written.

---

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