{
  "info": {
    "_postman_id": "a7c3e891-4b2d-4f6e-9c1a-8d5f2e704b31",
    "name": "Fly & Drive Connect API v1",
    "description": "Car rental (Drive) API for integrators.\n\n## Setup\n1. Import environment **Fly & Drive Connect — Sandbox**\n2. Set `apiKey` to your `fd_connect_test_*` key from [developer dashboard](https://developer.fly-and-drive.com/developer/dashboard?section=keys)\n3. Set `airport` to **SFO** (Golden Gate Drive sandbox, USD)\n4. Run **Discovery** → **Search drive** → **Create booking** → **Get booking** in order\n\n## OpenAPI\nImport or sync from: `https://api.fly-and-drive.com/openapi`\n\n## Auth\nCollection uses Bearer `{{apiKey}}`. Widget config uses query param `key`.\n\nConnect API v1 = Drive only.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Auto future dates if pickupDate looks like placeholder",
          "const pickup = pm.environment.get('pickupDate') || pm.collectionVariables.get('pickupDate');",
          "if (!pickup || pickup === '2026-09-01') {",
          "  const from = new Date();",
          "  from.setUTCDate(from.getUTCDate() + 30);",
          "  const to = new Date(from);",
          "  to.setUTCDate(to.getUTCDate() + 7);",
          "  const fromStr = from.toISOString().slice(0, 10);",
          "  const toStr = to.toISOString().slice(0, 10);",
          "  if (pm.environment.name) {",
          "    pm.environment.set('pickupDate', fromStr);",
          "    pm.environment.set('returnDate', toStr);",
          "  } else {",
          "    pm.collectionVariables.set('pickupDate', fromStr);",
          "    pm.collectionVariables.set('returnDate', toStr);",
          "  }",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Discovery",
      "item": [
        {
          "name": "API root (discovery)",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/json" }],
            "url": {
              "raw": "{{apiHost}}/",
              "host": ["{{apiHost}}"],
              "path": [""]
            },
            "description": "Public discovery JSON. Check `rateLimit.distributed` is true in production."
          },
          "response": []
        },
        {
          "name": "OpenAPI spec",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/yaml" }],
            "url": {
              "raw": "{{apiHost}}/openapi",
              "host": ["{{apiHost}}"],
              "path": ["openapi"]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Search",
      "item": [
        {
          "name": "Search drive offers",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status 200', () => pm.response.to.have.status(200));",
                  "if (pm.response.code === 200) {",
                  "  const json = pm.response.json();",
                  "  pm.test('Has offers', () => pm.expect(json.count ?? json.data?.length).to.be.above(0));",
                  "  const offer = json.data?.[0];",
                  "  if (offer?.id) {",
                  "    pm.environment.set('offerId', offer.id);",
                  "    pm.collectionVariables.set('offerId', offer.id);",
                  "    console.log('Saved offerId:', offer.id);",
                  "  }",
                  "  pm.test('Rate limit headers', () => {",
                  "    pm.expect(pm.response.headers.get('X-RateLimit-Limit')).to.eql('10');",
                  "  });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/json" }],
            "url": {
              "raw": "{{baseUrl}}/search/drive?airport={{airport}}&from={{pickupDate}}&to={{returnDate}}",
              "host": ["{{baseUrl}}"],
              "path": ["search", "drive"],
              "query": [
                { "key": "airport", "value": "{{airport}}" },
                { "key": "from", "value": "{{pickupDate}}" },
                { "key": "to", "value": "{{returnDate}}" }
              ]
            },
            "description": "Scope: `search:drive`. Sandbox keys return sandbox inventory only."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Bookings",
      "item": [
        {
          "name": "Create booking",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status 201', () => pm.response.to.have.status(201));",
                  "if (pm.response.code === 201) {",
                  "  const json = pm.response.json();",
                  "  if (json.bookingId) {",
                  "    pm.environment.set('bookingId', json.bookingId);",
                  "    pm.collectionVariables.set('bookingId', json.bookingId);",
                  "    console.log('Saved bookingId:', json.bookingId);",
                  "  }",
                  "  pm.test('Sandbox auto-paid', () => {",
                  "    if (json.environment === 'test') {",
                  "      pm.expect(json.status).to.be.oneOf(['pending_partner_approval']);",
                  "    }",
                  "  });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Accept", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{idempotencyKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"offerId\": \"{{offerId}}\",\n  \"airportIata\": \"{{airport}}\",\n  \"pickupDate\": \"{{pickupDate}}\",\n  \"returnDate\": \"{{returnDate}}\",\n  \"pickupTime\": \"10:00\",\n  \"returnTime\": \"10:00\",\n  \"customer\": {\n    \"firstName\": \"Postman\",\n    \"lastName\": \"Test\",\n    \"email\": \"postman-test@fly-and-drive.com\",\n    \"phone\": \"+33612345678\"\n  },\n  \"anytimeFlex\": { \"selected\": false }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/bookings",
              "host": ["{{baseUrl}}"],
              "path": ["bookings"]
            },
            "description": "Run **Search drive** first to populate `offerId`. Test keys skip Stripe (pending_partner_approval)."
          },
          "response": []
        },
        {
          "name": "Get booking",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status 200', () => pm.response.to.have.status(200));",
                  "if (pm.response.code === 200) {",
                  "  const json = pm.response.json();",
                  "  pm.test('Booking id matches', () => pm.expect(json.bookingId).to.eql(pm.environment.get('bookingId') || pm.collectionVariables.get('bookingId')));",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/json" }],
            "url": {
              "raw": "{{baseUrl}}/bookings/{{bookingId}}",
              "host": ["{{baseUrl}}"],
              "path": ["bookings", "{{bookingId}}"]
            }
          },
          "response": []
        },
        {
          "name": "Download voucher (PDF)",
          "request": {
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/pdf" }],
            "url": {
              "raw": "{{baseUrl}}/bookings/{{bookingId}}/voucher",
              "host": ["{{baseUrl}}"],
              "path": ["bookings", "{{bookingId}}", "voucher"]
            },
            "description": "Scope: `voucher`. Returns PDF once booking is **confirmed**. Expect 409 while pending."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Widget",
      "item": [
        {
          "name": "Widget config",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/json" }],
            "url": {
              "raw": "{{apiHost}}/widget-config?key={{apiKey}}",
              "host": ["{{apiHost}}"],
              "path": ["widget-config"],
              "query": [{ "key": "key", "value": "{{apiKey}}" }]
            },
            "description": "Browser widget theme. Uses query `key=` (not Bearer). IP allowlist not enforced."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Quick start (run folder)",
      "description": "Use Postman Collection Runner on this folder: Search → Create → Get",
      "item": [
        {
          "name": "1 — Search drive offers",
          "request": {
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/json" }],
            "url": {
              "raw": "{{baseUrl}}/search/drive?airport={{airport}}&from={{pickupDate}}&to={{returnDate}}",
              "host": ["{{baseUrl}}"],
              "path": ["search", "drive"],
              "query": [
                { "key": "airport", "value": "{{airport}}" },
                { "key": "from", "value": "{{pickupDate}}" },
                { "key": "to", "value": "{{returnDate}}" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const json = pm.response.json();",
                  "if (json.data?.[0]?.id) pm.environment.set('offerId', json.data[0].id);"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "2 — Create booking",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "postman-run-{{$timestamp}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"offerId\": \"{{offerId}}\",\n  \"airportIata\": \"{{airport}}\",\n  \"pickupDate\": \"{{pickupDate}}\",\n  \"returnDate\": \"{{returnDate}}\",\n  \"customer\": {\n    \"firstName\": \"Postman\",\n    \"lastName\": \"Runner\",\n    \"email\": \"postman-runner@fly-and-drive.com\",\n    \"phone\": \"+33612345678\"\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/bookings",
              "host": ["{{baseUrl}}"],
              "path": ["bookings"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const json = pm.response.json();",
                  "if (json.bookingId) pm.environment.set('bookingId', json.bookingId);"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "3 — Get booking status",
          "request": {
            "method": "GET",
            "header": [{ "key": "Accept", "value": "application/json" }],
            "url": {
              "raw": "{{baseUrl}}/bookings/{{bookingId}}",
              "host": ["{{baseUrl}}"],
              "path": ["bookings", "{{bookingId}}"]
            }
          },
          "response": []
        }
      ]
    }
  ]
}
