# Migration from v1

import { Badge } from '@astrojs/starlight/components';

This guide maps every legacy `/create*Message` field onto its Messaging API v2 equivalent. Use it as a reference while porting existing integrations.

<Aside type="note">
Legacy v1 methods are deprecated but fully operational. There is no hard sunset date, so you can migrate at your own pace. For new integrations, use Messaging API v2.
</Aside>

## High-level differences

| Aspect | v1 | v2 |
|---|---|---|
| Endpoint per channel | separate method per channel (`/createMessage`, `/createEmailMessage`, `/createSMSMessage`, `/createKakaoMessage`, …) | single endpoint — `POST /messaging/v2/notify` |
| Auth | `auth` field in the request body | `Authorization: Token <API_TOKEN>` header |
| Targeting | mixed: `filter` + `conditions` + `devices` + `users` on the same request | explicit split: `NotifySegment` vs `NotifyTransactional` |
| Content | flat `content` + sibling platform blocks | nested `payload.content.localized_content.{locale}.{platform}` |
| Response | `MessageCode[]` | `message_code` + optional `unknown_identifiers` |

## From `/createMessage`

v1 `notifications[*]` entries become individual `Notify` requests (one message each). If a v1 call has multiple entries, issue one `Notify` per entry.

**Targeting decision.** If the v1 entry uses `devices` or `users` (explicit lists), map it to `transactional`. Otherwise map it to `segment`.

### Request-level fields

