# Stowbo · Guest

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## GET /client/stowbo/init

**Get app launch data**

`operationId: StowboClientController_init`

Who the caller is and whether they are a host — the single call an app makes on launch to decide which surface to show.

#### Signature

```http
GET /client/stowbo/init () -> Identity and host status
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/host/me`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Identity and host status |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/bookings/{bookingId}/delegations

**List the hand-offs on my booking**

`operationId: StowboClientController_myDelegations`

Every hand-off on the booking, newest first, with its items and state.

#### Signature

```http
GET /client/stowbo/bookings/{bookingId}/delegations (bookingId: string) -> Hand-offs
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Hand-offs |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/bookings/{bookingId}/delegations

**Hand my pickup to someone else**

`operationId: StowboClientController_createDelegation`

Creates a hand-off for some of my items: its own pickup code and pass link, sent to the collector by SMS and/or email. The owner gets the link back too, to share it themselves. Each item must be on the booking, still in custody, and not already on another live hand-off.

#### Signature

```http
POST /client/stowbo/bookings/{bookingId}/delegations (bookingId: string, body) -> The hand-off, with `passUrl`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |
| `400` | ITEMS_REQUIRED | Pick at least one item to hand off | `items` is empty. | — |
| `409` | BOOKING_NOT_ACTIVE | This booking is no longer active | Cancelled, no-show, settled or completed. | — |

Plus the standard platform errors: `429`, `500`.

#### See also

