{
  "info": {
    "name": "Floee API v1",
    "description": "The Floee API v1 reference as a collection. Set the apiKey variable to a key from Settings, API keys (keys are shown once, when created). Every request inherits bearer auth from the collection.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "version": "v1"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://icebot.icebergaisolutions.com",
      "type": "string"
    },
    {
      "key": "apiKey",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Conversations",
      "item": [
        {
          "name": "List conversations  (GET /api/v1/conversations)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/conversations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "conversations"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50",
                  "description": "Page size, 1 to 100. Default 50.",
                  "disabled": true
                },
                {
                  "key": "before",
                  "value": "2026-09-20T08:00:00.000Z",
                  "description": "Cursor. Only conversations whose last message is older than this. Pass the last_message_at of the final row you received to fetch the next page.",
                  "disabled": true
                }
              ]
            },
            "description": "The key's agent's conversations on every channel, most recent activity first.\n\nAny valid API key for the agent."
          },
          "response": [
            {
              "name": "200 One page. hasMore says whether another page exists.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"6f1c2a90-3b7e-4d55-9a1e-2c8f0b7d4e11\",\n      \"channel\": \"whatsapp\",\n      \"external_user_id\": \"60123456789\",\n      \"status\": \"open\",\n      \"last_message_at\": \"2026-09-27T03:14:07.512Z\",\n      \"created_at\": \"2026-09-26T11:02:44.100Z\"\n    }\n  ],\n  \"hasMore\": false\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "List messages in a conversation  (GET /api/v1/conversations/:id/messages)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/conversations/:id/messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "conversations",
                ":id",
                "messages"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50",
                  "description": "Page size, 1 to 100. Default 50.",
                  "disabled": true
                },
                {
                  "key": "before",
                  "value": "2026-09-27T03:10:00.000Z",
                  "description": "Forward cursor, despite its name: only messages created after this instant. Pass the created_at of the last message you have to read what came next.",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "6f1c2a90-3b7e-4d55-9a1e-2c8f0b7d4e11",
                  "description": "The conversation id."
                }
              ]
            },
            "description": "The messages of one conversation, oldest first. Internal notes are never returned.\n\nAny valid API key for the agent."
          },
          "response": [
            {
              "name": "200 One page of messages, oldest first.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b\",\n      \"role\": \"user\",\n      \"content\": \"Ada nasi lemak hari ni?\",\n      \"channel\": \"whatsapp\",\n      \"created_at\": \"2026-09-27T03:13:58.004Z\"\n    },\n    {\n      \"id\": \"f2a3b4c5-d6e7-4f8a-9b0c-1d2e3f4a5b6c\",\n      \"role\": \"assistant\",\n      \"content\": \"Ada! Nasi lemak ayam goreng RM12.90. Nak order berapa?\",\n      \"channel\": \"whatsapp\",\n      \"created_at\": \"2026-09-27T03:14:07.512Z\"\n    }\n  ],\n  \"hasMore\": false\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "Start a script on a conversation  (POST /api/v1/conversations/:id/scripts)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/conversations/:id/scripts",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "conversations",
                ":id",
                "scripts"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "6f1c2a90-3b7e-4d55-9a1e-2c8f0b7d4e11",
                  "description": "The conversation id."
                }
              ]
            },
            "description": "Runs one of your scripts on a conversation from your own system, for example a pickup reminder after an order is packed.\n\nAny valid API key for the agent.\n\nA script sends real messages to a real customer. Only scripts you gave the API trigger when authoring them can be started here.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"script_id\": \"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 The script ran. messages is what the customer was sent, in order; outcome is waiting when it expects a reply, otherw",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"started\": true,\n  \"messages\": [\n    \"Hi Aisyah, your order #1042 is ready for pickup. Reply 1 to confirm a time.\"\n  ],\n  \"outcome\": \"waiting\"\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        }
      ]
    },
    {
      "name": "Orders",
      "item": [
        {
          "name": "List orders  (GET /api/v1/orders)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/orders",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "orders"
              ],
              "query": [
                {
                  "key": "since",
                  "value": "2026-09-27T00:00:00.000Z",
                  "description": "Only orders created after this instant. Page forward by passing the last created_at you saw.",
                  "disabled": true
                },
                {
                  "key": "unacknowledged",
                  "value": "1",
                  "description": "Only orders the store has not yet acknowledged (no external_id).",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "50",
                  "description": "Page size, 1 to 100. Default 50.",
                  "disabled": true
                }
              ]
            },
            "description": "Orders taken in conversation, oldest first, for a store connector to create in the store.\n\nAny valid API key for the agent.\n\nUntil the store acknowledges an order (POST /api/v1/orders/{id}/status with its own order id), Floee holds those units back as reserved."
          },
          "response": [
            {
              "name": "200 One page of orders. Amounts are ringgit, not sen.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": [\n    {\n      \"id\": \"b2d4e6f8-1a3c-4e5f-8a9b-0c1d2e3f4a5b\",\n      \"order_number\": \"IB-1042\",\n      \"status\": \"pending_payment\",\n      \"channel\": \"voice\",\n      \"currency\": \"MYR\",\n      \"subtotal\": 25.8,\n      \"total\": 25.8,\n      \"created_at\": \"2026-09-27T03:20:11.000Z\",\n      \"external_id\": null,\n      \"external_status\": null,\n      \"customer\": {\n        \"name\": \"Aisyah\",\n        \"phone\": \"+60123456789\"\n      },\n      \"lines\": [\n        {\n          \"name\": \"Nasi lemak ayam goreng\",\n          \"quantity\": 2,\n          \"unit_price\": 12.9,\n          \"special_instructions\": \"Sambal on the side\",\n          \"product_id\": \"0a7d3e21-5c44-4b8e-9f10-6e2d8c9b1a37\",\n          \"variant_id\": null,\n          \"product_external_id\": \"101\",\n          \"variant_external_id\": null,\n          \"sku\": \"NL-AYAM\"\n        }\n      ]\n    }\n  ],\n  \"hasMore\": false\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "Report an order status  (POST /api/v1/orders/:id/status)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/orders/:id/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "orders",
                ":id",
                "status"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "b2d4e6f8-1a3c-4e5f-8a9b-0c1d2e3f4a5b",
                  "description": "The Floee order id from GET /api/v1/orders."
                }
              ]
            },
            "description": "The store acknowledges an order, or reports its progress. The first external_id releases Floee's reservation; cancelled or refunded gives stock back.\n\nRequires the `catalogue:sync` scope on the API key.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"external_id\": \"5531\",\n  \"external_status\": \"processing\",\n  \"status\": \"paid\",\n  \"source\": \"woocommerce\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 Saved. stock_events lists what the stock ledger did; replays are safe.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": {\n    \"id\": \"b2d4e6f8-1a3c-4e5f-8a9b-0c1d2e3f4a5b\",\n    \"status\": \"paid\",\n    \"external_id\": \"5531\",\n    \"stock_events\": [\n      \"store_ack\"\n    ]\n  }\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        }
      ]
    },
    {
      "name": "Catalogue",
      "item": [
        {
          "name": "Create or update products  (POST /api/v1/catalogue/products)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/catalogue/products",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "catalogue",
                "products"
              ]
            },
            "description": "Batch upsert from your store, keyed on your own product id. At most 200 products per request, 100 variants per product.\n\nRequires the `catalogue:sync` scope on the API key.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"source\": \"woocommerce\",\n  \"products\": [\n    {\n      \"external_id\": \"101\",\n      \"name\": \"Nasi lemak ayam goreng\",\n      \"price\": 12.9,\n      \"sku\": \"NL-AYAM\",\n      \"stock\": 40,\n      \"category\": \"Mains\"\n    },\n    {\n      \"external_id\": \"102\",\n      \"name\": \"Teh tarik\",\n      \"price\": 3.5,\n      \"variants\": [\n        {\n          \"external_id\": \"102-R\",\n          \"name\": \"Regular\",\n          \"price\": 3.5,\n          \"stock\": null\n        },\n        {\n          \"external_id\": \"102-L\",\n          \"name\": \"Large\",\n          \"price\": 4.5,\n          \"stock\": null\n        }\n      ]\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 Per-item results, even when some failed, so you can retry exactly those.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": [\n    {\n      \"external_id\": \"101\",\n      \"status\": \"created\",\n      \"id\": \"0a7d3e21-5c44-4b8e-9f10-6e2d8c9b1a37\"\n    },\n    {\n      \"external_id\": \"102\",\n      \"status\": \"updated\",\n      \"id\": \"1b8e4f32-6d55-4c9f-8a21-7f3e9d0c2b48\"\n    }\n  ],\n  \"created\": 1,\n  \"updated\": 1,\n  \"failed\": 0\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "Archive products  (POST /api/v1/catalogue/products/archive)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/catalogue/products/archive",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "catalogue",
                "products",
                "archive"
              ]
            },
            "description": "Your store deleted these products. They go off sale; the rows are never deleted, and a later upsert restores them. At most 500 ids.\n\nRequires the `catalogue:sync` scope on the API key.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"external_ids\": [\n    \"101\",\n    \"gid://shopify/Product/8812\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 How many were archived, and which ids Floee did not know.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"archived\": 1,\n  \"unknown\": [\n    \"gid://shopify/Product/8812\"\n  ]\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "Set stock levels  (POST /api/v1/catalogue/stock)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/catalogue/stock",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "catalogue",
                "stock"
              ]
            },
            "description": "Your store's stock numbers, in bulk (up to 1,000). The store owns stock: Floee reads these and never writes them back.\n\nRequires the `catalogue:sync` scope on the API key.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"source\": \"api\",\n  \"levels\": [\n    {\n      \"product_external_id\": \"101\",\n      \"quantity\": 38\n    },\n    {\n      \"product_external_id\": \"102\",\n      \"variant_external_id\": \"102-L\",\n      \"quantity\": null\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 Per-level results.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": [\n    {\n      \"product_external_id\": \"101\",\n      \"variant_external_id\": null,\n      \"status\": \"set\"\n    },\n    {\n      \"product_external_id\": \"102\",\n      \"variant_external_id\": \"102-L\",\n      \"status\": \"cleared\"\n    }\n  ],\n  \"applied\": 2,\n  \"failed\": 0\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        }
      ]
    },
    {
      "name": "Calls",
      "item": [
        {
          "name": "Place a phone call  (POST /api/v1/calls)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/calls",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "calls"
              ]
            },
            "description": "Rings a number from your workspace's default outbound number, with the key's agent speaking. For apps and backends that want the agent to call a customer.\n\nRequires the `calls:create` scope on the API key.\n\nBilled as voice minutes (400 credits a minute), metered from the post-call webhook exactly like any other phone call.\n\nThe do-not-call list and contact opt-outs are always enforced. Every call placed through the API is written to the audit log (api.call_placed) with the key that placed it.\n\nThe calls:create scope is off by default. Grant it on the key under Settings, API keys.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"+60123456789\",\n  \"variables\": {\n    \"customer_name\": \"Aisyah\",\n    \"order_number\": \"IB-1042\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 The provider accepted the dial and the phone is ringing. id may be null if the call row is written later by the post",
              "status": "Accepted",
              "code": 202,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": {\n    \"id\": \"c3a1f0e2-7b64-4d9a-8e21-5f0c9d8b7a66\",\n    \"conversation_id\": \"conv_01j9x7k2m4p6q8r0s2t4v6w8y0\",\n    \"status\": \"dialing\",\n    \"to\": \"+60123456789\",\n    \"from\": \"+60360432781\"\n  }\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "Get a call  (GET /api/v1/calls/:id)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/calls/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "calls",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "c3a1f0e2-7b64-4d9a-8e21-5f0c9d8b7a66",
                  "description": "The id returned by POST /api/v1/calls."
                }
              ]
            },
            "description": "The status of a call you placed: live while it is going, then its summary once it has ended. Poll it, or use the post-call data in your dashboard.\n\nAny valid API key for the agent.\n\nstatus moves from dialing to in_progress, then to a final value such as completed, failed or no_answer.\n\nWhile the call is going, live is { connected, elapsed_seconds, turns } (null fields when the live view is not available for that call); after it ends, live is null.\n\nrecording is available, pending (still being stored) or none. Fetch it with GET /api/v1/calls/[id]/recording."
          },
          "response": [
            {
              "name": "200 The call. summary is null until the call has ended and been processed.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": {\n    \"id\": \"c3a1f0e2-7b64-4d9a-8e21-5f0c9d8b7a66\",\n    \"status\": \"completed\",\n    \"to\": \"+60123456789\",\n    \"duration_seconds\": 94,\n    \"summary\": \"Customer confirmed pickup of order IB-1042 at 6pm.\",\n    \"created_at\": \"2026-09-27T04:02:10.000Z\",\n    \"runtime\": \"call_server\",\n    \"answered_by\": \"human\",\n    \"recording\": \"available\",\n    \"live\": null\n  }\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        },
        {
          "name": "Get a call's recording  (GET /api/v1/calls/:id/recording)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/calls/:id/recording",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "calls",
                ":id",
                "recording"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "c3a1f0e2-7b64-4d9a-8e21-5f0c9d8b7a66",
                  "description": "The id returned by POST /api/v1/calls."
                }
              ]
            },
            "description": "A link to the recording of a call you placed, valid for 5 minutes.\n\nRequires the `calls:recordings` scope on the API key.\n\nA recording is a person's voice: the calls:recordings scope is off by default. Grant it on the key under Settings, API keys.\n\nEvery fetch is written to the audit log (api.call_recording_fetched) with the key that fetched it."
          },
          "response": [
            {
              "name": "200 A signed link to the MP3. Download it within expires_in seconds.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"data\": {\n    \"id\": \"c3a1f0e2-7b64-4d9a-8e21-5f0c9d8b7a66\",\n    \"url\": \"https://…/recordings/voice/…/call.mp3?token=…\",\n    \"expires_in\": 300\n  }\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        }
      ]
    },
    {
      "name": "MCP",
      "item": [
        {
          "name": "MCP server  (POST /api/mcp)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/mcp",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "mcp"
              ]
            },
            "description": "Your workspace as a Model Context Protocol server (JSON-RPC 2.0 over HTTP). Point any AI assistant or MCP client at it with an API key.\n\nAny valid API key for the agent.\n\nA key sees only the tools its scopes allow. With no scopes the server is read-only.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"list_conversations\",\n    \"arguments\": {\n      \"limit\": 5,\n      \"status\": \"open\"\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 A JSON-RPC response. Tool output is JSON inside a text content block.",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": {\n    \"content\": [\n      {\n        \"type\": \"text\",\n        \"text\": \"{\\\"conversations\\\":[{\\\"id\\\":\\\"6f1c2a90-3b7e-4d55-9a1e-2c8f0b7d4e11\\\",\\\"channel\\\":\\\"whatsapp\\\",\\\"status\\\":\\\"open\\\"}]}\"\n      }\n    ]\n  }\n}",
              "_postman_previewlanguage": "json"
            }
          ]
        }
      ]
    }
  ]
}