REST API Reference
Complete reference for the ParkOnce REST API. Authenticate, manage vehicles, look up parking locations, and start/stop sessions programmatically.
🔒 Authentication
Most endpoints require a Bearer token in the Authorization header. Obtain a token via POST /api/auth/login.
📦 Request Format
All request bodies must be JSON. Set Content-Type: application/json on every POST/PUT request.
✅ Response Format
All responses return JSON with a success boolean. Errors include a message string explaining what went wrong.
💰 Pricing
ParkOnce adds a 2.5% service fee on top of the location's hourly rate. Costs are returned in cents (amount_cents).
Authorization: Bearer <token>. The token is returned by the login and register endpoints and does not expire automatically.
Authentication
6 endpointsCreates a new user account. Returns a JWT token and user object on success. Passwords must be at least 6 characters.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| string | required | User email address | |
| password | string | required | Min 6 characters |
| name | string | optional | Display name |
| phone | string | optional | Phone number |
Response
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": 42,
"email": "user@example.com",
"name": "Alex Kim",
"phone": "604-555-0100"
}
}
{
"success": false,
"message": "Account already exists. Try logging in."
}
Authenticates a user and returns a JWT bearer token. Store this token and include it on all authenticated requests.
Request Body
| Field | Type | Required |
|---|---|---|
| string | required | |
| password | string | required |
Response · 200
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": 42,
"email": "user@example.com",
"name": "Alex Kim",
"phone": "604-555-0100"
}
}
Returns the profile of the authenticated user. No request body needed.
Response · 200
{
"success": true,
"user": {
"id": 42,
"email": "user@example.com",
"name": "Alex Kim",
"phone": "604-555-0100"
}
}
Request Body (partial update)
| Field | Type |
|---|---|
| name | string |
| phone | string |
Response · 200
{
"success": true,
"user": { "id": 42, "name": "New Name", ... }
}
Sends a password reset link to the provided email address. Always responds with success regardless of whether the account exists (security by design).
Request Body
| Field | Type | Required |
|---|---|---|
| string | required |
Response · 200
{
"success": true,
"message": "If an account exists for that email, we've sent a reset link."
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | required | Token from reset email link |
| password | string | required | New password (min 6 chars) |
Response · 200
{
"success": true,
"message": "Password updated. You can now log in."
}
Locations
4 endpointsReturns all active parking locations with lat/lng coordinates and real-time availability percentages. Used to populate the map view.
Response · 200
{
"success": true,
"locations": [
{
"id": 1,
"location_code": "VAN-1042",
"name": "Robson St Lot",
"address": "800 Robson St, Vancouver",
"rate_cents_per_hour": 300,
"max_duration_minutes": 120,
"lat": 49.2827,
"lng": -123.1207,
"availability_pct": 62,
"provider_name": "PayByPhone",
"provider_slug": "paybyphone"
}
]
}
Looks up a parking location by its location code (found on meters or parking signs).
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| code | string | required | Location code from the meter/sign |
Response · 200
{
"success": true,
"location": {
"id": 1,
"location_code": "VAN-1042",
"name": "Robson St Lot",
"address": "800 Robson St",
"rate_cents_per_hour": 300,
"max_duration_minutes": 120,
"provider_name": "PayByPhone",
"provider_slug": "paybyphone"
}
}
Returns available parking duration options and their costs for a given location. For PayByPhone and Passport locations, rates come directly from the provider's live API. For all other providers, durations are derived from the location's hourly rate.
Path Parameters
| Param | Type | Description |
|---|---|---|
| code | string | Location code |
Response · 200
{
"success": true,
"rate_options": [
{
"duration_minutes": 30,
"label": "30 min",
"amount_cents": 154,
"rate_option_id": "7001-30"
}
],
"location": { "name": "Robson St Lot", ... },
"source": "live"
}
Returns time limits, no-return rules, and other parking restrictions for a location.
Response · 200
{
"success": true,
"restrictions": {
"max_stay_minutes": 120,
"no_return_minutes": null,
"time_limit_enforced": true
}
}
Parking Sessions
5 endpointsStarts a parking session at a given location. Charges the user's default payment method. ParkOnce routes the transaction to the appropriate provider (PayByPhone, Passport, INRIX, or stub). A 2.5% service fee is added to the base rate. The user must have a saved payment method before starting a session.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| location_code | string | required | Location code from the sign/meter |
| vehicle_id | integer | optional* | ID of a saved vehicle |
| license_plate | string | optional* | One-time plate if no vehicle_id |
| duration_minutes | integer | required | Desired parking duration |
| rate_option_id | string | optional | Rate option ID from /rate-options |
* Either vehicle_id or license_plate is required.
Response · 200
{
"success": true,
"session": {
"id": 501,
"status": "active",
"start_time": "2026-04-21T14:00:00Z",
"end_time": "2026-04-21T15:00:00Z",
"duration_minutes": 60,
"amount_cents": 308,
"base_amount_cents": 300,
"fee_cents": 8,
"provider_session_id": "pbp-1713700800000-a1b2c3",
"provider_mode": "sandbox"
}
}
Returns the user's currently active parking session, if any. Returns null for session if no active session exists.
Response · 200
{
"success": true,
"session": {
"id": 501,
"status": "active",
"end_time": "2026-04-21T15:00:00Z",
"location_name": "Robson St Lot",
"license_plate": "ABC1234"
}
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| duration_minutes | integer | required | Additional minutes to add |
Response · 200
{
"success": true,
"session": {
"id": 501,
"end_time": "2026-04-21T16:00:00Z",
"duration_minutes": 120,
"amount_cents": 616
}
}
Stops an active parking session before its scheduled end time. No request body required.
Response · 200
{
"success": true,
"session": {
"id": 501,
"status": "stopped",
"stopped_at": "2026-04-21T14:42:00Z"
}
}
Returns a paginated list of the user's completed and stopped parking sessions, newest first.
Response · 200
{
"success": true,
"sessions": [
{
"id": 498,
"status": "completed",
"start_time": "2026-04-20T10:00:00Z",
"end_time": "2026-04-20T11:00:00Z",
"duration_minutes": 60,
"amount_cents": 308,
"location_name": "Robson St Lot",
"license_plate": "ABC1234"
}
]
}
Vehicles
4 endpointsResponse · 200
{
"success": true,
"vehicles": [
{
"id": 7,
"license_plate": "ABC1234",
"province": "BC",
"make": "Toyota",
"model": "Corolla",
"color": "Silver",
"nickname": "Daily Driver",
"is_default": true
}
]
}
Request Body
| Field | Type | Required |
|---|---|---|
| license_plate | string | required |
| province | string | optional |
| make | string | optional |
| model | string | optional |
| color | string | optional |
| nickname | string | optional |
| is_default | boolean | optional |
Response · 200
{
"success": true,
"vehicle": { "id": 8, "license_plate": "XYZ9999", ... }
}
Sets the specified vehicle as the user's default. Unsets all other vehicles. No request body needed.
Response · 200
{
"success": true,
"vehicle": { "id": 7, "is_default": true, ... }
}
Deletes a saved vehicle. Will fail if the vehicle has an active parking session.
Response · 200
{ "success": true }
Payment Methods
3 endpointsResponse · 200
{
"success": true,
"payment_methods": [
{
"id": 3,
"card_last4": "4242",
"card_brand": "visa",
"is_default": true
}
]
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| card_last4 | string | required | Last 4 digits of card |
| card_brand | string | optional | e.g. "visa", "mastercard" |
| is_default | boolean | optional | Set as default |
Response · 200
{
"success": true,
"payment_method": { "id": 4, "card_last4": "1234", ... }
}
Response · 200
{ "success": true }
Providers
2 endpointsResponse · 200
{
"success": true,
"providers": [
{
"id": 1,
"name": "PayByPhone",
"slug": "paybyphone",
"logo_emoji": "📱",
"is_active": true
}
]
}
Returns the live connection status for each provider integration (PayByPhone, Passport, INRIX). Useful for diagnosing provider availability.
Response · 200
{
"success": true,
"providers": {
"paybyphone": { "status": "live", "mode": "sandbox" },
"passport": { "status": "live", "mode": "sandbox" },
"inrix": { "status": "live", "mode": "sandbox" }
}
}