{"openapi":"3.1.0","info":{"title":"Park Graph Parking API","description":"Search, book, and manage parking at any Park Graph-connected lot.\nReal-time availability, dynamic pricing, and instant QR-free payments.\n\nMCP clients: connect to the hosted server at https://parkgraph.com/api/mcp,\nor run it locally with `npx -y parkgraph-mcp`.\n","version":"1.4.0","termsOfService":"https://parkgraph.com/legal/developers","contact":{"name":"Park Graph","url":"https://parkgraph.com/developers","email":"developers@parkgraph.com"}},"x-mcp":{"name":"com.parkgraph/mcp","url":"https://parkgraph.com/api/mcp","transport":"streamable-http","package":"parkgraph-mcp","package_version":"0.1.0","install":"npx -y parkgraph-mcp","docs":"https://parkgraph.com/developers/mcp"},"servers":[{"url":"https://parkgraph.com/api/v1","description":"Production"}],"paths":{"/lots/search":{"get":{"operationId":"searchParking","summary":"Search for available parking lots by coordinates","description":"Returns parking lots with real-time availability, pricing, and features using geospatial search. Works without an API key (60 requests a minute per IP); a key, if sent, must be valid.","security":[],"parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number"},"description":"Latitude of the search center","example":39.6403},{"name":"lng","in":"query","required":true,"schema":{"type":"number"},"description":"Longitude of the search center","example":-106.3742},{"name":"radius_km","in":"query","schema":{"type":"number","default":5},"description":"Search radius in kilometers"},{"name":"limit","in":"query","schema":{"type":"integer","default":10},"description":"Maximum results to return"}],"responses":{"200":{"description":"List of matching parking lots","content":{"application/json":{"schema":{"type":"object","required":["lots","summary","meta"],"properties":{"lots":{"type":"array","items":{"$ref":"#/components/schemas/Lot"}},"summary":{"type":"string","description":"One-line answer an assistant can quote. The MCP search_lots tool returns the same field.","example":"3 lots within 5 km. Top: Main Street Garage (0.4 km, $8/hr, 12 left); Center Lot (0.9 km, $6/hr, full); East Lot (2.1 km, $5/hr, 30 left)."},"meta":{"type":"object","required":["lat","lng","radius_km","count"],"properties":{"lat":{"type":"number"},"lng":{"type":"number"},"radius_km":{"type":"number"},"count":{"type":"integer"}}}}}}}},"401":{"description":"The API key sent is missing its Bearer prefix, invalid, revoked or expired. Send no Authorization header to search without a key."},"429":{"description":"RATE_LIMITED. Over 60 keyless requests a minute from this IP, or over the key's plan limit. Wait for Retry-After."}}}},"/health":{"get":{"operationId":"getHealth","summary":"Check API health","description":"Service status and the API version. Works without an API key. The SDK's health() calls this.","security":[],"responses":{"200":{"description":"The API is up (status ok or degraded)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"A health check failed (status error); error.message names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/reserve":{"post":{"operationId":"reserveParking","x-sdk-types":false,"summary":"Hold a space and get a one-tap pay link for the driver","description":"Holds a space at one lot at a frozen price and returns booking_url, the lot's pre-filled pay page. The driver confirms there with one tap and pays amount_due_cents. No API key needed; a pk_test_ key books sandbox lots. A Starter lot answers 403 with error.pay_url instead of a hold.","externalDocs":{"description":"How booking through an assistant works","url":"https://parkgraph.com/developers/openai-actions"},"security":[{},{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Required unless the body has idempotency_key (8-200 characters). Repeating a key returns the same hold.","schema":{"type":"string","minLength":8,"maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lot_id":{"type":"string","format":"uuid","description":"The lot to hold, from search results. Without it, intent picks the lot."},"duration_hours":{"type":"integer","enum":[1,2,4,8],"description":"Hours to book; the pay page opens on this length. Default 2."},"plate":{"type":"string","maxLength":20,"description":"Vehicle license plate, pre-filled on the pay page"},"agent_platform":{"type":"string","enum":["chatgpt","claude","gemini","grok","perplexity","copilot","custom"],"description":"The assistant booking. Recorded on the booking and the session."},"intent":{"type":"string","minLength":4,"maxLength":500,"description":"Plain-language request, used to find the lot when lot_id is omitted"},"idempotency_key":{"type":"string","minLength":8,"maxLength":200,"description":"For clients that can't send the Idempotency-Key header"}}}}}},"responses":{"200":{"description":"A hold with its pay link, or lot suggestions when no single lot matched. booking_url is signed with the hold and the assistant. The hold counts against the lot's capacity until the driver pays or it ends at hold.expires_at.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reservation"}}}},"403":{"description":"The lot takes payment at its QR page (error.pay_url), its owner turned this assistant off (AGENT_PLATFORM_OFF), or its owner turned off AI agent bookings for the lot (AGENT_BOOKING_OFF)"},"404":{"description":"Lot not found or not open for reservations"},"409":{"description":"lot_full. The lot is full right now; suggestions lists nearby lots."},"429":{"description":"RATE_LIMITED. Too many holds; wait for Retry-After."}}}},"/sessions":{"post":{"operationId":"createSession","summary":"Create a parking session","description":"Book a parking space at a specific lot. Returns session details with code and payment info.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["lot_id"],"properties":{"lot_id":{"type":"string","format":"uuid","description":"Parking lot UUID from search results"},"plate":{"type":"string","description":"Vehicle license plate","example":"CO-7891"},"test_card":{"type":"string","enum":["4242 4242 4242 4242","4000 0000 0000 0002","4000 0000 0000 3220"],"description":"Sandbox only (pk_test_ keys; a live key gets 400). The Stripe test card the authorization runs on, default 4242 4242 4242 4242. 4000 0000 0000 0002 answers 402 CARD_DECLINED; 4000 0000 0000 3220 creates the session with payment_status requires_action (3D Secure required)."}}}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"type":"object","required":["session"],"properties":{"session":{"$ref":"#/components/schemas/Session"}}}}}}}}},"/sessions/{sessionId}":{"get":{"operationId":"getSession","summary":"Get parking session by UUID","description":"Returns session details including status, timing, and rate information.","parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Session details","content":{"application/json":{"schema":{"type":"object","required":["session"],"properties":{"session":{"$ref":"#/components/schemas/SessionDetail"}}}}}}}}},"/sessions/end":{"post":{"operationId":"endSession","summary":"End a parking session","description":"Calculates final charges and processes payment. Returns receipt with duration and charges.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["session_id","session_code"],"properties":{"session_id":{"type":"string","description":"Session UUID"},"session_code":{"type":"string","description":"Session code for verification"}}}}}},"responses":{"200":{"description":"Session ended with receipt","content":{"application/json":{"schema":{"type":"object","required":["session"],"properties":{"session":{"$ref":"#/components/schemas/SessionReceipt"}}}}}}}}},"/sessions/{sessionId}/refund":{"post":{"operationId":"refundSession","summary":"Refund a parking session, in full or in part","description":"Refunds one of your sessions the way the dashboard's Refund button does, settled in Stripe: the driver is repaid, Park Graph's fee is returned in proportion and your share is pulled back. Never more than was charged. Needs a key with the sessions:write scope.","externalDocs":{"description":"Refund parameters, errors and retries","url":"https://parkgraph.com/developers/api-reference"},"parameters":[{"name":"sessionId","in":"path","required":true,"description":"A session UUID, or the sess_ id returned by /reserve/confirm.","schema":{"type":"string","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|sess_[A-Za-z0-9_-]{8,40})$"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Required for a partial refund (8-200 characters). Repeating it returns the same refund (replayed true) or finishes one that stopped part way, and never refunds twice.","schema":{"type":"string","minLength":8,"maxLength":200}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"amount_cents":{"type":"integer","minimum":1,"description":"Cents to give back. Omit for a full refund."},"reason":{"type":"string","enum":["requested_by_customer","duplicate","fraudulent"]}}}}}},"responses":{"200":{"description":"Refund settled in Stripe","content":{"application/json":{"schema":{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/SessionRefund"}}}}}},"400":{"description":"Invalid body, a partial refund without an Idempotency-Key, or AMOUNT_EXCEEDS_REFUNDABLE"},"404":{"description":"No such session on your account. Another owner's session answers 404 too."},"409":{"description":"ALREADY_REFUNDED (a full refund, which covers every payment on the session, was already made; nothing moves), SESSION_ACTIVE, NOTHING_TO_REFUND, IDEMPOTENCY_KEY_REUSED, PARTIAL_REFUND_HELD or PAYOUT_UNCONFIRMED"},"502":{"description":"REFUND_INCOMPLETE. Stripe didn't finish; nothing was refunded twice. Send the same request again to complete it."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key"}},"schemas":{"Lot":{"type":"object","required":["id","name","slug","address","city","state","latitude","longitude","lat","lng","total_spaces","lot_type","availability","pricing"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string","example":"Eagle County Airport Parking"},"slug":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"latitude":{"type":"number","description":"Lot latitude (WGS 84). Lots without a valid point are left out of results."},"longitude":{"type":"number","description":"Lot longitude (WGS 84)."},"lat":{"type":"number","description":"Same as latitude, for map libraries."},"lng":{"type":"number","description":"Same as longitude, for map libraries."},"total_spaces":{"type":"integer"},"lot_type":{"type":"string","enum":["surface","garage","underground","rooftop","valet","street"]},"distance_km":{"type":"number"},"verified":{"type":"boolean","description":"Whether the lot owner has completed property verification"},"availability":{"type":"object","required":["total_spaces","active_sessions","available_spaces","occupancy_pct","is_full"],"properties":{"total_spaces":{"type":"integer"},"active_sessions":{"type":"integer"},"available_spaces":{"type":"integer"},"occupancy_pct":{"type":"number"},"is_full":{"type":"boolean"}}},"pricing":{"type":["object","null"],"required":["base_rate","rate_unit","max_daily_rate","currency"],"properties":{"base_rate":{"type":"number"},"rate_unit":{"type":"string","enum":["hour","day","flat"]},"max_daily_rate":{"type":["number","null"]},"currency":{"type":"string"}}}}},"Health":{"type":"object","required":["status","version"],"properties":{"status":{"type":"string","enum":["ok","degraded","error"]},"version":{"type":"string","description":"The API (OpenAPI spec) version","example":"1.4.0"},"commit":{"type":"string"},"environment":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"services":{"type":"object","additionalProperties":{"type":"string"}},"error":{"$ref":"#/components/schemas/ErrorBody"}}},"ErrorBody":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"request_id":{"type":"string"}}},"Quote":{"type":"object","description":"The price the driver is charged, from the same engine as the driver page, reservations and agent tools. Amounts are integer cents (USD).","required":["duration_hours","currency","rate_unit","base_rate_cents","multiplier","effective_rate_cents","max_daily_cents","total_cents","capped_at_daily_max","surge"],"properties":{"duration_hours":{"type":"number"},"currency":{"type":"string","example":"usd"},"rate_unit":{"type":"string","enum":["hour","day","flat"],"description":"flat while a flat event price applies"},"base_rate_cents":{"type":"integer"},"multiplier":{"type":"number","description":"Multiplier applied to the base rate (1 when no surge applies)"},"effective_rate_cents":{"type":"integer"},"max_daily_cents":{"type":["integer","null"]},"total_cents":{"type":"integer"},"capped_at_daily_max":{"type":"boolean"},"surge":{"$ref":"#/components/schemas/Surge"}}},"Surge":{"type":["object","null"],"description":"Present while an event, Surge today or a dynamic rule raises the price (Pro and Enterprise lots only).","properties":{"active":{"type":"boolean"},"label":{"type":"string","description":"The owner's event name, or \"Surge active\"","example":"Game day"},"kind":{"type":"string","enum":["event","surge_today","busy_lot","peak","weekend","stacked"]},"multiplier":{"type":"number"},"event_price_cents":{"type":["integer","null"],"description":"The owner's flat event price, when that is what applies"},"bound":{"type":["string","null"],"enum":["floor","cap","daily_max",null],"description":"Which of the lot's limits held the price, if any"},"description":{"type":"string","example":"Game day · $18.00 per event"}}},"Reservation":{"x-sdk-types":false,"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["held","suggestions"]},"hold":{"type":"object","required":["id","expires_at","lot_name","reused","lot_id","duration_hours"],"properties":{"id":{"type":"string","format":"uuid"},"expires_at":{"type":"string","format":"date-time"},"lot_name":{"type":"string"},"reused":{"type":"boolean","description":"True when the Idempotency-Key matched an earlier hold"},"lot_id":{"type":"string","format":"uuid"},"lot_slug":{"type":["string","null"]},"duration_hours":{"type":"number"}}},"quote":{"$ref":"#/components/schemas/Quote"},"agent_platform":{"type":"string"},"agent_surcharge_cents":{"type":"integer","description":"The lot's agent surcharge, frozen in the hold (0 when none). Itemized on the pay page and the receipt."},"amount_due_cents":{"type":"integer","description":"What the driver pays at booking_url, quote.total_cents plus agent_surcharge_cents"},"booking_url":{"type":["string","null"],"format":"uri","description":"The lot's pay page, pre-filled and signed. Give it to the driver."},"suggestions":{"type":"array","items":{"type":"object"}}}},"Session":{"type":"object","required":["id","session_code","short_code","lot_id","status","started_at","rate","rate_unit","estimated_amount","quote","payment_intent_id","client_secret"],"properties":{"id":{"type":"string","format":"uuid"},"session_code":{"type":"string"},"short_code":{"type":"string"},"lot_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","completed","expired","cancelled"]},"started_at":{"type":"string","format":"date-time"},"rate":{"type":"number"},"rate_unit":{"type":"string"},"estimated_amount":{"type":"number"},"quote":{"$ref":"#/components/schemas/Quote"},"payment_intent_id":{"type":["string","null"]},"client_secret":{"type":["string","null"]}}},"SessionDetail":{"type":"object","required":["id","session_code","short_code","lot_id","vehicle_plate","status","started_at","ended_at","rate_at_start","rate_unit","payment_status"],"properties":{"id":{"type":"string","format":"uuid"},"session_code":{"type":"string"},"short_code":{"type":"string"},"lot_id":{"type":"string","format":"uuid"},"vehicle_plate":{"type":["string","null"]},"status":{"type":"string"},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"rate_at_start":{"type":"number"},"rate_unit":{"type":"string"},"effective_rate":{"type":"number"},"subtotal":{"type":["number","null"]},"platform_fee":{"type":["number","null"]},"owner_payout":{"type":["number","null"]},"payment_status":{"type":"string"},"expected_end":{"type":["string","null"],"format":"date-time"},"extended_count":{"type":"integer"},"source":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},"SessionRefund":{"type":"object","properties":{"session_id":{"type":"string","description":"The session's id (a UUID, or a sess_ id from /reserve/confirm)."},"type":{"type":"string","enum":["full","partial"]},"refunded_cents":{"type":"integer"},"fee_refunded_cents":{"type":"integer"},"owner_reversed_cents":{"type":"integer"},"replayed":{"type":"boolean"},"payments":{"type":"array","items":{"type":"object","properties":{"payment_intent_id":{"type":"string"},"kind":{"type":"string","enum":["session","extension"]},"amount_cents":{"type":"integer"},"fee_refunded_cents":{"type":"integer"},"owner_reversed_cents":{"type":"integer"},"refund_id":{"type":["string","null"]}}}}}},"SessionReceipt":{"type":"object","required":["id","status","started_at","ended_at","duration_hours","subtotal","platform_fee","owner_payout","payment_status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":"string","format":"date-time"},"duration_hours":{"type":"number"},"subtotal":{"type":"number"},"platform_fee":{"type":"number"},"owner_payout":{"type":"number"},"payment_status":{"type":"string"}}}}},"security":[{"bearerAuth":[]}]}