Skip to content

Notify

POST https://api.pushwoosh.com/messaging/v2/notify

Creates and schedules a single message.

Request structure

Anchor link to

The request body is a NotifyRequest with exactly one of two kinds:

  • segment: target an audience segment by segment code, or — without creating a segment first — a seglang expression or a structured filter expression.
  • transactional: send to an explicit list of hwids, user IDs, push tokens, or test devices.
Shape
{
"segment": { ... }, // OR
"transactional": { ... },
"transaction_id": "unique-uuid"
}
FieldTypeDescription
transaction_idstringOptional. Idempotency key for the request — works with both segment and transactional. A repeat call with the same transaction_id within 5 minutes returns the original message_code instead of sending a duplicate message. Use a UUID or another value unique per logical send.

NotifySegment

Anchor link to

Targets users who match an audience segment or filter expression. Set exactly one of code, expression, or filter_expression. For expression and filter_expression, no segment needs to be created beforehand — the expression is evaluated inline for this send only.

FieldTypeDescription
scheduleScheduleWhen and how to send. Required.
applicationstringApplication code.
platformsarray of PlatformPlatforms the message targets.
codestringSegment code of a segment saved in advance. Mutually exclusive with expression and filter_expression.
expressionstringSeglang expression, evaluated for this send only — no saved segment required. Mutually exclusive with code and filter_expression.
filter_expressionFilterExpressionThe same logic as expression, as a structured object instead of a seglang string. Mutually exclusive with code and expression. The FilterExpression schema isn’t publicly documented — request it from Pushwoosh support if you need the structured form.
payloadPayloadPush / SMS / Telegram / Kakao / LINE / WhatsApp / Viber / Facebook Messenger payload. Mutually exclusive with email_payload.
email_payloadEmailPayloadEmail payload.
campaignstringCampaign code to attribute this message to.
campaign_namestringInternal name for this message, shown as the row title in Message History and in its export. Unrelated to campaign above. It doesn’t group messages or affect delivery. Max 255 characters, trimmed. Omit it or leave it empty to keep the row titled from the payload’s own content instead.
frequency_cappingFrequencyCappingPer-user frequency limits.
send_rateSendRateThrottling for the send.
message_typeMessageTypeMESSAGE_TYPE_MARKETING (default) or MESSAGE_TYPE_TRANSACTIONAL. Controls control-group filtering.
dynamic_content_placeholdersmap<string, string>Replaces placeholders in content.
meta_dataobjectFree-form metadata forwarded to downstream analytics.
use_latest_user_deviceboolWhen true, delivers the message to each user’s most recently active device (the one with the latest Last Application Open) instead of every device matched by the segment. Scoped to platforms: only devices on those platforms are considered, and if none has Last Application Open data, the first matching device is used instead of dropping the send. Defaults to false (send to every device).
geo_zoneGeoZoneOptional. Narrows the audience to devices whose last recorded location is inside a circle you define in this request. It is combined with code, expression, or filter_expression by AND: a device must match the segment and be inside the circle. Devices with no recorded location are skipped. Not available in NotifyTransactional.
control_groupstringCode or name of a send’s own hold-out group to apply to this message. It doesn’t narrow which control groups apply: every enabled control group of the application holds users out of marketing sends whether this field is set or not. Mutually exclusive with holdout_percentage.
holdout_percentageuint32Draws this share of the segment, 1–20, into a control group scoped to this send, on top of the application’s enabled control groups, to measure a single campaign’s impact without spending down the shared ones. 0 (default) leaves it off. Mutually exclusive with control_group.

Devices drawn into the holdout are excluded from delivery the same way as by the application’s control groups. They aren’t sent to, and the tracking log records the drop as Device is in control group. A message_type of MESSAGE_TYPE_TRANSACTIONAL skips this exclusion entirely, same as it does for the application’s control groups.

Setting holdout_percentage together with control_group, or a holdout_percentage above 20, returns 400:

