{
    "info": {
        "name": "Smartcoop Broadcast API",
        "_postman_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
        "description": "API buat kirim broadcast/pesan WhatsApp lewat Smartcoop Broadcast — 1 API key berlaku buat semua nomor WhatsApp di akunmu.\n\n**Sebelum mulai:**\n1. Set variable `base_url` di collection ini ke domain aplikasi kamu (sudah keisi default sesuai domain saat file ini diunduh).\n2. Set variable `api_key` ke API key dari halaman API Key di dashboard (format `fvb_...`). Dikirim sebagai header `Authorization: Bearer <api_key>` — sudah otomatis dipasang di collection-level auth, semua request di bawah warisi ini.\n\n**Konsep dasar:**\n- **Template** = pesan berformat tetap yang disetujui Meta, satu-satunya cara mulai percakapan baru ke nomor yang belum pernah chat duluan.\n- **Teks bebas** (`/send-text`) cuma bisa dipakai dalam jendela 24 jam sejak pelanggan terakhir chat — di luar itu wajib pakai Template.\n- **OTP** (`/send-otp`) pakai Template kategori Authentication khusus, teksnya dibuat otomatis oleh Meta.\n- Template kategori **\"Media Card Carousel\" belum bisa dikirim lewat API** — cuma lewat dashboard untuk saat ini.\n- Ada 2 bentuk error: validasi field bawaan Laravel balikin `{\"message\": \"...\", \"errors\": {...}}` (422), error logika bisnis kami sendiri balikin `{\"error\": \"...\"}` — response contoh di tiap request di bawah pakai bentuk kedua.",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "auth": {
        "type": "bearer",
        "bearer": [
            {
                "key": "token",
                "value": "{{api_key}}",
                "type": "string"
            }
        ]
    },
    "variable": [
        {
            "key": "base_url",
            "value": "https://broadcaster.4visionmedia.net",
            "type": "string"
        },
        {
            "key": "api_key",
            "value": "GANTI_DENGAN_API_KEY_KAMU",
            "type": "string"
        }
    ],
    "item": [
        {
            "name": "Nomor & Koneksi",
            "description": "Satu API key berlaku buat semua nomor WhatsApp yang dimiliki akunmu. Resource ini yang ngasih tau nomor apa aja yang tersedia dan connection_id-nya masing-masing — nilai yang wajib disertakan di setiap operasi kirim pesan di bawah.",
            "item": [
                {
                    "name": "Daftar Nomor",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/numbers",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "numbers"
                            ]
                        },
                        "description": "Nomor WhatsApp mana aja yang bisa dipakai buat kirim lewat API key ini."
                    },
                    "response": [
                        {
                            "name": "Berhasil (200)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/numbers",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "numbers"
                                    ]
                                },
                                "description": "Nomor WhatsApp mana aja yang bisa dipakai buat kirim lewat API key ini."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"numbers\": [\n        {\n            \"connection_id\": 4,\n            \"phone_number\": \"6281234567890\",\n            \"label\": \"Nomor Utama\",\n            \"waba_name\": \"Toko Maju Jaya\"\n        }\n    ]\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Template",
            "description": "Template adalah pesan berformat tetap yang sudah disetujui Meta — satu-satunya cara memulai percakapan baru ke nomor yang belum pernah chat ke kamu (lihat Konsep Dasar). Resource ini cuma nampilin template yang sudah kamu tandai \"Bisa dipakai lewat API\" di dashboard.",
            "item": [
                {
                    "name": "Daftar Template",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/templates",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "templates"
                            ]
                        },
                        "description": "Template yang sudah disetujui Meta dan dicentang \"Bisa dipakai lewat API\". Variabelnya diberi posisi `{{1}}`, `{{2}}`, dst. — urutan diisinya diserahkan ke sistem kamu sendiri."
                    },
                    "response": [
                        {
                            "name": "Berhasil (200)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/templates",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "templates"
                                    ]
                                },
                                "description": "Template yang sudah disetujui Meta dan dicentang \"Bisa dipakai lewat API\". Variabelnya diberi posisi `{{1}}`, `{{2}}`, dst. — urutan diisinya diserahkan ke sistem kamu sendiri."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"templates\": [\n        {\n            \"name\": \"info_promo\",\n            \"category\": \"MARKETING\",\n            \"language\": \"id\",\n            \"body\": \"Halo {{1}}, ada promo spesial buat kamu: {{2}}!\",\n            \"variable_count\": 2,\n            \"waba_name\": \"Toko Maju Jaya\",\n            \"note\": null\n        },\n        {\n            \"name\": \"otp_login\",\n            \"category\": \"AUTHENTICATION\",\n            \"language\": \"id\",\n            \"body\": \"\",\n            \"variable_count\": 0,\n            \"waba_name\": \"Toko Maju Jaya\",\n            \"note\": \"Kategori Authentication: teks pesan dibuat otomatis oleh Meta, tidak ada variabel bebas. Kirim lewat POST /send-otp dengan parameter code.\"\n        }\n    ]\n}"
                        }
                    ]
                },
                {
                    "name": "Detail 1 Template",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/templates/:name",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "templates",
                                ":name"
                            ],
                            "variable": [
                                {
                                    "key": "name",
                                    "value": "info_promo",
                                    "description": ""
                                }
                            ]
                        },
                        "description": "Struktur komponen lengkap satu template (HEADER/BODY/FOOTER/BUTTONS) — buat yang cuma butuh tau 1 template doang, bukan nge-list semuanya.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `name` | string | wajib | Path parameter — nama template dari GET /templates. |"
                    },
                    "response": [
                        {
                            "name": "Berhasil (200)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/templates/:name",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "templates",
                                        ":name"
                                    ],
                                    "variable": [
                                        {
                                            "key": "name",
                                            "value": "info_promo",
                                            "description": ""
                                        }
                                    ]
                                },
                                "description": "Struktur komponen lengkap satu template (HEADER/BODY/FOOTER/BUTTONS) — buat yang cuma butuh tau 1 template doang, bukan nge-list semuanya.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `name` | string | wajib | Path parameter — nama template dari GET /templates. |"
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"name\": \"info_promo\",\n    \"category\": \"MARKETING\",\n    \"language\": \"id\",\n    \"variable_count\": 2,\n    \"waba_name\": \"Toko Maju Jaya\",\n    \"components\": [\n        {\n            \"type\": \"HEADER\",\n            \"format\": \"IMAGE\"\n        },\n        {\n            \"type\": \"BODY\",\n            \"text\": \"Halo {{1}}, ada promo spesial buat kamu: {{2}}!\"\n        },\n        {\n            \"type\": \"FOOTER\",\n            \"text\": \"Balas STOP untuk berhenti menerima pesan promosi.\"\n        }\n    ],\n    \"note\": null\n}"
                        },
                        {
                            "name": "Gagal (404)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/templates/:name",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "templates",
                                        ":name"
                                    ],
                                    "variable": [
                                        {
                                            "key": "name",
                                            "value": "info_promo",
                                            "description": ""
                                        }
                                    ]
                                },
                                "description": "Struktur komponen lengkap satu template (HEADER/BODY/FOOTER/BUTTONS) — buat yang cuma butuh tau 1 template doang, bukan nge-list semuanya.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `name` | string | wajib | Path parameter — nama template dari GET /templates. |"
                            },
                            "status": "Error",
                            "code": 404,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"Template 'info_promo' tidak ditemukan atau belum bisa dipakai lewat API.\"\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Pesan — Pakai Template",
            "description": "Kirim broadcast pakai Template yang sudah disetujui Meta — satu-satunya cara memulai percakapan baru ke nomor yang belum pernah chat ke kamu. Dua operasi di bawah beda cuma di sumber penerimanya: nomor mentah yang kamu kirim sendiri, atau grup kontak yang sudah tersimpan di dashboard.",
            "item": [
                {
                    "name": "Daftar Grup Kontak",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/groups",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "groups"
                            ]
                        },
                        "description": "Semua grup kontak client ini, berlaku buat semua nomor (gak terikat 1 WABA) — inilah yang ngasih tau `group_id` buat dipakai di operasi \"Kirim ke Grup Kontak\" di bawah."
                    },
                    "response": [
                        {
                            "name": "Berhasil (200)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/groups",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "groups"
                                    ]
                                },
                                "description": "Semua grup kontak client ini, berlaku buat semua nomor (gak terikat 1 WABA) — inilah yang ngasih tau `group_id` buat dipakai di operasi \"Kirim ke Grup Kontak\" di bawah."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"groups\": [\n        {\n            \"group_id\": 12,\n            \"name\": \"Pelanggan VIP\",\n            \"active_contacts_count\": 48,\n            \"total_contacts_count\": 50\n        }\n    ]\n}"
                        }
                    ]
                },
                {
                    "name": "Daftar Kontak dalam Grup",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/groups/:id/contacts",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "groups",
                                ":id",
                                "contacts"
                            ],
                            "variable": [
                                {
                                    "key": "id",
                                    "value": "12",
                                    "description": ""
                                }
                            ]
                        },
                        "description": "Isi satu grup kontak — nama, nomor, custom field, dan status opt-out tiap kontak. Berguna buat preview isi grup sebelum manggil \"Kirim ke Grup Kontak\" di bawah.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `id` | integer | wajib | Path parameter — group_id dari GET /groups. |"
                    },
                    "response": [
                        {
                            "name": "Berhasil (200)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/groups/:id/contacts",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "groups",
                                        ":id",
                                        "contacts"
                                    ],
                                    "variable": [
                                        {
                                            "key": "id",
                                            "value": "12",
                                            "description": ""
                                        }
                                    ]
                                },
                                "description": "Isi satu grup kontak — nama, nomor, custom field, dan status opt-out tiap kontak. Berguna buat preview isi grup sebelum manggil \"Kirim ke Grup Kontak\" di bawah.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `id` | integer | wajib | Path parameter — group_id dari GET /groups. |"
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"group_id\": 12,\n    \"name\": \"Pelanggan VIP\",\n    \"contacts\": [\n        {\n            \"contact_id\": 30,\n            \"name\": \"Andi Saputra\",\n            \"phone\": \"6281234567890\",\n            \"custom_fields\": {\n                \"kota\": \"Bandung\"\n            },\n            \"opted_out\": false\n        }\n    ]\n}"
                        },
                        {
                            "name": "Gagal (404)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/groups/:id/contacts",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "groups",
                                        ":id",
                                        "contacts"
                                    ],
                                    "variable": [
                                        {
                                            "key": "id",
                                            "value": "12",
                                            "description": ""
                                        }
                                    ]
                                },
                                "description": "Isi satu grup kontak — nama, nomor, custom field, dan status opt-out tiap kontak. Berguna buat preview isi grup sebelum manggil \"Kirim ke Grup Kontak\" di bawah.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `id` | integer | wajib | Path parameter — group_id dari GET /groups. |"
                            },
                            "status": "Error",
                            "code": 404,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"Grup kontak tidak ditemukan.\"\n}"
                        }
                    ]
                },
                {
                    "name": "Kirim ke Nomor Langsung",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/send",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "send"
                            ]
                        },
                        "description": "Kirim broadcast pakai template langsung ke daftar nomor yang kamu kirim di request — nomornya tidak disimpan sebagai kontak kami, cocok kalau sistem kamu sendiri yang mengelola data pelanggan.\n\n> ⚠️ **Perhatian:** Kalau `template_name` bertipe **Media Card Carousel**, parameter `cards` WAJIB disertakan (jumlahnya harus persis sama dengan jumlah kartu template-nya, urutan sama kayak di dashboard). Buat template biasa, jangan sertakan `cards` sama sekali.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `template_name` | string | wajib | Harus sudah approved di WABA ini. |\n| `numbers` | array<string> | wajib | Maksimal 1000 nomor per request. |\n| `variables` | array | opsional | Isi buat {{1}}, {{2}}, dst di body utama — sama buat semua nomor di request ini. |\n| `cards` | array | opsional | WAJIB kalau template-nya Carousel — 1 objek per kartu, urutan sama kayak di dashboard: header_media_url (wajib, URL gambar/video), header_type (image/video, default image), body_params (isi {{n}} khusus kartu itu), buttons ([{type, text, payload}]). |\n| `dry_run` | boolean | opsional | Kalau true, cuma validasi + balikin teks pesan yang bakal terkirim (dari template lokal kami) buat tiap nomor, termasuk preview tiap kartu carousel (cards_preview) kalau ada — gak ada Meta yang dipanggil, gak ada broadcast yang dibuat/diantre. |",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"connection_id\": 4,\n    \"template_name\": \"info_promo\",\n    \"numbers\": [\n        \"6281234567890\",\n        \"6281298765432\"\n    ],\n    \"variables\": [\n        \"Budi\",\n        \"Diskon 20%\"\n    ]\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "response": [
                        {
                            "name": "Berhasil (201)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send"
                                    ]
                                },
                                "description": "Kirim broadcast pakai template langsung ke daftar nomor yang kamu kirim di request — nomornya tidak disimpan sebagai kontak kami, cocok kalau sistem kamu sendiri yang mengelola data pelanggan.\n\n> ⚠️ **Perhatian:** Kalau `template_name` bertipe **Media Card Carousel**, parameter `cards` WAJIB disertakan (jumlahnya harus persis sama dengan jumlah kartu template-nya, urutan sama kayak di dashboard). Buat template biasa, jangan sertakan `cards` sama sekali.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `template_name` | string | wajib | Harus sudah approved di WABA ini. |\n| `numbers` | array<string> | wajib | Maksimal 1000 nomor per request. |\n| `variables` | array | opsional | Isi buat {{1}}, {{2}}, dst di body utama — sama buat semua nomor di request ini. |\n| `cards` | array | opsional | WAJIB kalau template-nya Carousel — 1 objek per kartu, urutan sama kayak di dashboard: header_media_url (wajib, URL gambar/video), header_type (image/video, default image), body_params (isi {{n}} khusus kartu itu), buttons ([{type, text, payload}]). |\n| `dry_run` | boolean | opsional | Kalau true, cuma validasi + balikin teks pesan yang bakal terkirim (dari template lokal kami) buat tiap nomor, termasuk preview tiap kartu carousel (cards_preview) kalau ada — gak ada Meta yang dipanggil, gak ada broadcast yang dibuat/diantre. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"template_name\": \"info_promo\",\n    \"numbers\": [\n        \"6281234567890\",\n        \"6281298765432\"\n    ],\n    \"variables\": [\n        \"Budi\",\n        \"Diskon 20%\"\n    ]\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Created",
                            "code": 201,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"broadcast_id\": 512,\n    \"status\": \"queued\",\n    \"template\": \"info_promo\",\n    \"total_recipients\": 2\n}"
                        },
                        {
                            "name": "Gagal (422)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send"
                                    ]
                                },
                                "description": "Kirim broadcast pakai template langsung ke daftar nomor yang kamu kirim di request — nomornya tidak disimpan sebagai kontak kami, cocok kalau sistem kamu sendiri yang mengelola data pelanggan.\n\n> ⚠️ **Perhatian:** Kalau `template_name` bertipe **Media Card Carousel**, parameter `cards` WAJIB disertakan (jumlahnya harus persis sama dengan jumlah kartu template-nya, urutan sama kayak di dashboard). Buat template biasa, jangan sertakan `cards` sama sekali.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `template_name` | string | wajib | Harus sudah approved di WABA ini. |\n| `numbers` | array<string> | wajib | Maksimal 1000 nomor per request. |\n| `variables` | array | opsional | Isi buat {{1}}, {{2}}, dst di body utama — sama buat semua nomor di request ini. |\n| `cards` | array | opsional | WAJIB kalau template-nya Carousel — 1 objek per kartu, urutan sama kayak di dashboard: header_media_url (wajib, URL gambar/video), header_type (image/video, default image), body_params (isi {{n}} khusus kartu itu), buttons ([{type, text, payload}]). |\n| `dry_run` | boolean | opsional | Kalau true, cuma validasi + balikin teks pesan yang bakal terkirim (dari template lokal kami) buat tiap nomor, termasuk preview tiap kartu carousel (cards_preview) kalau ada — gak ada Meta yang dipanggil, gak ada broadcast yang dibuat/diantre. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"template_name\": \"info_promo\",\n    \"numbers\": [\n        \"6281234567890\",\n        \"6281298765432\"\n    ],\n    \"variables\": [\n        \"Budi\",\n        \"Diskon 20%\"\n    ]\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Error",
                            "code": 422,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"Template 'info_promo' tidak ditemukan atau belum approved buat nomor ini.\"\n}"
                        }
                    ]
                },
                {
                    "name": "Kirim ke Grup Kontak",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/send-to-group",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "send-to-group"
                            ]
                        },
                        "description": "Kirim ke satu grup kontak yang sudah ada di dashboard kami — grup itu harus sudah punya tepat 1 template yang di-mapping (lewat halaman Template) kecuali kamu sertakan `template_name` buat pilih salah satu.\n\n> ⚠️ **Perhatian:** Sama kayak Kirim ke Nomor Langsung — kalau `template_name`/mapping grup-nya bertipe **Media Card Carousel**, parameter `cards` WAJIB disertakan (jumlah & urutan sama kayak di dashboard), dikirim sama persis ke semua kontak di grup ini. Buat template biasa, jangan sertakan `cards`.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `group_id` | integer | wajib | ID grup kontak. |\n| `template_name` | string | opsional | Wajib kalau grupnya punya >1 template ter-mapping. |\n| `variables` | array | opsional | Cuma buat variabel yang belum otomatis terisi dari data kontak. |\n| `cards` | array | opsional | WAJIB kalau template-nya Carousel — bentuknya sama persis kayak di Kirim ke Nomor Langsung: 1 objek per kartu (header_media_url, header_type, body_params, buttons), dikirim sama ke semua kontak di grup. |\n| `dry_run` | boolean | opsional | Kalau true, cuma validasi + balikin teks pesan yang bakal terkirim buat tiap kontak di grup (sudah termasuk variabel yang otomatis terisi dari data kontak), termasuk preview tiap kartu carousel (cards_preview) kalau ada — gak ada Meta yang dipanggil, gak ada broadcast yang dibuat/diantre. |",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"connection_id\": 4,\n    \"group_id\": 12\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "response": [
                        {
                            "name": "Berhasil (201)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send-to-group",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send-to-group"
                                    ]
                                },
                                "description": "Kirim ke satu grup kontak yang sudah ada di dashboard kami — grup itu harus sudah punya tepat 1 template yang di-mapping (lewat halaman Template) kecuali kamu sertakan `template_name` buat pilih salah satu.\n\n> ⚠️ **Perhatian:** Sama kayak Kirim ke Nomor Langsung — kalau `template_name`/mapping grup-nya bertipe **Media Card Carousel**, parameter `cards` WAJIB disertakan (jumlah & urutan sama kayak di dashboard), dikirim sama persis ke semua kontak di grup ini. Buat template biasa, jangan sertakan `cards`.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `group_id` | integer | wajib | ID grup kontak. |\n| `template_name` | string | opsional | Wajib kalau grupnya punya >1 template ter-mapping. |\n| `variables` | array | opsional | Cuma buat variabel yang belum otomatis terisi dari data kontak. |\n| `cards` | array | opsional | WAJIB kalau template-nya Carousel — bentuknya sama persis kayak di Kirim ke Nomor Langsung: 1 objek per kartu (header_media_url, header_type, body_params, buttons), dikirim sama ke semua kontak di grup. |\n| `dry_run` | boolean | opsional | Kalau true, cuma validasi + balikin teks pesan yang bakal terkirim buat tiap kontak di grup (sudah termasuk variabel yang otomatis terisi dari data kontak), termasuk preview tiap kartu carousel (cards_preview) kalau ada — gak ada Meta yang dipanggil, gak ada broadcast yang dibuat/diantre. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"group_id\": 12\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Created",
                            "code": 201,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"broadcast_id\": 513,\n    \"status\": \"queued\",\n    \"template\": \"info_promo\",\n    \"group\": \"Pelanggan VIP\",\n    \"total_recipients\": 48\n}"
                        },
                        {
                            "name": "Gagal (422)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send-to-group",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send-to-group"
                                    ]
                                },
                                "description": "Kirim ke satu grup kontak yang sudah ada di dashboard kami — grup itu harus sudah punya tepat 1 template yang di-mapping (lewat halaman Template) kecuali kamu sertakan `template_name` buat pilih salah satu.\n\n> ⚠️ **Perhatian:** Sama kayak Kirim ke Nomor Langsung — kalau `template_name`/mapping grup-nya bertipe **Media Card Carousel**, parameter `cards` WAJIB disertakan (jumlah & urutan sama kayak di dashboard), dikirim sama persis ke semua kontak di grup ini. Buat template biasa, jangan sertakan `cards`.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `group_id` | integer | wajib | ID grup kontak. |\n| `template_name` | string | opsional | Wajib kalau grupnya punya >1 template ter-mapping. |\n| `variables` | array | opsional | Cuma buat variabel yang belum otomatis terisi dari data kontak. |\n| `cards` | array | opsional | WAJIB kalau template-nya Carousel — bentuknya sama persis kayak di Kirim ke Nomor Langsung: 1 objek per kartu (header_media_url, header_type, body_params, buttons), dikirim sama ke semua kontak di grup. |\n| `dry_run` | boolean | opsional | Kalau true, cuma validasi + balikin teks pesan yang bakal terkirim buat tiap kontak di grup (sudah termasuk variabel yang otomatis terisi dari data kontak), termasuk preview tiap kartu carousel (cards_preview) kalau ada — gak ada Meta yang dipanggil, gak ada broadcast yang dibuat/diantre. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"group_id\": 12\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Error",
                            "code": 422,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"Grup \\\"Pelanggan VIP\\\" punya lebih dari 1 template ter-mapping — sertakan 'template_name' buat pilih salah satu.\",\n    \"available_templates\": [\n        \"info_promo\",\n        \"reminder_bayar\"\n    ]\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Pesan — Teks Bebas",
            "description": "Balasan bebas format, tanpa Template — tapi Meta cuma mengizinkannya dalam jendela 24 jam sejak pelanggan terakhir kali chat ke nomor kamu. Cocok buat auto-reply customer service, bukan buat broadcast promosi ke nomor baru (pakai resource Pesan — Pakai Template buat itu).",
            "item": [
                {
                    "name": "Balas Teks Bebas",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/send-text",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "send-text"
                            ]
                        },
                        "description": "Kirim pesan teks bebas (bukan template) — cuma jalan dalam jendela 24 jam Meta (nomor tujuan harus baru saja chat ke WABA ini). Dikirim langsung (synchronous), hasilnya dibalikin seketika, bukan diantre.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `phone` | string | wajib | Nomor tujuan. |\n| `message` | string | wajib | Maksimal 4096 karakter. |",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"connection_id\": 4,\n    \"phone\": \"6281234567890\",\n    \"message\": \"Terima kasih sudah menghubungi kami!\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "response": [
                        {
                            "name": "Berhasil (201)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send-text",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send-text"
                                    ]
                                },
                                "description": "Kirim pesan teks bebas (bukan template) — cuma jalan dalam jendela 24 jam Meta (nomor tujuan harus baru saja chat ke WABA ini). Dikirim langsung (synchronous), hasilnya dibalikin seketika, bukan diantre.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `phone` | string | wajib | Nomor tujuan. |\n| `message` | string | wajib | Maksimal 4096 karakter. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"phone\": \"6281234567890\",\n    \"message\": \"Terima kasih sudah menghubungi kami!\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Created",
                            "code": 201,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"status\": \"sent\",\n    \"message_id\": \"wamid.HBg...\",\n    \"broadcast_id\": 514\n}"
                        },
                        {
                            "name": "Gagal (422)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send-text",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send-text"
                                    ]
                                },
                                "description": "Kirim pesan teks bebas (bukan template) — cuma jalan dalam jendela 24 jam Meta (nomor tujuan harus baru saja chat ke WABA ini). Dikirim langsung (synchronous), hasilnya dibalikin seketika, bukan diantre.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `phone` | string | wajib | Nomor tujuan. |\n| `message` | string | wajib | Maksimal 4096 karakter. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"phone\": \"6281234567890\",\n    \"message\": \"Terima kasih sudah menghubungi kami!\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Error",
                            "code": 422,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"(#131047) Message failed to send because more than 24 hours have passed since the customer last replied to this number.\",\n    \"error_code\": 131047,\n    \"broadcast_id\": 514\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Pesan — OTP/Verifikasi",
            "description": "Kirim kode OTP/verifikasi (login, ganti password, dsb) lewat Template kategori khusus Authentication — beda dari Template biasa, teks pesannya dibuat otomatis oleh Meta (bukan kamu yang nulis), kamu cuma kirim kodenya. Kode OTP itu sendiri dibuat dan dicocokkan oleh sistem kamu sendiri — resource ini cuma tugas nganterinnya lewat WhatsApp. Dikirim langsung (synchronous), hasilnya dibalikin seketika, bukan diantre — cocok buat orang yang lagi nunggu kode di HP-nya.",
            "item": [
                {
                    "name": "Kirim Kode OTP",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/send-otp",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "send-otp"
                            ]
                        },
                        "description": "`template_name` wajib sudah approved, kategori Authentication, dan dicentang \"Bisa dipakai lewat API\" (lihat Daftar Template — template kategori ini juga muncul di situ, ditandai lewat field `note`).\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `template_name` | string | wajib | Template kategori Authentication yang sudah approved. |\n| `phone` | string | wajib | Nomor tujuan. |\n| `code` | string | wajib | Kode OTP-nya — maksimal 20 karakter. Dibuat & disimpan sendiri oleh sistem kamu, bukan oleh kami. |",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"connection_id\": 4,\n    \"template_name\": \"otp_login\",\n    \"phone\": \"6281234567890\",\n    \"code\": \"482913\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "response": [
                        {
                            "name": "Berhasil (201)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send-otp",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send-otp"
                                    ]
                                },
                                "description": "`template_name` wajib sudah approved, kategori Authentication, dan dicentang \"Bisa dipakai lewat API\" (lihat Daftar Template — template kategori ini juga muncul di situ, ditandai lewat field `note`).\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `template_name` | string | wajib | Template kategori Authentication yang sudah approved. |\n| `phone` | string | wajib | Nomor tujuan. |\n| `code` | string | wajib | Kode OTP-nya — maksimal 20 karakter. Dibuat & disimpan sendiri oleh sistem kamu, bukan oleh kami. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"template_name\": \"otp_login\",\n    \"phone\": \"6281234567890\",\n    \"code\": \"482913\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Created",
                            "code": 201,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"status\": \"sent\",\n    \"message_id\": \"wamid.HBg...\",\n    \"broadcast_id\": 515\n}"
                        },
                        {
                            "name": "Gagal (422)",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/send-otp",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "send-otp"
                                    ]
                                },
                                "description": "`template_name` wajib sudah approved, kategori Authentication, dan dicentang \"Bisa dipakai lewat API\" (lihat Daftar Template — template kategori ini juga muncul di situ, ditandai lewat field `note`).\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `connection_id` | integer | wajib | Dari GET /numbers. |\n| `template_name` | string | wajib | Template kategori Authentication yang sudah approved. |\n| `phone` | string | wajib | Nomor tujuan. |\n| `code` | string | wajib | Kode OTP-nya — maksimal 20 karakter. Dibuat & disimpan sendiri oleh sistem kamu, bukan oleh kami. |",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"connection_id\": 4,\n    \"template_name\": \"otp_login\",\n    \"phone\": \"6281234567890\",\n    \"code\": \"482913\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Error",
                            "code": 422,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"Template 'otp_login' tidak ditemukan, belum approved, bukan kategori Authentication, atau belum dicentang \\\"Bisa dipakai lewat API\\\".\"\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "Status Broadcast",
            "description": "Setiap operasi kirim di atas (/send, /send-to-group, /send-text, /send-otp) langsung balikin broadcast_id begitu request-nya diterima — resource ini yang biarin sistem kamu cek hasilnya belakangan, tanpa perlu buka dashboard.",
            "item": [
                {
                    "name": "Cek Status Broadcast",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}/api/broadcast/broadcasts/:id",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "api",
                                "broadcast",
                                "broadcasts",
                                ":id"
                            ],
                            "variable": [
                                {
                                    "key": "id",
                                    "value": "514",
                                    "description": ""
                                }
                            ]
                        },
                        "description": "Status keseluruhan (`sent_count`/`delivered_count`/`read_count`/`failed_count`) plus rincian per nomor penerima — status per-penerima naik bertahap `pending` → `sent` → `delivered` → `read` begitu Meta ngirim webhook callback-nya ke kami, atau langsung `failed` kalau gagal.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `id` | integer | wajib | Path parameter — broadcast_id dari response operasi kirim. |"
                    },
                    "response": [
                        {
                            "name": "Berhasil (200)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/broadcasts/:id",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "broadcasts",
                                        ":id"
                                    ],
                                    "variable": [
                                        {
                                            "key": "id",
                                            "value": "514",
                                            "description": ""
                                        }
                                    ]
                                },
                                "description": "Status keseluruhan (`sent_count`/`delivered_count`/`read_count`/`failed_count`) plus rincian per nomor penerima — status per-penerima naik bertahap `pending` → `sent` → `delivered` → `read` begitu Meta ngirim webhook callback-nya ke kami, atau langsung `failed` kalau gagal.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `id` | integer | wajib | Path parameter — broadcast_id dari response operasi kirim. |"
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"broadcast_id\": 514,\n    \"status\": \"completed\",\n    \"template\": \"info_promo\",\n    \"group\": null,\n    \"total_count\": 2,\n    \"sent_count\": 0,\n    \"delivered_count\": 1,\n    \"read_count\": 1,\n    \"failed_count\": 1,\n    \"created_at\": \"2026-08-18T02:10:00+00:00\",\n    \"recipients\": [\n        {\n            \"phone\": \"6281234567890\",\n            \"status\": \"read\",\n            \"message_id\": \"wamid.HBg...\",\n            \"error\": null,\n            \"sent_at\": \"2026-08-18T02:10:05+00:00\",\n            \"delivered_at\": \"2026-08-18T02:10:08+00:00\",\n            \"read_at\": \"2026-08-18T02:11:40+00:00\"\n        },\n        {\n            \"phone\": \"6281298765432\",\n            \"status\": \"failed\",\n            \"message_id\": null,\n            \"error\": \"Nomor Tidak Terdaftar di WhatsApp — (#131026) ...\",\n            \"sent_at\": null,\n            \"delivered_at\": null,\n            \"read_at\": null\n        }\n    ]\n}"
                        },
                        {
                            "name": "Gagal (404)",
                            "originalRequest": {
                                "method": "GET",
                                "header": [],
                                "url": {
                                    "raw": "{{base_url}}/api/broadcast/broadcasts/:id",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "api",
                                        "broadcast",
                                        "broadcasts",
                                        ":id"
                                    ],
                                    "variable": [
                                        {
                                            "key": "id",
                                            "value": "514",
                                            "description": ""
                                        }
                                    ]
                                },
                                "description": "Status keseluruhan (`sent_count`/`delivered_count`/`read_count`/`failed_count`) plus rincian per nomor penerima — status per-penerima naik bertahap `pending` → `sent` → `delivered` → `read` begitu Meta ngirim webhook callback-nya ke kami, atau langsung `failed` kalau gagal.\n\n**Parameter:**\n\n| Field | Tipe | Wajib | Keterangan |\n|---|---|---|---|\n| `id` | integer | wajib | Path parameter — broadcast_id dari response operasi kirim. |"
                            },
                            "status": "Error",
                            "code": 404,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"error\": \"Broadcast tidak ditemukan.\"\n}"
                        }
                    ]
                }
            ]
        }
    ]
}