# Send campaign

> POST /campaigns/:id/send — send a draft now or schedule it.

`POST /campaigns/:id/send`

Sends a draft campaign. With no body it begins sending immediately (status → `queued`). Pass a future `scheduled_at` to schedule it (status → `scheduled`). A campaign that has a `recurrence` becomes a template instead (status → `recurring`) and each occurrence is sent as its own child campaign. The campaign must be a draft with a `from`, a `subject`, a body, and a verified from-domain.

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | The campaign ID. |

**Body parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `scheduled_at` | string | No | Optional. Schedule the send for later. Accepts an ISO 8601 timestamp (e.g. `2026-07-01T09:00:00Z`) or natural language (e.g. `in 1 min`, `in 1 hour`). If in the future, the campaign is scheduled rather than sent now. Cannot be more than 30 days in the future. |
| `schedule_timezone` | string | No | Optional. IANA timezone name (e.g. `America/New_York`) the `scheduled_at` wall-clock time is interpreted in — also the zone daily batches roll over in. A `scheduled_at` that already carries a UTC offset (e.g. a trailing `Z`) is used as-is. Defaults to your account timezone for batch rollover; while it is unset, an offset-less `scheduled_at` is read as UTC. |

**Node.js**

```js
import { SendPing } from 'sendping';

const mb = new SendPing('mb_xxxxxxxxx');

const { data, error } = await mb.campaigns.send('8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90', { "scheduled_at": "2026-07-01T09:00:00Z" });
console.log({ data, error });
```

**Ruby**

```ruby
require "sendping"

SendPing.api_key = "mb_xxxxxxxxx"

SendPing::Campaigns.send("8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90", {
  "scheduled_at": "2026-07-01T09:00:00Z"
})
```

**PHP**

```php
<?php
require 'vendor/autoload.php';

use SendPing\SendPing;

$sendping = SendPing::client('mb_xxxxxxxxx');

$sendping->campaigns->send('8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90', [
  'scheduled_at' => "2026-07-01T09:00:00Z"
]);
```

**Python**

```python
import sendping

sendping.api_key = "mb_xxxxxxxxx"

sendping.Campaigns.send("8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90", {
  "scheduled_at": "2026-07-01T09:00:00Z"
})
```

**Go**

```go
import "github.com/shekhu10/sendping-sdks/sendping-go"

client := sendping.NewClient("mb_xxxxxxxxx")

sent, err := client.Campaigns.Send("8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90", &sendping.SendCampaignRequest{
    ScheduledAt: "2026-07-01T09:00:00Z",
})
```

**Rust**

```rust
use sendping::SendPing;

let mb = SendPing::new("mb_xxxxxxxxx");

let _sent = mb.campaigns.send("8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90", Some("2026-07-01T09:00:00Z")).await?;
```

**Java**

```java
import co.sendping.SendPing;
import co.sendping.SendPingResponse;

SendPing sendping = new SendPing("mb_xxxxxxxxx");

SendPingResponse response = sendping.campaigns().send(
        "8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90",
        "2026-07-01T09:00:00Z");
```

**.NET**

```csharp
using SendPing;

ISendPing sendping = SendPingClient.Create("mb_xxxxxxxxx");

var resp = await sendping.CampaignSendAsync(
    "8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90",
    "2026-07-01T09:00:00Z");
```

**cURL**

```bash
curl -X POST 'https://www.sendping.co/api/campaigns/8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90/send' \
  -H 'Authorization: Bearer mb_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{ "scheduled_at": "2026-07-01T09:00:00Z" }'
```

**CLI**

```bash
sendping campaigns send 8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90 \
  --scheduled-at '2026-07-01T09:00:00Z'
```

### Response

```json
{
  "id": "8f5c2a1e-7b3d-4f9a-9c12-2e6d4a7b8c90"
}
```

> **Note:** Errors: `not_found` if the campaign is not yours; `validation_error` if it is already queued/scheduled/sent, is missing a `from`/`subject`/body, has an unverified from-domain, or `scheduled_at` is not a valid timestamp.
