{
  "info": {
    "name": "BulkSMSRates API",
    "description": "Official Postman collection for the BulkSMSRates REST API.\n\n## Setup\n1. Import this collection.\n2. Import the **BulkSMSRates Environment** (or create one with the variables below).\n3. Set `api_key` and `api_secret` in the environment.\n4. Run **Auth → Login** to auto-populate `access_token`.\n\n## Environment Variables\n- `base_url` — API base URL (default: https://api.bulksmsrates.com/api/v1)\n- `api_key` — Your API key\n- `api_secret` — Your API secret\n- `access_token` — JWT access token (auto-set by Login)\n- `refresh_token` — JWT refresh token (auto-set by Login)\n- `sandbox` — Set to `true` to enable sandbox mode\n\n## Authentication\nAll requests use the **X-API-Key** + **X-API-Secret** headers by default.\nAfter running Login, the `access_token` is stored and used for Bearer auth endpoints.\n\n## Sandbox Mode\nSet the `sandbox` environment variable to `true` to add the `X-Sandbox: true` header\nto all requests. Messages will not be sent; fake IDs are returned instead.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "version": {
      "major": 1,
      "minor": 0,
      "patch": 0
    }
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.bulksmsrates.com/api/v1",
      "type": "string",
      "description": "API base URL"
    },
    {
      "key": "api_key",
      "value": "bsms_live_YOUR_KEY_HERE",
      "type": "string",
      "description": "Your API key"
    },
    {
      "key": "api_secret",
      "value": "sk_live_YOUR_SECRET_HERE",
      "type": "string",
      "description": "Your API secret"
    },
    {
      "key": "access_token",
      "value": "",
      "type": "string",
      "description": "JWT access token (auto-set by Login)"
    },
    {
      "key": "refresh_token",
      "value": "",
      "type": "string",
      "description": "JWT refresh token (auto-set by Login)"
    },
    {
      "key": "sandbox",
      "value": "false",
      "type": "string",
      "description": "Set to true to enable sandbox mode"
    }
  ],
  "auth": {
    "type": "apikey",
    "apikey": [
      { "key": "key", "value": "X-API-Key", "type": "string" },
      { "key": "value", "value": "{{api_key}}", "type": "string" },
      { "key": "in", "value": "header", "type": "string" }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Auto-inject X-API-Secret and X-Sandbox headers on every request",
          "pm.request.headers.upsert({",
          "  key: 'X-API-Secret',",
          "  value: pm.collectionVariables.get('api_secret') || pm.environment.get('api_secret') || ''",
          "});",
          "",
          "const sandbox = pm.collectionVariables.get('sandbox') || pm.environment.get('sandbox');",
          "if (sandbox === 'true' || sandbox === true) {",
          "  pm.request.headers.upsert({ key: 'X-Sandbox', value: 'true' });",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Auth",
      "description": "Authentication endpoints — register, login, refresh, and profile management.",
      "item": [
        {
          "name": "Register",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{base_url}}/auth/register",
              "host": ["{{base_url}}"],
              "path": ["auth", "register"]
            },
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\",\n  \"password\": \"SecurePass123!\",\n  \"name\": \"Alice Smith\",\n  \"company_name\": \"My Company Ltd\"\n}"
            },
            "description": "Create a new account. Returns access and refresh tokens."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 201 || pm.response.code === 200) {",
                  "  const body = pm.response.json();",
                  "  if (body.access_token) {",
                  "    pm.collectionVariables.set('access_token', body.access_token);",
                  "    pm.collectionVariables.set('refresh_token', body.refresh_token || '');",
                  "    console.log('Tokens stored from registration');",
                  "  }",
                  "}"
                ]
              }
            }
          ],
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "url": { "raw": "{{base_url}}/auth/register" }
              },
              "status": "Created",
              "code": 201,
              "header": [
                { "key": "Content-Type", "value": "application/json" },
                { "key": "X-RateLimit-Limit", "value": "100" },
                { "key": "X-RateLimit-Remaining", "value": "99" },
                { "key": "X-RateLimit-Reset", "value": "1708257660" }
              ],
              "body": "{\n  \"user_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"email\": \"user@example.com\",\n  \"message\": \"Registration successful\",\n  \"access_token\": \"eyJhbGciOiJSUzI1NiJ9...\",\n  \"refresh_token\": \"eyJhbGciOiJSUzI1NiJ9...\"\n}"
            }
          ]
        },
        {
          "name": "Login",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{base_url}}/auth/login",
              "host": ["{{base_url}}"],
              "path": ["auth", "login"]
            },
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\",\n  \"password\": \"SecurePass123!\"\n}"
            },
            "description": "Login with email and password. Tokens are automatically stored in collection variables."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const body = pm.response.json();",
                  "  pm.collectionVariables.set('access_token', body.access_token);",
                  "  pm.collectionVariables.set('refresh_token', body.refresh_token);",
                  "  console.log('✅ Login successful — tokens stored');",
                  "  pm.test('Login successful', () => pm.response.to.have.status(200));",
                  "  pm.test('Access token returned', () => pm.expect(body.access_token).to.be.a('string'));",
                  "}"
                ]
              }
            }
          ],
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "header": [
                { "key": "Content-Type", "value": "application/json" },
                { "key": "X-RateLimit-Limit", "value": "100" },
                { "key": "X-RateLimit-Remaining", "value": "99" },
                { "key": "X-RateLimit-Reset", "value": "1708257660" }
              ],
              "body": "{\n  \"access_token\": \"eyJhbGciOiJSUzI1NiJ9...\",\n  \"refresh_token\": \"eyJhbGciOiJSUzI1NiJ9...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 900\n}"
            }
          ]
        },
        {
          "name": "Refresh Token",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{base_url}}/auth/refresh",
              "host": ["{{base_url}}"],
              "path": ["auth", "refresh"]
            },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refresh_token\": \"{{refresh_token}}\"\n}"
            },
            "description": "Exchange a refresh token for a new access token."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const body = pm.response.json();",
                  "  pm.collectionVariables.set('access_token', body.access_token);",
                  "  pm.collectionVariables.set('refresh_token', body.refresh_token);",
                  "  console.log('✅ Token refreshed');",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Get Current User (me)",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/auth/me",
              "host": ["{{base_url}}"],
              "path": ["auth", "me"]
            },
            "header": [
              { "key": "Authorization", "value": "Bearer {{access_token}}" }
            ],
            "description": "Get the authenticated user's profile."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"email\": \"user@example.com\",\n  \"name\": \"Alice Smith\",\n  \"role\": \"tenant\",\n  \"tenantId\": \"660e8400-e29b-41d4-a716-446655440001\"\n}"
            }
          ]
        },
        {
          "name": "Logout",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{base_url}}/auth/logout",
              "host": ["{{base_url}}"],
              "path": ["auth", "logout"]
            },
            "header": [
              { "key": "Authorization", "value": "Bearer {{access_token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refresh_token\": \"{{refresh_token}}\"\n}"
            },
            "description": "Revoke the refresh token (blacklist it in Redis)."
          }
        },
        {
          "name": "Forgot Password",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/auth/forgot-password", "host": ["{{base_url}}"], "path": ["auth", "forgot-password"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"email\": \"user@example.com\"\n}" },
            "description": "Send a password reset email. Always returns 200 to prevent email enumeration."
          }
        },
        {
          "name": "Reset Password",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/auth/reset-password", "host": ["{{base_url}}"], "path": ["auth", "reset-password"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"token\": \"RESET_TOKEN_FROM_EMAIL\",\n  \"new_password\": \"NewSecurePass456!\"\n}" },
            "description": "Reset password using the token from the reset email."
          }
        },
        {
          "name": "Change Password",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/auth/change-password", "host": ["{{base_url}}"], "path": ["auth", "change-password"] },
            "header": [{ "key": "Authorization", "value": "Bearer {{access_token}}" }, { "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"oldPassword\": \"CurrentPass123!\",\n  \"newPassword\": \"NewPass456!\"\n}" },
            "description": "Change password for the authenticated user."
          }
        }
      ]
    },
    {
      "name": "SMS",
      "description": "Send and manage individual SMS messages.",
      "item": [
        {
          "name": "Send SMS",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{base_url}}/sms/send",
              "host": ["{{base_url}}"],
              "path": ["sms", "send"]
            },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"destination\": \"+447700900123\",\n  \"message\": \"Hello from BulkSMSRates!\",\n  \"sender_id\": \"MyApp\"\n}"
            },
            "description": "Send a single SMS message. Supports template variables, scheduling, and Unicode."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('SMS accepted', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Message ID returned', () => pm.expect(body.id || body.message_id).to.be.a('string'));",
                  "if (body.id) pm.collectionVariables.set('last_message_id', body.id);"
                ]
              }
            }
          ],
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "header": [
                { "key": "X-RateLimit-Limit", "value": "100" },
                { "key": "X-RateLimit-Remaining", "value": "98" },
                { "key": "X-RateLimit-Reset", "value": "1708257660" }
              ],
              "body": "{\n  \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"message_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"status\": \"queued\",\n  \"segments\": 1,\n  \"cost_micros\": null\n}"
            },
            {
              "name": "200 OK (Sandbox)",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"id\": \"sandbox-msg-550e8400\",\n  \"message_id\": \"sandbox-msg-550e8400\",\n  \"status\": \"delivered\",\n  \"segments\": 1,\n  \"cost_micros\": null,\n  \"sandbox\": true\n}"
            }
          ]
        },
        {
          "name": "Send SMS (Scheduled)",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/sms/send", "host": ["{{base_url}}"], "path": ["sms", "send"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"destination\": \"+447700900123\",\n  \"message\": \"Reminder: your appointment is tomorrow at 10am!\",\n  \"sender_id\": \"MyClinic\",\n  \"scheduled_at\": \"2026-03-15T09:00:00Z\",\n  \"client_ref\": \"appt-456\"\n}"
            },
            "description": "Send a scheduled SMS at a specific time (up to 30 days ahead)."
          }
        },
        {
          "name": "Send SMS (with Variables)",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/sms/send", "host": ["{{base_url}}"], "path": ["sms", "send"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"destination\": \"+447700900123\",\n  \"message\": \"Hi {{name}}, your OTP is {{code}}. Expires in 5 minutes.\",\n  \"sender_id\": \"MyApp\",\n  \"variables\": {\n    \"name\": \"Alice\",\n    \"code\": \"123456\"\n  }\n}"
            },
            "description": "Send SMS with template variable substitution."
          }
        },
        {
          "name": "Bulk Send",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{base_url}}/sms/bulk",
              "host": ["{{base_url}}"],
              "path": ["sms", "bulk"]
            },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"recipients\": [\n    \"+447700900001\",\n    \"+447700900002\",\n    \"+447700900003\"\n  ],\n  \"message\": \"Flash sale! 30% off this weekend at myshop.com\",\n  \"sender_id\": \"MyShop\"\n}"
            },
            "description": "Send SMS to up to 10,000 recipients in a single API call."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Bulk send accepted', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Batch ID returned', () => pm.expect(body.batch_id).to.be.a('string'));"
                ]
              }
            }
          ],
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"batch_id\": \"7f3e2a1c-b456-4321-9876-abc123def456\",\n  \"count\": 3,\n  \"accepted\": 3,\n  \"rejected\": 0,\n  \"messages\": [\n    {\"id\": \"msg-001\", \"message_id\": \"msg-001\", \"status\": \"queued\", \"segments\": 1, \"cost_micros\": null},\n    {\"id\": \"msg-002\", \"message_id\": \"msg-002\", \"status\": \"queued\", \"segments\": 1, \"cost_micros\": null},\n    {\"id\": \"msg-003\", \"message_id\": \"msg-003\", \"status\": \"queued\", \"segments\": 1, \"cost_micros\": null}\n  ]\n}"
            }
          ]
        },
        {
          "name": "Get Message Status",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/sms/{{last_message_id}}",
              "host": ["{{base_url}}"],
              "path": ["sms", "{{last_message_id}}"]
            },
            "description": "Get status and delivery info for a single message."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"destination\": \"+447700900123\",\n  \"source\": \"MyApp\",\n  \"status\": \"delivered\",\n  \"segments\": 1,\n  \"cost_micros\": 350000,\n  \"created_at\": \"2026-02-19T12:00:00Z\",\n  \"submitted_at\": \"2026-02-19T12:00:00Z\",\n  \"delivered_at\": \"2026-02-19T12:00:03Z\",\n  \"scheduled_at\": null\n}"
            }
          ]
        },
        {
          "name": "List Messages",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/sms?page=1&per_page=20",
              "host": ["{{base_url}}"],
              "path": ["sms"],
              "query": [
                { "key": "page", "value": "1" },
                { "key": "per_page", "value": "20" },
                { "key": "status", "value": "", "disabled": true, "description": "Filter: queued | sent | delivered | failed | expired" }
              ]
            },
            "description": "List messages with pagination and optional status filter."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n      \"to\": \"+447700900123\",\n      \"from\": \"MyApp\",\n      \"status\": \"delivered\",\n      \"segments\": 1,\n      \"cost\": 0.00035,\n      \"sentAt\": \"2026-02-19T12:00:00Z\",\n      \"deliveredAt\": \"2026-02-19T12:00:03Z\",\n      \"scheduledAt\": null\n    }\n  ],\n  \"total\": 1542\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Campaigns",
      "description": "Create and manage named SMS campaigns with scheduling, approval workflows, and real-time progress tracking.",
      "item": [
        {
          "name": "Create Campaign",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/campaigns", "host": ["{{base_url}}"], "path": ["campaigns"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Spring Sale 2026\",\n  \"message\": \"Hi {{name}}, get 30% off this weekend at myshop.com!\",\n  \"sender_id\": \"MyShop\",\n  \"scheduled_at\": \"2026-03-15T08:00:00Z\"\n}"
            },
            "description": "Create a new campaign in draft state."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (body.id) pm.collectionVariables.set('campaign_id', body.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List Campaigns",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/campaigns?page=1&per_page=20",
              "host": ["{{base_url}}"],
              "path": ["campaigns"],
              "query": [
                { "key": "page", "value": "1" },
                { "key": "per_page", "value": "20" }
              ]
            },
            "description": "List all campaigns with status and analytics."
          }
        },
        {
          "name": "Get Campaign",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/campaigns/{{campaign_id}}", "host": ["{{base_url}}"], "path": ["campaigns", "{{campaign_id}}"] },
            "description": "Get campaign details, progress stats, and status."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"id\": \"campaign-uuid\",\n  \"name\": \"Spring Sale 2026\",\n  \"status\": \"sending\",\n  \"message\": \"Hi {{name}}, get 30% off this weekend!\",\n  \"senderId\": \"MyShop\",\n  \"total\": 5000,\n  \"sent\": 2341,\n  \"delivered\": 2298,\n  \"failed\": 43,\n  \"pending\": 2659,\n  \"deliveryRate\": 98.16,\n  \"scheduledAt\": \"2026-03-15T08:00:00Z\",\n  \"createdAt\": \"2026-02-19T10:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Submit Campaign",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/campaigns/{{campaign_id}}/submit", "host": ["{{base_url}}"], "path": ["campaigns", "{{campaign_id}}", "submit"] },
            "description": "Submit a draft campaign for sending (transitions to pending_approval)."
          }
        },
        {
          "name": "Pause Campaign",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/campaigns/{{campaign_id}}/pause", "host": ["{{base_url}}"], "path": ["campaigns", "{{campaign_id}}", "pause"] },
            "description": "Pause an in-progress campaign."
          }
        },
        {
          "name": "Resume Campaign",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/campaigns/{{campaign_id}}/resume", "host": ["{{base_url}}"], "path": ["campaigns", "{{campaign_id}}", "resume"] },
            "description": "Resume a paused campaign."
          }
        },
        {
          "name": "Cancel Campaign",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/campaigns/{{campaign_id}}/cancel", "host": ["{{base_url}}"], "path": ["campaigns", "{{campaign_id}}", "cancel"] },
            "description": "Cancel a campaign."
          }
        },
        {
          "name": "Get Campaign Messages",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/campaigns/{{campaign_id}}/messages?page=1&per_page=20",
              "host": ["{{base_url}}"],
              "path": ["campaigns", "{{campaign_id}}", "messages"],
              "query": [{ "key": "page", "value": "1" }, { "key": "per_page", "value": "20" }]
            },
            "description": "List individual messages within a campaign."
          }
        },
        {
          "name": "Delete Campaign",
          "request": {
            "method": "DELETE",
            "url": { "raw": "{{base_url}}/campaigns/{{campaign_id}}", "host": ["{{base_url}}"], "path": ["campaigns", "{{campaign_id}}"] },
            "description": "Delete a cancelled or draft campaign."
          }
        }
      ]
    },
    {
      "name": "Contacts",
      "description": "Manage your contact database, groups, tags, and opt-outs.",
      "item": [
        {
          "name": "Create Contact",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/contacts", "host": ["{{base_url}}"], "path": ["contacts"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"phone\": \"+447700900123\",\n  \"name\": \"Alice Smith\",\n  \"email\": \"alice@example.com\",\n  \"custom_fields\": {\n    \"tier\": \"VIP\",\n    \"account_id\": \"ACC-001\"\n  }\n}"
            },
            "description": "Create a new contact."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (body.id) pm.collectionVariables.set('contact_id', body.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List Contacts",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/contacts?page=1&per_page=50",
              "host": ["{{base_url}}"],
              "path": ["contacts"],
              "query": [
                { "key": "page", "value": "1" },
                { "key": "per_page", "value": "50" },
                { "key": "search", "value": "", "disabled": true, "description": "Search by name or phone" },
                { "key": "group_id", "value": "", "disabled": true }
              ]
            },
            "description": "List contacts with optional search and group filter."
          }
        },
        {
          "name": "Update Contact",
          "request": {
            "method": "PATCH",
            "url": { "raw": "{{base_url}}/contacts/{{contact_id}}", "host": ["{{base_url}}"], "path": ["contacts", "{{contact_id}}"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"Alice Jones\",\n  \"email\": \"alice.jones@example.com\"\n}" },
            "description": "Update a contact's details."
          }
        },
        {
          "name": "Delete Contact",
          "request": {
            "method": "DELETE",
            "url": { "raw": "{{base_url}}/contacts/{{contact_id}}", "host": ["{{base_url}}"], "path": ["contacts", "{{contact_id}}"] },
            "description": "Delete a contact."
          }
        },
        {
          "name": "Create Group",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/contacts/groups", "host": ["{{base_url}}"], "path": ["contacts", "groups"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"VIP Customers\",\n  \"description\": \"High-value accounts\"\n}" },
            "description": "Create a new contact group."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (body.id) pm.collectionVariables.set('group_id', body.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List Groups",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/contacts/groups", "host": ["{{base_url}}"], "path": ["contacts", "groups"] },
            "description": "List all contact groups."
          }
        },
        {
          "name": "Add Group Members",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/contacts/groups/{{group_id}}/members", "host": ["{{base_url}}"], "path": ["contacts", "groups", "{{group_id}}", "members"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"contact_ids\": [\"{{contact_id}}\"]\n}" },
            "description": "Add contacts to a group."
          }
        },
        {
          "name": "Import Contacts (CSV)",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/contacts/import", "host": ["{{base_url}}"], "path": ["contacts", "import"] },
            "header": [{ "key": "Content-Type", "value": "text/csv" }],
            "body": {
              "mode": "raw",
              "raw": "phone,name,email\n+447700900001,Alice Smith,alice@example.com\n+447700900002,Bob Jones,bob@example.com"
            },
            "description": "Import contacts from a CSV file."
          }
        },
        {
          "name": "Create Tag",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/contacts/tags", "host": ["{{base_url}}"], "path": ["contacts", "tags"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"Newsletter\",\n  \"color\": \"#0066cc\"\n}" },
            "description": "Create a contact tag."
          }
        },
        {
          "name": "List Tags",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/contacts/tags", "host": ["{{base_url}}"], "path": ["contacts", "tags"] },
            "description": "List all tags."
          }
        },
        {
          "name": "List Opt-Outs",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/contacts/opt-outs", "host": ["{{base_url}}"], "path": ["contacts", "opt-outs"] },
            "description": "List all opted-out numbers."
          }
        }
      ]
    },
    {
      "name": "Billing",
      "description": "Wallet balance, transactions, invoices, and top-up management.",
      "item": [
        {
          "name": "Get Balance",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/billing/balance", "host": ["{{base_url}}"], "path": ["billing", "balance"] },
            "description": "Get the current wallet balance and credit limit."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Balance returned', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Balance is a number', () => pm.expect(body.balance).to.be.a('number'));"
                ]
              }
            }
          ],
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"balance\": 1250,\n  \"credit_limit\": 0,\n  \"available\": 1250,\n  \"currency\": \"GBP\",\n  \"low_balance_threshold\": 500,\n  \"auto_topup_enabled\": false\n}"
            }
          ]
        },
        {
          "name": "List Transactions",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/billing/transactions?page=1&per_page=20",
              "host": ["{{base_url}}"],
              "path": ["billing", "transactions"],
              "query": [{ "key": "page", "value": "1" }, { "key": "per_page", "value": "20" }]
            },
            "description": "List wallet transaction history."
          }
        },
        {
          "name": "List Invoices",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/billing/invoices", "host": ["{{base_url}}"], "path": ["billing", "invoices"] },
            "description": "List all invoices."
          }
        },
        {
          "name": "Get Invoice",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/billing/invoices/{{invoice_id}}", "host": ["{{base_url}}"], "path": ["billing", "invoices", "{{invoice_id}}"] },
            "description": "Get details of a specific invoice."
          }
        },
        {
          "name": "Download Invoice PDF",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/billing/invoices/{{invoice_id}}/pdf", "host": ["{{base_url}}"], "path": ["billing", "invoices", "{{invoice_id}}", "pdf"] },
            "description": "Download invoice as PDF."
          }
        },
        {
          "name": "Create Checkout (Top Up)",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/billing/checkout", "host": ["{{base_url}}"], "path": ["billing", "checkout"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"amount\": 5000,\n  \"currency\": \"GBP\",\n  \"success_url\": \"https://yourapp.com/billing?success=1\",\n  \"cancel_url\": \"https://yourapp.com/billing\"\n}" },
            "description": "Create a Stripe checkout session to top up the wallet."
          }
        },
        {
          "name": "Get Usage",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/billing/usage", "host": ["{{base_url}}"], "path": ["billing", "usage"] },
            "description": "Get usage summary for the current billing period."
          }
        },
        {
          "name": "Configure Auto Top-Up",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/billing/auto-topup", "host": ["{{base_url}}"], "path": ["billing", "auto-topup"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"enabled\": true,\n  \"threshold\": 500,\n  \"amount\": 2000\n}" },
            "description": "Configure automatic top-up when balance falls below a threshold."
          }
        }
      ]
    },
    {
      "name": "Reports",
      "description": "Message logs, delivery rate statistics, and analytics.",
      "item": [
        {
          "name": "Message Log",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/reports/messages?page_size=50",
              "host": ["{{base_url}}"],
              "path": ["reports", "messages"],
              "query": [
                { "key": "page_size", "value": "50" },
                { "key": "status", "value": "", "disabled": true, "description": "queued|sent|delivered|failed|expired" },
                { "key": "from_date", "value": "", "disabled": true, "description": "ISO 8601 start date" },
                { "key": "to_date", "value": "", "disabled": true, "description": "ISO 8601 end date" },
                { "key": "destination", "value": "", "disabled": true, "description": "E.164 filter" },
                { "key": "campaign_id", "value": "", "disabled": true }
              ]
            },
            "description": "Full message log with filters. Cached 10s."
          }
        },
        {
          "name": "DLR Stats",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/reports/dlr-stats", "host": ["{{base_url}}"], "path": ["reports", "dlr-stats"] },
            "description": "Delivery rate statistics (sent/delivered/failed). Cached 60s."
          }
        }
      ]
    },
    {
      "name": "Settings",
      "description": "Account settings, API keys, webhooks, and SMPP credentials.",
      "item": [
        {
          "name": "Get Profile",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/settings/profile", "host": ["{{base_url}}"], "path": ["settings", "profile"] },
            "description": "Get the account profile."
          }
        },
        {
          "name": "Update Profile",
          "request": {
            "method": "PATCH",
            "url": { "raw": "{{base_url}}/settings/profile", "host": ["{{base_url}}"], "path": ["settings", "profile"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"Alice Smith\",\n  \"timezone\": \"Europe/London\",\n  \"company_name\": \"My Company Ltd\"\n}" },
            "description": "Update profile settings."
          }
        },
        {
          "name": "List API Keys",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/settings/api-keys", "host": ["{{base_url}}"], "path": ["settings", "api-keys"] },
            "description": "List all API keys for the account."
          }
        },
        {
          "name": "Create API Key",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/settings/api-keys", "host": ["{{base_url}}"], "path": ["settings", "api-keys"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Production Server\",\n  \"permissions\": [\"sms.send\", \"billing.read\", \"reports.read\"]\n}"
            },
            "description": "Create a new API key pair. The secret is shown only once."
          },
          "response": [
            {
              "name": "201 Created",
              "status": "Created",
              "code": 201,
              "body": "{\n  \"id\": \"key_abc123\",\n  \"name\": \"Production Server\",\n  \"key\": \"bsms_live_abc123...\",\n  \"secret\": \"sk_live_xyz789...\",\n  \"permissions\": [\"sms.send\", \"billing.read\"],\n  \"created_at\": \"2026-02-19T12:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Delete API Key",
          "request": {
            "method": "DELETE",
            "url": { "raw": "{{base_url}}/settings/api-keys/{{api_key_id}}", "host": ["{{base_url}}"], "path": ["settings", "api-keys", "{{api_key_id}}"] },
            "description": "Revoke an API key."
          }
        },
        {
          "name": "List Webhooks",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/settings/webhooks", "host": ["{{base_url}}"], "path": ["settings", "webhooks"] },
            "description": "List configured webhook endpoints."
          }
        },
        {
          "name": "Create Webhook",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/settings/webhooks", "host": ["{{base_url}}"], "path": ["settings", "webhooks"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://yourapp.com/webhooks/sms\",\n  \"events\": [\"dlr\", \"opt_out\", \"campaign.completed\"],\n  \"description\": \"Production DLR handler\"\n}"
            },
            "description": "Create a webhook endpoint."
          }
        },
        {
          "name": "Get SMPP Credentials",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/settings/smpp", "host": ["{{base_url}}"], "path": ["settings", "smpp"] },
            "description": "Get SMPP connection credentials."
          }
        },
        {
          "name": "List Sender IDs",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/settings/sender-ids", "host": ["{{base_url}}"], "path": ["settings", "sender-ids"] },
            "description": "List approved sender IDs."
          }
        }
      ]
    },
    {
      "name": "Sandbox",
      "description": "Sandbox management endpoints. Set X-Sandbox: true to use sandbox mode.",
      "item": [
        {
          "name": "Reset Sandbox",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/sandbox/reset", "host": ["{{base_url}}"], "path": ["sandbox", "reset"] },
            "header": [{ "key": "X-Sandbox", "value": "true" }],
            "description": "Reset sandbox state — clears all fake messages and resets counters."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"message\": \"Sandbox state reset\",\n  \"cleared\": 42\n}"
            }
          ]
        },
        {
          "name": "Send Sandbox SMS",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/sms/send", "host": ["{{base_url}}"], "path": ["sms", "send"] },
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Sandbox", "value": "true" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"destination\": \"+447700900123\",\n  \"message\": \"Sandbox test message — not sent!\",\n  \"sender_id\": \"Test\"\n}"
            },
            "description": "Send an SMS in sandbox mode. Returns a fake message ID; no real message is sent."
          },
          "response": [
            {
              "name": "200 OK (Sandbox)",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"id\": \"sandbox-msg-550e8400\",\n  \"message_id\": \"sandbox-msg-550e8400\",\n  \"status\": \"delivered\",\n  \"segments\": 1,\n  \"cost_micros\": null,\n  \"sandbox\": true\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "WhatsApp",
      "description": "WhatsApp Business messaging (Beta). Requires a connected Meta Business account.",
      "item": [
        {
          "name": "Send WhatsApp Message",
          "request": {
            "method": "POST",
            "url": { "raw": "{{base_url}}/whatsapp/send", "host": ["{{base_url}}"], "path": ["whatsapp", "send"] },
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"+447700900123\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"order_shipped\",\n    \"language\": \"en\",\n    \"components\": [\n      {\n        \"type\": \"body\",\n        \"parameters\": [\n          {\"type\": \"text\", \"text\": \"Alice\"},\n          {\"type\": \"text\", \"text\": \"ORD-12345\"}\n        ]\n      }\n    ]\n  }\n}"
            },
            "description": "Send a WhatsApp template message."
          }
        },
        {
          "name": "List WhatsApp Templates",
          "request": {
            "method": "GET",
            "url": { "raw": "{{base_url}}/whatsapp/templates", "host": ["{{base_url}}"], "path": ["whatsapp", "templates"] },
            "description": "List approved WhatsApp message templates."
          }
        }
      ]
    },
    {
      "name": "Health",
      "description": "Health and readiness check endpoints.",
      "item": [
        {
          "name": "Health Check",
          "request": {
            "method": "GET",
            "url": { "raw": "https://api.bulksmsrates.com/health", "raw": "https://api.bulksmsrates.com/health" },
            "description": "Lightweight liveness check. Returns 200 if the API is running."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\"status\": \"ok\"}"
            }
          ]
        },
        {
          "name": "Ready Check",
          "request": {
            "method": "GET",
            "url": { "raw": "https://api.bulksmsrates.com/ready", "raw": "https://api.bulksmsrates.com/ready" },
            "description": "Readiness check — verifies DB and Redis connectivity."
          }
        }
      ]
    }
  ]
}
