Velomodo Controller API (1.0.0)

Download OpenAPI specification:

API for managing bike parking clusters, reservations, and pod access.

The Velomodo Controller API provides endpoints for:

  • Reservations: Create, manage, and cancel bike parking reservations
  • Holds: Temporary holds during checkout flow to prevent double-booking
  • Access: Validate and request access to pods (unlock, take picture)
  • Status: Query cluster and pod status
  • Admin: Administrative operations for gap management and status control

Authentication

All /api/* endpoints require authentication via Bearer token from Strata47. The token should be included in the Authorization header:

Authorization: Bearer <token>

Admin endpoints (/api/admin/*) additionally require membership in the Velomodo admin namespace.

Webhook Authentication

The /webhook endpoint uses API key authentication via the Authorization header (not Bearer format).

Public

Public endpoints (no authentication required)

Health check

Returns the health status of the Controller service.

Responses

Response Schema: application/json
status
required
string
Value: "healthy"
service
required
string
timestamp
required
string <date-time>

Response samples

Content type
application/json
{
  • "status": "healthy",
  • "service": "controller",
  • "timestamp": "2019-08-24T14:15:22Z"
}

Debug page

Returns an HTML debug page for a cluster. Only available when LOG_LEVEL=DEBUG is configured.

path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Responses

Response Schema: text/html
string

Strata47 webhook

Receives telemetry data from Strata47 data feed. Requires API key authentication via Authorization header.

Authorizations:
webhookApiKey
Request Body schema: application/json
required
Array of objects

Database insert IDs from Strata47

required
Array of objects (WebhookDataRecord)

Array of telemetry records

Responses

Response Schema: application/json
processed
required
boolean

Whether all records were processed successfully

required
Array of objects
totalRecords
required
integer
processedRecords
required
integer
totalEvents
required
integer
Array of objects
Array of objects

Request samples

Content type
application/json
{
  • "ids": [
    ],
  • "data": [
    ]
}

Response samples

Content type
application/json
{
  • "processed": true,
  • "clusters": [
    ],
  • "totalRecords": 0,
  • "processedRecords": 0,
  • "totalEvents": 0,
  • "clusterErrors": [
    ],
  • "parseErrors": [
    ]
}

Holds

Temporary holds during checkout flow

Create a hold

Creates a temporary hold on a time block for a specific pod. Holds prevent double-booking during the checkout flow and expire automatically.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Request Body schema: application/json
required
podId
required
string

Pod to hold

startTime
required
string <date-time>

Reservation start time

endTime
required
string <date-time>

Reservation end time

Responses

Response Schema: application/json
required
object (Hold)
required
Array of objects (StateTransitionEvent)

Request samples

Content type
application/json
{
  • "podId": "string",
  • "startTime": "2019-08-24T14:15:22Z",
  • "endTime": "2019-08-24T14:15:22Z"
}

Response samples

Content type
application/json
{
  • "hold": {
    },
  • "events": [
    ]
}

Confirm a hold

Confirms a hold, converting it to a reservation. The hold must belong to the authenticated user and not be expired.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

holdId
required
string <uuid>
Example: 01912345-6789-7abc-def0-123456789abc

Hold ID (UUIDv7)

Responses

Response Schema: application/json
required
object (Reservation)
required
Array of objects (StateTransitionEvent)

Response samples

Content type
application/json
{
  • "reservation": {
    },
  • "events": [
    ]
}

Release a hold

Releases a hold without converting to a reservation. The hold must belong to the authenticated user.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

holdId
required
string <uuid>
Example: 01912345-6789-7abc-def0-123456789abc

Hold ID (UUIDv7)

Responses

Response Schema: application/json
released
required
boolean
required
Array of objects (StateTransitionEvent)

Response samples

Content type
application/json
{
  • "released": true,
  • "events": [
    ]
}

Reservations

Reservation management

Create a reservation

Creates a reservation directly without a hold. Use this for immediate bookings when the hold flow is not needed.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Request Body schema: application/json
required
podId
required
string

Pod to reserve

startTime
required
string <date-time>

Reservation start time

endTime
required
string <date-time>

Reservation end time

Responses

Response Schema: application/json
required
object (Reservation)
required
Array of objects (StateTransitionEvent)

Request samples

Content type
application/json
{
  • "podId": "string",
  • "startTime": "2019-08-24T14:15:22Z",
  • "endTime": "2019-08-24T14:15:22Z"
}

Response samples

Content type
application/json
{
  • "reservation": {
    },
  • "events": [
    ]
}

Get a reservation

Retrieves a reservation by ID.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

reservationId
required
string <uuid>
Example: 01912345-6789-7abc-def0-123456789abc

Reservation ID (UUIDv7)

Responses

Response Schema: application/json
required
object (Reservation)

Response samples

Content type
application/json
{
  • "reservation": {
    }
}

Cancel a reservation

Cancels a pending reservation. Only reservations in pending status can be cancelled.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

reservationId
required
string <uuid>
Example: 01912345-6789-7abc-def0-123456789abc

Reservation ID (UUIDv7)

Responses

Response Schema: application/json
required
object (Reservation)
required
Array of objects (StateTransitionEvent)

Response samples

Content type
application/json
{
  • "reservation": {
    },
  • "events": [
    ]
}

Availability

Availability queries

Get availability

Gets availability blocks for a time range. Optionally filter by a specific pod.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

query Parameters
startTime
required
string <date-time>

Start of the time range (ISO 8601)

endTime
required
string <date-time>

End of the time range (ISO 8601)

podId
string

Filter by specific pod ID

Responses

Response Schema: application/json
required
Array of objects (AvailabilityBlock)
required
object (ReservationPolicy)

Response samples

Content type
application/json
{
  • "blocks": [
    ],
  • "policy": {
    }
}

Access

Pod access validation and commands

Validate access

Validates if the authenticated user can access a pod right now. Checks for an active reservation covering the current time.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Request Body schema: application/json
required
podId
required
string

Pod to validate access for

Responses

Response Schema: application/json
valid
required
boolean

Whether the user has valid access

required
Reservation (object) or null

Active reservation (null if none)

remainingMinutes
required
integer or null

Minutes remaining in reservation

reason
string

Reason if access is invalid

Request samples

Content type
application/json
{
  • "podId": "string"
}

Response samples

Content type
application/json
{
  • "valid": true,
  • "reservation": {
    },
  • "remainingMinutes": 0,
  • "reason": "string"
}

Request access (generic)

Generic access request endpoint. Validates reservation and sends the specified command to the pod.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Request Body schema: application/json
required
podId
required
string

Pod to access

command
required
string
Enum: "unlock_pod" "take_picture"

Command to execute

Responses

Response Schema: application/json
authorized
required
boolean

Whether access was granted

commandId
string <uuid>

Command ID (UUIDv7)

jwt
string

One-time JWT for the command

object

Request samples

Content type
application/json
{
  • "podId": "string",
  • "command": "unlock_pod"
}

Response samples

Content type
application/json
{
  • "authorized": true,
  • "commandId": "9e2dd63c-3478-489f-86d3-8c292a65a0aa",
  • "jwt": "string",
  • "reservation": {
    }
}

Unlock a pod

Requests to unlock a pod. Validates the user has an active reservation and sends the unlock command.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

podId
required
string
Example: pod-001-01

Pod device ID

Responses

Response Schema: application/json
authorized
required
boolean

Whether access was granted

commandId
string <uuid>

Command ID (UUIDv7)

jwt
string

One-time JWT for the command

object

Response samples

Content type
application/json
{
  • "authorized": true,
  • "commandId": "9e2dd63c-3478-489f-86d3-8c292a65a0aa",
  • "jwt": "string",
  • "reservation": {
    }
}

Take a picture

Requests to take a picture from a pod's camera. Validates the user has an active reservation and sends the capture command.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

podId
required
string
Example: pod-001-01

Pod device ID

Responses

Response Schema: application/json
authorized
required
boolean

Whether access was granted

commandId
string <uuid>

Command ID (UUIDv7)

jwt
string

One-time JWT for the command

object

Response samples

Content type
application/json
{
  • "authorized": true,
  • "commandId": "9e2dd63c-3478-489f-86d3-8c292a65a0aa",
  • "jwt": "string",
  • "reservation": {
    }
}

Status

Cluster and pod status queries

Get cluster status

Gets the current cluster status including all pod states.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Responses

Response Schema: application/json
required
object (ClusterState)
required
Array of objects (PodState)

Response samples

Content type
application/json
{
  • "cluster": {
    },
  • "pods": [
    ]
}

Get pod status

Gets the state of a specific pod.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

podId
required
string
Example: pod-001-01

Pod device ID

Responses

Response Schema: application/json
required
object (PodState)

Response samples

Content type
application/json
{
  • "pod": {
    }
}

Admin

Administrative operations (requires admin access)

Get gap status

Gets gap status for all devices in a cluster. Gaps occur when sequence numbers are missing from the telemetry stream.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Responses

Response Schema: application/json
required
Array of objects (DeviceGapStatus)

Response samples

Content type
application/json
{
  • "devices": [
    ]
}

Force process a gap

Forces processing of a gap for a specific device. Use when a gap is known to be unrecoverable (e.g., device reset). Requires a reason of at least 10 characters.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Request Body schema: application/json
required
deviceId
required
string

Device ID to force process gap for

reason
required
string >= 10 characters

Reason for forcing gap processing (min 10 characters)

Responses

Response Schema: application/json
skippedSequences
required
Array of integers

Sequences that were skipped

required
Array of objects (StateTransitionEvent)

Request samples

Content type
application/json
{
  • "deviceId": "string",
  • "reason": "stringstri"
}

Response samples

Content type
application/json
{
  • "skippedSequences": [
    ],
  • "events": [
    ]
}

Set cluster admin status

Sets the administrative status for a cluster. This affects whether the cluster accepts new reservations and access requests.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

Request Body schema: application/json
required
status
required
string
Enum: "online" "offline" "maintenance"

New admin status

reason
string

Reason for status change

Responses

Response Schema: application/json
success
required
boolean
required
object (ClusterState)
required
Array of objects (StateTransitionEvent)

Request samples

Content type
application/json
{
  • "status": "online",
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "success": true,
  • "cluster": {
    },
  • "events": [
    ]
}

Set pod admin status

Sets the administrative status for a specific pod. This affects whether the pod accepts reservations and access requests.

Authorizations:
bearerAuth
path Parameters
clusterId
required
string
Example: cluster-001

Cluster device ID

podId
required
string
Example: pod-001-01

Pod device ID

Request Body schema: application/json
required
status
required
string
Enum: "online" "offline" "maintenance"

New admin status

reason
string

Reason for status change

Responses

Response Schema: application/json
success
required
boolean
required
object (PodState)
required
Array of objects (StateTransitionEvent)

Request samples

Content type
application/json
{
  • "status": "online",
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "success": true,
  • "pod": {
    },
  • "events": [
    ]
}