POST /api/v1/campaigns/{campaignId}/schedule
Books the send of a draft campaign for a future instant, and answers 202. Called on a campaign
that is already scheduled, it moves it to the new instant. Nothing is sent by this call — the
campaign leaves on its own at the time you booked.
Authorization
| Role in the organization | This endpoint |
|---|---|
OWNER | Allowed |
ADMIN | Allowed |
MEMBER | Refused — 403 |
Path parameters
| Parameter | Type | Description |
|---|---|---|
campaignId | string | Unique identifier of a campaign |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
scheduledAt | string | yes | ISO 8601 instant with an explicit offset, at least one minute in the future |
json
{"scheduledAt": "2026-08-12T07:00:00Z"}Example
bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scheduledAt": "2026-08-12T07:00:00Z"}' \
"https://www.agentsmail.io/api/v1/campaigns/$CAMPAIGN_ID/schedule"Response
202 Accepted— the campaign isscheduledfor the instant returned inscheduledAt.400 Bad Request—scheduledAtis missing, has no offset, is in the past or less than a minute away (detailscarries the issue); or the content has no unsubscribe link.401 Unauthorized— missing or invalid key, or its owner left the organization.403 Forbidden— the key's owner is amemberof the organization, not anadmin.404 Not Found— no such campaign, or it belongs to another organization. The API never confirms that an id exists to a caller who has no right to it.409 Conflict— the sending domain is not verified yet, the balance cannot cover the campaign's recipients, its list was removed, or the campaign ispaused,sending,sentorfailed. Nothing is booked.429 Too Many Requests— over 120 requests in a minute for this key.
Response fields
| Field | Type | Description |
|---|---|---|
success | boolean | Indicates if the operation was successful |
data.campaignId | string | The campaign that was scheduled |
data.status | string | Always scheduled |
data.scheduledAt | string | The instant actually stored, in UTC, rounded to the minute |
Example response
json
{
"success": true,
"data": {
"campaignId": "6c6c6c6c-5555-4aaa-8bbb-999900001111",
"status": "scheduled",
"scheduledAt": "2026-08-12T07:00:00.000Z"
}
}Notes
A date with no timezone is refused, never read as UTC.
2026-08-12T09:00:00 answers 400:
the API does not guess an offset you did not write. Write 2026-08-12T07:00:00Z, or
2026-08-12T09:00:00+02:00 — both name the same instant.- The schedule is kept to the minute: the seconds you pass are dropped. Read
scheduledAtin the response to know the exact departure. - Calling it again on a
scheduledcampaign moves the departure; the previous time no longer applies. There is never more than one departure per campaign. - A
pausedcampaign answers409: resume it in the app, or put it back to draft with Unschedule a campaign and schedule it again. - To send now instead, use Send a campaign.
Never schedule without explicit human approval for that specific campaign. A scheduled
campaign leaves on its own; approval for one send never covers the next one.