---
title: "Set the ad schedule"
method: POST
path: "/api/v1/campaigns/{id}/ad-schedule"
tags: ["campaigns"]
---

# Set the ad schedule

`POST /api/v1/campaigns/{id}/ad-schedule`

## Request

- Base URL: `https://api.growomat.com`
- URL: `https://api.growomat.com/api/v1/campaigns/{id}/ad-schedule`
- Auth: HTTP bearer

Human-friendly schedule input: `HH:MM` windows, day shorthands (`MON`, `WEEKDAYS`, `WEEKEND`) and presets. The server expands them, splits windows that cross midnight, resolves the time zone and stores the result as `adScheduleStructured`.

Time zones: `"local"` means the searcher's own time zone on platforms that support it, and the account time zone elsewhere. An explicit IANA zone (`Europe/Berlin`) is converted into account-time hours before storing. *Introspect the current token* shows the account time zone per platform under `scheduling.timezones`.

Pass `dry_run: true` to get the expanded schedule and any warnings without changing the campaign. Changes reach the ad platforms on the next sync.

**Errors**

- `404 NOT_FOUND`: no campaign with this id in the active Space.
- `400 INVALID_REQUEST`: the body is not valid JSON.
- `400` with a schedule-specific `error.code` (for example `AD_SCHEDULE_TOO_MANY_WINDOWS`), or `VALIDATION_ERROR` with `details.issues[]`.

## Path parameters

- `id` string, required

## Headers

- `X-Space-Id` string

## Request body

- SetAdSchedule: Human-friendly ad schedule input. Provide a preset and/or windows; the server expands them, splits windows that cross midnight, resolves the time zone and stores the result as `adScheduleStructured`.
  - `timezone` string: `local` (the searcher's own time zone where the platform supports it, otherwise account time with a warning), `UTC`, or an IANA zone such as `Europe/Berlin`. A zone that differs from the account zone is converted into account-time hours before storing.
  - `windows` SetAdScheduleWindow[]: The windows to set.
    - `days` string[], required: Day tokens: `MON` to `SUN`, full day names, `WEEKDAYS` or `WEEKEND`.
    - `start` string, required: Start time as `HH:MM`. Minutes must be `00`, `15`, `30` or `45`.
    - `end` string, required: End time as `HH:MM`; `24:00` is allowed. An end at or before the start means the window crosses midnight and is split automatically.
    - `bidModifier` number: Bid multiplier for this window; `1.0` means no change.
  - `preset` 'ALL_HOURS_ALL_DAYS' | 'BUSINESS_HOURS' | 'OVERNIGHT' | 'WEEKENDS' | 'PHARMACY_OPEN_HOURS': A ready-made schedule. Can be combined with `windows`.
  - `platform` 'google-ads' | 'microsoft-ads' | 'reddit-ads' | 'meta-ads' | 'openai-ads' | 'linkedin-ads': Which platform's time zone rules to apply when the campaign runs on several. Inferred when omitted.
  - `dry_run` boolean: When `true`, return the expanded schedule and warnings without changing the campaign.

## Response `200`

OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.

- SuccessEnvelope: The shape of every successful response.
  - `data` unknown, required
  - `meta` object: Present on list responses.
    - `nextCursor` union: Pass as `cursor` to get the next page. `null` on the last page.
      - string
      - number
    - `total` integer: Number of items in this page.

## Other responses

- `201`: Created. The new resource is in `data`.
- `400`: The request is malformed or fails validation. `error.code` is `VALIDATION_ERROR` (with `details.issues[]` naming each field), `INVALID_REQUEST` (the body is not valid JSON or has the wrong shape), or one of the specific codes listed in this operation's description.
- `401`: The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).
- `403`: The token lacks the scope this operation needs (`INSUFFICIENT_SCOPE`), the plan is read-only (`READ_ONLY_MODE`), or the caller is not a member of the Space named in `X-Space-Id` (`NOT_A_MEMBER`).
- `404`: No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).
- `409`: The request conflicts with the current state, for example a delete blocked by dependents or a sync already running. This operation's description names the `error.code` values it can return.
- `429`: Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.

---

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