{
    "info": {
        "_postman_id": "6f1d2c3b-7a8e-4f5d-9c0b-1e2a3b4c5d6e",
        "name": "KD Push API v1",
        "description": "# KD Push API v1\n\nManaged push notifications for app platforms. Full reference: https://push.kd-app.com/docs/api\n\n1. Set `app_key` and `server_key` in the collection variables (portal → your app → Keys).\n2. Put a real FCM/APNs token in `device_token` and run **Register device**.\n3. Run **Send a notification** — the id is saved to `message_id` for the follow-up requests.\n\nErrors are always `{\"error\": {\"code\": \"…\", \"message\": \"…\"}}`.\n\nإشعارات مُدارة لمنصات التطبيقات — ضع المفاتيح في متغيرات المجموعة ثم جرّب الطلبات بالترتيب.",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "variable": [
        {
            "key": "base_url",
            "value": "https://push.kd-app.com/api/v1",
            "type": "string",
            "description": "API base URL"
        },
        {
            "key": "app_key",
            "value": "kdp_app_REPLACE_ME",
            "type": "string",
            "description": "Public App Key (portal → app → Keys). Used by the mobile app."
        },
        {
            "key": "server_key",
            "value": "kdp_live_REPLACE_ME",
            "type": "string",
            "description": "Secret Server Key (shown once when created). Never put it in an app."
        },
        {
            "key": "device_token",
            "value": "REPLACE_WITH_A_REAL_FCM_OR_APNS_TOKEN",
            "type": "string",
            "description": "A real push token from a test device."
        },
        {
            "key": "installation_id",
            "value": "",
            "type": "string",
            "description": "Filled automatically on the first request (uuid)."
        },
        {
            "key": "external_user_id",
            "value": "",
            "type": "string",
            "description": "Optional: your user id to link the device to."
        },
        {
            "key": "identity_secret",
            "value": "",
            "type": "string",
            "description": "Optional: the app identity secret — lets this collection compute user_signature."
        },
        {
            "key": "user_signature",
            "value": "",
            "type": "string",
            "description": "Computed automatically from external_user_id + identity_secret."
        },
        {
            "key": "message_id",
            "value": "",
            "type": "string",
            "description": "Filled automatically by \"Send a notification\"."
        },
        {
            "key": "scheduled_at",
            "value": "",
            "type": "string",
            "description": "Filled automatically by \"Schedule a notification\" (tomorrow)."
        }
    ],
    "event": [
        {
            "listen": "prerequest",
            "script": {
                "type": "text/javascript",
                "exec": [
                    "if (!pm.collectionVariables.get('installation_id')) {",
                    "    pm.collectionVariables.set('installation_id', pm.variables.replaceIn('{{$guid}}'));",
                    "}"
                ]
            }
        }
    ],
    "item": [
        {
            "name": "1 · Mobile app (App Key)",
            "description": "Endpoints the mobile app calls. Authenticated by the public **App Key** in the `X-KD-App-Key` header — it can only register devices for its own app and can never send.\n\nنقاط يستدعيها تطبيق الجوال بمفتاح التطبيق العام.",
            "auth": {
                "type": "apikey",
                "apikey": [
                    {
                        "key": "key",
                        "value": "X-KD-App-Key",
                        "type": "string"
                    },
                    {
                        "key": "value",
                        "value": "{{app_key}}",
                        "type": "string"
                    },
                    {
                        "key": "in",
                        "value": "header",
                        "type": "string"
                    }
                ]
            },
            "item": [
                {
                    "name": "Runtime config",
                    "request": {
                        "method": "GET",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/sdk/config",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "sdk",
                                "config"
                            ]
                        },
                        "description": "Call on app start. Android receives the Firebase options here, so the Firebase project can change without rebuilding the app."
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "GET",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/sdk/config",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "sdk",
                                        "config"
                                    ]
                                },
                                "description": "Call on app start. Android receives the Firebase options here, so the Firebase project can change without rebuilding the app."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"app\": {\n        \"id\": \"01JB2Q6V8K4Y3N5M7R9T1W0XZC\",\n        \"name\": \"My Store\"\n    },\n    \"identity_verification\": \"required\",\n    \"android\": {\n        \"provider\": \"fcm\",\n        \"firebase\": {\n            \"project_id\": \"neon-push\",\n            \"app_id\": \"1:123456789:android:abcdef\",\n            \"api_key\": \"AIza…\",\n            \"sender_id\": \"123456789\",\n            \"storage_bucket\": null\n        }\n    },\n    \"ios\": {\n        \"provider\": \"apns\"\n    },\n    \"web\": {\n        \"provider\": \"webpush\",\n        \"vapid_public_key\": \"BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U\",\n        \"icon_url\": null,\n        \"prompt\": {\n            \"enabled\": true,\n            \"delay_seconds\": 6,\n            \"title\": null,\n            \"text\": null\n        }\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Register device",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/sdk/devices",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "sdk",
                                "devices"
                            ]
                        },
                        "description": "Register or refresh this device — call on every start, token refresh, login and logout. 201 the first time, 200 afterwards.\n\n`external_user_id` needs `user_signature` = hex HMAC-SHA256(external_user_id, identity secret) while identity verification is on. Fill `identity_secret` + `external_user_id` in the collection variables and this request signs it for you; leave them empty to register an anonymous device.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"platform\": \"android\",\n    \"token\": \"{{device_token}}\",\n    \"installation_id\": \"{{installation_id}}\",\n    \"android\": {\n        \"fcm_project_id\": \"neon-push\"\n    },\n    \"locale\": \"ar\",\n    \"timezone\": \"Asia/Riyadh\",\n    \"app_version\": \"2.1.0\",\n    \"os_version\": \"14\",\n    \"device_model\": \"Pixel 8\",\n    \"sdk_version\": \"1.0.0\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "prerequest",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "const id = pm.collectionVariables.get('external_user_id');",
                                    "const secret = pm.collectionVariables.get('identity_secret');",
                                    "if (id) {",
                                    "    const body = JSON.parse(pm.request.body.raw);",
                                    "    body.external_user_id = id;",
                                    "    if (secret) body.user_signature = CryptoJS.HmacSHA256(id, secret).toString(CryptoJS.enc.Hex);",
                                    "    pm.request.body.raw = JSON.stringify(body, null, 2);",
                                    "}"
                                ]
                            }
                        },
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('device registered', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "201 Created",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/sdk/devices",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "sdk",
                                        "devices"
                                    ]
                                },
                                "description": "Register or refresh this device — call on every start, token refresh, login and logout. 201 the first time, 200 afterwards.\n\n`external_user_id` needs `user_signature` = hex HMAC-SHA256(external_user_id, identity secret) while identity verification is on. Fill `identity_secret` + `external_user_id` in the collection variables and this request signs it for you; leave them empty to register an anonymous device.",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"platform\": \"android\",\n    \"token\": \"{{device_token}}\",\n    \"installation_id\": \"{{installation_id}}\",\n    \"android\": {\n        \"fcm_project_id\": \"neon-push\"\n    },\n    \"locale\": \"ar\",\n    \"timezone\": \"Asia/Riyadh\",\n    \"app_version\": \"2.1.0\",\n    \"os_version\": \"14\",\n    \"device_model\": \"Pixel 8\",\n    \"sdk_version\": \"1.0.0\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Created",
                            "code": 201,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"device\": {\n        \"installation_id\": \"3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90\",\n        \"platform\": \"android\",\n        \"status\": \"active\",\n        \"external_user_id\": null\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Register device (iOS)",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/sdk/devices",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "sdk",
                                "devices"
                            ]
                        },
                        "description": "iOS variant. Read `bundle_id` from `Bundle.main.bundleIdentifier`, and `profile_app_id`, `team_id` and `environment` from the app's `embedded.mobileprovision` at runtime.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"platform\": \"ios\",\n    \"token\": \"{{device_token}}\",\n    \"installation_id\": \"{{installation_id}}\",\n    \"ios\": {\n        \"bundle_id\": \"com.example.store\",\n        \"profile_app_id\": \"app.example.store\",\n        \"team_id\": \"ABCDE12345\",\n        \"environment\": \"production\"\n    },\n    \"locale\": \"ar\",\n    \"app_version\": \"2.1.0\",\n    \"os_version\": \"17.5\",\n    \"device_model\": \"iPhone15,2\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('device registered', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));"
                                ]
                            }
                        }
                    ]
                },
                {
                    "name": "Register device (web)",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            },
                            {
                                "key": "Origin",
                                "value": "https://shop.example.com",
                                "description": "Browsers send it themselves; it must be one of the app's web sites."
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/sdk/devices",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "sdk",
                                "devices"
                            ]
                        },
                        "description": "Browser variant — the Web SDK (https://push.kd-app.com/sdk/web/v1/kdpush.js) does this for you. `token` is the subscription endpoint and `web` its keys, from `PushSubscription.toJSON()`. Accepted only from a page on one of the app's sites (the browser's `Origin` header) while web is switched on, and only for the push services of real browsers.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"platform\": \"web\",\n    \"token\": \"https://fcm.googleapis.com/fcm/send/{{device_token}}\",\n    \"installation_id\": \"{{installation_id}}\",\n    \"web\": {\n        \"p256dh\": \"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM\",\n        \"auth\": \"tBHItJI5svbpez7KI4CCXg\"\n    },\n    \"locale\": \"ar\",\n    \"os_version\": \"Android\",\n    \"device_model\": \"Chrome\",\n    \"tags\": {\n        \"os\": \"android\"\n    }\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('device registered', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));"
                                ]
                            }
                        }
                    ]
                },
                {
                    "name": "Unsubscribe device",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/sdk/devices/unsubscribe",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "sdk",
                                "devices",
                                "unsubscribe"
                            ]
                        },
                        "description": "Opt-out or logout: the device stops receiving until it registers again.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"installation_id\": \"{{installation_id}}\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/sdk/devices/unsubscribe",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "sdk",
                                        "devices",
                                        "unsubscribe"
                                    ]
                                },
                                "description": "Opt-out or logout: the device stops receiving until it registers again.",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"installation_id\": \"{{installation_id}}\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"unsubscribed\": true\n}"
                        }
                    ]
                },
                {
                    "name": "Report notification opened",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/sdk/events",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "sdk",
                                "events"
                            ]
                        },
                        "description": "Report a tap on a notification. `message_id` is the `kd_message_id` delivered in the payload. Counted once per installation and message.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"type\": \"opened\",\n    \"message_id\": \"{{message_id}}\",\n    \"installation_id\": \"{{installation_id}}\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "if (!pm.collectionVariables.get('message_id')) {",
                                    "    pm.test('run \"Send a notification\" first to get a message_id', () => pm.response.to.have.status(422));",
                                    "} else {",
                                    "    pm.test('status 202', () => pm.response.to.have.status(202));",
                                    "}"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "202 Accepted",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/sdk/events",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "sdk",
                                        "events"
                                    ]
                                },
                                "description": "Report a tap on a notification. `message_id` is the `kd_message_id` delivered in the payload. Counted once per installation and message.",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"type\": \"opened\",\n    \"message_id\": \"{{message_id}}\",\n    \"installation_id\": \"{{installation_id}}\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Accepted",
                            "code": 202,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"accepted\": true\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "2 · Platform backend (Server Key)",
            "description": "Endpoints your platform backend calls. Authenticated by the secret **Server Key** as a Bearer token — it can only send to and read its own app.\n\nنقاط يستدعيها خادم منصتك بالمفتاح السري.",
            "auth": {
                "type": "bearer",
                "bearer": [
                    {
                        "key": "token",
                        "value": "{{server_key}}",
                        "type": "string"
                    }
                ]
            },
            "item": [
                {
                    "name": "Get app",
                    "request": {
                        "method": "GET",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/app",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "app"
                            ]
                        },
                        "description": "Verify the Server Key and see whether sending is currently allowed: `sending.can_send` weighs the subscription and this month's quota, `sending.reason` uses the codes POST /messages answers with. `plan`, `usage` and `portal` show where the merchant stands and where they fix it."
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "GET",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/app",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "app"
                                    ]
                                },
                                "description": "Verify the Server Key and see whether sending is currently allowed: `sending.can_send` weighs the subscription and this month's quota, `sending.reason` uses the codes POST /messages answers with. `plan`, `usage` and `portal` show where the merchant stands and where they fix it."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"app\": {\n        \"id\": \"01JB2Q6V8K4Y3N5M7R9T1W0XZC\",\n        \"name\": \"My Store\",\n        \"platform\": \"neon\",\n        \"status\": \"active\",\n        \"ios_bundle_id\": \"com.store.app\",\n        \"android_package\": \"com.store.app\"\n    },\n    \"subscription\": {\n        \"status\": \"active\",\n        \"expires_at\": \"2026-11-02T12:00:00+00:00\",\n        \"grace_until\": null,\n        \"days_left\": 19,\n        \"can_send\": true\n    },\n    \"plan\": {\n        \"name\": \"Pro\",\n        \"period\": \"monthly\",\n        \"monthly_messages\": 100000,\n        \"max_devices\": null,\n        \"max_apps\": 3\n    },\n    \"usage\": {\n        \"deliveries_this_month\": 18240,\n        \"remaining_this_month\": 81760,\n        \"active_devices\": 1240\n    },\n    \"sending\": {\n        \"can_send\": true,\n        \"reason\": null\n    },\n    \"portal\": {\n        \"plans\": \"https://push.kd-app.com/portal/plans\",\n        \"subscription\": \"https://push.kd-app.com/portal/subscription\"\n    },\n    \"devices\": {\n        \"active\": 1240,\n        \"ios\": 800,\n        \"android\": 440,\n        \"web\": 0\n    },\n    \"web\": {\n        \"enabled\": false,\n        \"origins\": [],\n        \"vapid_public_key\": null,\n        \"subscribers\": 0,\n        \"icon_url\": null,\n        \"default_url\": null,\n        \"prompt\": {\n            \"enabled\": true,\n            \"delay_seconds\": 6,\n            \"title\": null,\n            \"text\": null\n        }\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Web notifications (browsers)",
                    "request": {
                        "method": "PUT",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/app/web",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "app",
                                "web"
                            ]
                        },
                        "description": "Switch web notifications on or off and set the sites allowed to subscribe browsers (\"shop.example.com\" is stored as https://shop.example.com). Switching on needs at least one site and creates the app's VAPID key pair. Optional: `icon_url` (https), `default_url` (click target when a message has no web link), `prompt` {enabled, delay_seconds, title, text}. Fields left out keep their value.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"enabled\": true,\n    \"origins\": [\n        \"shop.example.com\",\n        \"www.shop.example.com\"\n    ],\n    \"icon_url\": \"https://cdn.example.com/logo.png\",\n    \"prompt\": {\n        \"enabled\": true,\n        \"delay_seconds\": 6\n    }\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "PUT",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/app/web",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "app",
                                        "web"
                                    ]
                                },
                                "description": "Switch web notifications on or off and set the sites allowed to subscribe browsers (\"shop.example.com\" is stored as https://shop.example.com). Switching on needs at least one site and creates the app's VAPID key pair. Optional: `icon_url` (https), `default_url` (click target when a message has no web link), `prompt` {enabled, delay_seconds, title, text}. Fields left out keep their value.",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"enabled\": true,\n    \"origins\": [\n        \"shop.example.com\",\n        \"www.shop.example.com\"\n    ],\n    \"icon_url\": \"https://cdn.example.com/logo.png\",\n    \"prompt\": {\n        \"enabled\": true,\n        \"delay_seconds\": 6\n    }\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"web\": {\n        \"enabled\": true,\n        \"origins\": [\n            \"https://shop.example.com\",\n            \"https://www.shop.example.com\"\n        ],\n        \"vapid_public_key\": \"BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U\",\n        \"subscribers\": 0,\n        \"icon_url\": \"https://cdn.example.com/logo.png\",\n        \"default_url\": null,\n        \"prompt\": {\n            \"enabled\": true,\n            \"delay_seconds\": 6,\n            \"title\": null,\n            \"text\": null\n        }\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Android package",
                    "request": {
                        "method": "PUT",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/app/android",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "app",
                                "android"
                            ]
                        },
                        "description": "The Android package the store's app is built with, so the merchant portal shows it and a Firebase file uploaded later is checked against it. If the merchant chose their own Firebase project for this app and it is registered for another package, the call is refused with 409 `package_mismatch` (`firebase_package` says which).",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"package\": \"com.store.app\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "PUT",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/app/android",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "app",
                                        "android"
                                    ]
                                },
                                "description": "The Android package the store's app is built with, so the merchant portal shows it and a Firebase file uploaded later is checked against it. If the merchant chose their own Firebase project for this app and it is registered for another package, the call is refused with 409 `package_mismatch` (`firebase_package` says which).",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"package\": \"com.store.app\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"app\": {\n        \"id\": \"01JB2Q6V8K4Y3N5M7R9T1W0XZC\",\n        \"android_package\": \"com.store.app\"\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Device stats",
                    "request": {
                        "method": "GET",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/devices/stats",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "devices",
                                "stats"
                            ]
                        },
                        "description": "Active devices per platform."
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "GET",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/devices/stats",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "devices",
                                        "stats"
                                    ]
                                },
                                "description": "Active devices per platform."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"devices\": {\n        \"active\": 1240,\n        \"ios\": 800,\n        \"android\": 440,\n        \"web\": 0\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Send a notification",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            },
                            {
                                "key": "Idempotency-Key",
                                "value": "{{$guid}}",
                                "description": "Any unique string per logical send (e.g. the event id)."
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/messages",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "messages"
                            ]
                        },
                        "description": "Send (or schedule) a notification to this app's devices. Retries with the same `Idempotency-Key` never send twice: 202 = queued now, 200 = already queued with that key (24 h).\n\nAudience: `{\"type\":\"all\"}` · `{\"type\":\"users\",\"external_user_ids\":[\"42\"]}` · `{\"type\":\"devices\",\"installation_ids\":[\"<uuid>\"]}` · `{\"type\":\"tags\",\"tags\":{\"plan\":\"vip\"}}`.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"title\": \"تطبيق جديد\",\n    \"body\": \"تمت إضافة تطبيق جديد للمتجر\",\n    \"url\": \"neonstore://app/1234\",\n    \"data\": {\n        \"app_id\": \"1234\"\n    },\n    \"platforms\": [\n        \"ios\",\n        \"android\"\n    ],\n    \"audience\": {\n        \"type\": \"all\"\n    }\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('accepted', () => pm.expect(pm.response.code).to.be.oneOf([200, 202]));",
                                    "const json = pm.response.json();",
                                    "if (json.message) { pm.collectionVariables.set('message_id', json.message.id); }"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "202 Accepted",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Idempotency-Key",
                                        "value": "{{$guid}}",
                                        "description": "Any unique string per logical send (e.g. the event id)."
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/messages",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "messages"
                                    ]
                                },
                                "description": "Send (or schedule) a notification to this app's devices. Retries with the same `Idempotency-Key` never send twice: 202 = queued now, 200 = already queued with that key (24 h).\n\nAudience: `{\"type\":\"all\"}` · `{\"type\":\"users\",\"external_user_ids\":[\"42\"]}` · `{\"type\":\"devices\",\"installation_ids\":[\"<uuid>\"]}` · `{\"type\":\"tags\",\"tags\":{\"plan\":\"vip\"}}`.",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"title\": \"تطبيق جديد\",\n    \"body\": \"تمت إضافة تطبيق جديد للمتجر\",\n    \"url\": \"neonstore://app/1234\",\n    \"data\": {\n        \"app_id\": \"1234\"\n    },\n    \"platforms\": [\n        \"ios\",\n        \"android\"\n    ],\n    \"audience\": {\n        \"type\": \"all\"\n    }\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "Accepted",
                            "code": 202,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"message\": {\n        \"id\": \"01JB2QA4N7C2X9V5B8M3K6P1RD\",\n        \"status\": \"queued\",\n        \"source\": \"api\",\n        \"title\": \"تطبيق جديد\",\n        \"body\": \"تمت إضافة تطبيق جديد للمتجر\",\n        \"image_url\": null,\n        \"url\": \"neonstore://app/1234\",\n        \"data\": {\n            \"app_id\": \"1234\"\n        },\n        \"audience\": {\n            \"type\": \"all\"\n        },\n        \"platforms\": null,\n        \"scheduled_at\": null,\n        \"counts\": {\n            \"targeted\": 0,\n            \"sent\": 0,\n            \"failed\": 0,\n            \"invalid\": 0,\n            \"opened\": 0\n        },\n        \"error\": null,\n        \"failures\": [],\n        \"created_at\": \"2026-10-03T12:00:00+00:00\",\n        \"started_at\": null,\n        \"finished_at\": null\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Send to specific users",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            },
                            {
                                "key": "Idempotency-Key",
                                "value": "{{$guid}}"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/messages",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "messages"
                            ]
                        },
                        "description": "Target the devices linked to your user ids (requires devices registered with a valid user_signature).",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"title\": \"اشتراكك ينتهي قريباً\",\n    \"body\": \"جدّد اشتراكك قبل انتهائه بثلاثة أيام\",\n    \"audience\": {\n        \"type\": \"users\",\n        \"external_user_ids\": [\n            \"42\"\n        ]\n    },\n    \"priority\": \"high\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('accepted', () => pm.expect(pm.response.code).to.be.oneOf([200, 202]));"
                                ]
                            }
                        }
                    ]
                },
                {
                    "name": "Schedule a notification",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            },
                            {
                                "key": "Idempotency-Key",
                                "value": "{{$guid}}"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/messages",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "messages"
                            ]
                        },
                        "description": "`scheduled_at` (ISO 8601, future, within 30 days). The message stays `scheduled` until then and can be canceled.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"title\": \"عرض اليوم الوطني\",\n    \"body\": \"خصم 30٪ على الاشتراكات اليوم فقط\",\n    \"audience\": {\n        \"type\": \"all\"\n    },\n    \"scheduled_at\": \"{{scheduled_at}}\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        }
                    },
                    "event": [
                        {
                            "listen": "prerequest",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "// Tomorrow, same time — scheduled_at must be in the next 30 days.",
                                    "pm.collectionVariables.set('scheduled_at', new Date(Date.now() + 24 * 3600 * 1000).toISOString());"
                                ]
                            }
                        },
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('accepted', () => pm.expect(pm.response.code).to.be.oneOf([200, 202]));",
                                    "const json = pm.response.json();",
                                    "if (json.message) { pm.collectionVariables.set('message_id', json.message.id); }"
                                ]
                            }
                        }
                    ]
                },
                {
                    "name": "List notifications",
                    "request": {
                        "method": "GET",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/messages?per_page=20",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "messages"
                            ],
                            "query": [
                                {
                                    "key": "status",
                                    "value": "sent",
                                    "disabled": true,
                                    "description": "scheduled | queued | sending | sent | failed | canceled"
                                },
                                {
                                    "key": "per_page",
                                    "value": "20",
                                    "description": "1–100"
                                }
                            ]
                        },
                        "description": "Paginated history of this app, newest first."
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "GET",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/messages?per_page=20",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "messages"
                                    ],
                                    "query": [
                                        {
                                            "key": "status",
                                            "value": "sent",
                                            "disabled": true,
                                            "description": "scheduled | queued | sending | sent | failed | canceled"
                                        },
                                        {
                                            "key": "per_page",
                                            "value": "20",
                                            "description": "1–100"
                                        }
                                    ]
                                },
                                "description": "Paginated history of this app, newest first."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"data\": [\n        {\n            \"id\": \"01JB2QA4N7C2X9V5B8M3K6P1RD\",\n            \"status\": \"queued\",\n            \"source\": \"api\",\n            \"title\": \"تطبيق جديد\",\n            \"body\": \"تمت إضافة تطبيق جديد للمتجر\",\n            \"image_url\": null,\n            \"url\": \"neonstore://app/1234\",\n            \"data\": {\n                \"app_id\": \"1234\"\n            },\n            \"audience\": {\n                \"type\": \"all\"\n            },\n            \"platforms\": null,\n            \"scheduled_at\": null,\n            \"counts\": {\n                \"targeted\": 0,\n                \"sent\": 0,\n                \"failed\": 0,\n                \"invalid\": 0,\n                \"opened\": 0\n            },\n            \"error\": null,\n            \"failures\": [],\n            \"created_at\": \"2026-10-03T12:00:00+00:00\",\n            \"started_at\": null,\n            \"finished_at\": null\n        }\n    ],\n    \"meta\": {\n        \"current_page\": 1,\n        \"per_page\": 20,\n        \"total\": 1,\n        \"last_page\": 1\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Get a notification",
                    "request": {
                        "method": "GET",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/messages/{{message_id}}",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "messages",
                                "{{message_id}}"
                            ]
                        },
                        "description": "Status and delivery counters of one notification."
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "GET",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/messages/{{message_id}}",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "messages",
                                        "{{message_id}}"
                                    ]
                                },
                                "description": "Status and delivery counters of one notification."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"message\": {\n        \"id\": \"01JB2QA4N7C2X9V5B8M3K6P1RD\",\n        \"status\": \"sent\",\n        \"source\": \"api\",\n        \"title\": \"تطبيق جديد\",\n        \"body\": \"تمت إضافة تطبيق جديد للمتجر\",\n        \"image_url\": null,\n        \"url\": \"neonstore://app/1234\",\n        \"data\": {\n            \"app_id\": \"1234\"\n        },\n        \"audience\": {\n            \"type\": \"all\"\n        },\n        \"platforms\": null,\n        \"scheduled_at\": null,\n        \"counts\": {\n            \"targeted\": 1240,\n            \"sent\": 1227,\n            \"failed\": 3,\n            \"invalid\": 10,\n            \"opened\": 214\n        },\n        \"error\": null,\n        \"failures\": [],\n        \"created_at\": \"2026-10-03T12:00:00+00:00\",\n        \"started_at\": \"2026-10-03T12:00:01+00:00\",\n        \"finished_at\": \"2026-10-03T12:00:04+00:00\"\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Cancel a notification",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/messages/{{message_id}}/cancel",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "messages",
                                "{{message_id}}",
                                "cancel"
                            ]
                        },
                        "description": "Cancel a scheduled, queued or sending notification. 409 `not_cancelable` once it is final."
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('canceled or already final', () => pm.expect(pm.response.code).to.be.oneOf([200, 409]));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/messages/{{message_id}}/cancel",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "messages",
                                        "{{message_id}}",
                                        "cancel"
                                    ]
                                },
                                "description": "Cancel a scheduled, queued or sending notification. 409 `not_cancelable` once it is final."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"message\": {\n        \"id\": \"01JB2QA4N7C2X9V5B8M3K6P1RD\",\n        \"status\": \"canceled\",\n        \"source\": \"api\",\n        \"title\": \"تطبيق جديد\",\n        \"body\": \"تمت إضافة تطبيق جديد للمتجر\",\n        \"image_url\": null,\n        \"url\": \"neonstore://app/1234\",\n        \"data\": {\n            \"app_id\": \"1234\"\n        },\n        \"audience\": {\n            \"type\": \"all\"\n        },\n        \"platforms\": null,\n        \"scheduled_at\": null,\n        \"counts\": {\n            \"targeted\": 0,\n            \"sent\": 0,\n            \"failed\": 0,\n            \"invalid\": 0,\n            \"opened\": 0\n        },\n        \"error\": null,\n        \"failures\": [],\n        \"created_at\": \"2026-10-03T12:00:00+00:00\",\n        \"started_at\": null,\n        \"finished_at\": null\n    }\n}"
                        }
                    ]
                }
            ]
        },
        {
            "name": "3 · Connect with KD Push",
            "description": "Link a merchant's store to a KD Push app without copying keys: send the merchant to `/portal/connect/authorize?response_type=code&client_id=…&redirect_uri=…&state=…&code_challenge=…&code_challenge_method=S256` (optionally `app_name`, `ios_bundle_id`, `android_package`, `external_ref`, `external_label`, `hint_name`, `hint_email`, `hint_company`, `hint_phone`), then exchange the code here.\n\nاربط متجر التاجر بتطبيق KD Push بنقرة واحدة دون نسخ المفاتيح.",
            "item": [
                {
                    "name": "Exchange a connect code",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            },
                            {
                                "key": "Content-Type",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/connect/token",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "connect",
                                "token"
                            ]
                        },
                        "description": "Step 3 of Connect. After the merchant allowed your request at `/portal/connect/authorize`, KD Push redirected to your `redirect_uri` with `code` and `state`. Trade the code (single use, 10 minutes) for the app keys — server to server, with your client secret and the PKCE `code_verifier`.\n\nThe response carries a **new Server Key issued for this connection**; reconnecting the same store replaces it.",
                        "body": {
                            "mode": "raw",
                            "raw": "{\n    \"grant_type\": \"authorization_code\",\n    \"client_id\": \"kdp_cli_REPLACE_ME\",\n    \"client_secret\": \"kdp_cs_REPLACE_ME\",\n    \"code\": \"kdp_code_FROM_THE_REDIRECT\",\n    \"redirect_uri\": \"https://your-platform.example/kdpush/callback\",\n    \"code_verifier\": \"THE_PKCE_VERIFIER_YOU_KEPT\"\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        },
                        "auth": {
                            "type": "noauth"
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('keys issued', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    },
                                    {
                                        "key": "Content-Type",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/connect/token",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "connect",
                                        "token"
                                    ]
                                },
                                "description": "Step 3 of Connect. After the merchant allowed your request at `/portal/connect/authorize`, KD Push redirected to your `redirect_uri` with `code` and `state`. Trade the code (single use, 10 minutes) for the app keys — server to server, with your client secret and the PKCE `code_verifier`.\n\nThe response carries a **new Server Key issued for this connection**; reconnecting the same store replaces it.",
                                "body": {
                                    "mode": "raw",
                                    "raw": "{\n    \"grant_type\": \"authorization_code\",\n    \"client_id\": \"kdp_cli_REPLACE_ME\",\n    \"client_secret\": \"kdp_cs_REPLACE_ME\",\n    \"code\": \"kdp_code_FROM_THE_REDIRECT\",\n    \"redirect_uri\": \"https://your-platform.example/kdpush/callback\",\n    \"code_verifier\": \"THE_PKCE_VERIFIER_YOU_KEPT\"\n}",
                                    "options": {
                                        "raw": {
                                            "language": "json"
                                        }
                                    }
                                }
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"connection\": {\n        \"id\": \"01JB2QX5V8K4Y3N5M7R9T1W0XZ\",\n        \"connected_at\": \"2026-10-14T12:00:00+00:00\"\n    },\n    \"app\": {\n        \"id\": \"01JB2Q6V8K4Y3N5M7R9T1W0XZC\",\n        \"name\": \"My Store\",\n        \"platform\": \"neon\",\n        \"ios_bundle_id\": \"com.store.app\",\n        \"android_package\": null\n    },\n    \"merchant\": {\n        \"name\": \"Store owner\",\n        \"email\": \"owner@store.example\"\n    },\n    \"app_key\": \"kdp_app_REPLACE_ME\",\n    \"server_key\": \"kdp_live_REPLACE_ME\",\n    \"identity_secret\": \"REPLACE_ME\",\n    \"api_base\": \"https://push.kd-app.com/api/v1\",\n    \"account\": {\n        \"subscription\": {\n            \"status\": \"active\",\n            \"expires_at\": \"2026-11-14T12:00:00+00:00\",\n            \"grace_until\": null,\n            \"days_left\": 31,\n            \"can_send\": true\n        },\n        \"plan\": {\n            \"name\": \"Pro\",\n            \"period\": \"monthly\",\n            \"monthly_messages\": 100000,\n            \"max_devices\": null,\n            \"max_apps\": 3\n        },\n        \"usage\": {\n            \"deliveries_this_month\": 0,\n            \"remaining_this_month\": 100000,\n            \"active_devices\": 0\n        },\n        \"sending\": {\n            \"can_send\": true,\n            \"reason\": null\n        },\n        \"portal\": {\n            \"plans\": \"https://push.kd-app.com/portal/plans\",\n            \"subscription\": \"https://push.kd-app.com/portal/subscription\"\n        }\n    }\n}"
                        }
                    ]
                },
                {
                    "name": "Disconnect",
                    "request": {
                        "method": "POST",
                        "header": [
                            {
                                "key": "Accept",
                                "value": "application/json"
                            }
                        ],
                        "url": {
                            "raw": "{{base_url}}/connect/revoke",
                            "host": [
                                "{{base_url}}"
                            ],
                            "path": [
                                "connect",
                                "revoke"
                            ]
                        },
                        "description": "End the connection: the Server Key you send stops working at once. The app, its devices and its history stay with the merchant.",
                        "auth": {
                            "type": "bearer",
                            "bearer": [
                                {
                                    "key": "token",
                                    "value": "{{server_key}}",
                                    "type": "string"
                                }
                            ]
                        }
                    },
                    "event": [
                        {
                            "listen": "test",
                            "script": {
                                "type": "text/javascript",
                                "exec": [
                                    "pm.test('status 200', () => pm.response.to.have.status(200));"
                                ]
                            }
                        }
                    ],
                    "response": [
                        {
                            "name": "200 OK",
                            "originalRequest": {
                                "method": "POST",
                                "header": [
                                    {
                                        "key": "Accept",
                                        "value": "application/json"
                                    }
                                ],
                                "url": {
                                    "raw": "{{base_url}}/connect/revoke",
                                    "host": [
                                        "{{base_url}}"
                                    ],
                                    "path": [
                                        "connect",
                                        "revoke"
                                    ]
                                },
                                "description": "End the connection: the Server Key you send stops working at once. The app, its devices and its history stay with the merchant."
                            },
                            "status": "OK",
                            "code": 200,
                            "_postman_previewlanguage": "json",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": "{\n    \"revoked\": true\n}"
                        }
                    ]
                }
            ]
        }
    ]
}