- `GET /client/stowbo/bookings/{bookingId}/delegations`
- `DELETE /client/stowbo/delegations/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Request body

```json
{
  "items": [
    "ITM-4821"
  ],
  "name": "Sam Doe",
  "phone": "+15125550100",
  "verify": "qr_id"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The hand-off, with `passUrl` |
| `400` | Pick at least one item to hand off — `items` is empty. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `409` | This booking is no longer active — Cancelled, no-show, settled or completed. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/delegations/{id}/resend

**Re-send a pickup pass**

`operationId: StowboClientController_resendDelegation`

Issues a new pass link and sends it to the collector. The old link stops working.

#### Signature

```http
POST /client/stowbo/delegations/{id}/resend (id: string) -> The hand-off with its new `passUrl`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | HANDOFF_NOT_FOUND | Hand-off not found | No hand-off has that id. | — |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |
| `409` | HANDOFF_NOT_ACTIVE | This hand-off is no longer active | Completed, revoked or expired. | — |

Plus the standard platform errors: `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Hand-off (`stowbo_pickup_delegation`) sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The hand-off with its new `passUrl` |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Hand-off not found — No hand-off has that id. |
| `409` | This hand-off is no longer active — Completed, revoked or expired. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /client/stowbo/delegations/{id}

**Cancel a hand-off**

`operationId: StowboClientController_revokeDelegation`

Revokes the hand-off: the items go back to my own pickup code and the collector is told. Revoking one already revoked returns it unchanged.

#### Signature

```http
DELETE /client/stowbo/delegations/{id} (id: string) -> The hand-off
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | HANDOFF_NOT_FOUND | Hand-off not found | No hand-off has that id. | — |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |
| `409` | ALREADY_COLLECTED | Already collected | The items were already handed over. | — |

Plus the standard platform errors: `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Hand-off (`stowbo_pickup_delegation`) sk. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The hand-off |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Hand-off not found — No hand-off has that id. |
| `409` | Already collected — The items were already handed over. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/my-pickups

**List passes addressed to me**

`operationId: StowboClientController_myPickups`

Hand-offs where the signed-in customer is the collector, matched by their email or phone — the same pass as the link, in the app. Active passes first. Never the owner's identity.

#### Signature

```http
GET /client/stowbo/my-pickups () -> Passes (as `GET /client/stowbo/pickup/{token}`)
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Passes (as `GET /client/stowbo/pickup/{token}`) |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/checkout/quote

**Price a cart**

`operationId: StowboClientController_quote`

Server-computed breakdown for a whole cart. One call can mix a two-hour parking bay and a seven-day box, each priced on its own listing's unit and tax jurisdiction.

Discounts come off the goods **before** fees and tax and are allocated per line, so per-jurisdiction tax stays correct. Pass `discountCode` for a promo; auto-apply discounts are added on their own.

No side effects and nothing is thrown: a bad code and a line that is short on capacity both come back as reported problems, so the page can say which. `bookable` tells you whether the cart can proceed.

#### Signature

```http
POST /client/stowbo/checkout/quote (body) -> subtotal, serviceFee, protectionFee, tax, taxLines, total, bookable
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Read-only — nothing is held or charged.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `POST /client/stowbo/checkout/hold`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The cart.

```json
{
  "lines": [
    {
      "listingId": "LST-4821",
      "startDate": "2026-09-05",
      "endDate": "2026-09-12",
      "quantity": 1
    }
  ],
  "discountCode": "AUTUMN10"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | subtotal, serviceFee, protectionFee, tax, taxLines, total, bookable |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

Example response:

```json
{
  "subtotal": 8400,
  "serviceFee": 630,
  "protectionFee": 200,
  "tax": 742,
  "taxLines": [
    {
      "jurisdiction": "CA-SF",
      "amount": 742
    }
  ],
  "total": 9972,
  "bookable": true
}
```

## POST /client/stowbo/checkout/cart

**Save the cart**

`operationId: StowboClientController_saveCart`

Saves the cart and where the guest got to. **One open purchase per customer, updated in place** — closing the app loses nothing and reopening on another device picks up where they left off.

Saving claims **no capacity**: recording progress must not take a bay off the market for everyone else. Pass `step`, `returnTo` and `context` to record the client's own position in the flow.

#### Signature

```http
POST /client/stowbo/checkout/cart (body) -> The saved checkout
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Holds no capacity.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `409` | HOLD_IN_PROGRESS | A live hold is in progress — confirm or release it first | The customer already has a live hold. | Confirm it, or release it with `DELETE /client/stowbo/checkout/{checkoutId}`. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/checkout/resume`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The cart and the guest's position in the flow.

```json
{
  "lines": [
    {
      "listingId": "LST-4821",
      "startDate": "2026-09-05",
      "endDate": "2026-09-12",
      "quantity": 1
    }
  ],
  "step": "payment"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The saved checkout |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `409` | A live hold is in progress — confirm or release it first — The customer already has a live hold. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/checkout/resume

**Resume an unfinished purchase**

`operationId: StowboClientController_resume`

Returns the saved cart, **re-priced rather than replayed**: rates and tax may have moved, and a stale total is worse than no saved cart. `changed` says whether the price differs from what the guest last saw, so the UI can tell them.

A lapsed hold is not an error — the cart survives even though the claim on the capacity does not.

#### Signature

```http
GET /client/stowbo/checkout/resume () -> The re-priced cart, with a `changed` flag
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `PUT /client/stowbo/checkout/{checkoutId}/progress`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The re-priced cart, with a `changed` flag |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PUT /client/stowbo/checkout/{checkoutId}/progress

**Record checkout progress**

`operationId: StowboClientController_saveProgress`

Stamps the step the guest reached **without re-pricing** — for cheap, frequent position updates as they move through the flow.

#### Signature

```http
PUT /client/stowbo/checkout/{checkoutId}/progress (checkoutId: string, body) -> The result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | CHECKOUT_NOT_FOUND | Checkout not found | No checkout has that id. | Resume to get the current one. |
| `403` | NOT_YOUR_CHECKOUT | Not your checkout | The checkout belongs to another customer. | Check the id. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/checkout/cart`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `checkoutId` | path | string | yes | Checkout id. |

### Request body

The step.

```json
{
  "step": "payment"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The result |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your checkout — The checkout belongs to another customer. |
| `404` | Checkout not found — No checkout has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/checkout/hold

**Start checkout and hold the capacity**

`operationId: StowboClientController_startCheckout`

Takes the capacity off the market for a few minutes while the guest pays, so two people cannot both buy the last bay. Returns `holdId` and `expiresAt`.

**Nothing is charged here.** The hold expires on its own if the guest walks away — no cleanup call is required, though releasing early frees the space for someone else sooner.

#### Signature

```http
POST /client/stowbo/checkout/hold (body) -> The hold, with `holdId` and `expiresAt`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Holds capacity but charges nothing.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `409` | CART_UNAVAILABLE | Something in the cart went unavailable | A line can no longer be satisfied. | Re-quote and let the guest adjust. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `POST /client/stowbo/checkout/{checkoutId}/confirm`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The cart to hold.

```json
{
  "lines": [
    {
      "listingId": "LST-4821",
      "startDate": "2026-09-05",
      "endDate": "2026-09-12",
      "quantity": 1
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The hold, with `holdId` and `expiresAt` |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `409` | Something in the cart went unavailable — A line can no longer be satisfied. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

Example response:

```json
{
  "holdId": "HLD-4821",
  "checkoutId": "CHK-4821",
  "expiresAt": "2026-08-30T12:07:00.000Z"
}
```

## POST /client/stowbo/checkout/{checkoutId}/confirm

**Pay and confirm**

`operationId: StowboClientController_confirmCheckout`

Turns a hold into a booking and takes the money. **Captures by default** — the capacity is withheld from the moment it is confirmed, so the money should not be left contingent. Pass `captureNow: false` to authorise only.

A declined card leaves the hold intact until it expires rather than confirming an unpaid booking, so the guest can retry with another card without losing the space.

#### Signature

```http
POST /client/stowbo/checkout/{checkoutId}/confirm (checkoutId: string, body) -> The booking
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Charges the guest.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `402` | PAYMENT_FAILED | Payment failed — the hold is still live | The card was declined. | Retry with another method before the hold expires. |
| `409` | HOLD_EXPIRED_OR_CONFIRMED | Hold expired or already confirmed | The hold lapsed, or the checkout is already a booking. | Start a new hold. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `DELETE /client/stowbo/checkout/{checkoutId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `checkoutId` | path | string | yes | Checkout id. |

### Request body

The payment.

```json
{
  "paymentMethodId": "pm_1Abc123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The booking |
| `401` | Sign in required — No customer could be resolved from the token. |
| `402` | Payment failed — the hold is still live — The card was declined. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `409` | Hold expired or already confirmed — The hold lapsed, or the checkout is already a booking. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /client/stowbo/checkout/{checkoutId}

**Abandon checkout**

`operationId: StowboClientController_abandonCheckout`

Releases the held capacity and abandons the checkout. Optional — a hold expires by itself — but calling it returns the space to the market immediately.

#### Signature

```http
DELETE /client/stowbo/checkout/{checkoutId} (checkoutId: string) -> The result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | CHECKOUT_NOT_FOUND | Checkout not found | No checkout has that id. | Nothing to release. |
| `403` | NOT_YOUR_CHECKOUT | Not your checkout | The checkout belongs to someone else. | Check the id. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/checkout/hold`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `checkoutId` | path | string | yes | Checkout id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The result |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your checkout — The checkout belongs to someone else. |
| `404` | Checkout not found — No checkout has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/bookings

**List my bookings**

`operationId: StowboClientController_myBookings`

The caller's bookings.

#### Signature

```http
GET /client/stowbo/bookings (status?: string, page?: integer, pageSize?: integer) -> Bookings
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/bookings/{bookingId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `status` | query | string | — |  |
| `page` | query | integer | — |  |
| `pageSize` | query | integer | — | Default 100, max 500. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Bookings |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/bookings

**Book directly**

`operationId: StowboClientController_book`

Creates a booking without the hold-and-confirm flow — one order with a line per thing booked. Because it skips the hold, the capacity is only claimed at the moment of the call; prefer the checkout flow for anything a guest pays for interactively.

#### Signature

```http
POST /client/stowbo/bookings (body) -> The booking
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `400` | LINE_REQUIRED | At least one line is required | The booking has no lines. | Include what is being booked. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `POST /client/stowbo/checkout/hold`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The booking.

```json
{
  "lines": [
    {
      "listingId": "LST-4821",
      "startDate": "2026-09-05",
      "endDate": "2026-09-12",
      "quantity": 1
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The booking |
| `400` | At least one line is required — The booking has no lines. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/bookings/{bookingId}

**Get one of my bookings**

`operationId: StowboClientController_myBooking`

A booking with its access codes — what the guest shows or enters at the space.

#### Signature

```http
GET /client/stowbo/bookings/{bookingId} (bookingId: string) -> The booking with its codes
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Contains access codes — treat as sensitive.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `GET /client/stowbo/bookings/{bookingId}/host`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The booking with its codes |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/bookings/{bookingId}/host

**Get my booking's host**

`operationId: StowboClientController_bookingHost`

The host's contact card — name, email, phone — for a booking the caller made. Scoped to their own bookings, since it exposes a real person's contact details.

#### Signature

```http
GET /client/stowbo/bookings/{bookingId}/host (bookingId: string) -> The host contact card
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Personal contact details.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `GET /client/stowbo/host/bookings/{bookingId}/customer`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The host contact card |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/bookings/{bookingId}/request

**Ask the host to act**

`operationId: StowboClientController_requestBookingAction`

Raises a request against a booking — bring my things out, put them back, check me out. The host sees it and performs the move; the guest watches its state.

#### Signature

```http
POST /client/stowbo/bookings/{bookingId}/request (bookingId: string, body) -> The request
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/items/{itemId}/request`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Request body

What is being asked.

```json
{
  "type": "retrieval",
  "note": "Arriving around 3pm"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/my-items

**List everything I have booked**

`operationId: StowboClientController_myItems`

One entry per thing booked, from the **same records the host works from** — so guest and host cannot disagree about where something is.

`present` is the direction of the last movement, not whether the booking is over: being out at 2pm is lunch, not finished. Pass `active=true` for only what is still running.

#### Signature

```http
GET /client/stowbo/my-items (active?: boolean, booking?: string, page?: integer, pageSize?: integer) -> Booking items
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/my-stuff`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `active` | query | boolean | — | Only what is still running. |
| `booking` | query | string | — | Scope to one booking. |
| `page` | query | integer | — |  |
| `pageSize` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Booking items |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/my-stuff

**List what I have stored right now**

`operationId: StowboClientController_myStuff`

Only the things currently present at a space — the narrower "what have I actually left somewhere" view.

#### Signature

```http
GET /client/stowbo/my-stuff () -> Stored items
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/my-items`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Stored items |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/park

**Start a parking session**

`operationId: StowboClientController_startParking`

For a self-serve lot with no attendant: the guest scans the zone QR or follows an SMS link, taps start, and the meter runs. Opens an OPEN stay, already checked in.

**Self-serve spaces only** — an attended space is checked in by the host.

#### Signature

```http
POST /client/stowbo/park (body) -> The running session
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `400` | SPACE_ATTENDED | This space is attended — the host checks you in. | The listing is not self-serve. | The host will check you in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `POST /client/stowbo/park/{itemId}/stop`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

Where they are parking.

```json
{
  "listingId": "LST-4821",
  "plate": "7ABC123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The running session |
| `400` | This space is attended — the host checks you in. — The listing is not self-serve. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/park/{itemId}

**Get my running parking session**

`operationId: StowboClientController_parkingStatement`

What the session has cost so far. Read-only, and the figure keeps moving while the meter runs.

#### Signature

```http
GET /client/stowbo/park/{itemId} (itemId: string) -> The running statement
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | ITEM_NOT_FOUND | Booking item not found | No booking item has that id. | List the order's items. |
| `403` | NOT_YOUR_SESSION | Not your session | The session belongs to someone else. | Check the item id. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/park/{itemId}/stop`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `itemId` | path | string | yes | Booking **item** id — one thing booked, not the whole order. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The running statement |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your session — The session belongs to someone else. |
| `404` | Booking item not found — No booking item has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/park/{itemId}/stop

**Stop my parking session**

`operationId: StowboClientController_stopParking`

Closes the session and bills the time used. This is what ends the meter — leaving without calling it keeps the charge accruing.

#### Signature

```http
POST /client/stowbo/park/{itemId}/stop (itemId: string) -> The closed session and its charge
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Bills the guest for the time used.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | ITEM_NOT_FOUND | Booking item not found | No booking item has that id. | List the order's items. |
| `409` | SESSION_CLOSED | That session is already closed | It was already stopped. | Read the statement instead. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/park/{itemId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `itemId` | path | string | yes | Booking **item** id — one thing booked, not the whole order. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The closed session and its charge |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Booking item not found — No booking item has that id. |
| `409` | That session is already closed — It was already stopped. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/items/{itemId}/movement

**Record my own movement (self-serve)**

`operationId: StowboClientController_selfMovement`

The guest records their own in or out — taking tools out, putting them back. **Unmanned spaces only**; attended spaces stay host-driven, and calling this on one is refused.

A movement is not a check-out: the space stays held and nothing is billed.

#### Signature

```http
POST /client/stowbo/items/{itemId}/movement (itemId: string, body) -> The movement
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Never billable and never touches capacity.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | ITEM_NOT_FOUND | Booking item not found | No booking item has that id. | List the order's items. |
| `403` | NOT_YOUR_ITEM | Not your booking item | The item belongs to another customer. | Check the item id. |
| `400` | DIRECTION_INVALID | direction must be 'in' or 'out' | `direction` is missing or something else. | Send `in` or `out`. |
| `409` | ALREADY_THERE | That's already here / That's already out | The movement repeats the last direction. | Check the current state first. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/items/{itemId}/request`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `itemId` | path | string | yes | Booking **item** id — one thing booked, not the whole order. |

### Request body

The movement.

```json
{
  "direction": "out"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The movement |
| `400` | direction must be 'in' or 'out' — `direction` is missing or something else. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking item — The item belongs to another customer. |
| `404` | Booking item not found — No booking item has that id. |
| `409` | That's already here / That's already out — The movement repeats the last direction. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/items/{itemId}/request

**Request a retrieval or return (attended)**

`operationId: StowboClientController_requestMovement`

Raises a **tracked** request the guest can watch through `requested → preparing → ready → fulfilled`. The host performs the actual move.

For attended spaces; on a self-serve space the guest moves things themselves and the request is refused. One open request per item at a time.

#### Signature

```http
POST /client/stowbo/items/{itemId}/request (itemId: string, body) -> The request
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | ITEM_NOT_FOUND | Booking item not found | No booking item has that id. | List the order's items. |
| `403` | NOT_YOUR_ITEM | Not your booking item | The item belongs to another customer. | Check the item id. |
| `400` | SPACE_SELF_SERVE | This space is self-serve — move it yourself, no request needed. | The listing is unmanned. | Use `POST /client/stowbo/items/{itemId}/movement`. |
| `409` | REQUEST_OPEN | A request is already open on this item | An earlier request has not been closed. | Cancel it first. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `DELETE /client/stowbo/items/{itemId}/request`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `itemId` | path | string | yes | Booking **item** id — one thing booked, not the whole order. |

### Request body

What is being asked.

```json
{
  "type": "retrieval",
  "note": "Arriving around 3pm"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request |
| `400` | This space is self-serve — move it yourself, no request needed. — The listing is unmanned. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking item — The item belongs to another customer. |
| `404` | Booking item not found — No booking item has that id. |
| `409` | A request is already open on this item — An earlier request has not been closed. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /client/stowbo/items/{itemId}/request

**Cancel my open request**

`operationId: StowboClientController_cancelRequest`

Withdraws an open retrieval or return request. Worth doing if plans change — the host may already be preparing it.

#### Signature

```http
DELETE /client/stowbo/items/{itemId}/request (itemId: string) -> The result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | ITEM_NOT_FOUND | Booking item not found | No booking item has that id. | List the order's items. |
| `403` | NOT_YOUR_ITEM | Not your booking item | The item belongs to another customer. | Check the item id. |
| `400` | NO_OPEN_REQUEST | No open request on this item | Nothing is outstanding. | Nothing to cancel. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/items/{itemId}/request`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `itemId` | path | string | yes | Booking **item** id — one thing booked, not the whole order. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The result |
| `400` | No open request on this item — Nothing is outstanding. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking item — The item belongs to another customer. |
| `404` | Booking item not found — No booking item has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/bookings/{bookingId}/due

**Get what I owe or get back**

`operationId: StowboClientController_myBookingDue`

The one money question for the state I am about to enter: `action=current` (now, including any running meter) or `action=cancel` (if I cancel now — the fee and refund the cancel itself will use). Ask before cancelling.

#### Signature

```http
GET /client/stowbo/bookings/{bookingId}/due (bookingId: string, action?: string) -> The payment summary plus the answer for that action
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `PUT /client/stowbo/bookings/{bookingId}/cancel`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |
| `action` | query | "current" \| "cancel" | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The payment summary plus the answer for that action |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## PUT /client/stowbo/bookings/{bookingId}/cancel

**Cancel a booking**

`operationId: StowboClientController_cancel`

Cancels a booking — **only before anything has been handed over**. Once an item is checked in, the way out is to check it out, not to cancel, because the space has been used and the bill reflects that.

The refund and any cancellation fee follow the booking's cancellation policy — the same numbers `GET /client/stowbo/bookings/{bookingId}/due?action=cancel` shows. Held units are released and any uncaptured authorisation is voided.

#### Signature

```http
PUT /client/stowbo/bookings/{bookingId}/cancel (bookingId: string, body) -> The cancelled booking
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |
| `400` | ALREADY_CHECKED_IN | This is already checked in — check it out instead of cancelling | An item on the booking has been checked in. | Check it out. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `GET /client/stowbo/bookings/{bookingId}/due`
- `POST /client/stowbo/host/items/{itemId}/checkout`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Request body

Optional reason. Defaults to "Cancelled by guest".

```json
{
  "reason": "Plans changed"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The cancelled booking |
| `400` | This is already checked in — check it out instead of cancelling — An item on the booking has been checked in. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/bookings/{bookingId}/extend

**Extend a stay**

`operationId: StowboClientController_extend`

Moves the end of a stay later. Re-prices the space line over its new window, **refuses if the space is not free** for the added time (a later booking blocks it), and takes payment for the extra time **before** committing — pass `paymentMethodId` or a confirmed `paymentIntentId`.

A decline changes nothing: the booking keeps its original window.

#### Signature

```http
POST /client/stowbo/bookings/{bookingId}/extend (bookingId: string, body) -> The extended line, what was added, and the booking's new total and balance
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Charges before committing; a decline is a no-op.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |
| `400` | NOT_A_SPACE_LINE | Only a space line can be extended | The line is an add-on, not a space. | Extend the space line. |
| `409` | UNAVAILABLE | Cannot extend — the space is not free for that time (N of M available). | A later booking or a closure blocks the added time. The body carries `available` and, for a closure, `until: "blackout"`. | Try a shorter extension. |
| `402` | PAYMENT_REQUIRED | Extending adds <amount> <currency> — payment required. | The extra time costs more and no payment was sent. | — |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/bookings/{bookingId}/addons`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Request body

The new end and the payment.

```json
{
  "endDate": "2026-09-15",
  "paymentMethodId": "pm_1Abc123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The extended line, what was added, and the booking's new total and balance |
| `400` | Only a space line can be extended — The line is an add-on, not a space. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `402` | Extending adds <amount> <currency> — payment required. — The extra time costs more and no payment was sent. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `409` | Cannot extend — the space is not free for that time (N of M available). — A later booking or a closure blocks the added time. The body carries `available` and, for a closure, `until: "blackout"`. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/bookings/{bookingId}/addons

**Add an add-on to a booking**

`operationId: StowboClientController_addAddon`

Adds a priced add-on line — insurance, cleaning, an EV charge. Payment is taken **before** the add-on is attached; a decline adds nothing.

#### Signature

```http
POST /client/stowbo/bookings/{bookingId}/addons (bookingId: string, body) -> The updated booking
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Charges before attaching.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `POST /client/stowbo/bookings/{bookingId}/settle-payment`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Request body

The add-on and the payment.

```json
{
  "addonId": "ADD-3",
  "quantity": 1,
  "paymentMethodId": "pm_1Abc123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated booking |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/bookings/{bookingId}/settle-payment

**Settle an outstanding balance**

`operationId: StowboClientController_settlePayment`

Settles what is owed on a booking after an extension, an add-on, a damage fee, or a metered or deposit-first stay closing out. The app raises a Stripe sheet for the balance and hands the confirmed PaymentIntent here.

**Idempotent per intent** — replaying the same intent id settles once.

#### Signature

```http
POST /client/stowbo/bookings/{bookingId}/settle-payment (bookingId: string, body) -> The settlement result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Idempotent per payment intent.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |
| `400` | NOTHING_TO_PAY | Nothing to pay | The booking has no outstanding balance. | Read the booking first. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `GET /client/stowbo/pay/{bookingId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Request body

The confirmed intent.

```json
{
  "paymentIntentId": "pi_3Abc123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The settlement result |
| `400` | Nothing to pay — The booking has no outstanding balance. |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /client/stowbo/host/apply

**Apply to list space**

`operationId: StowboClientController_applyAsHost`

Enrols the caller in the `stowbo-host` CRM benefit. `formData` is validated against the benefit's own application form collection, so the fields required depend on how the benefit is configured rather than being fixed here.

#### Signature

```http
POST /client/stowbo/host/apply (body) -> The application
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `GET /client/stowbo/host/me`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The application.

```json
{
  "formData": {
    "businessName": "Acme Storage",
    "city": "San Francisco"
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The application |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/host/me

**Get my host application**

`operationId: StowboClientController_hostMe`

The caller's host application and where it stands.

#### Signature

```http
GET /client/stowbo/host/me () -> The application
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |

Plus the standard platform errors: `403`, `429`, `500`.

#### See also

- `POST /client/stowbo/host/apply`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The application |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /client/stowbo/bookings/{bookingId}/timeline

**Get my booking's timeline**

`operationId: StowboClientController_guestTimeline`

What happened to a booking, in order — check-ins, movements, charges, requests.

#### Signature

```http
GET /client/stowbo/bookings/{bookingId}/timeline (bookingId: string) -> The timeline
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | SIGN_IN_REQUIRED | Sign in required | No customer could be resolved from the token. | Sign in. |
| `404` | BOOKING_NOT_FOUND | Booking not found | No booking has that id. | List your bookings. |
| `403` | NOT_YOUR_BOOKING | Not your booking | The booking belongs to another customer. | Only the booking's customer can act on it. |

Plus the standard platform errors: `429`, `500`.

#### See also

- `GET /client/stowbo/host/bookings/{bookingId}/timeline`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `bookingId` | path | string | yes | Booking (order) id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The timeline |
| `401` | Sign in required — No customer could be resolved from the token. |
| `403` | Not your booking — The booking belongs to another customer. |
| `404` | Booking not found — No booking has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