Invalid holdout_percentage
{
"code": 3,
"message": "invalid argument",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "holdout_percentage",
"description": "holdout_percentage and control_group are mutually exclusive"
}
]
},
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "InvalidHoldoutPercentage",
"domain": "api.pushwoosh.com",
"metadata": {}
}
]
}

Example: Send to a segment

Anchor link to
Terminal window
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
-H "Authorization: Token YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"segment": {
"application": "XXXXX-XXXXX",
"platforms": ["IOS", "ANDROID"],
"code": "active_users",
"payload": {
"content": {
"localized_content": {
"en": {
"ios": { "body": "Hello!" },
"android": { "body": "Hello!" }
}
}
}
},
"schedule": { "at": "2026-05-01T12:00:00Z" },
"message_type": "MESSAGE_TYPE_MARKETING"
}
}'

NotifyTransactional

Anchor link to

Sends to an explicit list of recipients.

FieldTypeDescription
scheduleScheduleRequired.
applicationstringApplication code.
platformsarray of PlatformPlatforms the message targets.
test_devicesboolIf true, send to the app’s test devices only.
hwids{ "list": [string, ...] }Send to these hwids only.
users{ "list": [string, ...] }Send to these user IDs only.
push_tokens{ "list": [string, ...] }Send to these push tokens only.
payloadPayloadPush / SMS / Telegram / Kakao / LINE / WhatsApp / Viber / Facebook Messenger payload.
email_payloadEmailPayloadEmail payload.
return_unknown_identifiersboolWhen true, the response’s unknown_identifiers lists identifiers that were not found.
registration_typeRegistrationTypeWhat to do with hwids that aren’t registered in Pushwoosh yet. Defaults to REGISTRATION_TYPE_DEFAULT, which skips them. Set REGISTRATION_TYPE_FORCE to register and send to new email addresses or phone numbers in the same request.
use_latest_user_deviceboolOnly applies when you target users. Same behavior as in NotifySegment above — delivers one message per user instead of one per device, scoped to platforms. Defaults to false.
campaign, campaign_name, frequency_capping, send_rate, message_type, dynamic_content_placeholders, meta_dataSee NotifySegment above.

test_devices, hwids, users, and push_tokens are mutually exclusive. Exactly one must be set.

Send to new email addresses and phone numbers

Anchor link to

For email, SMS, WhatsApp, LINE, Viber, and Kakao, the hwid is the destination itself, such as the email address or the phone number. To send to an hwid that isn’t registered yet, list it in hwids and set registration_type to REGISTRATION_TYPE_FORCE. Pushwoosh registers it and delivers the message in the same request, so you don’t need a separate /registerDevice or /registerEmail call first.

registration_type works only with hwids. With users, an unknown user ID isn’t created, whatever registration_type is set to.

Write each hwid in the same format /registerDevice stores for that channel:

Channelhwid formatExample
EmailEmail addressnew.customer@example.com
SMSPhone number in international format+14155552671
WhatsAppPhone number with the whatsapp: prefixwhatsapp:+14155552671
KakaoPhone number with the kakao: prefixkakao:+821012345678
ViberPhone number with the viber: prefix. Pushwoosh adds the prefix if it’s missingviber:+14155552671
LINELINE user IDU4af4980629a1b2c3d4e5f6a7b8c9d0e1

Keep the whatsapp: and kakao: prefixes. Without them, the number is read as an SMS hwid. If that number is already registered for SMS, the send finds the SMS hwid, which doesn’t match the targeted platform, and the recipient is skipped.

Pushwoosh checks phone numbers only with return_unknown_identifiers: true. Then an SMS, WhatsApp, or Kakao hwid that isn’t a valid phone number isn’t registered and comes back in unknown_identifiers. Without that flag, the value is registered as is.

REGISTRATION_TYPE_FORCE doesn’t register hwids on push platforms, Telegram, or Facebook Messenger, as these need a token from the SDK or the messenger. An unknown hwid on these platforms is dropped silently. It doesn’t come back in unknown_identifiers, and nothing is sent to it.

