# Create a campaign

> Creates a draft campaign. Add recipients, then start it. Every recipient gets the same message, from the phone number you choose. It's sent as the connected admin, or as the API key's name.

Source: https://www.bloomtext.com/developers/api/reference/create-campaign/

`POST /campaigns`

Creates a draft campaign. Add recipients, then start it. Every recipient gets the same message, from the phone number you choose. It's sent as the connected admin, or as the API key's name.

**Credentials:** [API key](https://www.bloomtext.com/developers/api/api-keys/) or an [org admin’s OAuth token](https://www.bloomtext.com/developers/api/oauth/) · **Scope:** `messages.write` · **Idempotent:** With `Idempotency-Key`

### Headers

- `Idempotency-Key` (UUID, required): A UUID you generate for each write. Retrying with the same key returns the original result for 24 hours. Reusing a key with a different method, path, or body returns 409.

### Body parameters

- `name` (string, required): A name for staff.

- `body` (string, required): The message, up to 1,600 characters. Every recipient gets the same text.

- `channel` (secure, sms): `secure` (the default) or `sms`, as in Send a message to a patient.

- `from` (string): The clinic phone number to send from, in E.164 format, like +15125550100. Defaults to the default number: the one set on the API key, or the admin's own. See List phone numbers.

- `send_interval_seconds` (number): Seconds between messages.

### Returns

Returns `201 Created` with the [Campaign object](https://www.bloomtext.com/developers/api/reference/campaign-object/).

### Errors

Failed requests return `application/problem+json` [problem details](https://www.bloomtext.com/developers/api/errors/). The errors specific to this endpoint:

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_request` | The request is malformed, such as invalid JSON. |
| 401 | `invalid_token` | The API key or access token is missing, expired, or revoked. |
| 403 | `insufficient_scope` | The credential doesn’t have the scope this endpoint needs. |
| 409 | `idempotency_key_reused` | The Idempotency-Key was already used with a different request. |
| 409 | `idempotency_request_in_progress` | A request with this Idempotency-Key is still running. Retry shortly. |
| 422 | `validation_failed` | A field failed validation. See `field_errors`. |
| 422 | `from_required` | There are several phone numbers and no default. Pass `from`. |
| 429 | `rate_limited` | Too many requests. Retry after `Retry-After` seconds. |

```bash filename="Request"
curl -X POST "https://api.bloomtext.com/v1/campaigns" \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"Flu shot reminders, October","body":"Flu shots are available at Example Clinic. Reply here to book a time."}'
```

```js filename="Request"
const response = await fetch('https://api.bloomtext.com/v1/campaigns', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.BLOOMTEXT_API_KEY}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({"name":"Flu shot reminders, October","body":"Flu shots are available at Example Clinic. Reply here to book a time."}),
})

if (!response.ok) throw new Error(`BloomText API error: ${response.status}`)
const data = await response.json()
```

```python filename="Request"
import os
import uuid

import requests

response = requests.post(
    "https://api.bloomtext.com/v1/campaigns",
    headers={
        "Authorization": f"Bearer {os.environ['BLOOMTEXT_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"name":"Flu shot reminders, October","body":"Flu shots are available at Example Clinic. Reply here to book a time."},
)
response.raise_for_status()
data = response.json()
```

```go filename="Request"
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/google/uuid"
)

func main() {
	body := bytes.NewBufferString(`{"name":"Flu shot reminders, October","body":"Flu shots are available at Example Clinic. Reply here to book a time."}`)
	req, err := http.NewRequest("POST", "https://api.bloomtext.com/v1/campaigns", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("BLOOMTEXT_API_KEY"))
	req.Header.Set("Idempotency-Key", uuid.NewString())
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}
```

```ruby filename="Request"
require "net/http"
require "json"
require "securerandom"

uri = URI("https://api.bloomtext.com/v1/campaigns")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("BLOOMTEXT_API_KEY")}"
request["Idempotency-Key"] = SecureRandom.uuid
request["Content-Type"] = "application/json"
request.body = { name: "Flu shot reminders, October", body: "Flu shots are available at Example Clinic. Reply here to book a time." }.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
data = JSON.parse(response.body)
```

```json filename="Response · 201 Created"
{
  "id": "c4a7e2d1-9b3f-4e6a-8d2c-1f0e9b8a7c65",
  "name": "Flu shot reminders, October",
  "status": "draft",
  "channel": "secure",
  "body": "Flu shots are available at Example Clinic. Reply here to book a time.",
  "phone_number": {
    "number": "+15125550100",
    "team": "Front desk"
  },
  "sender": {
    "id": "b7e1c3a9-4d2f-4a8b-9c6e-0f1d2e3a4b5c",
    "name": "Reminder service",
    "kind": "api_key"
  },
  "send_interval_seconds": 1,
  "counts": {
    "recipients": 0,
    "pending": 0,
    "sent": 0,
    "delivered": 0,
    "failed": 0,
    "skipped": 0
  },
  "archived": false,
  "created_at": "2026-09-21T15:04:05Z",
  "started_at": null,
  "finished_at": null
}
```