- `application` → `segment.application` or `transactional.application` (same app-code format).
- `applications_group`: not supported in v2. Use multiple per-app requests.
- `auth`: moved to the `Authorization` header, no longer in the body.
- `transactionId` → `transaction_id`, a request-level field alongside `segment` / `transactional`. Same deduplication behavior as v1: a repeat call with the same value within 5 minutes returns the original `message_code` instead of resending, matched on the key alone, not the payload. See [`transaction_id`](/developer/api-reference/messaging-api-v2/notify/#request-structure).

### Scheduling

- `send_date` (`"YYYY-MM-DD HH:mm"` or `"now"`) → `schedule.at` (RFC 3339 UTC timestamp). To reproduce v1 `"now"`, set `schedule.at` to the current time. Any timestamp in the past is sent immediately.
- `ignore_user_timezone` → `schedule.follow_user_timezone`. **Inverted:** `ignore_user_timezone: true` becomes `follow_user_timezone: false`.
- `timezone`: not supported. v2 always uses UTC for `at`. Convert client-side.

### Targeting

- `filter` (segment name) → `segment.code`.
- `conditions` (`[[tag, op, value], ...]`) → `segment.expression`. Rewrite to a [seglang](/developer/api-reference/segmentation-filters-api/segmentation-language/) expression. In seglang, `*` is logical AND and app-specific tags are referenced as `Tag("<application-code>", "<tag>", <op>, <value>)`. Example: `[["Country","EQ","BR"],["Language","EQ","pt"]]` with `conditions_operator: AND` → `Tag("XXXXX-XXXXX", "Country", EQ, "br") * Tag("XXXXX-XXXXX", "Language", EQ, "pt")`.
- `conditions_operator` (`AND` / `OR`): folded into `segment.expression`.
- `devices` (hwids or push tokens) → `transactional.hwids.list` or `transactional.push_tokens.list`. v2 separates the two: hwids go into `hwids`, raw push tokens into `push_tokens`.
- `users` → `transactional.users.list`.
- `platforms` (numeric codes `[1, 3, …]`) → `segment.platforms` or `transactional.platforms` (string enums `["IOS", "ANDROID", …]`). See [Platform enum](/developer/api-reference/messaging-api-v2/notify/#platform-enum).

### Content

- `content` (string) → `payload.content.localized_content.default.{platform}.body`. In v2, content is always per-locale and per-platform. Put a plain v1 string under the special `"default"` key (catch-all translation, see [Locale selection](/developer/api-reference/messaging-api-v2/payload-reference/#locale-selection-for-a-device)).
- `content` (`{locale: text}`) → `payload.content.localized_content.{locale}.{platform}.body`. Duplicate the body into each targeted platform block.
- `preset` → `payload.preset`.
- `data` → `payload.custom_data`.
- `rich_media` → `payload.open_action.rich_media.code`.
- `link` → `payload.open_action.link.url`.
- `minimize_link` (`0` or `2`) → `payload.open_action.link.shortener` (`NONE` or `BITLY`).
- `inbox_image` → `payload.content.localized_content.{locale}.{platform}.inbox.image_url` (each platform block has its own `inbox`).
- `inbox_date` → `payload.content.localized_content.{locale}.{platform}.inbox.expiration_date`.
- `inbox_days`: not supported. Convert to an absolute `expiration_date` client-side.

### Delivery controls

- `dynamic_content` / `dynamic_content_placeholders` → `dynamic_content_placeholders` on `segment` or `transactional`.
- `campaign` → `campaign` on `segment` or `transactional`.
- `capping_days` → `frequency_capping.days`.
- `capping_count` → `frequency_capping.count`.
- `send_rate` (int) → `send_rate.value` with `send_rate.bucket: "1s"`.
- `message_type` (`"marketing"` / `"transactional"`) → `message_type` (`MESSAGE_TYPE_MARKETING` / `MESSAGE_TYPE_TRANSACTIONAL`).

### Not supported in v2

- `template_bindings`: Liquid template bindings are not available in v2. Keep using v1 if you rely on them.

### Platform-specific blocks

v1 accepts platform-specific parameters at the top level of each `notifications[*]` entry (`ios`, `android`, `safari`, `chrome`, …). In v2 they move inside the locale:

```
// v1
"notifications": [{
  "content": "Hello",
  "ios":     { "title": "Hi",    "sound": "default.caf" },
  "android": { "header": "Hi",   "led": "#ff0000" }
}]

// v2
"payload": {
  "content": {
    "localized_content": {
      "en": {
        "ios":     { "title": "Hi", "body": "Hello", "sound": "default.caf" },
        "android": { "title": "Hi", "body": "Hello", "led_color": "#ff0000" }
      }
    }
  }
}
```

Field names within platform blocks differ in places. See the [Payload reference](/developer/api-reference/messaging-api-v2/payload-reference/) for the exact v2 names.

### Example: Before and after

**v1 `/createMessage`** (push to a segment):

```json
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth":        "YOUR_API_TOKEN",
    "notifications": [{
      "send_date":  "2026-05-01 12:00",
      "content":    "Hello!",
      "platforms":  [1, 3],
      "filter":     "active_users",
      "campaign":   "YYYYY-YYYYY",
      "capping_days":  7,
      "capping_count": 3,
      "message_type":  "marketing"
    }]
  }
}
```

**v2 `/messaging/v2/notify`**:

```json
{
  "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" },
    "frequency_capping": { "days": 7, "count": 3 },
    "campaign":          "YYYYY-YYYYY",
    "message_type":      "MESSAGE_TYPE_MARKETING"
  }
}
```

## From `/createTargetedMessage`

`/createTargetedMessage` maps to `transactional` in most cases or to `segment` if you were using it purely as a cross-app `devices_filter` with no explicit identifiers.

- `devices_filter` → `segment.expression` (seglang) or `segment.filter_expression` (structured).
- `content` → `payload.content.localized_content.{locale}.{platform}.body`.
- All other fields, same as `/createMessage` above.

## From `/createEmailMessage`

Move to `Notify` with `platforms: ["EMAIL"]` and an `email_payload` block. Full reference: [Email payload reference](/developer/api-reference/messaging-api-v2/email-payload-reference/).

- `subject` → `email_payload.subject` (map keyed by locale. Wrap single-locale values in `{"en": "..."}`).
- `content` (HTML) → `email_payload.body`.
- `email_template` → `email_payload.email_template`.
- `from` / `from_name` → `email_payload.from` (`{ "name": "...", "email": "..." }`).
- `reply_to` / `reply_to_name` → `email_payload.reply_to`.
- `list_unsubscribe` → `email_payload.list_unsubscribe`.
- `attachments` → `email_payload.attachments` (`[{ "name": "...", "content": "<base64>" }]`).
- Targeting, schedule, campaign, etc., same as `/createMessage`.

## From `/createSMSMessage`

Move to `Notify` with `platforms: ["SMS"]`. The SMS body is delivered through the app's configured SMS provider. Put the content in `payload.content.localized_content.{locale}.{platform}.body` on any filled platform block.

SMS-specific provider options (sender ID, etc.) continue to come from the app's SMS configuration rather than the request body.

## From `/createKakaoMessage`

Move to `Notify` with `platforms: ["KAKAO"]`, using `payload.content.localized_content.{locale}.kakao`:

- `template_id` → `kakao.template`.
- `content` → `kakao.content`.
- `variables` → `kakao.content_variables` (JSON-stringified).

## From `/createWhatsAppMessage`

Move to `Notify` with `platforms: ["WHATS_APP"]`, using `payload.content.localized_content.{locale}.whatsapp`:

- `content` (free-form text) → `whatsapp.content`. Delivered by Meta only inside the 24-hour customer service window.
- `content_id` → `whatsapp.content_id`. Name of a pre-approved Meta template.
- `language` → `whatsapp.language`. Meta-template locale (e.g. `"en_US"`). Independent of the outer `LocalizedContent` locale key.
- `content_variables` (object in v1) → `whatsapp.content_variables` (JSON-stringified object). Example v1 `{"1": "John"}` becomes v2 `"{\"1\":\"John\"}"`.
- `button_url_variables` (object) → `whatsapp.button_url_variables` (JSON-stringified).
- `header_variables` (object) → `whatsapp.header_variables` (JSON-stringified).
- `preset` → `payload.preset` (generic preset on the payload level).
- Targeting: the WhatsApp phone number that in v1 went into `devices` (e.g. `"whatsapp:+1234567890"`) must be registered via [`/registerDevice`](/developer/api-reference/device-api#registerdevice) against a user. In v2, target the resulting user with `transactional.users.list` (or the hwid via `transactional.hwids.list`).
- `use_auto_registration`: not supported. Register the WhatsApp number before sending.

## From `/createLineMessage`

Move to `Notify` with `platforms: ["LINE"]`, using `payload.content.localized_content.{locale}.line`:

- `content` (plain text) → `line.content`.
- `preset` (LINE preset code) → `line.template`. The v2 field stores a code that references a LINE template configured in the Pushwoosh Control Panel.
- Inline `template` (v1 image, carousel, or flex message structures): not supported directly in v2. Pre-configure the rich message as a LINE preset in the Control Panel and reference it through `line.template`.
- Targeting: the v1 `devices` list (LINE user IDs registered through the SDK / `/registerDevice`) becomes `transactional.users.list` (or the hwid via `transactional.hwids.list`) in v2.

## Response differences

v1 `/createMessage` returns:

```json
{
  "status_code": 200,
  "status_message": "OK",
  "response": { "Messages": ["XXXXX-XXXXX-AAAAA"] }
}
```

v2 `Notify` returns:

```json
{
  "result": {
    "message_code": "XXXXX-XXXXX-AAAAA",
    "unknown_identifiers": []
  }
}
```

Non-200 responses follow the standard gRPC-Gateway error envelope (`{ "code": ..., "message": ..., "details": [...] }`) instead of the v1 `status_code` / `status_message` pair.