{
  "info": {
    "name": "DeskOS Aggregator Partner API",
    "_postman_id": "d5a9c1f0-9b3a-4e2b-8c44-aggregator0001",
    "description": "Collection for the DeskOS Aggregator Partner API (v1).\n\nSetup:\n1. Open the collection's Variables tab.\n2. Set `baseUrl` (provided by the DeskOS team), `client_id`, and `client_secret`.\n3. Run \"Authentication > Get access token\" first. It stores the bearer token in the `access_token` variable automatically; every other request uses it.\n4. Set `campus_id`, `meeting_room_id`, `space_id`, and `external_ref` to real values from the Discovery responses.\n\nAll requests except the token endpoint use collection-level Bearer auth ({{access_token}}).",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{access_token}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.deskos.net/api/v1/integrations/aggregator/v1",
      "type": "string"
    },
    {
      "key": "client_id",
      "value": "YOUR_CLIENT_ID",
      "type": "string"
    },
    {
      "key": "client_secret",
      "value": "YOUR_CLIENT_SECRET",
      "type": "string"
    },
    {
      "key": "access_token",
      "value": "",
      "type": "string"
    },
    {
      "key": "campus_id",
      "value": "12",
      "type": "string"
    },
    {
      "key": "meeting_room_id",
      "value": "5",
      "type": "string"
    },
    {
      "key": "space_id",
      "value": "9",
      "type": "string"
    },
    {
      "key": "external_ref",
      "value": "GF-DEMO-001",
      "type": "string"
    },
    {
      "key": "operator_id",
      "value": "1",
      "description": "Operator (organization) id, from GET /operators."
    },
    {
      "key": "inventory_id",
      "value": "5",
      "description": "Meeting room id or space id, for GET /availability."
    }
  ],
  "item": [
    {
      "name": "Authentication",
      "item": [
        {
          "name": "Get access token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const json = pm.response.json();",
                  "if (json && json.access_token) {",
                  "  pm.collectionVariables.set('access_token', json.access_token);",
                  "  console.log('Saved access_token (expires_in ' + json.expires_in + 's)');",
                  "}",
                  "pm.test('200 OK', function () { pm.response.to.have.status(200); });",
                  "pm.test('Has access_token', function () { pm.expect(json.access_token).to.be.a('string'); });"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "grant_type",
                  "value": "client_credentials",
                  "type": "text"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}",
                  "type": "text"
                },
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}",
                  "type": "text"
                },
                {
                  "key": "scope",
                  "value": "",
                  "description": "Optional: space-separated subset of granted scopes. Leave blank for all.",
                  "type": "text",
                  "disabled": true
                }
              ]
            },
            "url": {
              "raw": "{{baseUrl}}/oauth/token",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "oauth",
                "token"
              ]
            },
            "description": "Exchanges your client credentials for a short-lived bearer access token. The test script stores it in the `access_token` collection variable."
          }
        }
      ]
    },
    {
      "name": "Discovery",
      "item": [
        {
          "name": "List operators",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/operators",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "operators"
              ]
            },
            "description": "Every operator (organization) that has enabled your integration. Start here instead of hardcoding a default operator: the returned operator_id feeds GET /campuses?operator_id=. Scope: campuses:read"
          }
        },
        {
          "name": "List cities",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/cities",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cities"
              ]
            },
            "description": "Distinct cities where opted-in operators are active. Scope: cities:read"
          }
        },
        {
          "name": "List campuses",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/campuses?city=&operator_id=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "campuses"
              ],
              "query": [
                {
                  "key": "city",
                  "value": "",
                  "description": "Optional. Filter by city (case/space-insensitive)."
                },
                {
                  "key": "operator_id",
                  "value": "",
                  "description": "Optional. Filter by operator id, from GET /operators."
                },
                {
                  "key": "organization_id",
                  "value": "",
                  "disabled": true,
                  "description": "Alias for operator_id, kept for existing integrations."
                }
              ]
            },
            "description": "Campuses across all opted-in operators. Scope: campuses:read"
          }
        },
        {
          "name": "Get campus inventory",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/campuses/{{campus_id}}/inventory",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "campuses",
                "{{campus_id}}",
                "inventory"
              ]
            },
            "description": "Sellable inventory at a campus: meeting_rooms, seats (day passes), virtual_offices (listing only). Scope: inventory:read"
          }
        },
        {
          "name": "Check availability - day view",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/availability?inventory_id={{meeting_room_id}}&date=2026-07-01",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "availability"
              ],
              "query": [
                {
                  "key": "inventory_id",
                  "value": "{{meeting_room_id}}",
                  "description": "Required."
                },
                {
                  "key": "date",
                  "value": "2026-07-01",
                  "description": "Optional. YYYY-MM-DD."
                }
              ]
            },
            "description": "Omit `time` to get the full slot grid for the day plus the windows already taken. Best for rendering a date/time picker in one call. Scope: inventory:read"
          }
        },
        {
          "name": "Check availability - meeting room slot",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/availability?inventory_id={{inventory_id}}&date=2026-07-01&time=10:00&duration_minutes=60",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "availability"
              ],
              "query": [
                {
                  "key": "inventory_id",
                  "value": "{{inventory_id}}",
                  "description": "Required. Meeting room id or space id from the inventory response."
                },
                {
                  "key": "date",
                  "value": "2026-07-01",
                  "description": "Optional. YYYY-MM-DD. Defaults to today in the campus timezone."
                },
                {
                  "key": "time",
                  "value": "10:00",
                  "description": "Optional. HH:MM campus-local. Omit for a whole-day slot grid."
                },
                {
                  "key": "duration_minutes",
                  "value": "60",
                  "description": "Optional. Defaults to the room's minimum booking time."
                },
                {
                  "key": "end_time",
                  "value": "",
                  "disabled": true,
                  "description": "Optional. HH:MM. Use instead of duration_minutes."
                }
              ]
            },
            "description": "Standalone availability check. Reads only, reserves nothing. Returns available true/false, a machine-readable reason when false, and the price the booking would cost. Scope: inventory:read"
          }
        },
        {
          "name": "Check availability - day pass seats",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/availability?inventory_id={{space_id}}&date=2026-07-01&seats=2",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "availability"
              ],
              "query": [
                {
                  "key": "inventory_id",
                  "value": "{{space_id}}",
                  "description": "Required. Space id of a day-pass desk area."
                },
                {
                  "key": "date",
                  "value": "2026-07-01",
                  "description": "Optional. YYYY-MM-DD."
                },
                {
                  "key": "seats",
                  "value": "2",
                  "description": "Optional. Defaults to 1."
                }
              ]
            },
            "description": "Day-pass availability for a date, including live available_seats and the price for the requested seat count. Scope: inventory:read"
          }
        }
      ]
    },
    {
      "name": "Bookings",
      "item": [
        {
          "name": "Create booking - meeting room",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Default start/end to tomorrow 10:00-11:00 in the client's local timezone.",
                  "const d = new Date(); d.setDate(d.getDate() + 1);",
                  "const pad = (n) => String(n).padStart(2, '0');",
                  "const tzMin = -d.getTimezoneOffset();",
                  "const sign = tzMin >= 0 ? '+' : '-';",
                  "const off = pad(Math.floor(Math.abs(tzMin) / 60)) + ':' + pad(Math.abs(tzMin) % 60);",
                  "const day = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());",
                  "pm.collectionVariables.set('mr_start', day + 'T10:00:00' + sign + off);",
                  "pm.collectionVariables.set('mr_end', day + 'T11:00:00' + sign + off);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const json = pm.response.json();",
                  "pm.test('201 or 200', function () { pm.expect(pm.response.code).to.be.oneOf([200, 201]); });",
                  "if (json && json.external_ref) { pm.collectionVariables.set('external_ref', json.external_ref); }",
                  "console.log('Booking status: ' + (json && json.status));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_type\": \"meeting_room\",\n  \"campus_id\": {{campus_id}},\n  \"resource_id\": {{meeting_room_id}},\n  \"start_time\": \"{{mr_start}}\",\n  \"end_time\": \"{{mr_end}}\",\n  \"guest\": {\n    \"name\": \"Riya Sharma\",\n    \"email\": \"riya@example.com\",\n    \"phone\": \"+919990001111\"\n  },\n  \"external_ref\": \"{{external_ref}}\",\n  \"idempotency_key\": \"{{external_ref}}\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/bookings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "bookings"
              ]
            },
            "description": "Create a meeting-room booking. start_time/end_time are auto-set to tomorrow 10:00-11:00 by the pre-request script. Returns status 'confirmed' (auto) or 'pending_approval'. Scope: bookings:write"
          }
        },
        {
          "name": "Create booking - day pass",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Default the pass date to tomorrow (YYYY-MM-DD).",
                  "const d = new Date(); d.setDate(d.getDate() + 1);",
                  "const pad = (n) => String(n).padStart(2, '0');",
                  "const day = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());",
                  "pm.collectionVariables.set('dp_date', day);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const json = pm.response.json();",
                  "pm.test('201 or 200', function () { pm.expect(pm.response.code).to.be.oneOf([200, 201]); });",
                  "console.log('Booking status: ' + (json && json.status));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"booking_type\": \"day_pass\",\n  \"campus_id\": {{campus_id}},\n  \"resource_id\": {{space_id}},\n  \"date\": \"{{dp_date}}\",\n  \"seats\": 1,\n  \"guest\": {\n    \"name\": \"Amit Verma\",\n    \"email\": \"amit@example.com\"\n  },\n  \"external_ref\": \"GF-DP-001\",\n  \"idempotency_key\": \"GF-DP-001\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/bookings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "bookings"
              ]
            },
            "description": "Create a day-pass booking. date defaults to tomorrow via the pre-request script. resource_id is a space_id from the inventory 'seats' section. Scope: bookings:write"
          }
        },
        {
          "name": "Booking history",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/bookings?status=confirmed&date_from=2026-07-01&date_to=2026-07-31&page=1&page_size=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "bookings"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "confirmed",
                  "description": "Optional. Repeat or comma-separate: confirmed,cancelled."
                },
                {
                  "key": "date_from",
                  "value": "2026-07-01",
                  "description": "Optional. Service date (when the space is used)."
                },
                {
                  "key": "date_to",
                  "value": "2026-07-31",
                  "description": "Optional."
                },
                {
                  "key": "page",
                  "value": "1",
                  "description": "Optional. Default 1."
                },
                {
                  "key": "page_size",
                  "value": "50",
                  "description": "Optional. Default 50, maximum 200."
                },
                {
                  "key": "booking_type",
                  "value": "",
                  "disabled": true,
                  "description": "Optional. meeting_room and/or day_pass."
                },
                {
                  "key": "operator_id",
                  "value": "",
                  "disabled": true,
                  "description": "Optional. Restrict to one operator."
                },
                {
                  "key": "campus_id",
                  "value": "",
                  "disabled": true,
                  "description": "Optional. Restrict to one campus."
                },
                {
                  "key": "external_ref",
                  "value": "",
                  "disabled": true,
                  "description": "Optional. Look up one booking by your own reference."
                },
                {
                  "key": "created_from",
                  "value": "",
                  "disabled": true,
                  "description": "Optional. When the booking was pushed to DeskOS."
                },
                {
                  "key": "created_to",
                  "value": "",
                  "disabled": true,
                  "description": "Optional."
                }
              ]
            },
            "description": "Your own bookings, past and current, newest first. Paginated, filterable, and returns per-currency totals (gross, commission, net_payable) for the filtered set so month-end reconciliation is a single call. Scope: bookings:read or bookings:write"
          }
        },
        {
          "name": "Get booking",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/bookings/{{external_ref}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "bookings",
                "{{external_ref}}"
              ]
            },
            "description": "Fetch one booking by your external reference, with the full detail shape (operator, campus, inventory, guest, net_payable, approval timestamps). Use to poll the status of approval-mode bookings. Scope: bookings:read or bookings:write"
          }
        },
        {
          "name": "Cancel booking",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reason\": \"Customer cancelled\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/bookings/{{external_ref}}/cancel",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "bookings",
                "{{external_ref}}",
                "cancel"
              ]
            },
            "description": "Cancel a booking by your external reference. Works for pending and confirmed bookings. Scope: bookings:cancel"
          }
        }
      ]
    }
  ]
}
