Replace the store's flow sending policy
Authorizations
A secret API key. Publishable keys cannot reach this API. A key may carry an expiry, and an expired key is refused exactly like an unknown one, with a 401 that names no reason; check the key's expires_at in the dashboard rather than inferring it from a response. When a merchant rolls a key's secret they choose a grace window of up to 3 days, and for its duration BOTH the new secret and the one it replaced authenticate, so an integration moves over on its own deploy schedule instead of at the instant the button is pressed. Move before the window closes: after it, the old secret is refused. Nothing else about this contract moves with a roll. The key keeps its id and its scopes, so the only thing an integration updates is the credential itself.
Headers
A unique key per logical write. Replaying a request with the same key returns the first response byte for byte instead of applying the write twice.
Body
An UPSERT behind a PUT: a store with no policy row gets one, a store with one has it replaced whole. EVERY FIELD IS REQUIRED. A dropped respect_quiet_hours would decode to false and switch the store's send window off entirely, which on a store with SMS steps means mailing people in the middle of their night, and it would answer 200. Read GET /api/v1/marketing-flow-settings and send every field back. THE TWO HOURS ARE A SEND WINDOW, NOT A QUIET PERIOD, and reading them backwards is the single likeliest way to blast a list at 3am. Sending is PERMITTED while the recipient's local hour is in [start, end) and deferred otherwise, so the policy "quiet hours 21:00 to 09:00" is the window [9, 21), which is the default. quiet_start_hour and quiet_end_hour are refused by name. expected_updated_at IS REQUIRED and is the other half. This row has several writers, so an integration echoing back what it read ten seconds ago would silently revert the change the merchant just made in their dashboard, with a 200 on both calls. A mismatch is 409 settings_modified: re-read and send the new updated_at. NULL is legal and means "I expect this store to have no settings row yet"; it is refused the moment a row exists, so it cannot be used to opt out of the check. An ABSENT key is 400, because absence asks for the blind overwrite this exists to prevent.
Local hour sending becomes permitted, 0 to 23.
Local hour sending stops, 0 to 23, exclusive.
The master switch for the window. False sends whenever a step comes due. The message spine keeps its own statutory floor for marketing SMS regardless, so turning this off widens email rather than defeating that.
Applies the per-flow re-enrollment policy. With it off a contact may re-enter as soon as their previous run finished. The one-live-run-per-contact guard is a database index and holds either way.
What happens to a contact whose time zone cannot be resolved. NEITHER VALUE EVER SENDS IMMEDIATELY: us_safe clamps to a window that is daytime across the continental US, store_tz uses the store's own zone, and an unresolvable zone always defers.
us_safe, store_tz Spreads a cohort that all finished the same wait at the same instant. Applied forward-only and BEFORE the send-window clamp, so jitter can never push a send back out of the window it was just moved into.
How far one wake may walk a run before yielding, 1 to 500.
An ENROLLMENT ceiling, which is a different thing from a send ceiling: per-contact send frequency belongs to the message spine's gate, and counting it here as well would double-count and drift out of agreement with it. 0 means unlimited.
The updated_at you last read. Send null only when you expect this store to have no settings row yet.
Response
Success