For WhatsApp, registering a number doesn’t open the 24-hour customer service window. A number that hasn’t messaged you can receive only a pre-approved template set in whatsapp.content_id, not free-form whatsapp.content.

Example: Send to a new email address

Anchor link to
Terminal window
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
-H "Authorization: Token YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transactional": {
"application": "XXXXX-XXXXX",
"platforms": ["EMAIL"],
"hwids": { "list": ["new.customer@example.com"] },
"registration_type": "REGISTRATION_TYPE_FORCE",
"email_payload": {
"subject": { "en": "Welcome aboard" },
"body": "<p>Thanks for signing up!</p>"
},
"schedule": { "at": "2026-05-01T12:00:00Z" },
"message_type": "MESSAGE_TYPE_TRANSACTIONAL",
"return_unknown_identifiers": true
}
}'

Example: Transactional by user IDs

Anchor link to
Terminal window
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
-H "Authorization: Token YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transactional": {
"application": "XXXXX-XXXXX",
"platforms": ["IOS", "ANDROID"],
"users": { "list": ["user-123", "user-456"] },
"payload": {
"content": {
"localized_content": {
"en": { "ios": { "body": "Your order has shipped." } }
}
}
},
"schedule": { "at": "2026-05-01T12:00:00Z" },
"message_type": "MESSAGE_TYPE_TRANSACTIONAL",
"return_unknown_identifiers": true,
"use_latest_user_device": true
}
}'
{
"result": {
"message_code": "XXXXX-XXXXX-XXXXX",
"unknown_identifiers": []
}
}
FieldTypeDescription
message_codestringUnique message code. Use it with /getMessageDetails and message statistics endpoints.
unknown_identifiersarray of stringIdentifiers not found on the account. Populated only for a transactional send with return_unknown_identifiers: true or with push_tokens targeting. Identifiers registered through REGISTRATION_TYPE_FORCE aren’t listed.

No recipients found

Anchor link to

With return_unknown_identifiers: true or push_tokens targeting, Pushwoosh looks each recipient up before creating the message. If none of them is registered, Notify returns HTTP 400 with this error, and no message is created:

{
"code": 9,
"message": "No recipients found",
"details": [ ... ]
}

If at least one recipient is found, the request succeeds and the rest are listed in unknown_identifiers. To send to new email addresses or phone numbers instead, set registration_type to REGISTRATION_TYPE_FORCE.

For email, invalid addresses and addresses that bounced earlier are removed before this check, without being listed in unknown_identifiers. If no address is left after that, Notify returns 200 with a message_code, but no message is created and nothing is sent.

Shared types

Anchor link to
{
"at": "2026-05-01T12:00:00Z",
"follow_user_timezone": true,
"past_timezones_behaviour": "PAST_TIMEZONES_BEHAVIOUR_SEND_IMMEDIATELY"
}
FieldTypeDescription
attimestampAbsolute send time (RFC 3339). Maximum 30 days in the future. With follow_user_timezone unset or false, a past at is replaced with the time the request was received, so the message goes out immediately to everyone. With follow_user_timezone: true, a past at is kept as is. See the caution below.
afterdurationAlternative to at. Send after this offset from “now” (e.g. "3600s"). Maximum 30 days.
follow_user_timezoneboolWhen true, each device receives the message at at in its local timezone.
past_timezones_behaviourenumPAST_TIMEZONES_BEHAVIOUR_SEND_IMMEDIATELY (default), PAST_TIMEZONES_BEHAVIOUR_DO_NOT_SEND, or PAST_TIMEZONES_BEHAVIOUR_NEXT_DAY. Only meaningful when follow_user_timezone is true or best_time_to_send is set.
best_time_to_sendtimestampOptional. Segment sends only. Each device gets the message at its own best hour within 24 hours of this time. Devices without a best hour get it at the date and time of at / after in their local timezone. See Best time to send.

Best time to send

Anchor link to

