{
  "info": {
    "_postman_id": "9a1f2c30-6b47-4a51-9d18-5f0c7f2a41c3",
    "name": "Voice Infra — Sandbox v2 API",
    "description": "The public **v2 API** for Voice Infra — outbound AI voice calls for India.\n\nEverything you need to go from an empty workspace to a running campaign:\ncreate an agent, place a call, watch it settle against your wallet, then dial a\nwhole contact list on a schedule with retries, a budget and a do-not-call list.\n\n## Start here\n\n1. Import `sandbox.postman_environment.json` and select the **Voice Infra — Sandbox** environment.\n2. Paste your `sk_live_…` key into the `api_key` variable (it is marked secret — it is never written into the collection file).\n3. Set `test_phone` to a number you own, in E.164 (`+919876543210`).\n4. Work through **0-Quickstart** top to bottom. Each step stores the id the next one needs.\n\n## Conventions\n\n- **Base URL** — `{{base_url}}`, from the environment.\n- **Auth** — `Authorization: Bearer sk_live_…`, set once at the collection level. Every request inherits it.\n- **Ids** — `agt_`, `call_`, `cmp_` followed by a sortable 26-character id. Treat them as opaque strings.\n- **Lists** — `{data, has_more, next_cursor}`, newest first. Loop while `has_more` is true, passing `next_cursor` back as `cursor`. `limit` is 1–100, default 20.\n- **Errors** — always `{\"error\": {\"code\", \"message\"}}`. Branch on `code`; log `message` but never parse it. `code` is an open set, so keep a default branch keyed on the HTTP status.\n- **Phone numbers** — E.164 with the country code: `+919876543210`.\n- **Money** — calls and the wallet are in **rupees** (`balance_inr`, `cost_inr`, `amount_inr`); campaign budgets and campaign spend are in **paise** (`budget_paise`, `spent_paise`), where **100 paise = ₹1**.\n- **Times** — RFC 3339 UTC (`2026-08-10T09:16:38Z`), except campaign dates and slots, which are local to the campaign's `timezone`.\n\n## Status and error codes\n\n| Status | `error.code` | What to do |\n| --- | --- | --- |\n| 400 | `invalid_request` | Fix the request; `message` names the field. |\n| 401 | `unauthorized` | Check the key. A trailing newline from a `cat`-ed key file is the usual culprit. |\n| 402 | `insufficient_balance` | Top up, then retry with the **same** `Idempotency-Key`. |\n| 403 | `forbidden` | The key was revoked. Nothing else returns 403. |\n| 404 | `not_found` | Unknown id — including one that belongs to another workspace. |\n| 409 | `conflict` | The operation is illegal in the current state. Read `status` first. |\n| 429 | `rate_limited` | Request rate or queue depth. Honour `Retry-After` and back off. |\n| 429 | `at_capacity` | All lines are busy right now. Honour `Retry-After` (30s). |\n| 501 | `tier_unavailable` | The tier is published but not live. Use `t3`. |\n| 502 | `upstream_error` | The carrier refused the call. Retry with backoff. |\n| 503 | `campaigns_unavailable` | Campaigns are not switched on for this workspace yet — ask your account contact. |\n| 503 | `fleet_offline` | No speech capacity online. Honour `Retry-After` (300s). |\n| 500 | `internal` | Ours. Retry with backoff; send us the id if it persists. |\n\n`error.code` is an **open set** — new codes appear without a version bump, so\nbranch on the HTTP status first and the code second. In particular, accept both\n`insufficient_funds` and `insufficient_balance` on a `402`: the status is the\ncontract, the string is not worth a broken integration.\n\nRetry `429`, `500`, `502`, `503`, `504` and network errors with exponential\nbackoff and jitter. Never retry `400`, `401`, `403`, `404`, `501` — they fail the\nsame way every time. `402` is retryable only after a top-up, not on a timer.\n\n## Limits\n\n| Limit | Default | Over the limit |\n| --- | --- | --- |\n| Request rate | 10 requests/second, burst 20 | `429 rate_limited`, `Retry-After: 1` |\n| Concurrent live calls | 5 | Nothing fails — extra calls wait in `queued` |\n| Queue depth (accepted, not yet ended) | 500 | `429 rate_limited`, `Retry-After: 30` |\n| Contacts per campaign request | 1000 | `400 invalid_request` |\n| Page size | 20 default, 100 max | Clamped |\n\nConcurrency is a **pacer, not a gate**: over-cap calls are still accepted with 202\nand dial as slots free. Only queue depth and request rate reject.\n\nRate and queue limits are provisioned per key. If you need more, that is a\nprovisioning change, not a code change — ask before the campaign, not during it.\n\nFull reference: **https://docs.miraiminds.co**",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Fail early and clearly rather than sending an unauthenticated request.",
          "const key = pm.environment.get(\"api_key\") || pm.collectionVariables.get(\"api_key\");",
          "if (!key && pm.request.url.getHost().indexOf(\"base_url\") !== -1) {",
          "  console.warn(\"api_key is empty — select the Sandbox environment and paste your sk_live_ key.\");",
          "}"
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Surface the one-line reason on any failure, for every request.",
          "if (pm.response.code >= 400) {",
          "  try {",
          "    const e = pm.response.json().error;",
          "    console.error(`${pm.response.code} ${e.code}: ${e.message}`);",
          "  } catch (_) { /* not a JSON error body */ }",
          "}"
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "agent_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "call_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "campaign_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "cursor",
      "value": "",
      "type": "string"
    },
    {
      "key": "idempotency_key",
      "value": "",
      "type": "string"
    },
    {
      "key": "start_date",
      "value": "",
      "type": "string"
    },
    {
      "key": "end_date",
      "value": "",
      "type": "string"
    },
    {
      "key": "webhook_signature",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "0-Quickstart",
      "description": "Ten requests, top to bottom. Run them in order with an environment selected and\nyou will have created an agent, placed one real call, checked what it cost, run a\nfive-contact campaign end to end, and read back what was said on the call.\n\nEach step stores the id it created in a collection variable — `{{agent_id}}`,\n`{{call_id}}`, `{{campaign_id}}` — so the next step needs no copy-paste.\n\nBefore you start:\n\n1. Select the **Voice Infra — Sandbox** environment.\n2. Put your `sk_live_…` secret key in the `api_key` variable.\n3. Set `test_phone` to a number you own, in E.164 (`+919876543210`). Step 2\n   rings it for real and debits your wallet.",
      "item": [
        {
          "name": "1. Create an agent",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/agents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "agents"
              ]
            },
            "description": "Creates the voice agent the rest of this walkthrough uses.\n\n`system_prompt` and `first_message` may contain `{{variable}}` placeholders; you fill them per call. The response id is saved to `{{agent_id}}`."
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/agents",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents"
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"object\": \"agent\",\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  },\n  \"created_at\": \"2026-08-10T09:14:02Z\",\n  \"updated_at\": \"2026-08-10T09:14:02Z\"\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"201 Created\", () => pm.response.to.have.status(201));",
                  "const a = pm.response.json();",
                  "pm.test(\"agent id looks right\", () => pm.expect(a.id).to.match(/^agt_/));",
                  "pm.collectionVariables.set(\"agent_id\", a.id);",
                  "console.log(\"agent_id =\", a.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "2. Place one call",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}",
                "description": "Always send this on POST /v2/calls. A retry with the same key returns the original call instead of ringing the phone twice."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/calls",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls"
              ]
            },
            "description": "Rings `{{test_phone}}` with `{{agent_id}}`.\n\nAnswers **202 Accepted** with `{id, status}` — accepted, not yet connected. One minute at the call's tier is held from your wallet up front and reconciled when the call ends.\n\n**Idempotency-Key** (max 255 characters, remembered for 24 hours) is what makes a retry safe. A replay comes back with the header `Idempotent-Replay: true` and the original `call_id`; no second call is placed."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "quickstart-1786440000000"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"status\": \"queued\"\n}"
            },
            {
              "name": "402 Wallet empty",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "quickstart-1786440000000"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Payment Required",
              "code": 402,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"insufficient_balance\",\n    \"message\": \"Wallet balance does not cover one minute at this tier.\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// One stable idempotency key per walkthrough run, so a retry is a replay.",
                  "if (!pm.collectionVariables.get(\"idempotency_key\")) {",
                  "  pm.collectionVariables.set(\"idempotency_key\", \"quickstart-\" + Date.now());",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"202 Accepted\", () => pm.response.to.have.status(202));",
                  "const c = pm.response.json();",
                  "pm.collectionVariables.set(\"call_id\", c.id);",
                  "if (pm.response.headers.get(\"Idempotent-Replay\")) {",
                  "  console.log(\"replayed the earlier call:\", c.id);",
                  "}",
                  "console.log(\"call_id =\", c.id, \"status =\", c.status);"
                ]
              }
            }
          ]
        },
        {
          "name": "3. Get the call",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Poll until `status` is terminal: `completed`, `voicemail`, `no_answer`, `busy`, `failed`, `timeout` or `aborted`.\n\nPolling is the fallback. In production, subscribe to webhooks instead — see the **Webhooks** folder."
          },
          "response": [
            {
              "name": "200 OK — completed",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"object\": \"call\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"to\": \"+919876543210\",\n  \"status\": \"completed\",\n  \"tier\": \"t3\",\n  \"ended_reason\": \"customer-ended-call\",\n  \"duration_secs\": 96,\n  \"cost_inr\": 6,\n  \"created_at\": \"2026-08-10T09:15:00Z\",\n  \"started_at\": \"2026-08-10T09:15:02Z\",\n  \"ended_at\": \"2026-08-10T09:16:38Z\",\n  \"recording_available\": true,\n  \"transcript_available\": true,\n  \"analysis\": {\n    \"summary\": \"Rohit ne order AC-88213 ki delivery confirm kar di. Kal shaam 6 baje ka slot theek hai, thoda late ho to bhi chalega. Koi complaint nahi.\",\n    \"success\": true,\n    \"data\": {\n      \"slot_confirmed\": \"कल शाम 6 बजे\",\n      \"address\": null\n    }\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "const c = pm.response.json();",
                  "console.log(c.status, \"| duration\", c.duration_secs, \"| cost ₹\", c.cost_inr);"
                ]
              }
            }
          ]
        },
        {
          "name": "4. List recent calls",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "10",
                  "description": "1–100, default 20."
                },
                {
                  "key": "status",
                  "value": "completed",
                  "description": "Filter by call status.",
                  "disabled": true
                },
                {
                  "key": "agent_id",
                  "value": "{{agent_id}}",
                  "description": "Filter to one agent.",
                  "disabled": true
                }
              ]
            },
            "description": "Newest first. Pages are `{data, has_more, next_cursor}` — pass `next_cursor` back as `cursor` while `has_more` is true."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "10"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n      \"object\": \"call\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"to\": \"+919876543210\",\n      \"status\": \"completed\",\n      \"tier\": \"t3\",\n      \"ended_reason\": \"customer-ended-call\",\n      \"duration_secs\": 96,\n      \"cost_inr\": 6,\n      \"created_at\": \"2026-08-10T09:15:00Z\",\n      \"started_at\": \"2026-08-10T09:15:02Z\",\n      \"ended_at\": \"2026-08-10T09:16:38Z\",\n      \"recording_available\": true,\n      \"transcript_available\": true,\n      \"analysis\": {\n        \"summary\": \"Rohit ne order AC-88213 ki delivery confirm kar di. Kal shaam 6 baje ka slot theek hai, thoda late ho to bhi chalega. Koi complaint nahi.\",\n        \"success\": true,\n        \"data\": {\n          \"slot_confirmed\": \"कल शाम 6 बजे\",\n          \"address\": null\n        }\n      }\n    },\n    {\n      \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YB\",\n      \"object\": \"call\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"to\": \"+919876543211\",\n      \"status\": \"in_progress\",\n      \"tier\": \"t3\",\n      \"ended_reason\": null,\n      \"duration_secs\": null,\n      \"cost_inr\": null,\n      \"created_at\": \"2026-08-10T09:20:01Z\",\n      \"started_at\": \"2026-08-10T09:20:09Z\",\n      \"ended_at\": null,\n      \"recording_available\": false,\n      \"transcript_available\": false,\n      \"analysis\": null\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "const page = pm.response.json();",
                  "pm.test(\"page envelope\", () => pm.expect(page).to.have.property(\"has_more\"));",
                  "if (page.next_cursor) pm.collectionVariables.set(\"cursor\", page.next_cursor);"
                ]
              }
            }
          ]
        },
        {
          "name": "5. Check the wallet",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/wallet",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "wallet"
              ]
            },
            "description": "Your prepaid balance in rupees. Every call debits it when it ends; top-ups are applied by your account contact.\n\nPoll this hourly and alert your own team below a day of traffic — a 402 mid-campaign is a worse way to find out."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/wallet",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "wallet"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"balance_inr\": 494,\n  \"currency\": \"INR\",\n  \"updated_at\": \"2026-08-10T09:16:38Z\"\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "console.log(\"balance ₹\", pm.response.json().balance_inr);"
                ]
              }
            }
          ]
        },
        {
          "name": "6. Create a 5-contact campaign",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/campaigns",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns"
              ]
            },
            "description": "A campaign dials a contact list for you inside a calling window you define, with retries and a spend cap.\n\nIt is created in `draft` — nothing rings until step 7.\n\n`start_date` and `end_date` are filled in by a pre-request script (today and today + 7) so this request works whenever you run it. Times are local to `timezone`.\n\n`budget_paise` is money in **paise**: 100 paise = ₹1, so `50000` is a ₹500 cap. Omit it for no cap — the wallet is still the hard limit."
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"draft\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 0,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T09:21:44Z\",\n  \"contacts_accepted\": 5,\n  \"contacts_duplicate\": 0,\n  \"contacts_rejected\": []\n}"
            }
          ],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Keep the calling window valid whenever this collection is run.",
                  "const pad = (n) => String(n).padStart(2, \"0\");",
                  "const fmt = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;",
                  "const today = new Date();",
                  "const later = new Date(today.getTime() + 7 * 24 * 3600 * 1000);",
                  "pm.collectionVariables.set(\"start_date\", fmt(today));",
                  "pm.collectionVariables.set(\"end_date\", fmt(later));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"201 Created\", () => pm.response.to.have.status(201));",
                  "const b = pm.response.json();",
                  "pm.collectionVariables.set(\"campaign_id\", b.id);",
                  "console.log(\"campaign_id =\", b.id,",
                  "            \"| accepted\", b.contacts_accepted,",
                  "            \"| duplicate\", b.contacts_duplicate,",
                  "            \"| rejected\", b.contacts_rejected.length);"
                ]
              }
            }
          ]
        },
        {
          "name": "7. Start the campaign",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/start",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "start"
              ]
            },
            "description": "Moves the campaign to `play` and begins dialling inside the next open slot. **202 Accepted**.\n\nIf the wallet cannot cover every remaining contact the campaign still starts and the response carries `funded_calls` — how many calls the current balance pays for. The campaign dials those, then pauses itself with `pause_reason: \"insufficient_balance\"`."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "start"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"play\"\n}"
            },
            {
              "name": "202 Accepted — partially funded",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "start"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"play\",\n  \"funded_calls\": 3\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"202 Accepted\", () => pm.response.to.have.status(202));",
                  "const r = pm.response.json();",
                  "if (r.funded_calls !== undefined) {",
                  "  console.warn(\"balance covers only\", r.funded_calls, \"calls — top up to finish the list\");",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "8. Poll the campaign",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}"
              ]
            },
            "description": "The campaign object plus live `counters` — contacts grouped by status (`pending`, `claimed`, `dialing`, `completed`, `voicemail`, `no_answer`, `busy`, `failed`, `timeout`, `exhausted`, `suppressed`, `aborted`).\n\n`spent_paise` is money already billed, in paise."
          },
          "response": [
            {
              "name": "200 OK — running",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"play\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 900,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T10:04:12Z\",\n  \"counters\": {\n    \"pending\": 2,\n    \"dialing\": 1,\n    \"completed\": 2\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "const c = pm.response.json();",
                  "console.log(c.status, JSON.stringify(c.counters || {}), \"| spent ₹\", (c.spent_paise / 100).toFixed(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "9. Campaign report",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/report",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "report"
              ],
              "query": [
                {
                  "key": "format",
                  "value": "csv",
                  "description": "Add `format=csv` for the per-contact CSV export.",
                  "disabled": true
                }
              ]
            },
            "description": "Roll-up for the campaign: contacts, dial attempts, connections and spend.\n\n`connected` counts calls that reached a person or a machine (`completed` + `voicemail` + `timeout`). `spent_paise` is authoritative; `spent_inr` is the same number in rupees for convenience.\n\nAdd `?format=csv` for one row per contact — see the **Campaigns** folder."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/report",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "report"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"campaign_id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"completed\",\n  \"counters\": {\n    \"completed\": 3,\n    \"voicemail\": 1,\n    \"no_answer\": 1\n  },\n  \"contacts\": 5,\n  \"dialed\": 5,\n  \"attempts\": 6,\n  \"connected\": 4,\n  \"spent_paise\": 2100,\n  \"spent_inr\": 21,\n  \"budget_paise\": 50000\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "const r = pm.response.json();",
                  "console.log(`${r.connected}/${r.contacts} connected, ${r.attempts} attempts, ₹${r.spent_inr}`);"
                ]
              }
            }
          ]
        },
        {
          "name": "10. Read the transcript",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "What was actually said on the call from step 2, turn by turn.\n\nThis is the point of the whole walkthrough: you now have a call you can read, audio you can play (**Get recording**), and — if the agent was created with `analysis.enabled` — a summary on the call object itself.\n\n**404** here means the call has not ended yet, or nobody spoke. Poll step 3 until `transcript_available` is `true`."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA/transcript",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                    "transcript"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"call_id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"turns\": [\n    {\n      \"role\": \"assistant\",\n      \"text\": \"नमस्ते Rohit जी, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n      \"start_ms\": 420,\n      \"end_ms\": 4980\n    },\n    {\n      \"role\": \"user\",\n      \"text\": \"हाँ बोलिए\",\n      \"start_ms\": 5640,\n      \"end_ms\": 6390\n    },\n    {\n      \"role\": \"assistant\",\n      \"text\": \"आपका order AC-88213 kal shaam 6 baje deliver hoga, Flat 402 Sunrise Apartments Andheri West. Ye slot theek hai?\",\n      \"start_ms\": 7010,\n      \"end_ms\": 14880\n    },\n    {\n      \"role\": \"user\",\n      \"text\": \"haan theek hai, lekin thoda late aaye to chalega\",\n      \"start_ms\": 15900,\n      \"end_ms\": 19240\n    },\n    {\n      \"role\": \"assistant\",\n      \"text\": \"बिल्कुल, हम delivery partner ko note kar denge. Aur koi help chahiye?\",\n      \"start_ms\": 19960,\n      \"end_ms\": 24110\n    },\n    {\n      \"role\": \"user\",\n      \"text\": \"nahi bas itna hi, thank you\",\n      \"start_ms\": 25020,\n      \"end_ms\": 26980\n    },\n    {\n      \"role\": \"assistant\",\n      \"text\": \"धन्यवाद Rohit जी, आपका दिन शुभ हो।\",\n      \"start_ms\": 27600,\n      \"end_ms\": 30640\n    }\n  ]\n}"
            },
            {
              "name": "404 Call has not ended yet",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YB/transcript",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YB",
                    "transcript"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"transcript not available (call still in progress or produced no speech)\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "const t = pm.response.json();",
                  "pm.test(\"turns are assistant/user only\", () =>",
                  "  t.turns.forEach((x) => pm.expect([\"assistant\", \"user\"]).to.include(x.role)));",
                  "t.turns.forEach((x) => console.log(`${(x.start_ms / 1000).toFixed(1)}s ${x.role}: ${x.text}`));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Agents",
      "description": "An agent is a reusable configuration: prompt, opening line, voice, language and\ncall limits. Calls and campaigns reference one by id.\n\nCeilings enforced on create and update: `system_prompt` 8,000 characters,\n`first_message` 500 characters, `max_duration_secs` 30–1800 (default 300).\n\n`voice` is an **object** — `\"voice\": \"ashutosh\"` is rejected — and\n`voice.voice_id` is a slug drawn from the catalogue of the **tier** the agent\nruns on. On `t3`, the default tier, the default voice is `ashutosh`. On\n`t1` the catalogue is exactly two: `ashu` (the default there) and `aishe`.\nA slug from outside the tier's catalogue is refused at write time with\n**400 `invalid_request`**, rather than failing inside the synthesiser with\nthe customer already on the line.\n\n**About `{{customer_name}}` in the prompts below.** Those are *agent* placeholders,\nfilled by the platform from each call's `variables` — they are not Postman\nvariables. Postman leaves an undefined `{{…}}` untouched, so they reach the API\nintact. Just do not define a Postman variable with the same name, or Postman will\nsubstitute it before we ever see it.\n\n**`analysis`** is optional and off unless you ask for it. With\n`{\"enabled\": true}` every call this agent runs is read once after it ends and\nthe call object gains `{summary, success, data}`. Add `analysis.prompt`\n(up to 2,000 characters) to say what you want pulled out of the conversation —\nthe answer lands in `analysis.data`. Analysis never delays a call, never\ndelays its webhook, and a failure leaves `analysis: null` rather than\nretrying.",
      "item": [
        {
          "name": "Create agent",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/agents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "agents"
              ]
            },
            "description": "**201 Created** with the full agent object.\n\nRequired: `name`, `system_prompt`, `first_message`, `voice.voice_id`, `language`.\nOptional: `max_duration_secs`, `end_call`, `voicemail`."
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/agents",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents"
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"object\": \"agent\",\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  },\n  \"created_at\": \"2026-08-10T09:14:02Z\",\n  \"updated_at\": \"2026-08-10T09:14:02Z\"\n}"
            },
            {
              "name": "400 Missing field",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmation\",\n  \"language\": \"hi-IN\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/agents",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents"
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"invalid_request\",\n    \"message\": \"system_prompt is required.\"\n  }\n}"
            },
            {
              "name": "401 Bad key",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/agents",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents"
                  ]
                }
              },
              "status": "Unauthorized",
              "code": 401,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"unauthorized\",\n    \"message\": \"Unknown or invalid key.\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"201 Created\", () => pm.response.to.have.status(201));",
                  "pm.collectionVariables.set(\"agent_id\", pm.response.json().id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List agents",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/agents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "agents"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–100, default 20."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "`next_cursor` from the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "Newest first. Deleted agents are omitted."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/agents",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "20"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"object\": \"agent\",\n      \"name\": \"Order confirmation\",\n      \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n      \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n      \"voice\": {\n        \"voice_id\": \"ashutosh\",\n        \"language\": \"hi-IN\"\n      },\n      \"language\": \"hi-IN\",\n      \"max_duration_secs\": 300,\n      \"end_call\": {\n        \"enabled\": true,\n        \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n        \"confirm\": true\n      },\n      \"voicemail\": {\n        \"action\": \"hangup\",\n        \"message\": \"\"\n      },\n      \"analysis\": {\n        \"enabled\": true,\n        \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n      },\n      \"created_at\": \"2026-08-10T09:14:02Z\",\n      \"updated_at\": \"2026-08-10T09:14:02Z\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ]
        },
        {
          "name": "Get agent",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/agents/{{agent_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "agents",
                "{{agent_id}}"
              ]
            },
            "description": "An id that belongs to someone else answers **404**, exactly like an id that never existed. That is deliberate — a 403 would confirm the id is real."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/agents/agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents",
                    "agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"object\": \"agent\",\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"ashutosh\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 300,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  },\n  \"created_at\": \"2026-08-10T09:14:02Z\",\n  \"updated_at\": \"2026-08-10T09:14:02Z\"\n}"
            },
            {
              "name": "404 Not found",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/agents/agt_01K7Q9F3K7M2N5P9R4T6V8W0X0",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents",
                    "agt_01K7Q9F3K7M2N5P9R4T6V8W0X0"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"No such agent.\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Update agent",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_message\": \"नमस्ते {{customer_name}}, Acme Retail से Priya बोल रही हूँ।\",\n  \"voice\": {\n    \"voice_id\": \"aishe\",\n    \"language\": \"hi-IN\"\n  },\n  \"max_duration_secs\": 240\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/agents/{{agent_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "agents",
                "{{agent_id}}"
              ]
            },
            "description": "Send only the fields you are changing.\n\nNested objects (`voice`, `end_call`, `voicemail`) are **replaced whole**, not merged — send every key you want to keep. Calls already running are unaffected."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"voice\": {\n    \"voice_id\": \"aishe\",\n    \"language\": \"hi-IN\"\n  },\n  \"max_duration_secs\": 240\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/agents/agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents",
                    "agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"object\": \"agent\",\n  \"name\": \"Order confirmation\",\n  \"system_prompt\": \"You are Priya from Acme Retail. Confirm order {{order_id}} with {{customer_name}}, read back the delivery address, and ask whether the slot on {{slot}} works. Speak Hindi with English product names. Keep every turn under two sentences.\",\n  \"first_message\": \"नमस्ते {{customer_name}}, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n  \"voice\": {\n    \"voice_id\": \"aishe\",\n    \"language\": \"hi-IN\"\n  },\n  \"language\": \"hi-IN\",\n  \"max_duration_secs\": 240,\n  \"end_call\": {\n    \"enabled\": true,\n    \"message\": \"धन्यवाद, आपका दिन शुभ हो।\",\n    \"confirm\": true\n  },\n  \"voicemail\": {\n    \"action\": \"hangup\",\n    \"message\": \"\"\n  },\n  \"analysis\": {\n    \"enabled\": true,\n    \"prompt\": \"Did the customer accept the delivery slot? Put the slot they agreed to in `slot_confirmed`, and any new address they gave in `address`.\"\n  },\n  \"created_at\": \"2026-08-10T09:14:02Z\",\n  \"updated_at\": \"2026-08-10T11:02:19Z\"\n}"
            }
          ]
        },
        {
          "name": "Delete agent",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/agents/{{agent_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "agents",
                "{{agent_id}}"
              ]
            },
            "description": "**204 No Content**. The agent stops appearing in lists and can no longer start new calls; calls already in flight finish normally and past calls keep resolving their `agent_id`."
          },
          "response": [
            {
              "name": "204 No Content",
              "originalRequest": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/agents/agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "agents",
                    "agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ"
                  ]
                }
              },
              "status": "No Content",
              "code": 204,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "Content-Length",
                  "value": "0"
                }
              ],
              "cookie": [],
              "body": ""
            }
          ]
        }
      ]
    },
    {
      "name": "Calls",
      "description": "One outbound call. `POST /v2/calls` answers **202 Accepted** — the request was\naccepted, the phone has not rung yet.\n\n**Money.** Rates are per minute, rounded up, one-minute minimum. Tier `t3` —\nthe default — is ₹3/minute (300 paise); tier `t1` is ₹1/minute (100 paise).\nOne minute is held from the wallet when the call is accepted and reconciled\nagainst the real duration when it ends; unbilled endings\n(`no_answer`, `busy`, `failed`, an abort before answer) release the hold in\nfull and report `cost_inr: null`.\n\n**Statuses.** `queued` → `dialing` → `in_progress` → one of `completed`,\n`voicemail`, `no_answer`, `busy`, `failed`, `timeout`, `aborted`. Terminal\nstatuses never change.\n\n**Phone numbers** are E.164 with the country code: `+919876543210`.\n\n**After the call.** Every ended call carries `recording_available` and\n`transcript_available`. When they are true, `GET /v2/calls/{id}/transcript`\nreturns the turns and `GET /v2/calls/{id}/recording` redirects to a signed\n15-minute URL for the audio. Both are kept for 90 days. Agents created with\n`analysis.enabled` also get an `analysis` object on the call — a summary, a\nsuccess flag, and whatever you asked for in `analysis.prompt`.",
      "item": [
        {
          "name": "Create call",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}",
                "description": "Up to 255 characters, remembered for 24 hours. Always send one."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/calls",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls"
              ]
            },
            "description": "Places one call. **202 Accepted** with `{id, status: \"queued\"}`.\n\n| Field | Required | Notes |\n| --- | --- | --- |\n| `agent_id` | yes | Must exist in your workspace. |\n| `to` | yes | E.164, e.g. `+919876543210`. |\n| `variables` | no | Flat string map, at most 32 keys, 512 characters per value. |\n| `webhook_url` | no | HTTPS, public host. |\n| `max_duration_secs` | no | 30–1800. Overrides the agent for this call. |\n| `tier` | no | Defaults to your key's tier, `t3` (₹3/minute). Pass `t1` for the ₹1/minute tier — a `t1` call needs an agent whose `voice.voice_id` is `ashu` or `aishe`. |\n\n**Idempotency.** Send `Idempotency-Key` on every attempt. A retry with the same key returns the original response with `Idempotent-Replay: true` — no second call is placed. A timeout tells you nothing about whether the phone rang; the key is what makes the retry safe.\n\n**Over capacity.** Past your queue depth you get `429 rate_limited` with `Retry-After`. Being merely at your concurrency ceiling does not fail — those calls wait in `queued` and dial as slots free."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "order-AC-88213-attempt-1"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"status\": \"queued\"\n}"
            },
            {
              "name": "202 Accepted — idempotent replay",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "order-AC-88213-attempt-1"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "Idempotent-Replay",
                  "value": "true"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"status\": \"queued\"\n}"
            },
            {
              "name": "202 Accepted — t1 (GPU voices)",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "order-AC-88213-attempt-1-t1"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t1\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                },
                "description": "The same call placed on `t1` instead of the default `t3`. `t1` bills ₹1/minute against ₹3, and its voices are `ashu` and `aishe` — so the `agent_id` you send must be an agent created with one of those two, not the `ashutosh` agent the quickstart builds."
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"status\": \"queued\"\n}"
            },
            {
              "name": "402 Insufficient balance",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "order-AC-88213-attempt-1"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Payment Required",
              "code": 402,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"insufficient_balance\",\n    \"message\": \"Wallet balance does not cover one minute at this tier.\"\n  }\n}"
            },
            {
              "name": "404 Unknown agent",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0X0\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"No such agent.\"\n  }\n}"
            },
            {
              "name": "429 Queue full",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Too Many Requests",
              "code": 429,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "Retry-After",
                  "value": "30"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"rate_limited\",\n    \"message\": \"queue depth limit reached for this workspace (500 calls accepted and not yet ended, limit 500); wait for calls to drain or ask ops to raise it\"\n  }\n}"
            },
            {
              "name": "400 Bad phone number",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"agent_id\": \"{{agent_id}}\",\n  \"to\": \"9876543210\",\n  \"variables\": {\n    \"customer_name\": \"Rohit Sharma\",\n    \"order_id\": \"AC-88213\",\n    \"slot\": \"कल शाम 6 बजे\"\n  },\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"max_duration_secs\": 300,\n  \"tier\": \"t3\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"invalid_request\",\n    \"message\": \"to must be an E.164 number, e.g. +919876543210.\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get(\"idempotency_key\")) {",
                  "  pm.collectionVariables.set(\"idempotency_key\", \"call-\" + Date.now());",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"202 Accepted\", () => pm.response.to.have.status(202));",
                  "pm.collectionVariables.set(\"call_id\", pm.response.json().id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List calls",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–100, default 20."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "`next_cursor` from the previous page.",
                  "disabled": true
                },
                {
                  "key": "agent_id",
                  "value": "{{agent_id}}",
                  "description": "Only calls run by this agent.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "completed",
                  "description": "Exact status match.",
                  "disabled": true
                },
                {
                  "key": "from",
                  "value": "2026-08-01T00:00:00Z",
                  "description": "Created at or after (RFC 3339).",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "2026-08-31T23:59:59Z",
                  "description": "Created at or before (RFC 3339).",
                  "disabled": true
                }
              ]
            },
            "description": "Newest first, `{data, has_more, next_cursor}`. Loop while `has_more` is true, passing `next_cursor` back as `cursor`."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "20"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YB\",\n      \"object\": \"call\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"to\": \"+919876543211\",\n      \"status\": \"in_progress\",\n      \"tier\": \"t3\",\n      \"ended_reason\": null,\n      \"duration_secs\": null,\n      \"cost_inr\": null,\n      \"created_at\": \"2026-08-10T09:20:01Z\",\n      \"started_at\": \"2026-08-10T09:20:09Z\",\n      \"ended_at\": null,\n      \"recording_available\": false,\n      \"transcript_available\": false,\n      \"analysis\": null\n    },\n    {\n      \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n      \"object\": \"call\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"to\": \"+919876543210\",\n      \"status\": \"completed\",\n      \"tier\": \"t3\",\n      \"ended_reason\": \"customer-ended-call\",\n      \"duration_secs\": 96,\n      \"cost_inr\": 6,\n      \"created_at\": \"2026-08-10T09:15:00Z\",\n      \"started_at\": \"2026-08-10T09:15:02Z\",\n      \"ended_at\": \"2026-08-10T09:16:38Z\",\n      \"recording_available\": true,\n      \"transcript_available\": true,\n      \"analysis\": {\n        \"summary\": \"Rohit ne order AC-88213 ki delivery confirm kar di. Kal shaam 6 baje ka slot theek hai, thoda late ho to bhi chalega. Koi complaint nahi.\",\n        \"success\": true,\n        \"data\": {\n          \"slot_confirmed\": \"कल शाम 6 बजे\",\n          \"address\": null\n        }\n      }\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ]
        },
        {
          "name": "Get call",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "The call object.\n\n| Field | Notes |\n| --- | --- |\n| `status` | See the folder description. |\n| `ended_reason` | Why it ended — `customer-ended-call`, `assistant-ended-call`, `customer-did-not-answer`, `exceeded-max-duration`, `voicemail`, … `null` while running. Treat as an open vocabulary. |\n| `duration_secs` | Seconds of live audio. `null` until the call ends. |\n| `cost_inr` | Rupees charged. `null` means nothing was charged. |\n| `started_at` | Set when audio goes live, not when we dialled. `null` if never answered. |\n| `recording_available` | `true` once the call's audio is stored. Fetch it from **Get recording**. |\n| `transcript_available` | `true` once the call produced speech. Fetch it from **Get transcript**. |\n| `analysis` | `{summary, success, data}` — only for agents created with `analysis.enabled: true`. `null` otherwise, and `null` while the call is still running. |\n\nThe two `*_available` booleans exist so you do not have to fetch a transcript to find out whether there is one. Both are `false` until the call ends."
          },
          "response": [
            {
              "name": "200 OK — completed",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"object\": \"call\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"to\": \"+919876543210\",\n  \"status\": \"completed\",\n  \"tier\": \"t3\",\n  \"ended_reason\": \"customer-ended-call\",\n  \"duration_secs\": 96,\n  \"cost_inr\": 6,\n  \"created_at\": \"2026-08-10T09:15:00Z\",\n  \"started_at\": \"2026-08-10T09:15:02Z\",\n  \"ended_at\": \"2026-08-10T09:16:38Z\",\n  \"recording_available\": true,\n  \"transcript_available\": true,\n  \"analysis\": {\n    \"summary\": \"Rohit ne order AC-88213 ki delivery confirm kar di. Kal shaam 6 baje ka slot theek hai, thoda late ho to bhi chalega. Koi complaint nahi.\",\n    \"success\": true,\n    \"data\": {\n      \"slot_confirmed\": \"कल शाम 6 बजे\",\n      \"address\": null\n    }\n  }\n}"
            },
            {
              "name": "200 OK — no answer",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YB",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YB"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YB\",\n  \"object\": \"call\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"to\": \"+919876543211\",\n  \"status\": \"no_answer\",\n  \"tier\": \"t3\",\n  \"ended_reason\": \"customer-did-not-answer\",\n  \"duration_secs\": 0,\n  \"cost_inr\": null,\n  \"created_at\": \"2026-08-10T09:20:01Z\",\n  \"started_at\": null,\n  \"ended_at\": \"2026-08-10T09:22:09Z\",\n  \"recording_available\": false,\n  \"transcript_available\": false,\n  \"analysis\": null\n}"
            },
            {
              "name": "404 Not found",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1Y0",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1Y0"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"No such call.\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Get transcript",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "What was said, as turns, once the call has ended.\n\n| Field | Notes |\n| --- | --- |\n| `role` | `assistant` or `user`. Nothing else appears — internal notes and tool steps are not conversation. |\n| `text` | The turn as spoken. Hindi, Hinglish and English arrive as they were said; nothing is translated. |\n| `start_ms`, `end_ms` | Milliseconds from the start of the **recording**, so a turn lines up with the audio from **Get recording**. `end_ms` can be `null` on the last turn of a call that was cut off. |\n\n**404 while the call is still running**, and **404 for a call that produced no speech at all** (nobody answered, or the line was silent). Check `transcript_available` on the call first if you want to tell those apart from a mistyped id.\n\nTranscripts are kept for 90 days, the same as the call record itself."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA/transcript",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                    "transcript"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"call_id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"turns\": [\n    {\n      \"role\": \"assistant\",\n      \"text\": \"नमस्ते Rohit जी, मैं Acme Retail से Priya बोल रही हूँ। एक मिनट बात कर सकती हूँ?\",\n      \"start_ms\": 420,\n      \"end_ms\": 4980\n    },\n    {\n      \"role\": \"user\",\n      \"text\": \"हाँ बोलिए\",\n      \"start_ms\": 5640,\n      \"end_ms\": 6390\n    },\n    {\n      \"role\": \"assistant\",\n      \"text\": \"आपका order AC-88213 kal shaam 6 baje deliver hoga, Flat 402 Sunrise Apartments Andheri West. Ye slot theek hai?\",\n      \"start_ms\": 7010,\n      \"end_ms\": 14880\n    },\n    {\n      \"role\": \"user\",\n      \"text\": \"haan theek hai, lekin thoda late aaye to chalega\",\n      \"start_ms\": 15900,\n      \"end_ms\": 19240\n    },\n    {\n      \"role\": \"assistant\",\n      \"text\": \"बिल्कुल, हम delivery partner ko note kar denge. Aur koi help chahiye?\",\n      \"start_ms\": 19960,\n      \"end_ms\": 24110\n    },\n    {\n      \"role\": \"user\",\n      \"text\": \"nahi bas itna hi, thank you\",\n      \"start_ms\": 25020,\n      \"end_ms\": 26980\n    },\n    {\n      \"role\": \"assistant\",\n      \"text\": \"धन्यवाद Rohit जी, आपका दिन शुभ हो।\",\n      \"start_ms\": 27600,\n      \"end_ms\": 30640\n    }\n  ]\n}"
            },
            {
              "name": "404 Not available yet",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YB/transcript",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YB",
                    "transcript"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"transcript not available (call still in progress or produced no speech)\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"200 OK\", () => pm.response.to.have.status(200));",
                  "const t = pm.response.json();",
                  "console.log(t.turns.length, \"turns\");",
                  "t.turns.forEach((x) => console.log(`${x.start_ms}ms ${x.role}: ${x.text}`));"
                ]
              }
            }
          ]
        },
        {
          "name": "Get recording",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls/{{call_id}}/recording",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls",
                "{{call_id}}",
                "recording"
              ]
            },
            "description": "**302** to a signed, time-limited URL for the call's audio (WAV). Follow the redirect — most HTTP clients do by default; `curl` needs `-L`.\n\nThe signed link is valid for **15 minutes**. It is a way to fetch this recording now, not a permanent URL: do not store it, store the `call_id` and ask again. Anyone holding the link can fetch the audio until it expires, so treat it as you would the recording itself.\n\n**404** while the call is running, for a call that produced no audio, and for a call whose audio was never stored. `recording_available` on the call object tells you which requests are worth making."
          },
          "response": [
            {
              "name": "302 Found",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA/recording",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                    "recording"
                  ]
                }
              },
              "status": "Found",
              "code": 302,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "Location",
                  "value": "https://storage.miraiminds.co/voice-agents/production/call_records/call_01K7Q9B4M8N3P6R2S5T7V9W1YA/call.wav?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=…"
                }
              ],
              "cookie": [],
              "body": ""
            },
            {
              "name": "404 No recording",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YB/recording",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YB",
                    "recording"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"recording not available (call still in progress, or it produced no audio)\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"302 or 200 after following the redirect\", () =>",
                  "  pm.expect([200, 302]).to.include(pm.response.code));",
                  "const loc = pm.response.headers.get(\"Location\");",
                  "if (loc) console.log(\"signed for 15 minutes:\", loc.split(\"?\")[0]);"
                ]
              }
            }
          ]
        },
        {
          "name": "Abort call",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/calls/{{call_id}}/abort",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "calls",
                "{{call_id}}",
                "abort"
              ]
            },
            "description": "Cancels a call that is `queued`, `dialing` or `in_progress`. **202 Accepted**, then the call settles as `aborted`.\n\nA call aborted before it was answered is not billed. One that had already connected is billed for the time it ran.\n\nAborting a call that has already ended answers **409 conflict** — read `status` first."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA/abort",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                    "abort"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n  \"status\": \"aborted\"\n}"
            },
            {
              "name": "409 Already ended",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/calls/call_01K7Q9B4M8N3P6R2S5T7V9W1YA/abort",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "calls",
                    "call_01K7Q9B4M8N3P6R2S5T7V9W1YA",
                    "abort"
                  ]
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"Call is already completed.\"\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Campaigns",
      "description": "A campaign dials a contact list for you: it respects a calling window, paces\nitself to a concurrency you choose, retries the ones that did not connect, skips\nanything on your do-not-call list, and stops at a budget.\n\n**Lifecycle.** `draft` → `play` → (`paused` ⇄ `play`) → `completed` or\n`stopped`. Only `play` dials. `stopped` and `completed` are terminal.\n\n**Editing.** `PATCH` works on `draft` and `paused` campaigns only — pause first\nif you need to change a running one. New contacts can be added to a running\ncampaign; a `stopped` or `completed` one refuses them, because nothing would\never dial them.\n\n**Money is in paise.** 100 paise = ₹1. `budget_paise: 50000` is a ₹500 cap;\n`spent_paise` is what has actually been billed. Omit `budget_paise` for no cap —\nyour wallet is still the hard limit.\n\n**Bounds** (rejected with `400 invalid_request` outside these):\n\n| Field | Range | Default |\n| --- | --- | --- |\n| `name` | 1–120 characters | — |\n| `slots` | 1–4 windows, `HH:MM` | — |\n| `max_concurrent` | 1–50 | 1 |\n| `retry_count` | 0–5 (retries after the first attempt) | 0 |\n| `re_attempt_period_secs` | 60–86400 | 900 |\n| `max_duration_secs` | 30–1800 | the agent's value |\n| `contacts` per request | up to 1000 rows | — |\n\n**`503 campaigns_unavailable`** means campaigns are not switched on for this\nworkspace yet — ask your account contact, it is a provisioning step, not a bug in\nyour request.",
      "item": [
        {
          "name": "Create campaign",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/campaigns",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns"
              ]
            },
            "description": "Creates a campaign in `draft` and loads its first contacts in the same request. **201 Created**.\n\nRequired: `name`, `agent_id`, `timezone` (IANA, e.g. `Asia/Kolkata`), `start_date`, `end_date` (`YYYY-MM-DD`, inclusive, local to `timezone`), `slots`.\n\n**The response is the campaign object itself** — every field `GET /v2/campaigns/{id}` returns, at the top level — plus `contacts_accepted`, `contacts_duplicate` and `contacts_rejected`. There is no wrapper to unwrap.\n\n**Bad rows do not fail the batch.** `contacts_rejected` is always an array (never `null`) and names each bad row by its **index** in your list, with the `reason` it was refused, so you can fix them without diffing your own file. A request only fails outright if it carries more than 1000 rows.\n\nContact `id` is yours — pass your own CRM id and it comes back in the report. Omit it and one is assigned."
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"draft\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 0,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T09:21:44Z\",\n  \"contacts_accepted\": 5,\n  \"contacts_duplicate\": 0,\n  \"contacts_rejected\": []\n}"
            },
            {
              "name": "201 Created — two rows rejected",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"draft\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 0,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T09:21:44Z\",\n  \"contacts_accepted\": 3,\n  \"contacts_duplicate\": 1,\n  \"contacts_rejected\": [\n    {\n      \"index\": 1,\n      \"phone\": \"9876543211\",\n      \"reason\": \"phone must be an E.164 number\"\n    },\n    {\n      \"index\": 4,\n      \"phone\": \"+91984500112\",\n      \"reason\": \"phone must be an E.164 number\"\n    }\n  ]\n}"
            },
            {
              "name": "404 Unknown agent",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0X0\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"no agent agt_01K7Q9F3K7M2N5P9R4T6V8W0X0\"\n  }\n}"
            },
            {
              "name": "400 Bad calling window",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"10:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"invalid_request\",\n    \"message\": \"slots[0].start and .end are both \\\"10:00\\\"; a slot must have a length\"\n  }\n}"
            },
            {
              "name": "503 Campaigns not enabled",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"{{agent_id}}\",\n  \"tier\": \"t3\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"{{start_date}}\",\n  \"end_date\": \"{{end_date}}\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"contacts\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      }\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      }\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      }\n    },\n    {\n      \"id\": \"cust-1004\",\n      \"phone\": \"+919833221100\",\n      \"variables\": {\n        \"customer_name\": \"Meera Nair\",\n        \"order_id\": \"AC-88231\"\n      }\n    },\n    {\n      \"id\": \"cust-1005\",\n      \"phone\": \"+919845001122\",\n      \"variables\": {\n        \"customer_name\": \"Imran Qureshi\",\n        \"order_id\": \"AC-88240\"\n      }\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ]
                }
              },
              "status": "Service Unavailable",
              "code": 503,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"campaigns_unavailable\",\n    \"message\": \"campaigns are not enabled on this deployment (no campaign database is configured)\"\n  }\n}"
            }
          ],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const pad = (n) => String(n).padStart(2, \"0\");",
                  "const fmt = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;",
                  "const today = new Date();",
                  "pm.collectionVariables.set(\"start_date\", fmt(today));",
                  "pm.collectionVariables.set(\"end_date\", fmt(new Date(today.getTime() + 7 * 864e5)));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"201 Created\", () => pm.response.to.have.status(201));",
                  "pm.collectionVariables.set(\"campaign_id\", pm.response.json().id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List campaigns",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–100, default 20."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "Newest first, `{data, has_more, next_cursor}`."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "20"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n      \"object\": \"campaign\",\n      \"name\": \"Order confirmations — Mumbai batch\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"tier\": \"t3\",\n      \"status\": \"play\",\n      \"pause_reason\": null,\n      \"timezone\": \"Asia/Kolkata\",\n      \"start_date\": \"2026-08-11\",\n      \"end_date\": \"2026-08-18\",\n      \"slots\": [\n        {\n          \"start\": \"10:00\",\n          \"end\": \"19:00\"\n        }\n      ],\n      \"max_concurrent\": 2,\n      \"retry_count\": 1,\n      \"re_attempt_period_secs\": 900,\n      \"max_duration_secs\": 300,\n      \"budget_paise\": 50000,\n      \"spent_paise\": 900,\n      \"webhook_url\": \"https://example.com/mirai/webhook\",\n      \"created_at\": \"2026-08-10T09:21:44Z\",\n      \"updated_at\": \"2026-08-10T10:04:12Z\",\n      \"counters\": {\n        \"pending\": 2,\n        \"dialing\": 1,\n        \"completed\": 2\n      }\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ]
        },
        {
          "name": "Get campaign",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}"
              ]
            },
            "description": "The campaign plus `counters` — contacts grouped by status. This is the poll target while a campaign runs.\n\n`pause_reason` is `null` when a human paused it, and names the cause when the platform did: `insufficient_balance` (wallet ran dry) or `budget_exhausted` (`budget_paise` reached)."
          },
          "response": [
            {
              "name": "200 OK — running",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"play\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 900,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T10:04:12Z\",\n  \"counters\": {\n    \"pending\": 2,\n    \"dialing\": 1,\n    \"completed\": 2\n  }\n}"
            },
            {
              "name": "200 OK — paused, out of balance",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"paused\",\n  \"pause_reason\": \"insufficient_balance\",\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 900,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T10:04:12Z\",\n  \"counters\": {\n    \"pending\": 2,\n    \"completed\": 3\n  }\n}"
            },
            {
              "name": "404 Not found",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4Y0",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4Y0"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"no campaign cmp_01K7QAC5N9P4R7S3T6V8W2X4Y0\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Update campaign",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Order confirmations — Mumbai batch (evening)\",\n  \"slots\": [\n    {\n      \"start\": \"17:00\",\n      \"end\": \"20:00\"\n    }\n  ],\n  \"max_concurrent\": 5,\n  \"retry_count\": 2,\n  \"budget_paise\": 100000\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}"
              ]
            },
            "description": "Send only what you are changing. **Draft or paused only** — editing a running campaign answers **409 conflict**, because its own report would otherwise describe settings that were never in force for half its calls. Pause, edit, resume."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"max_concurrent\": 5,\n  \"budget_paise\": 100000\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"paused\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 5,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 100000,\n  \"spent_paise\": 0,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-11T06:02:10Z\"\n}"
            },
            {
              "name": "409 Campaign is running",
              "originalRequest": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"max_concurrent\": 5\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB"
                  ]
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"campaign cmp_01K7QAC5N9P4R7S3T6V8W2X4YB is play; only a draft or paused campaign can be edited\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Add contacts",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"contacts\": [\n    {\n      \"id\": \"cust-1006\",\n      \"phone\": \"+919876500001\",\n      \"variables\": {\n        \"customer_name\": \"Sneha Kulkarni\",\n        \"order_id\": \"AC-88255\"\n      }\n    },\n    {\n      \"id\": \"cust-1007\",\n      \"phone\": \"+919876500002\",\n      \"variables\": {\n        \"customer_name\": \"Arjun Menon\",\n        \"order_id\": \"AC-88261\"\n      }\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/contacts",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "contacts"
              ]
            },
            "description": "Adds rows to a `draft`, `play` or `paused` campaign — up to **1000 per request**. **200 OK** with exactly the body create answers: the campaign object at the top level, plus `contacts_accepted`, `contacts_duplicate` and `contacts_rejected`.\n\nA number already on the campaign counts toward `contacts_duplicate`, not an error. A `stopped` or `completed` campaign answers **409** rather than accepting rows nothing will ever dial."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"contacts\": [\n    {\n      \"id\": \"cust-1006\",\n      \"phone\": \"+919876500001\"\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/contacts",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "contacts"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"draft\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 0,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T09:21:44Z\",\n  \"contacts_accepted\": 2,\n  \"contacts_duplicate\": 0,\n  \"contacts_rejected\": []\n}"
            },
            {
              "name": "200 OK — one duplicate, one rejected",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"contacts\": [\n    {\n      \"phone\": \"+919876543210\"\n    },\n    {\n      \"phone\": \"98765\"\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/contacts",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "contacts"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"object\": \"campaign\",\n  \"name\": \"Order confirmations — Mumbai batch\",\n  \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n  \"tier\": \"t3\",\n  \"status\": \"draft\",\n  \"pause_reason\": null,\n  \"timezone\": \"Asia/Kolkata\",\n  \"start_date\": \"2026-08-11\",\n  \"end_date\": \"2026-08-18\",\n  \"slots\": [\n    {\n      \"start\": \"10:00\",\n      \"end\": \"19:00\"\n    }\n  ],\n  \"max_concurrent\": 2,\n  \"retry_count\": 1,\n  \"re_attempt_period_secs\": 900,\n  \"max_duration_secs\": 300,\n  \"budget_paise\": 50000,\n  \"spent_paise\": 0,\n  \"webhook_url\": \"https://example.com/mirai/webhook\",\n  \"created_at\": \"2026-08-10T09:21:44Z\",\n  \"updated_at\": \"2026-08-10T09:21:44Z\",\n  \"contacts_accepted\": 0,\n  \"contacts_duplicate\": 1,\n  \"contacts_rejected\": [\n    {\n      \"index\": 1,\n      \"phone\": \"98765\",\n      \"reason\": \"phone must be an E.164 number\"\n    }\n  ]\n}"
            },
            {
              "name": "409 Campaign finished",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"contacts\": [\n    {\n      \"phone\": \"+919876500001\"\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/contacts",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "contacts"
                  ]
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"campaign cmp_01K7QAC5N9P4R7S3T6V8W2X4YB is completed; it will never dial new contacts\"\n  }\n}"
            }
          ]
        },
        {
          "name": "List contacts",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/contacts",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "contacts"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–100, default 20."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From the previous page.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "no_answer",
                  "description": "Filter by contact status.",
                  "disabled": true
                }
              ]
            },
            "description": "One row per contact.\n\n| Status | Meaning |\n| --- | --- |\n| `pending` | Waiting for a slot. |\n| `claimed` / `dialing` | Being dialled now. |\n| `completed` `voicemail` `timeout` `no_answer` `busy` `failed` | The last call's outcome. |\n| `exhausted` | Retried up to `retry_count` and never connected. |\n| `suppressed` | On your do-not-call list. |\n| `aborted` | The campaign was stopped while this contact was in flight. |\n\n`last_call_id` joins the row to `GET /v2/calls/{id}` for the transcript-level detail."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/contacts",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "contacts"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "20"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"cust-1001\",\n      \"phone\": \"+919876543210\",\n      \"status\": \"completed\",\n      \"attempts\": 1,\n      \"variables\": {\n        \"customer_name\": \"Rohit Sharma\",\n        \"order_id\": \"AC-88213\"\n      },\n      \"last_call_id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n      \"last_status\": \"completed\",\n      \"eligible_at\": \"2026-08-11T04:30:00Z\",\n      \"updated_at\": \"2026-08-11T05:12:44Z\"\n    },\n    {\n      \"id\": \"cust-1002\",\n      \"phone\": \"+919876543211\",\n      \"status\": \"no_answer\",\n      \"attempts\": 2,\n      \"variables\": {\n        \"customer_name\": \"Anita Desai\",\n        \"order_id\": \"AC-88219\"\n      },\n      \"last_call_id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YB\",\n      \"last_status\": \"no_answer\",\n      \"eligible_at\": \"2026-08-11T04:30:00Z\",\n      \"updated_at\": \"2026-08-11T05:12:44Z\"\n    },\n    {\n      \"id\": \"cust-1003\",\n      \"phone\": \"+919812345678\",\n      \"status\": \"pending\",\n      \"attempts\": 0,\n      \"variables\": {\n        \"customer_name\": \"Vikram Rao\",\n        \"order_id\": \"AC-88224\"\n      },\n      \"eligible_at\": \"2026-08-11T04:30:00Z\",\n      \"updated_at\": \"2026-08-11T05:12:44Z\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ]
        },
        {
          "name": "Start campaign",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/start",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "start"
              ]
            },
            "description": "Starts (or resumes) dialling. **202 Accepted** with `{id, status: \"play\"}`.\n\nBefore starting, the platform checks that speech capacity is online and estimates whether your wallet covers the remaining contacts. If it does not, the campaign still starts and the response adds `funded_calls` — the number of calls the current balance pays for. It dials those and pauses itself with `pause_reason: \"insufficient_balance\"`; top up and start it again.\n\nStarting a campaign that is already `play`, `stopped` or `completed` answers **409**."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "start"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"play\"\n}"
            },
            {
              "name": "202 Accepted — partially funded",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "start"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"play\",\n  \"funded_calls\": 3\n}"
            },
            {
              "name": "409 Already running",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "start"
                  ]
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"campaign cmp_01K7QAC5N9P4R7S3T6V8W2X4YB is already play\"\n  }\n}"
            },
            {
              "name": "503 No capacity online",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "start"
                  ]
                }
              },
              "status": "Service Unavailable",
              "code": 503,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "Retry-After",
                  "value": "300"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"fleet_offline\",\n    \"message\": \"no speech nodes are online right now; the fleet is starting or stopped\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Pause campaign",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/pause",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "pause"
              ]
            },
            "description": "Stops dialling new contacts; calls already connected finish. **202 Accepted**. Only a `play` campaign can be paused. A paused campaign can be edited, then resumed."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/pause",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "pause"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"paused\"\n}"
            },
            {
              "name": "409 Not running",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/pause",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "pause"
                  ]
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"campaign cmp_01K7QAC5N9P4R7S3T6V8W2X4YB is draft and cannot be paused\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Resume campaign",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/resume",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "resume"
              ]
            },
            "description": "Picks a paused campaign back up. **202 Accepted**.\n\nIf the campaign has been paused long enough that nothing is holding its place, resume answers **409** and names the fix: call `/start` instead, which re-runs the wallet and capacity checks a bare resume would skip."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/resume",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "resume"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"play\"\n}"
            },
            {
              "name": "409 Use start instead",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/resume",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "resume"
                  ]
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"campaign cmp_01K7QAC5N9P4R7S3T6V8W2X4YB has no running dialer; POST /v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/start to resume it\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Stop campaign",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/stop",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "stop"
              ]
            },
            "description": "Ends the campaign for good. **202 Accepted**. Works from `draft`, `play` or `paused`; `stopped` is terminal and the campaign cannot be restarted or given new contacts. Use pause if you might want it back."
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/stop",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "stop"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"stopped\"\n}"
            }
          ]
        },
        {
          "name": "Campaign report",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/report",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "report"
              ]
            },
            "description": "The roll-up.\n\n| Field | Meaning |\n| --- | --- |\n| `contacts` | Rows in the campaign. |\n| `dialed` | Contacts with at least one attempt. |\n| `attempts` | Total dial attempts, retries included. |\n| `connected` | Reached a person or a machine (`completed` + `voicemail` + `timeout`). |\n| `spent_paise` | Billed so far, in paise (100 paise = ₹1). |\n| `spent_inr` | The same amount in rupees. |\n| `budget_paise` | Your cap, or `null` if uncapped. |"
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/report",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "report"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"campaign_id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n  \"status\": \"completed\",\n  \"counters\": {\n    \"completed\": 3,\n    \"voicemail\": 1,\n    \"no_answer\": 1\n  },\n  \"contacts\": 5,\n  \"dialed\": 5,\n  \"attempts\": 6,\n  \"connected\": 4,\n  \"spent_paise\": 2100,\n  \"spent_inr\": 21,\n  \"budget_paise\": 50000\n}"
            }
          ]
        },
        {
          "name": "Campaign report (CSV)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/campaigns/{{campaign_id}}/report",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "campaigns",
                "{{campaign_id}}",
                "report"
              ],
              "query": [
                {
                  "key": "format",
                  "value": "csv",
                  "description": "Returns text/csv instead of JSON."
                }
              ]
            },
            "description": "One row per contact, streamed as a download named `<campaign_id>.csv`.\n\nColumns: `id`, `phone`, `status`, `attempts`, `last_call_id`. `id` is your own contact id when you supplied one, so this joins straight back to your CRM."
          },
          "response": [
            {
              "name": "200 OK — CSV",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/campaigns/cmp_01K7QAC5N9P4R7S3T6V8W2X4YB/report",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "campaigns",
                    "cmp_01K7QAC5N9P4R7S3T6V8W2X4YB",
                    "report"
                  ],
                  "query": [
                    {
                      "key": "format",
                      "value": "csv"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "text",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "text/csv; charset=utf-8"
                },
                {
                  "key": "Content-Disposition",
                  "value": "attachment; filename=\"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB.csv\""
                }
              ],
              "cookie": [],
              "body": "id,phone,status,attempts,last_call_id\ncust-1001,+919876543210,completed,1,call_01K7Q9B4M8N3P6R2S5T7V9W1YA\ncust-1002,+919876543211,no_answer,2,call_01K7Q9B4M8N3P6R2S5T7V9W1YB\ncust-1003,+919812345678,voicemail,1,call_01K7Q9B4M8N3P6R2S5T7V9W1YC\ncust-1004,+919833221100,suppressed,0,\ncust-1005,+919845001122,exhausted,3,call_01K7Q9B4M8N3P6R2S5T7V9W1YD\n"
            }
          ]
        }
      ]
    },
    {
      "name": "DNC",
      "description": "Your workspace's do-not-call list. Any number on it is skipped by every campaign\nin the workspace and the contact is marked `suppressed` in the report.\n\nThis is **your** suppression list, not a national registry. Scrubbing against the\nnational DND/NCPR list, keeping consent records and honouring an opt-out spoken\non a call remain your obligations as the business placing the calls.\n\nAdd a number the moment someone opts out — during a call, by SMS, by email,\nanywhere.",
      "item": [
        {
          "name": "Add number",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"phone\": \"{{test_phone}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/v2/dnc",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "dnc"
              ]
            },
            "description": "**201 Created** the first time (`added: true`), **200 OK** if it was already there (`added: false`). Adding the same number twice is not an error — you got what you asked for.\n\nE.164 only: `+919876543210`."
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"phone\": \"+919876543210\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/dnc",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "dnc"
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"object\": \"dnc_entry\",\n  \"phone\": \"+919876543210\",\n  \"added\": true\n}"
            },
            {
              "name": "200 OK — already listed",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"phone\": \"+919876543210\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/dnc",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "dnc"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"object\": \"dnc_entry\",\n  \"phone\": \"+919876543210\",\n  \"added\": false\n}"
            },
            {
              "name": "400 Not E.164",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"phone\": \"9876543210\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{base_url}}/v2/dnc",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "dnc"
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"invalid_request\",\n    \"message\": \"phone must be an E.164 phone number, got '9876543210'\"\n  }\n}"
            }
          ]
        },
        {
          "name": "List numbers",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/dnc",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "dnc"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–100, default 20."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "Newest first, `{data, has_more, next_cursor}`."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/dnc",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "dnc"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "20"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"object\": \"dnc_entry\",\n      \"phone\": \"+919876543210\",\n      \"created_at\": \"2026-08-10T12:01:09Z\"\n    },\n    {\n      \"object\": \"dnc_entry\",\n      \"phone\": \"+919833221100\",\n      \"created_at\": \"2026-08-09T18:44:02Z\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ]
        },
        {
          "name": "Remove number",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/dnc/{{test_phone}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "dnc",
                "{{test_phone}}"
              ]
            },
            "description": "**204 No Content** on removal, **404** if the number was not listed. URL-encode the leading `+` as `%2B` if your client does not do it for you.\n\nRemoving a number means you have fresh consent to call it. Keep the record."
          },
          "response": [
            {
              "name": "204 No Content",
              "originalRequest": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/dnc/+919876543210",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "dnc",
                    "+919876543210"
                  ]
                }
              },
              "status": "No Content",
              "code": 204,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "Content-Length",
                  "value": "0"
                }
              ],
              "cookie": [],
              "body": ""
            },
            {
              "name": "404 Not listed",
              "originalRequest": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/dnc/+919000000000",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "dnc",
                    "+919000000000"
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"error\": {\n    \"code\": \"not_found\",\n    \"message\": \"+919000000000 is not on the do-not-call list\"\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Wallet",
      "description": "Voice Infra is prepaid: one INR wallet per workspace. Calls debit it as they end;\ntop-ups are applied by your account contact.\n\n**Units.** The wallet and the call ledger are in **rupees** (`balance_inr`,\n`amount_inr`, `cost_inr`). Campaign budgets and campaign spend are in **paise**\n(`budget_paise`, `spent_paise`) — 100 paise = ₹1. Nothing in the API mixes the\ntwo inside one object.\n\n**Rate.** Tier `t3` — the default — bills ₹3 per minute (300 paise); tier\n`t1` bills ₹1 per minute (100 paise). Both round up, with a one-minute\nminimum. Calls that never connect are not billed at all.",
      "item": [
        {
          "name": "Get balance",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/wallet",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "wallet"
              ]
            },
            "description": "`balance_inr` is spendable rupees and is never negative. `updated_at` is when it last moved.\n\nA call reserves one minute at accept time, so the balance drops the moment you place a call and is reconciled — refunded or topped up to the real cost — when the call ends."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/wallet",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "wallet"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"balance_inr\": 494,\n  \"currency\": \"INR\",\n  \"updated_at\": \"2026-08-10T09:16:38Z\"\n}"
            }
          ]
        },
        {
          "name": "List transactions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v2/wallet/transactions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v2",
                "wallet",
                "transactions"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50",
                  "description": "1–100, default 20."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "The ledger, newest first. One row per credit or debit.\n\n| Field | Meaning |\n| --- | --- |\n| `type` | `credit` (top-up) or `debit` (a call). |\n| `amount_inr` | Rupees moved. |\n| `balance_after_inr` | Balance once this row was applied. |\n| `call_id` | The call this row paid for, or `null` for a top-up. |\n\nReconcile by summing `amount_inr` where `type` is `debit` — not by differencing balances."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/v2/wallet/transactions",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v2",
                    "wallet",
                    "transactions"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "50"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"txn_01K7QAD6P1Q5R8T4V7W9X3Y5ZD\",\n      \"object\": \"wallet_transaction\",\n      \"type\": \"debit\",\n      \"amount_inr\": 6,\n      \"balance_after_inr\": 494,\n      \"call_id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n      \"description\": \"call call_01K7Q9B4M8N3P6R2S5T7V9W1YA (2 min, t3)\",\n      \"created_at\": \"2026-08-10T09:16:38Z\"\n    },\n    {\n      \"id\": \"txn_01K7QAD6P1Q5R8T4V7W9X3Y5ZC\",\n      \"object\": \"wallet_transaction\",\n      \"type\": \"credit\",\n      \"amount_inr\": 500,\n      \"balance_after_inr\": 500,\n      \"call_id\": null,\n      \"description\": \"top-up\",\n      \"created_at\": \"2026-08-10T08:00:00Z\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Webhooks",
      "description": "Webhooks are how you learn what happened. Polling is the fallback, not the design.\n\nPass `webhook_url` when you create a **call** or a **campaign** and we POST a\nsigned JSON event at each lifecycle transition. Your endpoint must be public\nHTTPS and must return any `2xx` quickly.\n\n**Nothing in this folder calls the Voice Infra API** — these items document the\ncontract and let you fire a correctly signed test delivery at your own endpoint.\n\n## Event types\n\nCall events (`webhook_url` on `POST /v2/calls`):\n\n| Type | Fires when | `data.call.status` |\n| --- | --- | --- |\n| `call.queued` | Accepted, waiting to dial. | `queued` |\n| `call.started` | Audio is live, the conversation has begun. | `in_progress` |\n| `call.completed` | Connected and finished normally. | `completed` |\n| `call.voicemail` | An answering machine picked up. | `voicemail` |\n| `call.failed` | Did not connect, or the call errored. | `no_answer`, `busy`, `failed` |\n| `call.aborted` | You cancelled it. | `aborted` |\n\nExactly one terminal call event fires per call.\n\nCampaign events (`webhook_url` on `POST /v2/campaigns`):\n\n| Type | Fires when |\n| --- | --- |\n| `campaign.started` | The campaign began dialling. |\n| `campaign.paused` | Paused — by you, or by the platform (see `pause_reason`). |\n| `campaign.resumed` | Dialling picked back up. |\n| `campaign.completed` | Every contact reached a final state. |\n| `campaign.stopped` | You ended it. |\n\nCampaign events are **one per lifecycle transition, not one per call**. A 50,000-\ncontact campaign that posted an event per call would flood your own endpoint; the\nper-call events are already available on the call webhook if you want them.\n\n## Envelope\n\nBoth families share one envelope — `{id, type, created_at, data}`. Call events\ncarry `data.call`; campaign events carry `data.campaign`.\n\nTreat the envelope as additive: new fields appear without a version bump, so\nignore what you do not recognise, and always have a default branch on `type`.\n\n## Signature\n\nEvery delivery carries this header:\n\n```\nX-Mirai-Signature: t=1786440000,v1=771bca27e1647dfbe59ff5d8f32d9de69be246f6e604c05bddcbaefd8604ccb3\n```\n\n- `t` — Unix seconds at signing time.\n- `v1` — lowercase hex **HMAC-SHA256** over the exact string `\"<t>\" + \".\" + <raw request body>`, keyed with your workspace's `whsec_…` secret (issued with your API key).\n\nVerify in four steps:\n\n1. Parse `t` and `v1` out of the header.\n2. Reject if `|now − t| > 300` seconds.\n3. Recompute the HMAC over `t + \".\" + raw_body`.\n4. Compare in **constant time**.\n\n```javascript\nimport crypto from \"node:crypto\";\n\nexport function verify(rawBody, header, secret) {\n  const parts = Object.fromEntries(\n    header.split(\",\").map((p) => {\n      const i = p.indexOf(\"=\");\n      return [p.slice(0, i).trim(), p.slice(i + 1).trim()];\n    })\n  );\n  const { t, v1 } = parts;\n  if (!t || !v1) return false;\n  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;   // replay window\n\n  const expected = crypto\n    .createHmac(\"sha256\", secret)\n    .update(`${t}.`)\n    .update(rawBody)          // the RAW bytes, never a re-serialised object\n    .digest(\"hex\");\n\n  const a = Buffer.from(expected, \"utf8\");\n  const b = Buffer.from(v1, \"utf8\");\n  return a.length === b.length && crypto.timingSafeEqual(a, b);\n}\n```\n\n```python\nimport hashlib, hmac, time\n\ndef verify(raw_body: bytes, header: str, secret: str) -> bool:\n    parts = dict(p.split(\"=\", 1) for p in header.split(\",\"))\n    t, v1 = parts.get(\"t\"), parts.get(\"v1\")\n    if not t or not v1:\n        return False\n    if abs(time.time() - int(t)) > 300:          # replay window\n        return False\n    expected = hmac.new(secret.encode(), f\"{t}.\".encode() + raw_body,\n                        hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, v1)\n```\n\n**The single most common integration bug** is signing a re-serialised body. Capture\nthe raw bytes before any JSON parser touches them: `express.raw({ type: \"application/json\" })`\nin Express, `request.body` in Django, `request.raw_post` in Rails.\n\n## Retries, ordering and dead-letter\n\n- A delivery succeeds on any `2xx`. Anything else — `4xx`, `5xx`, timeout, TLS or DNS failure — is retried: **six attempts with exponential backoff over roughly 36 minutes**, the first immediately, gaps widening to a half-hour cap.\n- After the sixth attempt the event goes to a **dead-letter queue**. It can be replayed for you on request: a replayed event is byte-identical, **including its `id`**, and carries a fresh `t`/`v1` so it still passes your replay window.\n- **Order is not guaranteed.** A retried `call.started` can arrive after `call.completed`. Drive your state machine off `data.call.status`, not off arrival order.\n- **Slow endpoints are retried.** If your handler is slower than our client timeout we treat it as a failure. Acknowledge first, do the work after.\n- **Assume at-least-once delivery.** Dedupe on the event `id` with a TTL longer than the retry window (24 hours is comfortable), or make your handler naturally idempotent.\n- A `401` from your own signature check counts as a retryable failure to us — deliberately, so a transient secret-loading bug on your side does not lose the event.\n\n## Checklist\n\n- [ ] Public HTTPS endpoint that returns 2xx in well under a second.\n- [ ] Signature verified against the **raw** body, in constant time.\n- [ ] Replay window enforced at 300 seconds.\n- [ ] Events deduped on `id`.\n- [ ] Unknown `type` values ignored, not crashed on.\n- [ ] No slow work inline — enqueue and return.",
      "item": [
        {
          "name": "Send a signed test event to your endpoint (call.completed)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "X-Mirai-Signature",
                "value": "{{webhook_signature}}"
              },
              {
                "key": "User-Agent",
                "value": "MiraiVoice-Webhooks/2"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01K7QAE7Q3S6T9V5W8X1Y4Z6AC\",\n  \"type\": \"call.completed\",\n  \"created_at\": \"2026-08-10T09:16:38Z\",\n  \"data\": {\n    \"call\": {\n      \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n      \"object\": \"call\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"to\": \"+919876543210\",\n      \"status\": \"completed\",\n      \"tier\": \"t3\",\n      \"ended_reason\": \"customer-ended-call\",\n      \"duration_secs\": 96,\n      \"cost_inr\": 6,\n      \"created_at\": \"2026-08-10T09:15:00Z\",\n      \"started_at\": \"2026-08-10T09:15:02Z\",\n      \"ended_at\": \"2026-08-10T09:16:38Z\",\n      \"recording_available\": true,\n      \"transcript_available\": true,\n      \"analysis\": {\n        \"summary\": \"Rohit ne order AC-88213 ki delivery confirm kar di. Kal shaam 6 baje ka slot theek hai, thoda late ho to bhi chalega. Koi complaint nahi.\",\n        \"success\": true,\n        \"data\": {\n          \"slot_confirmed\": \"कल शाम 6 बजे\",\n          \"address\": null\n        }\n      }\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{webhook_endpoint}}",
              "host": [
                "{{webhook_endpoint}}"
              ]
            },
            "description": "Fires a **correctly signed** sample `call.completed` delivery at your own endpoint, so you can test your receiver before burning wallet balance on a real call.\n\nSet two environment variables first:\n\n- `webhook_endpoint` — your receiver, e.g. `https://example.com/mirai/webhook`\n- `webhook_secret` — the `whsec_…` issued with your API key\n\nThe pre-request script signs the exact body bytes the way the platform does — `HMAC-SHA256(secret, \"<t>.<raw body>\")` — and puts the header value in `{{webhook_signature}}`. If your handler accepts this, it will accept the real thing.\n\nThis request does **not** touch the Voice Infra API and sends no credentials.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": [
            {
              "name": "200 OK — your endpoint accepted it",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "X-Mirai-Signature",
                    "value": "t=1786440000,v1=771bca27e1647dfbe59ff5d8f32d9de69be246f6e604c05bddcbaefd8604ccb3"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"id\": \"evt_01K7QAE7Q3S6T9V5W8X1Y4Z6AC\",\n  \"type\": \"call.completed\",\n  \"created_at\": \"2026-08-10T09:16:38Z\",\n  \"data\": {\n    \"call\": {\n      \"id\": \"call_01K7Q9B4M8N3P6R2S5T7V9W1YA\",\n      \"object\": \"call\",\n      \"agent_id\": \"agt_01K7Q9F3K7M2N5P9R4T6V8W0XZ\",\n      \"to\": \"+919876543210\",\n      \"status\": \"completed\",\n      \"tier\": \"t3\",\n      \"ended_reason\": \"customer-ended-call\",\n      \"duration_secs\": 96,\n      \"cost_inr\": 6,\n      \"created_at\": \"2026-08-10T09:15:00Z\",\n      \"started_at\": \"2026-08-10T09:15:02Z\",\n      \"ended_at\": \"2026-08-10T09:16:38Z\",\n      \"recording_available\": true,\n      \"transcript_available\": true,\n      \"analysis\": {\n        \"summary\": \"Rohit ne order AC-88213 ki delivery confirm kar di. Kal shaam 6 baje ka slot theek hai, thoda late ho to bhi chalega. Koi complaint nahi.\",\n        \"success\": true,\n        \"data\": {\n          \"slot_confirmed\": \"कल शाम 6 बजे\",\n          \"address\": null\n        }\n      }\n    }\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{webhook_endpoint}}",
                  "host": [
                    "{{webhook_endpoint}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"received\": true\n}"
            }
          ],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Sign the request body exactly as the platform does:",
                  "//   X-Mirai-Signature: t=<unix>,v1=hex HMAC-SHA256(secret, \"<t>.<raw body>\")",
                  "const secret = pm.environment.get(\"webhook_secret\") || pm.collectionVariables.get(\"webhook_secret\");",
                  "if (!secret) {",
                  "  console.warn(\"set webhook_secret (your whsec_…) before sending this\");",
                  "}",
                  "const t = Math.floor(Date.now() / 1000);",
                  "const raw = pm.request.body ? pm.request.body.toString() : \"\";",
                  "const sig = CryptoJS.HmacSHA256(t + \".\" + raw, secret || \"\").toString(CryptoJS.enc.Hex);",
                  "pm.collectionVariables.set(\"webhook_signature\", `t=${t},v1=${sig}`);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"your endpoint answered 2xx\", () => pm.expect(pm.response.code).to.be.within(200, 299));"
                ]
              }
            }
          ]
        },
        {
          "name": "Send a signed test event to your endpoint (campaign.paused)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "X-Mirai-Signature",
                "value": "{{webhook_signature}}"
              },
              {
                "key": "User-Agent",
                "value": "MiraiVoice-Webhooks/2"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01K7QAE7Q3S6T9V5W8X1Y4Z6AD\",\n  \"type\": \"campaign.paused\",\n  \"created_at\": \"2026-08-11T07:40:11Z\",\n  \"data\": {\n    \"campaign\": {\n      \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n      \"object\": \"campaign\",\n      \"name\": \"Order confirmations — Mumbai batch\",\n      \"status\": \"paused\",\n      \"counters\": {\n        \"pending\": 2,\n        \"completed\": 3\n      },\n      \"dialed\": 3,\n      \"pause_reason\": \"insufficient_balance\"\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{webhook_endpoint}}",
              "host": [
                "{{webhook_endpoint}}"
              ]
            },
            "description": "The campaign flavour of the same envelope: `data.campaign` instead of `data.call`, same header, same signature scheme, same 300-second replay window — one verification code path covers both.\n\n`pause_reason` appears only when the platform paused the campaign itself: `insufficient_balance` (wallet ran dry) or `budget_exhausted` (`budget_paise` reached). A pause you requested carries no reason.\n\nUses the same `webhook_endpoint` and `webhook_secret` variables as the previous request.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": [
            {
              "name": "200 OK — your endpoint accepted it",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "X-Mirai-Signature",
                    "value": "t=1786440611,v1=3f1c9a0b5d8e27461fa0c3b98e5d2761aa48c0f5b217d3e690c4a8517d2be043"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"id\": \"evt_01K7QAE7Q3S6T9V5W8X1Y4Z6AD\",\n  \"type\": \"campaign.paused\",\n  \"created_at\": \"2026-08-11T07:40:11Z\",\n  \"data\": {\n    \"campaign\": {\n      \"id\": \"cmp_01K7QAC5N9P4R7S3T6V8W2X4YB\",\n      \"object\": \"campaign\",\n      \"name\": \"Order confirmations — Mumbai batch\",\n      \"status\": \"paused\",\n      \"counters\": {\n        \"pending\": 2,\n        \"completed\": 3\n      },\n      \"dialed\": 3,\n      \"pause_reason\": \"insufficient_balance\"\n    }\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{webhook_endpoint}}",
                  "host": [
                    "{{webhook_endpoint}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "cookie": [],
              "body": "{\n  \"received\": true\n}"
            }
          ],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const secret = pm.environment.get(\"webhook_secret\") || pm.collectionVariables.get(\"webhook_secret\");",
                  "const t = Math.floor(Date.now() / 1000);",
                  "const raw = pm.request.body ? pm.request.body.toString() : \"\";",
                  "const sig = CryptoJS.HmacSHA256(t + \".\" + raw, secret || \"\").toString(CryptoJS.enc.Hex);",
                  "pm.collectionVariables.set(\"webhook_signature\", `t=${t},v1=${sig}`);"
                ]
              }
            }
          ]
        }
      ]
    }
  ]
}
