{
  "info": {
    "name": "Ghala Developer API (v2)",
    "description": "Official Postman collection for the Ghala Developer API.\n\nBase URL: https://v2.ghala.io/api/v2\n\nAuthentication is the connected number's WhatsApp access token, sent as a bearer token. Copy it from Dashboard -> Developer -> Credentials.\n\nImport the companion environment file (ghala-api.postman_environment.json) and fill in ACCESS_TOKEN and TO before sending anything: there is no sandbox, so every send is a real WhatsApp message to a real handset.\n\nDocs: https://ghala.tz/help-center/api-reference/ghala-api-reference",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{ACCESS_TOKEN}}", "type": "string" }]
  },
  "variable": [
    {
      "key": "BASE_URL",
      "value": "https://v2.ghala.io/api/v2",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Templates",
      "item": [
        {
          "name": "List sendable templates",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 OK', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "if (body.items && body.items.length) {",
                  "  // Remember the first sendable template so the send request below can use it.",
                  "  pm.environment.set('TEMPLATE_NAME', body.items[0].name);",
                  "  pm.environment.set('TEMPLATE_LANGUAGE', body.items[0].language);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{BASE_URL}}/templates?sendable=true&limit=25",
              "host": ["{{BASE_URL}}"],
              "path": ["templates"],
              "query": [
                {
                  "key": "sendable",
                  "value": "true",
                  "description": "true for templates a send would accept right now, false for the ones it would refuse. Omit for all."
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, default 25, max 100."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "next_cursor from a previous response.",
                  "disabled": true
                }
              ]
            },
            "description": "Every message template on the connected number, newest first.\n\nThis is also the cheapest way to verify your token: a 200 proves authentication without messaging anybody.\n\nBranch on `sendable`, not on `status`."
          }
        }
      ]
    },
    {
      "name": "Messages",
      "item": [
        {
          "name": "Send text",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              {
                "key": "Idempotency-Key",
                "value": "postman-text-{{$guid}}",
                "description": "Derive this from the thing you are messaging about in real code, not a random value."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"text\",\n  \"text\": \"Habari! Hii ni ujumbe wa majaribio.\"\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "Free-form text. Only works inside the 24-hour window: message the number from a real handset first, or this returns 409 outside_messaging_window.\n\nMax 4096 characters."
          }
        },
        {
          "name": "Send template",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-template-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"template\",\n  \"template_name\": \"{{TEMPLATE_NAME}}\",\n  \"template_language\": \"{{TEMPLATE_LANGUAGE}}\"\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "The only message type allowed outside the 24-hour window.\n\nRun 'List sendable templates' first: it populates TEMPLATE_NAME and TEMPLATE_LANGUAGE from a template this number can actually send."
          }
        },
        {
          "name": "Send template with variables",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-template-vars-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"template\",\n  \"template_name\": \"{{TEMPLATE_NAME}}\",\n  \"template_language\": \"{{TEMPLATE_LANGUAGE}}\",\n  \"template_components\": [\n    {\n      \"type\": \"body\",\n      \"parameters\": [\n        { \"type\": \"text\", \"text\": \"Amina\" },\n        { \"type\": \"text\", \"text\": \"1042\" }\n      ]\n    }\n  ]\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "Parameters fill the template's {{1}}, {{2}} placeholders in order.\n\nRead the BODY component from GET /templates to see how many the template expects. The wrong number returns 400."
          }
        },
        {
          "name": "Send image",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-image-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"image\",\n  \"media_url\": \"https://example.com/product.png\",\n  \"media_caption\": \"Bidhaa yetu mpya\"\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "media_url must be public HTTPS: Meta fetches it directly, so a URL that only resolves on your machine or inside your VPC returns 502.\n\ntype can be image, document, video, or audio. Documents also take media_filename. Audio takes no caption."
          }
        },
        {
          "name": "Send document",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-document-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"document\",\n  \"media_url\": \"https://example.com/receipts/1042.pdf\",\n  \"media_filename\": \"Risiti-1042.pdf\",\n  \"media_caption\": \"Risiti yako\"\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "media_filename is the name the customer sees. Without it they get something unhelpful."
          }
        },
        {
          "name": "Send interactive buttons",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-buttons-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"interactive\",\n  \"interactive\": {\n    \"type\": \"buttons\",\n    \"body\": \"Oda yako #1042 iko tayari. Ungependa vipi kuipata?\",\n    \"footer\": \"Duka la Amina\",\n    \"buttons\": [\n      { \"id\": \"delivery\", \"title\": \"Niletewe\" },\n      { \"id\": \"pickup\", \"title\": \"Nitakuja kuchukua\" }\n    ]\n  }\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "Up to 3 reply buttons. The id you set comes back on the message.received event when the customer taps it, so make it something you can act on.\n\nMore than 3 buttons, or repeated ids, is rejected locally with 422."
          }
        },
        {
          "name": "Send interactive list",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-list-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"interactive\",\n  \"interactive\": {\n    \"type\": \"list\",\n    \"body\": \"Chagua bidhaa\",\n    \"button_text\": \"Ona bidhaa\",\n    \"sections\": [\n      {\n        \"title\": \"Vinywaji\",\n        \"rows\": [\n          { \"id\": \"sku-101\", \"title\": \"Chai\", \"description\": \"TZS 1,500\" },\n          { \"id\": \"sku-102\", \"title\": \"Kahawa\", \"description\": \"TZS 2,000\" }\n        ]\n      }\n    ]\n  }\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "Up to 10 rows in total across all sections, not 10 per section."
          }
        },
        {
          "name": "Send URL button (cta_url)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-cta-{{$guid}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"interactive\",\n  \"interactive\": {\n    \"type\": \"cta_url\",\n    \"body\": \"Angalia oda yako mtandaoni.\",\n    \"cta\": {\n      \"display_text\": \"Fungua oda\",\n      \"url\": \"https://shop.example/orders/1042\"\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "One button that opens a URL."
          }
        },
        {
          "name": "Idempotency replay (run twice)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const replayed = pm.response.headers.get('Idempotency-Replayed');",
                  "console.log('Idempotency-Replayed:', replayed);",
                  "pm.test('accepted', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              {
                "key": "Idempotency-Key",
                "value": "order-1042-confirmation",
                "description": "A fixed, derived key. Send this request twice: the customer gets one message, and the second response carries Idempotency-Replayed: true."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{TO}}\",\n  \"type\": \"text\",\n  \"text\": \"Asante! Oda yako #1042 imethibitishwa.\"\n}"
            },
            "url": {
              "raw": "{{BASE_URL}}/messages",
              "host": ["{{BASE_URL}}"],
              "path": ["messages"]
            },
            "description": "Send this twice to see idempotency work. The second call replays the first response and nothing reaches WhatsApp.\n\nA repeat while the first is still in flight returns 409 idempotency_in_progress. The same key with a different body returns 422 idempotency_key_reused."
          }
        }
      ]
    }
  ]
}