best_time_to_send is the API counterpart of the Best time option in the Control Panel. at / after sets the fallback for devices without enough data for a best hour. Pushwoosh takes the UTC date and time of at / after and delivers the fallback at that date and time in each device’s local timezone, as with follow_user_timezone: true. This applies implicitly: leave follow_user_timezone unset, since true is rejected with BestTimeToSendWithUserTimezone. In the example below, devices without a best hour receive the message at 18:00 local time on May 1. Where that local time has already passed when the send starts, past_timezones_behaviour applies.

{
"at": "2026-05-01T18:00:00Z",
"best_time_to_send": "2026-05-01T00:00:00Z"
}

Pushwoosh rejects the request with 400 unless all of these hold:

  • The request uses the segment kind. transactional sends don’t support best_time_to_send.
  • follow_user_timezone is unset or false.
  • The send time from at / after is in the future. A past at, after: "0s", or an omitted after count as “send now” and are rejected.
  • best_time_to_send is no earlier than the next full hour and no more than 30 days ahead. It is rounded down to the full hour.
  • The send time from at / after is no later than best_time_to_send plus 24 hours.

The error’s reason (in the ErrorInfo detail) names the failed check:

reasonCause
BestTimeToSendWithUserTimezonefollow_user_timezone is true.
BestTimeToSendTooEarlybest_time_to_send is earlier than the next full hour.
BestTimeToSendTooFarInFuturebest_time_to_send is more than 30 days ahead.
SendDateAfterBestTimeToSendat / after is later than best_time_to_send plus 24 hours.
BestTimeToSendNotAllowedbest_time_to_send is set on a transactional send.
BestTimeToSendWithSendNowat / after resolves to “now” (past at or zero after).

A one-off circle for geo_zone, defined in the request itself. It isn’t a saved geozone: geo campaigns and the Geozones API aren’t involved, and nothing is stored in the application.

{ "lat": 40.7128, "lng": -74.006, "range": 500 }
FieldTypeDescription
latdoubleLatitude of the circle center, in degrees. Required.
lngdoubleLongitude of the circle center, in degrees. Required.
rangedoubleCircle radius in meters. Required.

FrequencyCapping

Anchor link to

Per-user frequency limits for marketing sends. To disable capping, omit frequency_capping entirely, or send days: 0 together with count: 0.

{ "days": 7, "count": 3, "exclude": false, "avoid": true }
  • days (int, 1–30, or 0 to disable capping): look-back window. Must be sent together with count — if one is 0, the other must also be 0; sending one as 0 while the other is nonzero returns 400.
  • count (int, 1 or higher, or 0 to disable capping): maximum messages allowed within days. Same pairing rule as days above.
  • exclude (bool): hard-exclude users who have already hit the cap.
  • avoid (bool): soft-avoid users who have already hit the cap (they still count toward analytics).
{ "value": 500, "bucket": "1s", "avoid": false }

Throttles the send. value is messages per bucket; typical bucket is "1s".

Platform enum

Anchor link to

IOS, ANDROID, OSX, WINDOWS, AMAZON, SAFARI, CHROME, FIREFOX, IE, EMAIL, HUAWEI_ANDROID, SMS, WEB, KAKAO, TELEGRAM, LINE, WHATS_APP, VIBER, FB_MESSENGER.

RegistrationType enum

Anchor link to
  • REGISTRATION_TYPE_DEFAULT: skips identifiers that aren’t registered in Pushwoosh. Default.
  • REGISTRATION_TYPE_NONE: sends to every listed hwid without saving a device for it.
  • REGISTRATION_TYPE_FORCE: registers each unknown hwid as a device on the targeted channel, then sends to it. Applies to email, SMS, WhatsApp, LINE, Viber, and Kakao.

MessageType enum

Anchor link to
  • MESSAGE_TYPE_UNSPECIFIED: equivalent to MESSAGE_TYPE_MARKETING.
  • MESSAGE_TYPE_MARKETING: subject to control-group filtering and frequency capping.
  • MESSAGE_TYPE_TRANSACTIONAL: skips control-group filtering and frequency capping. Use for order confirmations, OTPs, and similar critical flows.