Launch Outbound
curl --request POST \
--url https://app.pixie.ai/api/campaigns/{id}/launch \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.pixie.ai/api/campaigns/{id}/launch"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.pixie.ai/api/campaigns/{id}/launch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.pixie.ai/api/campaigns/{id}/launch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.pixie.ai/api/campaigns/{id}/launch"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.pixie.ai/api/campaigns/{id}/launch")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.pixie.ai/api/campaigns/{id}/launch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"message": "<string>",
"runId": "<string>",
"prospectsToProcess": 4503599627370495,
"warning": "<string>",
"warnings": [
"<string>"
],
"skippedProspects": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"linkedin_url": "<string>"
}
],
"skippedCount": 4503599627370495
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Outbound
Launch Outbound
Validate and launch a draft Outbound execution.
POST
/
api
/
campaigns
/
{id}
/
launch
Launch Outbound
curl --request POST \
--url https://app.pixie.ai/api/campaigns/{id}/launch \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.pixie.ai/api/campaigns/{id}/launch"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.pixie.ai/api/campaigns/{id}/launch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.pixie.ai/api/campaigns/{id}/launch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.pixie.ai/api/campaigns/{id}/launch"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.pixie.ai/api/campaigns/{id}/launch")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.pixie.ai/api/campaigns/{id}/launch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"message": "<string>",
"runId": "<string>",
"prospectsToProcess": 4503599627370495,
"warning": "<string>",
"warnings": [
"<string>"
],
"skippedProspects": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"linkedin_url": "<string>"
}
],
"skippedCount": 4503599627370495
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}CLI:
After a successful launch:
pixie campaign launch --id <id>
Overview
The call is synchronous about validation (returns 400 if the draft is not ready) but asynchronous about sending: once it returns200, a Trigger.dev task owns the work. Poll GET /api/campaigns/:id for progress.
The status transitions atomically via compare-and-swap: draft -> launching, then inside the background task launching -> active. Only one launch can run at a time; a second concurrent call returns 409.
Error reference
| Status | When |
|---|---|
400 | Draft is not in draft status; the message names the current status |
400 | Draft has no sender accounts, no pending contacts, or no sendable sequence nodes |
400 | Sequence contains nodes with missing content; message lists the positions |
400 | Email senders are not active or are missing a configured sending inbox |
400 | LinkedIn connected account no longer exists upstream and must be reconnected |
400 | All contacts were skipped by dedup; nothing to send |
401 | Missing or invalid Bearer token |
404 | Outbound row does not exist or is not owned by the authenticated user |
409 | A launch is already in progress or the row changed concurrently |
500 | Message resolution or Trigger.dev dispatch failed. Status rolled back to draft; retry is safe |
503 | LinkedIn transient sender verification error. Retry after 30 seconds |
AI agent notes
Before calling this endpoint, verify the draft is ready:
GET /api/campaigns/:id->statusmust be"draft"GET /api/campaigns/:id/leads?status=pending->totalmust be greater than zero; review the actual recipients, not only an aggregate on the Outbound row- At least one sender account is attached
- Every sendable sequence node has non-empty content and no unresolved personalization placeholder
- For email: senders are
activeand have a configured sending inbox - For LinkedIn: the connected account resolves through the sending backend
- Present the channel, sender identity, pending recipient count, and final sequence to the human and obtain explicit launch approval
GET /api/campaigns/{id} first when you need to inspect the draft before launch; this endpoint still performs final readiness checks and returns a specific blocker when launch is not allowed.- Status is
"launching"immediately; do not re-launch. - Within about 2 minutes the background task transitions to
"active"and begins staggered sends. - Poll
GET /api/campaigns/:idevery 30 seconds until status is"active", then at 1-5 minute cadence for progress. stats.totalis set at launch; counters such assent,replied, andbouncedincrement as contacts execute.
400, do not retry the same request. Surface the blocker, correct the draft, repeat the readiness checks, and ask for confirmation again if the recipient set, sender, or sequence changed. On 409, fetch the Outbound before deciding whether another launch is needed. On 500, the server restores draft; verify that state before one retry.
Idempotency: retrying the same call is safe. The background task is keyed by campaignId + updated_at, and on any failure the server rolls back writes.
Stopping: use POST /api/campaigns/:id/pause to halt mid-flight; POST /api/campaigns/:id/resume to continue.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Campaign UUID
Response
Launch dispatched. Background task is running.
Available options:
true Always "Campaign launch started" on success
Trigger.dev run identifier.
Required range:
0 <= x <= 9007199254740991Show child attributes
Show child attributes
Required range:
0 <= x <= 9007199254740991Was this page helpful?